Compare commits
152 commits
dev/duhowe
...
main
Author | SHA1 | Date | |
---|---|---|---|
f2386de422 | |||
80f8383860 | |||
bb71179a24 | |||
df06c54e70 | |||
a98d18c030 | |||
dc01926e3e | |||
6ad848e158 | |||
ea456cf121 | |||
442432ea15 | |||
fc85bdf314 | |||
a68b0d4f08 | |||
6742965bb8 | |||
131f5d2b32 | |||
7bfaad4592 | |||
c455418659 | |||
c79334ffbb | |||
2353349fe5 | |||
f6965aeb53 | |||
d5974f4c91 | |||
3fc82c2d4b | |||
305255c658 | |||
7db7ba1ac9 | |||
89c2b6d580 | |||
92643c1d34 | |||
e591d29000 | |||
88d58d313a | |||
87182554c8 | |||
5ff412fe6e | |||
30f7f2082b | |||
e6b871da47 | |||
f9c0f86705 | |||
86da0e041f | |||
a89b66f770 | |||
f9b97c4880 | |||
a74c37bbcd | |||
55dbda02a5 | |||
7aae2e9100 | |||
74d21afacf | |||
52b4bb760f | |||
b604117421 | |||
761203d95e | |||
4241d1c19e | |||
ab6ba9bdbb | |||
8826cc028b | |||
d3ca0e7878 | |||
726b428184 | |||
27e042b784 | |||
3667678df1 | |||
a7ce93a357 | |||
b90f3605a2 | |||
95cc7d9625 | |||
1cedac6a33 | |||
756fd444b1 | |||
5bc094e12a | |||
8560b4adfb | |||
fe26a6efc0 | |||
6d091f37b3 | |||
9aa4a115aa | |||
7b7dea009c | |||
a916a5d9de | |||
def1bdd693 | |||
9662bc6910 | |||
f63159db59 | |||
e10e1d8ef1 | |||
a2f26850c6 | |||
670ae2bd1c | |||
5cd9663269 | |||
8b8ad75024 | |||
6bf1507a6c | |||
5a23029dac | |||
284257a383 | |||
0d5af3fedc | |||
fd93c54ae3 | |||
c1d326693c | |||
2cf31ac72d | |||
02dd463b35 | |||
51c3011950 | |||
1c8b71b6e7 | |||
b3cc618af8 | |||
02ac246807 | |||
a89c3e2f85 | |||
462d8003a7 | |||
275cdcf63f | |||
08e36123b3 | |||
b036cab850 | |||
4a3e50cb16 | |||
0ab1529487 | |||
8dd317313b | |||
479ef264b2 | |||
dd5dbb2a40 | |||
bc4f410788 | |||
2b1468eaa2 | |||
e1c830dde0 | |||
84e7ec4f96 | |||
694c6b263f | |||
bd8bfa13bb | |||
c727762602 | |||
35ce8cc858 | |||
925b05a3b7 | |||
43ce9fda09 | |||
14d068f73b | |||
f7b5b5caf8 | |||
0b552e1ae8 | |||
6295c8cc45 | |||
3f1befb06e | |||
e5180fe880 | |||
703e349fd3 | |||
856081229f | |||
b2fd65c601 | |||
99b1190734 | |||
e5293b7814 | |||
85f067403d | |||
6657d2c3e5 | |||
37e8769b37 | |||
d869913cfd | |||
cf00ad7ad4 | |||
ba239026f3 | |||
d25ca26142 | |||
dacff61f88 | |||
c0574f5ece | |||
6939112820 | |||
856f8764ce | |||
3b3b72e9cf | |||
43297315ba | |||
75e2b5fae7 | |||
2d583fc860 | |||
5542e727d0 | |||
5fa379e707 | |||
09d0ac768a | |||
2be394f421 | |||
9708a75131 | |||
0f122ca290 | |||
c070be12d3 | |||
e21eba8932 | |||
3b7049c5b7 | |||
e75f848cf3 | |||
171e0a0242 | |||
f04fd089fe | |||
86ba1fc6c3 | |||
3afcd575df | |||
5deb332607 | |||
168d28b036 | |||
dc865529b5 | |||
591a67111e | |||
d26353bb32 | |||
431d51de4c | |||
fbd50af8af | |||
3b666b9517 | |||
184919fb24 | |||
01b5195275 | |||
dfda41074d | |||
26f4b0eacb |
|
@ -1,6 +1,7 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
|
|
2
.gitattributes
vendored
2
.gitattributes
vendored
|
@ -3,6 +3,8 @@
|
|||
###############################################################################
|
||||
* -text
|
||||
|
||||
*.inc linguist-language=cpp
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
|
|
19
.github/ISSUE_TEMPLATE/Bug_Report.yml
vendored
19
.github/ISSUE_TEMPLATE/Bug_Report.yml
vendored
|
@ -10,14 +10,21 @@ body:
|
|||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Windows Terminal version (or Windows build number)
|
||||
placeholder: "10.0.19042.0, 1.7.3651.0"
|
||||
label: Windows Terminal version
|
||||
placeholder: "1.7.3651.0"
|
||||
description: |
|
||||
If you are reporting an issue in Windows Terminal, you can find the version in the about dialog.
|
||||
|
||||
If you are reporting an issue with the Windows Console, please run `ver` or `[Environment]::OSVersion`.
|
||||
You can find the version in the about dialog, or by running `wt -v` at the commandline.
|
||||
validations:
|
||||
required: true
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Windows build number
|
||||
placeholder: "10.0.19042.0"
|
||||
description: |
|
||||
Please run `ver` or `[Environment]::OSVersion`.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
|
14
.github/actions/spelling/allow/allow.txt
vendored
14
.github/actions/spelling/allow/allow.txt
vendored
|
@ -1,11 +1,14 @@
|
|||
admins
|
||||
apc
|
||||
Apc
|
||||
bsd
|
||||
calt
|
||||
ccmp
|
||||
cybersecurity
|
||||
Apc
|
||||
changelog
|
||||
clickable
|
||||
clig
|
||||
copyable
|
||||
cybersecurity
|
||||
dalet
|
||||
dcs
|
||||
Dcs
|
||||
|
@ -29,19 +32,22 @@ hyperlink
|
|||
hyperlinking
|
||||
hyperlinks
|
||||
img
|
||||
inlined
|
||||
It'd
|
||||
kje
|
||||
liga
|
||||
lje
|
||||
locl
|
||||
lorem
|
||||
Llast
|
||||
Lmid
|
||||
locl
|
||||
lorem
|
||||
Lorigin
|
||||
maxed
|
||||
mkmk
|
||||
mnt
|
||||
mru
|
||||
nje
|
||||
noreply
|
||||
ogonek
|
||||
ok'd
|
||||
overlined
|
||||
|
|
13
.github/actions/spelling/allow/apis.txt
vendored
13
.github/actions/spelling/allow/apis.txt
vendored
|
@ -1,5 +1,7 @@
|
|||
ACCEPTFILES
|
||||
ACCESSDENIED
|
||||
acl
|
||||
aclapi
|
||||
alignas
|
||||
alignof
|
||||
APPLYTOSUBMENUS
|
||||
|
@ -9,6 +11,7 @@ BUILDBRANCH
|
|||
BUILDMSG
|
||||
BUILDNUMBER
|
||||
BYPOSITION
|
||||
BYCOMMAND
|
||||
charconv
|
||||
CLASSNOTAVAILABLE
|
||||
cmdletbinding
|
||||
|
@ -16,15 +19,18 @@ COLORPROPERTY
|
|||
colspan
|
||||
COMDLG
|
||||
comparand
|
||||
commandlinetoargv
|
||||
cstdint
|
||||
CXICON
|
||||
CYICON
|
||||
Dacl
|
||||
dataobject
|
||||
dcomp
|
||||
DERR
|
||||
dlldata
|
||||
DONTADDTORECENT
|
||||
DWORDLONG
|
||||
enumset
|
||||
environstrings
|
||||
EXPCMDFLAGS
|
||||
EXPCMDSTATE
|
||||
|
@ -84,6 +90,7 @@ LSHIFT
|
|||
MENUCOMMAND
|
||||
MENUDATA
|
||||
MENUINFO
|
||||
MENUITEMINFOW
|
||||
memicmp
|
||||
mptt
|
||||
mov
|
||||
|
@ -113,15 +120,19 @@ OSVERSIONINFOEXW
|
|||
otms
|
||||
OUTLINETEXTMETRICW
|
||||
overridable
|
||||
PACL
|
||||
PAGESCROLL
|
||||
PEXPLICIT
|
||||
PICKFOLDERS
|
||||
pmr
|
||||
ptstr
|
||||
rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
roundf
|
||||
RSHIFT
|
||||
SACL
|
||||
schandle
|
||||
semver
|
||||
serializer
|
||||
|
@ -158,6 +169,8 @@ toupper
|
|||
TTask
|
||||
TVal
|
||||
UChar
|
||||
UFIELD
|
||||
ULARGE
|
||||
UPDATEINIFILE
|
||||
userenv
|
||||
wcsstr
|
||||
|
|
8
.github/actions/spelling/allow/math.txt
vendored
8
.github/actions/spelling/allow/math.txt
vendored
|
@ -1,3 +1,11 @@
|
|||
atan
|
||||
CPrime
|
||||
HBar
|
||||
HPrime
|
||||
isnan
|
||||
LPrime
|
||||
LStep
|
||||
powf
|
||||
RSub
|
||||
sqrtf
|
||||
ULP
|
||||
|
|
5
.github/actions/spelling/allow/microsoft.txt
vendored
5
.github/actions/spelling/allow/microsoft.txt
vendored
|
@ -1,5 +1,6 @@
|
|||
ACLs
|
||||
ADMINS
|
||||
advapi
|
||||
altform
|
||||
altforms
|
||||
appendwttlogging
|
||||
|
@ -15,6 +16,7 @@ CPLs
|
|||
cpptools
|
||||
cppvsdbg
|
||||
CPRs
|
||||
cryptbase
|
||||
DACL
|
||||
DACLs
|
||||
diffs
|
||||
|
@ -24,7 +26,9 @@ DTDs
|
|||
DWINRT
|
||||
enablewttlogging
|
||||
Intelli
|
||||
IVisual
|
||||
LKG
|
||||
LOCKFILE
|
||||
Lxss
|
||||
mfcribbon
|
||||
microsoft
|
||||
|
@ -44,6 +48,7 @@ powershell
|
|||
propkey
|
||||
pscustomobject
|
||||
QWORD
|
||||
regedit
|
||||
robocopy
|
||||
SACLs
|
||||
sdkddkver
|
||||
|
|
2
.github/actions/spelling/allow/names.txt
vendored
2
.github/actions/spelling/allow/names.txt
vendored
|
@ -9,6 +9,7 @@ Diviness
|
|||
dsafa
|
||||
duhowett
|
||||
ekg
|
||||
eryksun
|
||||
ethanschoonover
|
||||
Firefox
|
||||
Gatta
|
||||
|
@ -53,6 +54,7 @@ oldnewthing
|
|||
opengl
|
||||
osgwiki
|
||||
pabhojwa
|
||||
panos
|
||||
paulcam
|
||||
pauldotknopf
|
||||
PGP
|
||||
|
|
2
.github/actions/spelling/excludes.txt
vendored
2
.github/actions/spelling/excludes.txt
vendored
|
@ -61,12 +61,14 @@ SUMS$
|
|||
^src/host/runft\.bat$
|
||||
^src/host/runut\.bat$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^src/renderer/atlas/
|
||||
^src/renderer/wddmcon/WddmConRenderer\.
|
||||
^src/terminal/adapter/ut_adapter/run\.bat$
|
||||
^src/terminal/parser/delfuzzpayload\.bat$
|
||||
^src/terminal/parser/ft_fuzzer/run\.bat$
|
||||
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
|
||||
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
|
||||
^src/terminal/parser/ut_parser/Base64Test.cpp$
|
||||
^src/terminal/parser/ut_parser/run\.bat$
|
||||
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
|
||||
^src/tools/lnkd/lnkd\.bat$
|
||||
|
|
17
.github/actions/spelling/expect/expect.txt
vendored
17
.github/actions/spelling/expect/expect.txt
vendored
|
@ -189,6 +189,7 @@ cacafire
|
|||
callee
|
||||
capslock
|
||||
CARETBLINKINGENABLED
|
||||
carlos
|
||||
CARRIAGERETURN
|
||||
cascadia
|
||||
cassert
|
||||
|
@ -507,6 +508,7 @@ dealloc
|
|||
Debian
|
||||
debolden
|
||||
debugtype
|
||||
DECAC
|
||||
DECALN
|
||||
DECANM
|
||||
DECAUPSS
|
||||
|
@ -557,6 +559,7 @@ DECSTR
|
|||
DECSWL
|
||||
DECTCEM
|
||||
Dedupe
|
||||
deduplicate
|
||||
deduplicated
|
||||
DEFAPP
|
||||
DEFAULTBACKGROUND
|
||||
|
@ -666,6 +669,7 @@ dwriteglyphrundescriptionclustermap
|
|||
dxgi
|
||||
dxgidwm
|
||||
dxinterop
|
||||
dxsm
|
||||
dxttbmp
|
||||
eachother
|
||||
eae
|
||||
|
@ -784,6 +788,7 @@ FINDSTRINGEXACT
|
|||
FINDUP
|
||||
FIter
|
||||
FIXEDCONVERTED
|
||||
FIXEDFILEINFO
|
||||
Flg
|
||||
flyout
|
||||
fmodern
|
||||
|
@ -934,6 +939,7 @@ GTP
|
|||
guc
|
||||
gui
|
||||
guidatom
|
||||
guiddef
|
||||
GValue
|
||||
GWL
|
||||
GWLP
|
||||
|
@ -1228,6 +1234,7 @@ KLF
|
|||
KLMNO
|
||||
KLMNOPQRST
|
||||
KLMNOPQRSTQQQQQ
|
||||
KPRIORITY
|
||||
KVM
|
||||
langid
|
||||
LANGUAGELIST
|
||||
|
@ -1530,6 +1537,7 @@ NOCOLOR
|
|||
NOCOMM
|
||||
NOCONTEXTHELP
|
||||
NOCOPYBITS
|
||||
nodefaultlib
|
||||
nodiscard
|
||||
NODUP
|
||||
noexcept
|
||||
|
@ -1638,6 +1646,7 @@ onecoreuapuuid
|
|||
onecoreuuid
|
||||
ONECOREWINDOWS
|
||||
onehalf
|
||||
oneseq
|
||||
ONLCR
|
||||
openbash
|
||||
opencode
|
||||
|
@ -1704,6 +1713,7 @@ pcch
|
|||
PCCHAR
|
||||
PCCONSOLE
|
||||
PCD
|
||||
pcg
|
||||
pch
|
||||
PCHAR
|
||||
PCIDLIST
|
||||
|
@ -1799,6 +1809,7 @@ POSX
|
|||
POSXSCROLL
|
||||
POSYSCROLL
|
||||
ppci
|
||||
PPEB
|
||||
ppf
|
||||
ppguid
|
||||
ppidl
|
||||
|
@ -1992,6 +2003,7 @@ resx
|
|||
retval
|
||||
rfa
|
||||
rfc
|
||||
rfid
|
||||
rftp
|
||||
rgb
|
||||
rgba
|
||||
|
@ -2017,6 +2029,7 @@ Rike
|
|||
RIPMSG
|
||||
RIS
|
||||
RMENU
|
||||
rng
|
||||
roadmap
|
||||
robomac
|
||||
roundtrip
|
||||
|
@ -2222,6 +2235,7 @@ STARTWPARMSW
|
|||
Statusline
|
||||
stdafx
|
||||
STDAPI
|
||||
stdc
|
||||
stdcall
|
||||
stdcpp
|
||||
stderr
|
||||
|
@ -2687,6 +2701,7 @@ WINDOWPOSCHANGING
|
|||
windowproc
|
||||
windowrect
|
||||
windowsapp
|
||||
windowsdeveloper
|
||||
windowsinternalstring
|
||||
WINDOWSIZE
|
||||
windowsx
|
||||
|
@ -2718,6 +2733,7 @@ wixproj
|
|||
wline
|
||||
wlinestream
|
||||
wmain
|
||||
wmemory
|
||||
WMSZ
|
||||
wnd
|
||||
WNDALLOC
|
||||
|
@ -2844,6 +2860,7 @@ YSize
|
|||
YSubstantial
|
||||
YVIRTUALSCREEN
|
||||
YWalk
|
||||
zamora
|
||||
ZCmd
|
||||
ZCtrl
|
||||
zsh
|
||||
|
|
2
.github/actions/spelling/expect/web.txt
vendored
2
.github/actions/spelling/expect/web.txt
vendored
|
@ -1,5 +1,7 @@
|
|||
http
|
||||
www
|
||||
easyrgb
|
||||
php
|
||||
ecma
|
||||
rapidtables
|
||||
WCAG
|
||||
|
|
|
@ -4,7 +4,7 @@ https://www\.itscj\.ipsj\.or\.jp/iso-ir/[-0-9]+\.pdf
|
|||
https://www\.vt100\.net/docs/[-a-zA-Z0-9#_\/.]*
|
||||
https://www.w3.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
https://(?:(?:www\.|)youtube\.com|youtu.be)/[-a-zA-Z0-9?&=]*
|
||||
https://(?:[a-z-]+\.|)github(?:usercontent|)\.com/[-a-zA-Z0-9?%&=_\/.]*
|
||||
https://(?:[a-z-]+\.|)github(?:usercontent|)\.com/[-a-zA-Z0-9?%&=_\/.+]*
|
||||
https://www.xfree86.org/[-a-zA-Z0-9?&=\/_#]*
|
||||
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
|
||||
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
|
||||
|
@ -24,3 +24,4 @@ VERIFY_ARE_EQUAL\(L"[^"]+"
|
|||
std::memory_order_[\w]+
|
||||
D2DERR_SHADER_COMPILE_FAILED
|
||||
TIL_FEATURE_[0-9A-Z_]+
|
||||
vcvars\w*
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"Microsoft.Net.Component.4.5.TargetingPack",
|
||||
"Microsoft.VisualStudio.Component.DiagnosticTools",
|
||||
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
|
||||
"Microsoft.VisualStudio.Component.Windows10SDK.19041",
|
||||
"Microsoft.VisualStudio.Component.Windows10SDK.22000",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
|
||||
"Microsoft.VisualStudio.Component.VC.CoreIde",
|
||||
"Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core",
|
||||
|
|
|
@ -99,15 +99,29 @@ If you don't have any additional info/context to add but would like to indicate
|
|||
|
||||
## Contributing fixes / features
|
||||
|
||||
For those able & willing to help fix issues and/or implement features ...
|
||||
If you're able & willing to help fix issues and/or implement features, we'd love your contribution!
|
||||
|
||||
The best place to start is the list of ["Easy Starter"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22+label%3A%22Easy+Starter%22+) issues. These are bugs or tasks that we on the team believe would be easier to implement for someone without any prior experience in the codebase. Once you're feeling more comfortable in the codebase, feel free to just use the ["Help Wanted"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22+) label, or just find an issue your interested in and hop in!
|
||||
|
||||
Generally, we categorize issues in the following way, which is largely derived from our old internal work tracking system:
|
||||
* ["Bugs"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Bug%22+) are parts of the Terminal & Console that are not quite working the right way. There's code to already support some scenario, but it's not quite working right. Fixing these is generally a matter of debugging the broken functionality and fixing the wrong code.
|
||||
* ["Tasks"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Task%22+) are usually new pieces of functionality that aren't yet implemented for the Terminal/Console. These are usually smaller features, which we believe
|
||||
- could be a single, atomic PR
|
||||
- Don't require much design consideration, or we've already written the spec for the larger feature they belong to.
|
||||
* ["Features"](https://github.com/microsoft/terminal/issues?q=is%3Aopen+is%3Aissue+label%3A%22Issue-Feature%22+) are larger pieces of new functionality. These are usually things we believe would require larger discussion of how they should be implemented, or they'll require some complicated new settings. They might just be features that are composed of many individual tasks. Often times, with features, we like to have a spec written before development work is started, to make sure we're all on the same page (see below).
|
||||
|
||||
Bugs and tasks are obviously the easiest to get started with, but don't feel afraid of features either! We've had some community members contribute some amazing "feature"-level work to the Terminal (albeit, with lots of discussion 😄).
|
||||
|
||||
|
||||
Often, we like to assign issues that generally belong to somebody's area of expertise to the team member that owns that area. This doesn't mean the community can't jump in -- they should reach out and have a chat with the assignee to see if it'd okay to take. If an issue's been assigned more than a month ago, there's a good chance it's fair game to try yourself.
|
||||
|
||||
### To Spec or not to Spec
|
||||
|
||||
Some issues/features may be quick and simple to describe and understand. For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Fork, Branch, and Create your PR", below.
|
||||
|
||||
Small issues that do not require a spec will be labelled Issue-Bug or Issue-Task.
|
||||
Small issues that do not require a spec will be labelled `Issue-Bug` or `Issue-Task`.
|
||||
|
||||
However, some issues/features will require careful thought & formal design before implementation. For these scenarios, we'll request that a spec is written and the associated issue will be labeled Issue-Feature.
|
||||
However, some issues/features will require careful thought & formal design before implementation. For these scenarios, we'll request that a spec is written and the associated issue will be labeled `Issue-Feature`. More often than not, we'll add such features to the ["Specification Tracker" project](https://github.com/microsoft/terminal/projects/1).
|
||||
|
||||
Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run.
|
||||
|
||||
|
@ -125,7 +139,7 @@ Team members will be happy to help review specs and guide them to completion.
|
|||
|
||||
### Help Wanted
|
||||
|
||||
Once the team have approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/terminal/labels/Help%20Wanted).
|
||||
Once the team has approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/terminal/labels/Help%20Wanted).
|
||||
|
||||
---
|
||||
|
||||
|
|
67
NOTICE.md
67
NOTICE.md
|
@ -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
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
|
||||
## 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,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
```
|
||||
|
||||
## \{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,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
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,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
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
|
||||
|
|
|
@ -400,6 +400,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests",
|
|||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-ms-win-core-synch-l1-2-0", "src\api-ms-win-core-synch-l1-2-0\api-ms-win-core-synch-l1-2-0.vcxproj", "{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererAtlas", "src\renderer\atlas\atlas.vcxproj", "{8222900C-8B6C-452A-91AC-BE95DB04B95F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
AuditMode|Any CPU = AuditMode|Any CPU
|
||||
|
@ -3339,6 +3341,46 @@ Global
|
|||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x64.Build.0 = Release|x64
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.ActiveCfg = Release|Win32
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.Build.0 = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x64.ActiveCfg = AuditMode|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x64.Build.0 = AuditMode|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x86.ActiveCfg = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.AuditMode|x86.Build.0 = AuditMode|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x64.Build.0 = Debug|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Debug|x86.Build.0 = Debug|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x64.Build.0 = Fuzzing|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Fuzzing|x86.Build.0 = Fuzzing|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.ActiveCfg = Release|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x64.Build.0 = Release|x64
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x86.ActiveCfg = Release|Win32
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -3438,6 +3480,7 @@ Global
|
|||
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
|
||||
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
|
||||
{8222900C-8B6C-452A-91AC-BE95DB04B95F} = {05500DEF-2294-41E3-AF9A-24E580B82836}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
|
||||
|
|
|
@ -214,7 +214,7 @@ resources useful and interesting:
|
|||
* Windows Terminal Launch: [Build 2019
|
||||
Session](https://www.youtube.com/watch?v=KMudkRcwjCw)
|
||||
* Run As Radio: [Show 645 - Windows Terminal with Richard
|
||||
Turner](http://www.runasradio.com/Shows/Show/645)
|
||||
Turner](https://www.runasradio.com/Shows/Show/645)
|
||||
* Azure Devops Podcast: [Episode 54 - Kayla Cinnamon and Rich Turner on DevOps
|
||||
on the Windows
|
||||
Terminal](http://azuredevopspodcast.clear-measure.com/kayla-cinnamon-and-rich-turner-on-devops-on-the-windows-terminal-team-episode-54)
|
||||
|
@ -289,6 +289,7 @@ If you would like to ask a question that you feel doesn't warrant an issue
|
|||
* You must [enable Developer Mode in the Windows Settings
|
||||
app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development)
|
||||
to locally install and run Windows Terminal
|
||||
* You must have [PowerShell 7 or later](https://github.com/PowerShell/PowerShell/releases/latest) installed
|
||||
* You must have the [Windows 10 1903
|
||||
SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk)
|
||||
installed
|
||||
|
|
|
@ -63,8 +63,14 @@
|
|||
Outputs="$(OpenConsoleCommonOutDir)\inc\TilFeatureStaging.h"
|
||||
DependsOnTargets="_GenerateBranchAndBrandingCache">
|
||||
<MakeDir Directories="$(OpenConsoleCommonOutDir)\inc" />
|
||||
<!-- This commandline is escaped like:
|
||||
|
||||
powershell -Command "&'$(SolutionDir)\tools\Generate-FeatureStagingHeader.ps1' -Path '%(FeatureFlagFile.FullPath)'' -Branding $(_WTBrandingName)"
|
||||
|
||||
which was the only way I could find to get it to obey spaces in the SolutionDir
|
||||
-->
|
||||
<Exec
|
||||
Command="powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy ByPass -Command "$(SolutionDir)\tools\Generate-FeatureStagingHeader.ps1" -Path "%(FeatureFlagFile.FullPath)" -Branding $(_WTBrandingName)"
|
||||
Command="powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy ByPass -Command "&'$(SolutionDir)\tools\Generate-FeatureStagingHeader.ps1' -Path '%(FeatureFlagFile.FullPath)' -Branding $(_WTBrandingName)""
|
||||
ConsoleToMsBuild="true"
|
||||
StandardOutputImportance="low">
|
||||
<Output TaskParameter="ConsoleOutput" ItemName="_FeatureFlagFileLines" />
|
||||
|
|
|
@ -22,7 +22,7 @@ Param(
|
|||
[Parameter(HelpMessage="Path to makeappx.exe")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\MakeAppx.exe"
|
||||
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x86\MakeAppx.exe"
|
||||
)
|
||||
|
||||
If ($null -Eq (Get-Item $MakeAppxPath -EA:SilentlyContinue)) {
|
||||
|
|
|
@ -8,7 +8,7 @@ Param(
|
|||
[Parameter(HelpMessage="Path to Windows Kit")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0"
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>12</VersionMinor>
|
||||
<VersionMinor>13</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
Introducing exceptions to an existing non-exception-based codebase can be perilous. The console was originally written
|
||||
in C at a time when C++ was relatively unused in the Windows operating system. As part of our project to modernize the
|
||||
Windows console, we converted to use C++, but still had an aversion to using exception-based error handling in
|
||||
our code for fear that it introduce unexpected failures. However, the STL and other libraries like it are so useful that
|
||||
our code for fear that it might introduce unexpected failures. However, the STL and other libraries like it are so useful that
|
||||
sometimes it's significantly simpler to use them. Given that, we have a set of rules that we follow when considering
|
||||
exception use.
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ I think there might be a bit of a misunderstanding here - there are two differen
|
|||
* shell applications, like `cmd.exe`, `powershell`, `zsh`, etc. These are text-only applications that emit streams of characters. They don't care at all about how they're eventually rendered to the user. These are also sometimes referred to as "commandline client" applications.
|
||||
* terminal applications, like the Windows Terminal, gnome-terminal, xterm, iterm2, hyper. These are graphical applications that can be used to render the output of commandline clients.
|
||||
|
||||
On Windows, if you just run `cmd.exe` directly, the OS will create an instance of `conhost.exe` as the _terminal_ for `cmd.exe`. The same thing happens for `powershell.exe`, the system will creates a new conhost window for any client that's not already connected to a terminal of some sort. This has lead to an enormous amount of confusion for people thinking that a conhost window is actually a "`cmd` window". `cmd` can't have a window, it's just a commandline application. Its window is always some other terminal.
|
||||
On Windows, if you just run `cmd.exe` directly, the OS will create an instance of `conhost.exe` as the _terminal_ for `cmd.exe`. The same thing happens for `powershell.exe`, the system will create a new conhost window for any client that's not already connected to a terminal of some sort. This has lead to an enormous amount of confusion for people thinking that a conhost window is actually a "`cmd` window". `cmd` can't have a window, it's just a commandline application. Its window is always some other terminal.
|
||||
|
||||
Any terminal can run any commandline client application. So you can use the Windows Terminal to run whatever shell you want. I use mine for both `cmd` and `powershell`, and also WSL:
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ Import-Module .\tools\OpenConsole.psm1
|
|||
Set-MsBuildDevEnvironment
|
||||
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
|
||||
|
||||
|
|
|
@ -380,7 +380,7 @@ Here's the AppxManifest we're using:
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
@ -517,7 +517,7 @@ This is because of a few key lines we already put in the appxmanifest:
|
|||
|
||||
```xml
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -268,7 +268,7 @@ Today, if the deserialization of `CascadiaSettings` encounters any errors, an ex
|
|||
To get around this issue, when `CascadiaSettings` encounters a serialization error, it must internally record
|
||||
any pertinent information for that error, and return the simple `CascadiaSettings` as if nothing happened.
|
||||
The consumer must then call `CascadiaSettings::GetErrors()` and `CascadiaSettings::GetWarnings()` to properly
|
||||
understand whether an error ocurred and how to present that to the user.
|
||||
understand whether an error occurred and how to present that to the user.
|
||||
|
||||
|
||||
#### TerminalApp: Loading and Reloading Changes
|
||||
|
|
171
doc/specs/Keyboard-Selection.md
Normal file
171
doc/specs/Keyboard-Selection.md
Normal file
|
@ -0,0 +1,171 @@
|
|||
---
|
||||
author: Carlos Zamora @carlos-zamora
|
||||
created on: 2019-08-30
|
||||
last updated: 2021-09-17
|
||||
issue id: 715
|
||||
---
|
||||
|
||||
# Keyboard Selection
|
||||
|
||||
## Abstract
|
||||
|
||||
This spec describes a new set of non-configurable keybindings that allows the user to update a selection without the use of a mouse or stylus.
|
||||
|
||||
## Inspiration
|
||||
|
||||
ConHost allows the user to modify a selection using the keyboard. Holding `Shift` allows the user to move the second selection endpoint in accordance with the arrow keys. The selection endpoint updates by one cell per key event, allowing the user to refine the selected region.
|
||||
|
||||
Mark mode allows the user to create a selection using only the keyboard, then edit it as mentioned above.
|
||||
|
||||
|
||||
## Solution Design
|
||||
|
||||
The fundamental solution design for keyboard selection is that the responsibilities between the Terminal Control and Terminal Core must be very distinct. The Terminal Control is responsible for handling user interaction and directing the Terminal Core to update the selection. The Terminal Core will need to update the selection according to the preferences of the Terminal Control.
|
||||
|
||||
Relatively recently, TerminalControl was split into `TerminalControl`, `ControlInteractivity`, and `ControlCore`. Changes made to `ControlInteractivity`, `ControlCore`, and below propagate functionality to all consumers, meaning that the WPF terminal would benefit from these changes with no additional work required.
|
||||
|
||||
### Fundamental Terminal Control Changes
|
||||
|
||||
`ControlCore::TrySendKeyEvent()` is responsible for handling the key events after key bindings are dealt with in `TermControl`. At the time of writing this spec, there are 2 cases handled in this order:
|
||||
- Clear the selection (except in a few key scenarios)
|
||||
- Send Key Event
|
||||
|
||||
The first branch will be updated to _modify_ the selection instead of usually _clearing_ it. This will happen by converting the key event into parameters to forward to `TerminalCore`, which then updates the selection appropriately.
|
||||
|
||||
#### Idea: Make keyboard selection a collection of standard keybindings
|
||||
One idea is to introduce an `updateSelection` action that conditionally works if a selection is active (similar to the `copy` action). For these key bindings, if there is no selection, the key events are forwarded to the application.
|
||||
|
||||
Thanks to Keybinding Args, there would only be 1 new command:
|
||||
| Action | Keybinding Args | Description |
|
||||
|--|--|--|
|
||||
| `updateSelection` | | If a selection exists, moves the last selection endpoint. |
|
||||
| | `Enum direction { up, down, left, right }` | The direction the selection will be moved in. |
|
||||
| | `Enum mode { char, word, view, buffer }` | The context for which to move the selection endpoint to. (defaults to `char`) |
|
||||
|
||||
|
||||
By default, the following keybindings will be set:
|
||||
```JS
|
||||
// Character Selection
|
||||
{ "command": {"action": "updateSelection", "direction": "left", "mode": "char" }, "keys": "shift+left" },
|
||||
{ "command": {"action": "updateSelection", "direction": "right", "mode": "char" }, "keys": "shift+right" },
|
||||
{ "command": {"action": "updateSelection", "direction": "up", "mode": "char" }, "keys": "shift+up" },
|
||||
{ "command": {"action": "updateSelection", "direction": "down", "mode": "char" }, "keys": "shift+down" },
|
||||
|
||||
// Word Selection
|
||||
{ "command": {"action": "updateSelection", "direction": "left", "mode": "word" }, "keys": "ctrl+shift+left" },
|
||||
{ "command": {"action": "updateSelection", "direction": "right", "mode": "word" }, "keys": "ctrl+shift+right" },
|
||||
|
||||
// Viewport Selection
|
||||
{ "command": {"action": "updateSelection", "direction": "left", "mode": "view" }, "keys": "shift+home" },
|
||||
{ "command": {"action": "updateSelection", "direction": "right", "mode": "view" }, "keys": "shift+end" },
|
||||
{ "command": {"action": "updateSelection", "direction": "up", "mode": "view" }, "keys": "shift+pgup" },
|
||||
{ "command": {"action": "updateSelection", "direction": "down", "mode": "view" }, "keys": "shift+pgdn" },
|
||||
|
||||
// Buffer Corner Selection
|
||||
{ "command": {"action": "updateSelection", "direction": "up", "mode": "buffer" }, "keys": "ctrl+shift+home" },
|
||||
{ "command": {"action": "updateSelection", "direction": "down", "mode": "buffer" }, "keys": "ctrl+shift+end" },
|
||||
```
|
||||
These are in accordance with ConHost's keyboard selection model.
|
||||
|
||||
This idea was abandoned due to several reasons:
|
||||
1. Keyboard selection should be a standard way to interact with a terminal across all consumers (i.e. WPF control, etc.)
|
||||
2. There isn't really another set of key bindings that makes sense for this. We already hardcoded <kbd>ESC</kbd> as a way to clear the selection. This is just an extension of that.
|
||||
3. Adding 12 conditionally effective key bindings takes the spot of 12 potential non-conditional key bindings. It would be nice if a different key binding could be set when the selection is not active, but that makes the settings design much more complicated.
|
||||
4. 12 new items in the command palette is also pretty excessive.
|
||||
5. If proven wrong when this is in WT Preview, we can revisit this and make them customizable then. It's better to add the ability to customize it later than take it away.
|
||||
|
||||
#### Idea: Make keyboard selection a simulation of mouse selection
|
||||
It may seem that some effort can be saved by making the keyboard selection act as a simulation of mouse selection. There is a union of mouse and keyboard activity that can be represented in a single set of selection motion interfaces that are commanded by the TermControl's Mouse/Keyboard handler and adapted into appropriate motions in the Terminal Core.
|
||||
|
||||
However, the mouse handler operates by translating a pixel coordinate on the screen to a text buffer coordinate. This would have to be rewritten and the approach was deemed unworthy.
|
||||
|
||||
|
||||
### Fundamental Terminal Core Changes
|
||||
|
||||
The Terminal Core will need to expose a `UpdateSelection()` function that is called by the keybinding handler. The following parameters will need to be passed in:
|
||||
- `enum SelectionDirection`: the direction that the selection endpoint will attempt to move to. Possible values include `Up`, `Down`, `Left`, and `Right`.
|
||||
- `enum SelectionExpansion`: the selection expansion mode that the selection endpoint will adhere to. Possible values include `Char`, `Word`, `View`, `Buffer`.
|
||||
|
||||
#### Moving by Cell
|
||||
For `SelectionExpansion = Char`, the selection endpoint will be updated according to the buffer's output pattern. For **horizontal movements**, the selection endpoint will attempt to move left or right. If a viewport boundary is hit, the endpoint will wrap appropriately (i.e.: hitting the left boundary moves it to the last cell of the line above it).
|
||||
|
||||
For **vertical movements**, the selection endpoint will attempt to move up or down. If a **viewport boundary** is hit and there is a scroll buffer, the endpoint will move and scroll accordingly by a line.
|
||||
|
||||
If a **buffer boundary** is hit, the endpoint will not move. In this case, however, the event will still be considered handled.
|
||||
|
||||
**NOTE**: An important thing to handle properly in all cases is wide glyphs. The user should not be allowed to select a portion of a wide glyph; it should be all or none of it. When calling `_ExpandWideGlyphSelection` functions, the result must be saved to the endpoint.
|
||||
|
||||
#### Moving by Word
|
||||
For `SelectionExpansion = Word`, the selection endpoint will also be updated according to the buffer's output pattern, as above. However, the selection will be updated in accordance with "chunk selection" (performing a double-click and dragging the mouse to expand the selection). For **horizontal movements**, the selection endpoint will be updated according to the `_ExpandDoubleClickSelection` functions. The result must be saved to the endpoint. As before, if a boundary is hit, the endpoint will wrap appropriately. See [Future Considerations](#FutureConsiderations) for how this will interact with line wrapping.
|
||||
|
||||
For **vertical movements**, the movement is a little more complicated than before. The selection will still respond to buffer and viewport boundaries as before. If the user is trying to move up, the selection endpoint will attempt to move up by one line, then selection will be expanded leftwards. Alternatively, if the user is trying to move down, the selection endpoint will attempt to move down by one line, then the selection will be expanded rightwards.
|
||||
|
||||
#### Moving by Viewport
|
||||
For `SelectionExpansion = View`, the selection endpoint will be updated according to the viewport's height. Horizontal movements will be updated according to the viewport's width, thus resulting in the endpoint being moved to the left/right boundary of the viewport.
|
||||
|
||||
#### Moving by Buffer
|
||||
|
||||
For `SelectionExpansion = Buffer`, the selection endpoint will be moved to the beginning or end of all the text within the buffer. If moving up or left, set the position to 0,0 (the origin of the buffer). If moving down or right, set the position to the last character in the buffer.
|
||||
|
||||
|
||||
**NOTE**: In all cases, horizontal movements attempting to move past the left/right viewport boundaries result in a wrap. Vertical movements attempting to move past the top/bottom viewport boundaries will scroll such that the selection is at the edge of the screen. Vertical movements attempting to move past the top/bottom buffer boundaries will be clamped to be within buffer boundaries.
|
||||
|
||||
Every combination of the `SelectionDirection` and `SelectionExpansion` will map to a keybinding. These pairings are shown below in the UI/UX Design --> Keybindings section.
|
||||
|
||||
**NOTE**: If `copyOnSelect` is enabled, we need to make sure we **DO NOT** update the clipboard on every change in selection. The user must explicitly choose to copy the selected text from the buffer.
|
||||
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### Key Bindings
|
||||
|
||||
There will only be 1 new command that needs to be added:
|
||||
| Action | Keybinding Args | Description |
|
||||
|--|--|--|
|
||||
| `selectAll` | | Select the entire text buffer.
|
||||
|
||||
By default, the following key binding will be set:
|
||||
```JS
|
||||
{ "command": "selectAll", "keys": "ctrl+shift+a" },
|
||||
```
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Accessibility
|
||||
|
||||
Using the keyboard is generally a more accessible experience than using the mouse. Being able to modify a selection by using the keyboard is a good first step towards making selecting text more accessible.
|
||||
|
||||
### Security
|
||||
|
||||
N/A
|
||||
|
||||
### Reliability
|
||||
|
||||
With regards to the Terminal Core, the newly introduced code should rely on already existing and tested code. Thus no crash-related bugs are expected.
|
||||
|
||||
With regards to Terminal Control and the settings model, crash-related bugs are not expected. However, ensuring that the selection is updated and cleared in general use-case scenarios must be ensured.
|
||||
|
||||
### Compatibility
|
||||
|
||||
N/A
|
||||
|
||||
### Performance, Power, and Efficiency
|
||||
|
||||
## Potential Issues
|
||||
|
||||
### Grapheme Clusters
|
||||
When grapheme cluster support is inevitably added to the Text Buffer, moving by "cell" is expected to move by "character" or "cluster". This is similar to how wide glyphs are handled today. Either all of it is selected, or none of it.
|
||||
|
||||
## Future considerations
|
||||
|
||||
### Word Selection Wrap
|
||||
At the time of writing this spec, expanding or moving by word is interrupted by the beginning or end of the line, regardless of the wrap flag being set. In the future, selection and the accessibility models will respect the wrap flag on the text buffer.
|
||||
|
||||
## Mark Mode
|
||||
|
||||
This functionality will be expanded to create a feature similar to Mark Mode. This will allow a user to create a selection using only the keyboard.
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
- https://blogs.windows.com/windowsdeveloper/2014/10/07/console-improvements-in-the-windows-10-technical-preview/
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Overview
|
||||
|
||||
This document outlines the roadmap towards delivering Windows Terminal 2.0 by Winter 2021.
|
||||
This document outlines the roadmap towards delivering Windows Terminal 2.0.
|
||||
|
||||
## Milestones
|
||||
|
||||
|
@ -31,9 +31,9 @@ Below is the schedule for when milestones will be included in release builds of
|
|||
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/) |
|
||||
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/) |
|
||||
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
|
||||
| 2021-10-31 | 1.12 in Windows Terminal Preview<br>1.11 in Windows Terminal | |
|
||||
| 2021-11-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-12-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
| 2021-10-20 | [1.12] in Windows Terminal Preview<br>[1.11] in Windows Terminal | [Windows Terminal Preview 1.12 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-12-release/) |
|
||||
| | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
|
||||
## Issue Triage & Prioritization
|
||||
|
||||
|
@ -49,28 +49,32 @@ The following are a list of the key scenarios we're aiming to deliver for Termin
|
|||
|
||||
> 👉 Note: There are many other features that don't fit within 2.0, but will be re-assessed and prioritized for 3.0, the plan for which will be published in 2021.
|
||||
|
||||
| Priority\* | Scenario | Description/Notes |
|
||||
| ---------- | -------- | ----------------- |
|
||||
| 0 | Settings UI | A user interface that connects to settings.json. This provides a way for people to edit their settings without having to edit a JSON file.<br><br>Issue: [#1564]<br>Specs: [#6720], [#6904]<br>Implementation: [#7283], [#7370], [#8048] |
|
||||
| 0 | Command palette | A popup menu to list possible actions and commands.<br><br>Issues: [#5400], [#2046]<br>Spec: [#2193]<br>Implementation: [#6635] |
|
||||
| 1 | Tab tear-off | The ability to tear a tab out of the current window and spawn a new window or attach it to a separate window.<br><br>Issue: [#1256], [#5000]<br>Spec: [#2080], [#7240] |
|
||||
| 1 | Clickable links | Hyperlinking any links that appear in the text buffer. When clicking on the link, the link will open in your default browser.<br><br>Issue: [#574]<br>Implementation: [#7251] |
|
||||
| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal<br><br>Issue: [#492]<br>Spec: [#2080], [#7414] |
|
||||
| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme<br><br>Issue: [#3327]<br>Spec: [#5772] |
|
||||
| 1 | Open profile elevated | Configure profiles to always open elevated (if Terminal was run unelevated)<br><br>Issue: [#5000], [#632]<br>Spec: [#8455] |
|
||||
| 1 | Open tab in existing window | Open new tabs in existing Terminal windows<br><br>Issue: [#5000], [#4472]<br>Spec: [#8135] |
|
||||
| 1 | Traditional opacity | Have a transparent background without the acrylic blur.<br><br>Issue: [#603] <br>**Current State**: Blocked on WinUI 3.0 |
|
||||
| 2 | SnapOnOutput, scroll lock | Pause output or scrolling on click.<br><br>Issue: [#980]<br>Spec: [#2529]<br>Implementation: [#6062] |
|
||||
| 2 | Infinite scrollback | Have an infinite history for the text buffer.<br><br>Issue: [#1410] |
|
||||
| 2 | Pane management | All issues listed out in the original issue. Some features include pane resizing with mouse, pane zooming, and opening a pane by prompting which profile to use.<br><br>Issue: [#1000] |
|
||||
| 2 | Theme marketplace | Marketplace for creation and distribution of themes.<br>Dependent on overall theming |
|
||||
| 2 | Jump list | Show profiles from task bar (on right click)/start menu.<br><br>Issue: [#576]<br>Implementation: [#7515] |
|
||||
| 2 | Open with multiple tabs | A setting that allows Windows Terminal to launch with a specific tab configuration (not using only command line arguments).<br><br>Issue: [#756] |
|
||||
| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.<br><br>Issue: [#1060]<br>Implementation: [#6100] |
|
||||
| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.<br><br>Issues: [#961], [#960], [#766] |
|
||||
| 3 | Quake mode | Provide a quick launch terminal that appears and disappears when a hotkey is pressed.<br><br>Issue: [#653] |
|
||||
| 3 | Settings migration infrastructure | Migrate people's settings without breaking them. Hand-in-hand with settings UI. |
|
||||
| 3 | Pointer bindings | Provide settings that can be bound to the mouse.<br><br>Issue: [#1553] |
|
||||
| Priority\* | Scenario | Description/Notes | State |
|
||||
| ---------- | -------- | ----------------- | ----- |
|
||||
| 0 | Settings UI | A user interface that connects to settings.json. This provides a way for people to edit their settings without having to edit a JSON file.<br><br>Issue: [#1564]<br>Specs: [#6720], [#6904]<br>Implementation: [#7283], [#7370], [#8048] | ✔️ |
|
||||
| 0 | Command palette | A popup menu to list possible actions and commands.<br><br>Issues: [#5400], [#2046]<br>Spec: [#2193]<br>Implementation: [#6635] | ✔️ |
|
||||
| 1 | Tab tear-off | The ability to tear a tab out of the current window and spawn a new window or attach it to a separate window.<br><br>Issue: [#1256], [#5000]<br>Spec: [#2080], [#7240] | 📝 |
|
||||
| 1 | Clickable links | Hyperlinking any links that appear in the text buffer. When clicking on the link, the link will open in your default browser.<br><br>Issue: [#574]<br>Implementation: [#7251] | ✔️ |
|
||||
| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal<br><br>Issue: [#492]<br>Spec: [#2080], [#7414] | ✔️ |
|
||||
| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme<br><br>Issue: [#3327]<br>Spec: [#5772] | 🦶 |
|
||||
| 1 | Open profile elevated | Configure profiles to always open elevated (if Terminal was run unelevated)<br><br>Issue: [#5000], [#632]<br>Spec: [#8455] | 📝 |
|
||||
| 1 | Open tab in existing window | Open new tabs in existing Terminal windows<br><br>Issue: [#5000], [#4472]<br>Spec: [#8135] | ✔️ |
|
||||
| 1 | Traditional opacity | Have a transparent background without the acrylic blur.<br><br>Issue: [#603] | ✔️ |
|
||||
| 2 | SnapOnOutput, scroll lock | Pause output or scrolling on click.<br><br>Issue: [#980]<br>Spec: [#2529]<br>Implementation: [#6062] | ✔️ |
|
||||
| 2 | Infinite scrollback | Have an infinite history for the text buffer.<br><br>Issue: [#1410] | 🦶 |
|
||||
| 2 | Pane management | All issues listed out in the original issue. Some features include pane resizing with mouse, pane zooming, and opening a pane by prompting which profile to use.<br><br>Issue: [#1000] | 📝 |
|
||||
| 2 | Theme marketplace | Marketplace for creation and distribution of themes.<br>Dependent on overall theming | 🦶 |
|
||||
| 2 | Jump list | Show profiles from task bar (on right click)/start menu.<br><br>Issue: [#576]<br>Implementation: [#7515] | ✔️ |
|
||||
| 2 | Open with multiple tabs | A setting that allows Windows Terminal to launch with a specific tab configuration (not using only command line arguments).<br><br>Issue: [#756] | ✔️ |
|
||||
| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.<br><br>Issue: [#1060]<br>Implementation: [#6100] | ✔️ |
|
||||
| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.<br><br>Issues: [#961], [#960], [#766] | ✔️ |
|
||||
| 3 | Quake mode | Provide a quick launch terminal that appears and disappears when a hotkey is pressed.<br><br>Issue: [#653] | ✔️ |
|
||||
| 3 | Settings migration infrastructure | Migrate people's settings without breaking them. Hand-in-hand with settings UI. | 🦶 |
|
||||
| 3 | Pointer bindings | Provide settings that can be bound to the mouse.<br><br>Issue: [#1553] | 🦶 |
|
||||
|
||||
* 📝: The feature is currently in progress
|
||||
* ✔️: The feature is complete and shipped in a Preview build
|
||||
* 🦶: The feature is at risk of being punted to a future release cycle (beyond 2.0)
|
||||
|
||||
Feature Notes:
|
||||
|
||||
|
@ -91,6 +95,8 @@ Feature Notes:
|
|||
[1.9]: https://github.com/microsoft/terminal/milestone/34
|
||||
[1.10]: https://github.com/microsoft/terminal/milestone/35
|
||||
[1.11]: https://github.com/microsoft/terminal/milestone/36
|
||||
[1.12]: https://github.com/microsoft/terminal/milestone/38
|
||||
[1.13]: https://github.com/microsoft/terminal/milestone/39
|
||||
[2.0]: https://github.com/microsoft/terminal/milestone/22
|
||||
[#1564]: https://github.com/microsoft/terminal/issues/1564
|
||||
[#6720]: https://github.com/microsoft/terminal/pull/6720
|
||||
|
|
201
oss/pcg/LICENSE-APACHE.txt
Normal file
201
oss/pcg/LICENSE-APACHE.txt
Normal 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
19
oss/pcg/LICENSE-MIT.txt
Normal 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
14
oss/pcg/cgmanifest.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"Registrations": [
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"repositoryUrl": "https://github.com/imneme/pcg-cpp",
|
||||
"commitHash": "ffd522e7188bef30a00c74dc7eb9de5faff90092"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Version": 1
|
||||
}
|
82
oss/pcg/include/pcg_random.hpp
Normal file
82
oss/pcg/include/pcg_random.hpp
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
<ProjectGuid>{96274800-9574-423E-892A-909FBE2AC8BE}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>EchoCon</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.22000.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
|
@ -163,4 +163,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
|
|
@ -118,6 +118,23 @@
|
|||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
BODGY
|
||||
|
||||
The wapproj `GetResolvedWinMD` target tries to get a winmd from every cppwinrt
|
||||
executable we put in the package. But we DON'T produce a winmd. This makes the
|
||||
FastUpToDate check fail every time, and leads to the whole wapproj build
|
||||
running even if you're just f5'ing the package. EVEN AFTER A SUCCESSFUL BUILD.
|
||||
|
||||
Setting GenerateWindowsMetadata=false is enough to tell the build system that
|
||||
we don't produce one, and get it off our backs.
|
||||
-->
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
|
|
@ -12,6 +12,16 @@
|
|||
<EventProvider Id="EventProvider_TerminalWin32Host" Name="56c06166-2e2e-5f4d-7ff3-74f4b78c87d6" />
|
||||
<EventProvider Id="EventProvider_TerminalRemoting" Name="d6f04aad-629f-539a-77c1-73f5c3e4aa7b" />
|
||||
<EventProvider Id="EventProvider_TerminalDirectX" Name="c93e739e-ae50-5a14-78e7-f171e947535d" />
|
||||
<EventProvider Id="EventProvider_TerminalUIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
|
||||
<!-- Console providers here -->
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Launcher" Name="770aa552-671a-5e97-579b-151709ec0dbd"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Host" Name="fe1ff234-1f09-50a8-d38d-c44fab43e818"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Server" Name="1A541C01-589A-496E-85A7-A9E02170166D"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser" Name="c9ba2a84-d3ca-5e19-2bd6-776a0910cb9d"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Render.VtEngine" Name="c9ba2a95-d3ca-5e19-2bd6-776a0910cb9d"/>
|
||||
<EventProvider Id="EventProvider-Microsoft.Windows.Console.UIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
|
||||
|
||||
<!-- Profile for General Terminal logging -->
|
||||
<Profile Id="Terminal.Verbose.File" Name="Terminal" Description="Terminal" LoggingMode="File" DetailLevel="Verbose">
|
||||
<Collectors>
|
||||
<EventCollectorId Value="EventCollector_Terminal">
|
||||
|
@ -23,6 +33,7 @@
|
|||
<EventProviderId Value="EventProvider_TerminalWin32Host" />
|
||||
<EventProviderId Value="EventProvider_TerminalRemoting" />
|
||||
<EventProviderId Value="EventProvider_TerminalDirectX" />
|
||||
<EventProviderId Value="EventProvider_TerminalUIA" />
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
|
@ -30,5 +41,27 @@
|
|||
<Profile Id="Terminal.Light.File" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="File" DetailLevel="Light" />
|
||||
<Profile Id="Terminal.Verbose.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
|
||||
<Profile Id="Terminal.Light.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
|
||||
|
||||
<!-- Profile for DefTerm logging. Includes some conhost logging. -->
|
||||
<Profile Id="DefTerm.Verbose.File" Name="DefTerm" Description="DefTerm" LoggingMode="File" DetailLevel="Verbose">
|
||||
<Collectors>
|
||||
<EventCollectorId Value="EventCollector_Terminal">
|
||||
<EventProviders>
|
||||
<EventProviderId Value="EventProvider_TerminalControl" />
|
||||
<EventProviderId Value="EventProvider_TerminalConnection" />
|
||||
<EventProviderId Value="EventProvider_TerminalSettingsModel" />
|
||||
<EventProviderId Value="EventProvider_TerminalApp" />
|
||||
<EventProviderId Value="EventProvider_TerminalWin32Host" />
|
||||
<EventProviderId Value="EventProvider_TerminalRemoting" />
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Launcher" />
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Host" />
|
||||
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Server" />
|
||||
</EventProviders>
|
||||
</EventCollectorId>
|
||||
</Collectors>
|
||||
</Profile>
|
||||
<Profile Id="DefTerm.Light.File" Name="DefTerm" Description="DefTerm" Base="DefTerm.Verbose.File" LoggingMode="File" DetailLevel="Light" />
|
||||
<Profile Id="DefTerm.Verbose.Memory" Name="DefTerm" Description="DefTerm" Base="DefTerm.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
|
||||
<Profile Id="DefTerm.Light.Memory" Name="DefTerm" Description="DefTerm" Base="DefTerm.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
|
||||
</Profiles>
|
||||
</WindowsPerformanceRecorder>
|
||||
</WindowsPerformanceRecorder>
|
||||
|
|
|
@ -12,8 +12,30 @@ static_assert(alignof(TextAttribute) == 2);
|
|||
// Ensure that we can memcpy() and memmove() the struct for performance.
|
||||
static_assert(std::is_trivially_copyable_v<TextAttribute>);
|
||||
|
||||
BYTE TextAttribute::s_legacyDefaultForeground = 7;
|
||||
BYTE TextAttribute::s_legacyDefaultBackground = 0;
|
||||
namespace
|
||||
{
|
||||
constexpr std::array<TextColor, 16> s_initLegacyColorMap(const BYTE defaultIndex)
|
||||
{
|
||||
std::array<TextColor, 16> legacyColorMap;
|
||||
for (auto i = 0u; i < legacyColorMap.size(); i++)
|
||||
{
|
||||
const auto legacyIndex = TextColor::TransposeLegacyIndex(i);
|
||||
gsl::at(legacyColorMap, i) = i == defaultIndex ? TextColor{} : TextColor{ legacyIndex, true };
|
||||
}
|
||||
return legacyColorMap;
|
||||
}
|
||||
|
||||
BYTE s_legacyDefaultForeground = 7;
|
||||
BYTE s_legacyDefaultBackground = 0;
|
||||
BYTE s_ansiDefaultForeground = 7;
|
||||
BYTE s_ansiDefaultBackground = 0;
|
||||
}
|
||||
|
||||
// These maps allow for an efficient conversion from a legacy attribute index
|
||||
// to a TextColor with the corresponding ANSI index, also taking into account
|
||||
// the legacy index values that need to be converted to a default TextColor.
|
||||
std::array<TextColor, 16> TextAttribute::s_legacyForegroundColorMap = s_initLegacyColorMap(7);
|
||||
std::array<TextColor, 16> TextAttribute::s_legacyBackgroundColorMap = s_initLegacyColorMap(0);
|
||||
|
||||
// Routine Description:
|
||||
// - Sets the legacy attributes which map to and from the default colors.
|
||||
|
@ -23,8 +45,22 @@ BYTE TextAttribute::s_legacyDefaultBackground = 0;
|
|||
// - None
|
||||
void TextAttribute::SetLegacyDefaultAttributes(const WORD defaultAttributes) noexcept
|
||||
{
|
||||
// First we reset the current default color map entries to what they should
|
||||
// be for a regular translation from a legacy index to an ANSI TextColor.
|
||||
gsl::at(s_legacyForegroundColorMap, s_legacyDefaultForeground) = TextColor{ s_ansiDefaultForeground, true };
|
||||
gsl::at(s_legacyBackgroundColorMap, s_legacyDefaultBackground) = TextColor{ s_ansiDefaultBackground, true };
|
||||
|
||||
// Then we save the new default attribute values and their corresponding
|
||||
// ANSI translations. We use the latter values to more efficiently handle
|
||||
// the "VT Quirk" conversion below.
|
||||
s_legacyDefaultForeground = defaultAttributes & FG_ATTRS;
|
||||
s_legacyDefaultBackground = (defaultAttributes & BG_ATTRS) >> 4;
|
||||
s_ansiDefaultForeground = TextColor::TransposeLegacyIndex(s_legacyDefaultForeground);
|
||||
s_ansiDefaultBackground = TextColor::TransposeLegacyIndex(s_legacyDefaultBackground);
|
||||
|
||||
// Finally we set the new default color map entries.
|
||||
gsl::at(s_legacyForegroundColorMap, s_legacyDefaultForeground) = TextColor{};
|
||||
gsl::at(s_legacyBackgroundColorMap, s_legacyDefaultBackground) = TextColor{};
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -55,13 +91,13 @@ TextAttribute TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults(const Te
|
|||
const auto bg{ attribute.GetBackground() };
|
||||
auto copy{ attribute };
|
||||
if (fg.IsIndex16() &&
|
||||
attribute.IsBold() == WI_IsFlagSet(s_legacyDefaultForeground, FOREGROUND_INTENSITY) &&
|
||||
fg.GetIndex() == (s_legacyDefaultForeground & ~FOREGROUND_INTENSITY))
|
||||
attribute.IsBold() == WI_IsFlagSet(s_ansiDefaultForeground, FOREGROUND_INTENSITY) &&
|
||||
fg.GetIndex() == (s_ansiDefaultForeground & ~FOREGROUND_INTENSITY))
|
||||
{
|
||||
// We don't want to turn 1;37m into 39m (or even 1;39m), as this was meant to mimic a legacy color.
|
||||
copy.SetDefaultForeground();
|
||||
}
|
||||
if (bg.IsIndex16() && bg.GetIndex() == s_legacyDefaultBackground)
|
||||
if (bg.IsIndex16() && bg.GetIndex() == s_ansiDefaultBackground)
|
||||
{
|
||||
copy.SetDefaultBackground();
|
||||
}
|
||||
|
@ -92,22 +128,22 @@ bool TextAttribute::IsLegacy() const noexcept
|
|||
// - Calculates rgb colors based off of current color table and active modification attributes.
|
||||
// Arguments:
|
||||
// - colorTable: the current color table rgb values.
|
||||
// - defaultFgColor: the default foreground color rgb value.
|
||||
// - defaultBgColor: the default background color rgb value.
|
||||
// - defaultFgIndex: the color table index of the default foreground color.
|
||||
// - defaultBgIndex: the color table index of the default background color.
|
||||
// - reverseScreenMode: true if the screen mode is reversed.
|
||||
// - blinkingIsFaint: true if blinking should be interpreted as faint. (defaults to false)
|
||||
// - boldIsBright: true if "bold" should be interpreted as bright. (defaults to true)
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, TextColor::TABLE_SIZE>& colorTable,
|
||||
const size_t defaultFgIndex,
|
||||
const size_t defaultBgIndex,
|
||||
const bool reverseScreenMode,
|
||||
const bool blinkingIsFaint,
|
||||
const bool boldIsBright) const noexcept
|
||||
{
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgColor, boldIsBright && IsBold());
|
||||
auto bg = _background.GetColor(colorTable, defaultBgColor);
|
||||
auto fg = _foreground.GetColor(colorTable, defaultFgIndex, boldIsBright && IsBold());
|
||||
auto bg = _background.GetColor(colorTable, defaultBgIndex);
|
||||
if (IsFaint() || (IsBlinking() && blinkingIsFaint))
|
||||
{
|
||||
fg = (fg >> 1) & 0x7F7F7F; // Divide foreground color components by two.
|
||||
|
|
|
@ -41,8 +41,8 @@ public:
|
|||
|
||||
explicit constexpr TextAttribute(const WORD wLegacyAttr) noexcept :
|
||||
_wAttrLegacy{ gsl::narrow_cast<WORD>(wLegacyAttr & META_ATTRS) },
|
||||
_foreground{ s_LegacyIndexOrDefault(wLegacyAttr & FG_ATTRS, s_legacyDefaultForeground) },
|
||||
_background{ s_LegacyIndexOrDefault((wLegacyAttr & BG_ATTRS) >> 4, s_legacyDefaultBackground) },
|
||||
_foreground{ gsl::at(s_legacyForegroundColorMap, wLegacyAttr & FG_ATTRS) },
|
||||
_background{ gsl::at(s_legacyBackgroundColorMap, (wLegacyAttr & BG_ATTRS) >> 4) },
|
||||
_extendedAttrs{ ExtendedAttributes::Normal },
|
||||
_hyperlinkId{ 0 }
|
||||
{
|
||||
|
@ -64,9 +64,9 @@ public:
|
|||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::array<COLORREF, TextColor::TABLE_SIZE>& colorTable,
|
||||
const size_t defaultFgIndex,
|
||||
const size_t defaultBgIndex,
|
||||
const bool reverseScreenMode = false,
|
||||
const bool blinkingIsFaint = false,
|
||||
const bool boldIsBright = true) const noexcept;
|
||||
|
@ -167,13 +167,8 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
static constexpr TextColor s_LegacyIndexOrDefault(const BYTE requestedIndex, const BYTE defaultIndex)
|
||||
{
|
||||
return requestedIndex == defaultIndex ? TextColor{} : TextColor{ requestedIndex, true };
|
||||
}
|
||||
|
||||
static BYTE s_legacyDefaultForeground;
|
||||
static BYTE s_legacyDefaultBackground;
|
||||
static std::array<TextColor, 16> s_legacyForegroundColorMap;
|
||||
static std::array<TextColor, 16> s_legacyBackgroundColorMap;
|
||||
|
||||
uint16_t _wAttrLegacy; // sizeof: 2, alignof: 2
|
||||
uint16_t _hyperlinkId; // sizeof: 2, alignof: 2
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "precomp.h"
|
||||
#include "TextColor.h"
|
||||
|
||||
#include <til/bit.h>
|
||||
|
||||
// clang-format off
|
||||
|
||||
// A table mapping 8-bit RGB colors, in the form RRRGGGBB,
|
||||
|
@ -30,7 +32,7 @@ constexpr std::array<BYTE, 256> CompressedRgbToIndex16 = {
|
|||
// A table mapping indexed colors from the 256-color palette,
|
||||
// down to one of the 16 colors in the legacy palette.
|
||||
constexpr std::array<BYTE, 256> Index256ToIndex16 = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15,
|
||||
0, 1, 1, 1, 9, 9, 2, 1, 1, 1, 1, 1, 2, 2, 3, 3,
|
||||
3, 3, 2, 2, 11, 11, 3, 3, 10, 10, 11, 11, 11, 11, 10, 10,
|
||||
10, 10, 11, 11, 5, 5, 5, 5, 1, 1, 8, 8, 1, 1, 9, 9,
|
||||
|
@ -137,14 +139,16 @@ void TextColor::SetDefault() noexcept
|
|||
// Arguments:
|
||||
// - colorTable: The table of colors we should use to look up the value of
|
||||
// an indexed attribute from.
|
||||
// - defaultColor: The color value to use if we're a default attribute.
|
||||
// - defaultIndex: The color table index to use if we're a default attribute.
|
||||
// - brighten: if true, we'll brighten a dark color table index.
|
||||
// Return Value:
|
||||
// - a COLORREF containing the real value of this TextColor.
|
||||
COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten) const noexcept
|
||||
COLORREF TextColor::GetColor(const std::array<COLORREF, TextColor::TABLE_SIZE>& colorTable, const size_t defaultIndex, bool brighten) const noexcept
|
||||
{
|
||||
if (IsDefault())
|
||||
{
|
||||
const auto defaultColor = til::at(colorTable, defaultIndex);
|
||||
|
||||
if (brighten)
|
||||
{
|
||||
// See MSFT:20266024 for context on this fix.
|
||||
|
@ -186,7 +190,7 @@ COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const
|
|||
// the result will be something like 0b00100000.
|
||||
// 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 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 mask = _mm256_movemask_ps(_mm256_castsi256_ps(result)); // 4.
|
||||
unsigned long index;
|
||||
|
@ -203,7 +207,7 @@ COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const
|
|||
// --> 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 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 result2 = _mm_cmpeq_epi32(haystack2, needle);
|
||||
const auto result = _mm_packs_epi32(result1, result2); // 3.5
|
||||
|
@ -250,11 +254,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
|
|||
{
|
||||
return defaultIndex;
|
||||
}
|
||||
else if (IsIndex16())
|
||||
{
|
||||
return GetIndex();
|
||||
}
|
||||
else if (IsIndex256())
|
||||
else if (IsIndex16() || IsIndex256())
|
||||
{
|
||||
return til::at(Index256ToIndex16, GetIndex());
|
||||
}
|
||||
|
|
|
@ -48,6 +48,28 @@ enum class ColorType : BYTE
|
|||
struct TextColor
|
||||
{
|
||||
public:
|
||||
static constexpr BYTE DARK_BLACK = 0;
|
||||
static constexpr BYTE DARK_RED = 1;
|
||||
static constexpr BYTE DARK_GREEN = 2;
|
||||
static constexpr BYTE DARK_YELLOW = 3;
|
||||
static constexpr BYTE DARK_BLUE = 4;
|
||||
static constexpr BYTE DARK_MAGENTA = 5;
|
||||
static constexpr BYTE DARK_CYAN = 6;
|
||||
static constexpr BYTE DARK_WHITE = 7;
|
||||
static constexpr BYTE BRIGHT_BLACK = 8;
|
||||
static constexpr BYTE BRIGHT_RED = 9;
|
||||
static constexpr BYTE BRIGHT_GREEN = 10;
|
||||
static constexpr BYTE BRIGHT_YELLOW = 11;
|
||||
static constexpr BYTE BRIGHT_BLUE = 12;
|
||||
static constexpr BYTE BRIGHT_MAGENTA = 13;
|
||||
static constexpr BYTE BRIGHT_CYAN = 14;
|
||||
static constexpr BYTE BRIGHT_WHITE = 15;
|
||||
|
||||
static constexpr size_t DEFAULT_FOREGROUND = 256;
|
||||
static constexpr size_t DEFAULT_BACKGROUND = 257;
|
||||
static constexpr size_t CURSOR_COLOR = 258;
|
||||
static constexpr size_t TABLE_SIZE = 259;
|
||||
|
||||
constexpr TextColor() noexcept :
|
||||
_meta{ ColorType::IsDefault },
|
||||
_red{ 0 },
|
||||
|
@ -86,7 +108,7 @@ public:
|
|||
void SetIndex(const BYTE index, const bool isIndex256) noexcept;
|
||||
void SetDefault() noexcept;
|
||||
|
||||
COLORREF GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten = false) const noexcept;
|
||||
COLORREF GetColor(const std::array<COLORREF, TABLE_SIZE>& colorTable, const size_t defaultIndex, bool brighten = false) const noexcept;
|
||||
BYTE GetLegacyIndex(const BYTE defaultIndex) const noexcept;
|
||||
|
||||
constexpr BYTE GetIndex() const noexcept
|
||||
|
@ -96,6 +118,16 @@ public:
|
|||
|
||||
COLORREF GetRGB() const noexcept;
|
||||
|
||||
static constexpr BYTE TransposeLegacyIndex(const size_t index)
|
||||
{
|
||||
// When converting a 16-color index in the legacy Windows order to or
|
||||
// from an ANSI-compatible order, we need to swap the bits in positions
|
||||
// 0 and 2. We do this by XORing the index with 00000101, but only if
|
||||
// one (but not both) of those bit positions is set.
|
||||
const auto oneBitSet = (index ^ (index >> 2)) & 1;
|
||||
return gsl::narrow_cast<BYTE>(index ^ oneBitSet ^ (oneBitSet << 2));
|
||||
}
|
||||
|
||||
private:
|
||||
union
|
||||
{
|
||||
|
|
|
@ -27,9 +27,7 @@ Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept :
|
|||
_fDeferCursorRedraw(false),
|
||||
_fHaveDeferredCursorRedraw(false),
|
||||
_ulSize(ulSize),
|
||||
_cursorType(CursorType::Legacy),
|
||||
_fUseColor(false),
|
||||
_color(s_InvertCursorColor)
|
||||
_cursorType(CursorType::Legacy)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -143,10 +141,9 @@ void Cursor::SetSize(const ULONG ulSize) noexcept
|
|||
_RedrawCursor();
|
||||
}
|
||||
|
||||
void Cursor::SetStyle(const ULONG ulSize, const COLORREF color, const CursorType type) noexcept
|
||||
void Cursor::SetStyle(const ULONG ulSize, const CursorType type) noexcept
|
||||
{
|
||||
_ulSize = ulSize;
|
||||
_color = color;
|
||||
_cursorType = type;
|
||||
|
||||
_RedrawCursor();
|
||||
|
@ -285,7 +282,6 @@ void Cursor::CopyProperties(const Cursor& OtherCursor) noexcept
|
|||
// Size will be handled separately in the resize operation.
|
||||
//_ulSize = OtherCursor._ulSize;
|
||||
_cursorType = OtherCursor._cursorType;
|
||||
_color = OtherCursor._color;
|
||||
}
|
||||
|
||||
void Cursor::DelayEOLWrap(const COORD coordDelayedAt) noexcept
|
||||
|
@ -335,21 +331,6 @@ const CursorType Cursor::GetType() const noexcept
|
|||
return _cursorType;
|
||||
}
|
||||
|
||||
const bool Cursor::IsUsingColor() const noexcept
|
||||
{
|
||||
return GetColor() != INVALID_COLOR;
|
||||
}
|
||||
|
||||
const COLORREF Cursor::GetColor() const noexcept
|
||||
{
|
||||
return _color;
|
||||
}
|
||||
|
||||
void Cursor::SetColor(const unsigned int color) noexcept
|
||||
{
|
||||
_color = gsl::narrow_cast<COLORREF>(color);
|
||||
}
|
||||
|
||||
void Cursor::SetType(const CursorType type) noexcept
|
||||
{
|
||||
_cursorType = type;
|
||||
|
|
|
@ -24,7 +24,6 @@ class TextBuffer;
|
|||
class Cursor final
|
||||
{
|
||||
public:
|
||||
static const unsigned int s_InvertCursorColor = INVALID_COLOR;
|
||||
// the following values are used to create the textmode cursor.
|
||||
static constexpr unsigned int CURSOR_SMALL_SIZE = 25; // large enough to be one pixel on a six pixel font
|
||||
|
||||
|
@ -51,8 +50,6 @@ public:
|
|||
COORD GetPosition() const noexcept;
|
||||
|
||||
const CursorType GetType() const noexcept;
|
||||
const bool IsUsingColor() const noexcept;
|
||||
const COLORREF GetColor() const noexcept;
|
||||
|
||||
void StartDeferDrawing() noexcept;
|
||||
bool IsDeferDrawing() noexcept;
|
||||
|
@ -67,7 +64,7 @@ public:
|
|||
void SetIsPopupShown(const bool fIsPopupShown) noexcept;
|
||||
void SetDelay(const bool fDelay) noexcept;
|
||||
void SetSize(const ULONG ulSize) noexcept;
|
||||
void SetStyle(const ULONG ulSize, const COLORREF color, const CursorType type) noexcept;
|
||||
void SetStyle(const ULONG ulSize, const CursorType type) noexcept;
|
||||
|
||||
void SetPosition(const COORD cPosition) noexcept;
|
||||
void SetXPosition(const int NewX) noexcept;
|
||||
|
@ -84,7 +81,6 @@ public:
|
|||
COORD GetDelayedAtPosition() const noexcept;
|
||||
bool IsDelayedEOLWrap() const noexcept;
|
||||
|
||||
void SetColor(const unsigned int color) noexcept;
|
||||
void SetType(const CursorType type) noexcept;
|
||||
|
||||
private:
|
||||
|
@ -117,6 +113,4 @@ private:
|
|||
void _RedrawCursorAlways() noexcept;
|
||||
|
||||
CursorType _cursorType;
|
||||
bool _fUseColor;
|
||||
COLORREF _color;
|
||||
};
|
||||
|
|
|
@ -1430,12 +1430,13 @@ const til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<t
|
|||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update pos to be the end of the current glyph/character. This is used for accessibility
|
||||
// - Update pos to be the end of the current glyph/character.
|
||||
// Arguments:
|
||||
// - pos - a COORD on the word you are currently on
|
||||
// - accessibilityMode - this is being used for accessibility; make the end exclusive.
|
||||
// Return Value:
|
||||
// - pos - The COORD for the last cell of the current glyph (exclusive)
|
||||
const til::point TextBuffer::GetGlyphEnd(const til::point pos, std::optional<til::point> limitOptional) const
|
||||
const til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode, std::optional<til::point> limitOptional) const
|
||||
{
|
||||
COORD resultPos = pos;
|
||||
const auto bufferSize = GetSize();
|
||||
|
@ -1453,7 +1454,10 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos, std::optional<til
|
|||
}
|
||||
|
||||
// increment one more time to become exclusive
|
||||
bufferSize.IncrementInBounds(resultPos, true);
|
||||
if (accessibilityMode)
|
||||
{
|
||||
bufferSize.IncrementInBounds(resultPos, true);
|
||||
}
|
||||
return resultPos;
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ public:
|
|||
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
|
||||
|
||||
const til::point GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
const til::point GetGlyphEnd(const til::point pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
const til::point GetGlyphEnd(const til::point pos, bool accessibilityMode = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
bool MoveToNextGlyph(til::point& pos, bool allowBottomExclusive = false, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
bool MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional = std::nullopt) const;
|
||||
|
||||
|
|
|
@ -24,9 +24,11 @@ class TextAttributeTests
|
|||
TEST_METHOD(TestRoundtripDefaultColors);
|
||||
TEST_METHOD(TestBoldAsBright);
|
||||
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
std::array<COLORREF, TextColor::TABLE_SIZE> _colorTable;
|
||||
const COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
const COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
const size_t _defaultFgIndex = TextColor::DEFAULT_FOREGROUND;
|
||||
const size_t _defaultBgIndex = TextColor::DEFAULT_BACKGROUND;
|
||||
};
|
||||
|
||||
bool TextAttributeTests::ClassSetup()
|
||||
|
@ -47,6 +49,8 @@ bool TextAttributeTests::ClassSetup()
|
|||
_colorTable[13] = RGB(180, 0, 158); // Bright Magenta
|
||||
_colorTable[14] = RGB(249, 241, 165); // Bright Yellow
|
||||
_colorTable[15] = RGB(242, 242, 242); // White
|
||||
_colorTable[_defaultFgIndex] = _defaultFg;
|
||||
_colorTable[_defaultBgIndex] = _defaultBg;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -132,17 +136,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
// reset the reverse video
|
||||
attr.SetReverseVideo(false);
|
||||
|
@ -151,17 +155,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
// while the background and getters stay the same
|
||||
attr.SetFaint(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched, and the background fainter, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
// reset the reverse video and faint attributes
|
||||
attr.SetReverseVideo(false);
|
||||
|
@ -171,17 +175,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
// background, while getters stay the same
|
||||
attr.SetInvisible(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
// with reverse video set, the calculated background value should match
|
||||
// the foreground, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestReverseDefaultColors()
|
||||
|
@ -194,34 +198,34 @@ void TextAttributeTests::TestReverseDefaultColors()
|
|||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
attr.SetForeground(red);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
|
||||
attr.Invert();
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
attr.SetDefaultForeground();
|
||||
attr.SetBackground(green);
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripDefaultColors()
|
||||
|
@ -237,7 +241,7 @@ void TextAttributeTests::TestRoundtripDefaultColors()
|
|||
Log::Comment(L"Foreground legacy default index should map to default text color.");
|
||||
legacyAttribute = fgLegacyDefault | BACKGROUND_GREEN;
|
||||
textAttribute.SetDefaultForeground();
|
||||
textAttribute.SetIndexedBackground256(BACKGROUND_GREEN >> 4);
|
||||
textAttribute.SetIndexedBackground256(TextColor::DARK_GREEN);
|
||||
VERIFY_ARE_EQUAL(textAttribute, TextAttribute{ legacyAttribute });
|
||||
|
||||
Log::Comment(L"Default foreground text color should map back to legacy default index.");
|
||||
|
@ -245,7 +249,7 @@ void TextAttributeTests::TestRoundtripDefaultColors()
|
|||
|
||||
Log::Comment(L"Background legacy default index should map to default text color.");
|
||||
legacyAttribute = FOREGROUND_GREEN | bgLegacyDefault;
|
||||
textAttribute.SetIndexedForeground256(FOREGROUND_GREEN);
|
||||
textAttribute.SetIndexedForeground256(TextColor::DARK_GREEN);
|
||||
textAttribute.SetDefaultBackground();
|
||||
VERIFY_ARE_EQUAL(textAttribute, TextAttribute{ legacyAttribute });
|
||||
|
||||
|
@ -277,43 +281,43 @@ void TextAttributeTests::TestBoldAsBright()
|
|||
// values when not bold
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFgIndex));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBgIndex));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, false));
|
||||
|
||||
// with bold set, calculated foreground/background values shouldn't change for the default colors.
|
||||
attr.SetBold(true);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, false));
|
||||
|
||||
attr.SetIndexedForeground(0);
|
||||
attr.SetIndexedForeground(TextColor::DARK_BLACK);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"Foreground should be bright black when bold is bright is enabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, true));
|
||||
|
||||
Log::Comment(L"Foreground should be dark black when bold is bright is disabled");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, false));
|
||||
|
||||
attr.SetIndexedBackground(2);
|
||||
attr.SetIndexedBackground(TextColor::DARK_GREEN);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
|
||||
Log::Comment(L"background should be unaffected by 'bold is bright'");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, false));
|
||||
|
||||
attr.SetBold(false);
|
||||
VERIFY_IS_FALSE(attr.IsBold());
|
||||
Log::Comment(L"when not bold, 'bold is bright' changes nothing");
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, false));
|
||||
|
||||
Log::Comment(L"When set to a bright color, and bold, 'bold is bright' changes nothing");
|
||||
attr.SetBold(true);
|
||||
attr.SetIndexedForeground(8);
|
||||
attr.SetIndexedForeground(TextColor::BRIGHT_BLACK);
|
||||
VERIFY_IS_TRUE(attr.IsBold());
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, false, false, false));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, true));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), attr.CalculateRgbColors(_colorTable, _defaultFgIndex, _defaultBgIndex, false, false, false));
|
||||
}
|
||||
|
|
|
@ -23,9 +23,11 @@ class TextColorTests
|
|||
TEST_METHOD(TestRgbColor);
|
||||
TEST_METHOD(TestChangeColor);
|
||||
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
std::array<COLORREF, TextColor::TABLE_SIZE> _colorTable;
|
||||
const COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
const COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
const size_t _defaultFgIndex = TextColor::DEFAULT_FOREGROUND;
|
||||
const size_t _defaultBgIndex = TextColor::DEFAULT_BACKGROUND;
|
||||
};
|
||||
|
||||
bool TextColorTests::ClassSetup()
|
||||
|
@ -46,6 +48,8 @@ bool TextColorTests::ClassSetup()
|
|||
_colorTable[13] = RGB(180, 0, 158); // Bright Magenta
|
||||
_colorTable[14] = RGB(249, 241, 165); // Bright Yellow
|
||||
_colorTable[15] = RGB(242, 242, 242); // White
|
||||
_colorTable[_defaultFgIndex] = _defaultFg;
|
||||
_colorTable[_defaultBgIndex] = _defaultBg;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -57,16 +61,16 @@ void TextColorTests::TestDefaultColor()
|
|||
VERIFY_IS_FALSE(defaultColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(defaultColor.IsRgb());
|
||||
|
||||
auto color = defaultColor.GetColor(_colorTable, _defaultFg, false);
|
||||
auto color = defaultColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
}
|
||||
|
||||
|
@ -78,16 +82,16 @@ void TextColorTests::TestDarkIndexColor()
|
|||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
|
@ -99,16 +103,16 @@ void TextColorTests::TestBrightIndexColor()
|
|||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
|
@ -121,16 +125,16 @@ void TextColorTests::TestRgbColor()
|
|||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
}
|
||||
|
||||
|
@ -143,55 +147,55 @@ void TextColorTests::TestChangeColor()
|
|||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
rgbColor.SetDefault();
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
rgbColor.SetIndex(7, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
rgbColor.SetIndex(15, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBgIndex, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
|
|
@ -61,13 +61,26 @@
|
|||
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\CascadiaResources.build.items" />
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.post.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
|
||||
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
|
||||
<ProjectReference Include="..\..\host\proxy\Host.Proxy.vcxproj" />
|
||||
<ProjectReference Include="..\TerminalAzBridge\TerminalAzBridge.vcxproj" />
|
||||
<ProjectReference Include="..\ShellExtension\WindowsTerminalShellExt.vcxproj" />
|
||||
<ProjectReference Include="..\wt\wt.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj">
|
||||
<Project>{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalAzBridge\TerminalAzBridge.vcxproj">
|
||||
<Project>{067F0A06-FCB7-472C-96E9-B03B54E8E18D}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\ShellExtension\WindowsTerminalShellExt.vcxproj">
|
||||
<Project>{f2ed628a-db22-446f-a081-4cc845b51a2b}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\wt\wt.vcxproj">
|
||||
<Project>{506fd703-baa7-4f6e-9361-64f550ec8fca}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\host\exe\Host.EXE.vcxproj">
|
||||
<Project>{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\host\proxy\Host.Proxy.vcxproj">
|
||||
<Project>{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="OpenConsoleStompSourceProjectForWapProject" BeforeTargets="_ConvertItems">
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.22000.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
@ -74,7 +74,7 @@
|
|||
Enabled="false"
|
||||
DisplayName="ms-resource:AppName" />
|
||||
</uap5:Extension>
|
||||
<!-- <uap3:Extension Category="windows.appExtension">
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.console.host"
|
||||
Id="OpenConsole"
|
||||
DisplayName="OpenConsole"
|
||||
|
@ -102,15 +102,15 @@
|
|||
<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:ComInterface>
|
||||
</com:Extension> -->
|
||||
</com:Extension>
|
||||
<com:Extension Category="windows.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:ExeServer>
|
||||
<com:ExeServer DisplayName="WindowsTerminal" Executable="WindowsTerminal.exe">
|
||||
<com:Class Id="E12CFF52-A866-4C77-9A90-F570A7AA2C6B"/>
|
||||
</com:ExeServer> -->
|
||||
</com:ExeServer>
|
||||
<com:SurrogateServer DisplayName="WindowsTerminalShellExt">
|
||||
<com:Class Id="9f156763-7844-4dc4-b2b1-901f640f5155" Path="WindowsTerminalShellExt.dll" ThreadingModel="STA"/>
|
||||
</com:SurrogateServer>
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
|
||||
#include "../TerminalSettingsModel/ColorScheme.h"
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../types/inc/colorTable.hpp"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model::implementation;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
@ -32,339 +34,293 @@ namespace SettingsModelLocalTests
|
|||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(CanLayerColorScheme);
|
||||
TEST_METHOD(LayerColorSchemeProperties);
|
||||
TEST_METHOD(ParseSimpleColorScheme);
|
||||
TEST_METHOD(LayerColorSchemesOnArray);
|
||||
TEST_METHOD(UpdateSchemeReferences);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
static Core::Color rgb(uint8_t r, uint8_t g, uint8_t b) noexcept
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
return Core::Color{ r, g, b, 255 };
|
||||
}
|
||||
};
|
||||
|
||||
void ColorSchemeTests::CanLayerColorScheme()
|
||||
void ColorSchemeTests::ParseSimpleColorScheme()
|
||||
{
|
||||
const std::string scheme0String{ R"({
|
||||
"name": "scheme0",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101"
|
||||
})" };
|
||||
const std::string scheme1String{ R"({
|
||||
"name": "scheme1",
|
||||
"foreground": "#020202",
|
||||
"background": "#030303"
|
||||
})" };
|
||||
const std::string scheme2String{ R"({
|
||||
"name": "scheme0",
|
||||
"foreground": "#040404",
|
||||
"background": "#050505"
|
||||
})" };
|
||||
const std::string scheme3String{ R"({
|
||||
// "name": "scheme3",
|
||||
"foreground": "#060606",
|
||||
"background": "#070707"
|
||||
})" };
|
||||
const std::string campbellScheme{ "{"
|
||||
"\"background\" : \"#0C0C0C\","
|
||||
"\"black\" : \"#0C0C0C\","
|
||||
"\"blue\" : \"#0037DA\","
|
||||
"\"brightBlack\" : \"#767676\","
|
||||
"\"brightBlue\" : \"#3B78FF\","
|
||||
"\"brightCyan\" : \"#61D6D6\","
|
||||
"\"brightGreen\" : \"#16C60C\","
|
||||
"\"brightPurple\" : \"#B4009E\","
|
||||
"\"brightRed\" : \"#E74856\","
|
||||
"\"brightWhite\" : \"#F2F2F2\","
|
||||
"\"brightYellow\" : \"#F9F1A5\","
|
||||
"\"cursorColor\" : \"#FFFFFF\","
|
||||
"\"cyan\" : \"#3A96DD\","
|
||||
"\"foreground\" : \"#F2F2F2\","
|
||||
"\"green\" : \"#13A10E\","
|
||||
"\"name\" : \"Campbell\","
|
||||
"\"purple\" : \"#881798\","
|
||||
"\"red\" : \"#C50F1F\","
|
||||
"\"selectionBackground\" : \"#131313\","
|
||||
"\"white\" : \"#CCCCCC\","
|
||||
"\"yellow\" : \"#C19C00\""
|
||||
"}" };
|
||||
|
||||
const auto scheme0Json = VerifyParseSucceeded(scheme0String);
|
||||
const auto scheme1Json = VerifyParseSucceeded(scheme1String);
|
||||
const auto scheme2Json = VerifyParseSucceeded(scheme2String);
|
||||
const auto scheme3Json = VerifyParseSucceeded(scheme3String);
|
||||
const auto schemeObject = VerifyParseSucceeded(campbellScheme);
|
||||
auto scheme = ColorScheme::FromJson(schemeObject);
|
||||
VERIFY_ARE_EQUAL(L"Campbell", scheme->Name());
|
||||
VERIFY_ARE_EQUAL(til::color(0xf2, 0xf2, 0xf2, 255), til::color{ scheme->Foreground() });
|
||||
VERIFY_ARE_EQUAL(til::color(0x0c, 0x0c, 0x0c, 255), til::color{ scheme->Background() });
|
||||
VERIFY_ARE_EQUAL(til::color(0x13, 0x13, 0x13, 255), til::color{ scheme->SelectionBackground() });
|
||||
VERIFY_ARE_EQUAL(til::color(0xFF, 0xFF, 0xFF, 255), til::color{ scheme->CursorColor() });
|
||||
|
||||
const auto scheme0 = ColorScheme::FromJson(scheme0Json);
|
||||
std::array<COLORREF, COLOR_TABLE_SIZE> expectedCampbellTable;
|
||||
const auto campbellSpan = gsl::make_span(expectedCampbellTable);
|
||||
Utils::InitializeColorTable(campbellSpan);
|
||||
Utils::SetColorTableAlpha(campbellSpan, 0);
|
||||
|
||||
VERIFY_IS_TRUE(scheme0->ShouldBeLayered(scheme0Json));
|
||||
VERIFY_IS_FALSE(scheme0->ShouldBeLayered(scheme1Json));
|
||||
VERIFY_IS_TRUE(scheme0->ShouldBeLayered(scheme2Json));
|
||||
VERIFY_IS_FALSE(scheme0->ShouldBeLayered(scheme3Json));
|
||||
for (size_t i = 0; i < expectedCampbellTable.size(); i++)
|
||||
{
|
||||
const auto& expected = expectedCampbellTable.at(i);
|
||||
const til::color actual{ scheme->Table().at(static_cast<uint32_t>(i)) };
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
|
||||
const auto scheme1 = ColorScheme::FromJson(scheme1Json);
|
||||
|
||||
VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme0Json));
|
||||
VERIFY_IS_TRUE(scheme1->ShouldBeLayered(scheme1Json));
|
||||
VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme2Json));
|
||||
VERIFY_IS_FALSE(scheme1->ShouldBeLayered(scheme3Json));
|
||||
|
||||
const auto scheme3 = ColorScheme::FromJson(scheme3Json);
|
||||
|
||||
VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme0Json));
|
||||
VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme1Json));
|
||||
VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme2Json));
|
||||
VERIFY_IS_FALSE(scheme3->ShouldBeLayered(scheme3Json));
|
||||
}
|
||||
|
||||
void ColorSchemeTests::LayerColorSchemeProperties()
|
||||
{
|
||||
const std::string scheme0String{ R"({
|
||||
"name": "scheme0",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010100",
|
||||
"cursorColor": "#010001",
|
||||
"red": "#010000",
|
||||
"green": "#000100",
|
||||
"blue": "#000001"
|
||||
})" };
|
||||
const std::string scheme1String{ R"({
|
||||
"name": "scheme1",
|
||||
"foreground": "#020202",
|
||||
"background": "#030303",
|
||||
"selectionBackground": "#020200",
|
||||
"cursorColor": "#040004",
|
||||
"red": "#020000",
|
||||
|
||||
"blue": "#000002"
|
||||
})" };
|
||||
const std::string scheme2String{ R"({
|
||||
"name": "scheme0",
|
||||
"foreground": "#040404",
|
||||
"background": "#050505",
|
||||
"selectionBackground": "#030300",
|
||||
"cursorColor": "#060006",
|
||||
"red": "#030000",
|
||||
"green": "#000300"
|
||||
})" };
|
||||
|
||||
const auto scheme0Json = VerifyParseSucceeded(scheme0String);
|
||||
const auto scheme1Json = VerifyParseSucceeded(scheme1String);
|
||||
const auto scheme2Json = VerifyParseSucceeded(scheme2String);
|
||||
|
||||
auto scheme0 = ColorScheme::FromJson(scheme0Json);
|
||||
VERIFY_ARE_EQUAL(L"scheme0", scheme0->_Name);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 0), scheme0->_SelectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 1), scheme0->_CursorColor);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 0, 0), scheme0->_table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0->_table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 1), scheme0->_table[XTERM_BLUE_ATTR]);
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Layering scheme1 on top of scheme0"));
|
||||
scheme0->LayerJson(scheme1Json);
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 0), scheme0->_SelectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 0, 4), scheme0->_CursorColor);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 0, 0), scheme0->_table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 1, 0), scheme0->_table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0->_table[XTERM_BLUE_ATTR]);
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"Layering scheme2Json on top of (scheme0+scheme1)"));
|
||||
scheme0->LayerJson(scheme2Json);
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 0), scheme0->_SelectionBackground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 6, 0, 6), scheme0->_CursorColor);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 0, 0), scheme0->_table[XTERM_RED_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 3, 0), scheme0->_table[XTERM_GREEN_ATTR]);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 2), scheme0->_table[XTERM_BLUE_ATTR]);
|
||||
Log::Comment(L"Roundtrip Test for Color Scheme");
|
||||
Json::Value outJson{ scheme->ToJson() };
|
||||
VERIFY_ARE_EQUAL(schemeObject, outJson);
|
||||
}
|
||||
|
||||
void ColorSchemeTests::LayerColorSchemesOnArray()
|
||||
{
|
||||
const std::string scheme0String{ R"({
|
||||
"name": "scheme0",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101"
|
||||
static constexpr std::string_view inboxSettings{ R"({
|
||||
"schemes": [
|
||||
{
|
||||
"background": "#0C0C0C",
|
||||
"black": "#0C0C0C",
|
||||
"blue": "#0037DA",
|
||||
"brightBlack": "#767676",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightRed": "#E74856",
|
||||
"brightWhite": "#F2F2F2",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"cyan": "#3A96DD",
|
||||
"foreground": "#CCCCCC",
|
||||
"green": "#13A10E",
|
||||
"name": "Campbell",
|
||||
"purple": "#881798",
|
||||
"red": "#C50F1F",
|
||||
"selectionBackground": "#FFFFFF",
|
||||
"white": "#CCCCCC",
|
||||
"yellow": "#C19C00"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
const std::string scheme1String{ R"({
|
||||
"name": "scheme1",
|
||||
"foreground": "#020202",
|
||||
"background": "#030303"
|
||||
})" };
|
||||
const std::string scheme2String{ R"({
|
||||
"name": "scheme0",
|
||||
"foreground": "#040404",
|
||||
"background": "#050505"
|
||||
})" };
|
||||
const std::string scheme3String{ R"({
|
||||
// by not providing a name, the scheme will have the name ""
|
||||
"foreground": "#060606",
|
||||
"background": "#070707"
|
||||
static constexpr std::string_view userSettings{ R"({
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0"
|
||||
}
|
||||
],
|
||||
"schemes": [
|
||||
{
|
||||
"background": "#121314",
|
||||
"black": "#121314",
|
||||
"blue": "#121314",
|
||||
"brightBlack": "#121314",
|
||||
"brightBlue": "#121314",
|
||||
"brightCyan": "#121314",
|
||||
"brightGreen": "#121314",
|
||||
"brightPurple": "#121314",
|
||||
"brightRed": "#121314",
|
||||
"brightWhite": "#121314",
|
||||
"brightYellow": "#121314",
|
||||
"cursorColor": "#121314",
|
||||
"cyan": "#121314",
|
||||
"foreground": "#121314",
|
||||
"green": "#121314",
|
||||
"name": "Campbell",
|
||||
"purple": "#121314",
|
||||
"red": "#121314",
|
||||
"selectionBackground": "#121314",
|
||||
"white": "#121314",
|
||||
"yellow": "#121314"
|
||||
},
|
||||
{
|
||||
"background": "#012456",
|
||||
"black": "#0C0C0C",
|
||||
"blue": "#0037DA",
|
||||
"brightBlack": "#767676",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightRed": "#E74856",
|
||||
"brightWhite": "#F2F2F2",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"cyan": "#3A96DD",
|
||||
"foreground": "#CCCCCC",
|
||||
"green": "#13A10E",
|
||||
"name": "Campbell Powershell",
|
||||
"purple": "#881798",
|
||||
"red": "#C50F1F",
|
||||
"selectionBackground": "#FFFFFF",
|
||||
"white": "#CCCCCC",
|
||||
"yellow": "#C19C00"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto scheme0Json = VerifyParseSucceeded(scheme0String);
|
||||
const auto scheme1Json = VerifyParseSucceeded(scheme1String);
|
||||
const auto scheme2Json = VerifyParseSucceeded(scheme2String);
|
||||
const auto scheme3Json = VerifyParseSucceeded(scheme3String);
|
||||
const auto settings = winrt::make_self<CascadiaSettings>(userSettings, inboxSettings);
|
||||
|
||||
auto settings = winrt::make_self<CascadiaSettings>();
|
||||
const auto colorSchemes = settings->GlobalSettings().ColorSchemes();
|
||||
VERIFY_ARE_EQUAL(2u, colorSchemes.Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_globals->ColorSchemes().Size());
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
|
||||
const auto scheme0 = winrt::get_self<ColorScheme>(colorSchemes.Lookup(L"Campbell"));
|
||||
VERIFY_ARE_EQUAL(rgb(0x12, 0x13, 0x14), scheme0->Foreground());
|
||||
VERIFY_ARE_EQUAL(rgb(0x12, 0x13, 0x14), scheme0->Background());
|
||||
|
||||
settings->_LayerOrCreateColorScheme(scheme0Json);
|
||||
{
|
||||
for (auto kv : settings->_globals->ColorSchemes())
|
||||
{
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"kv:%s->%s", kv.Key().data(), kv.Value().Name().data()));
|
||||
}
|
||||
VERIFY_ARE_EQUAL(1u, settings->_globals->ColorSchemes().Size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
|
||||
}
|
||||
|
||||
settings->_LayerOrCreateColorScheme(scheme1Json);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
|
||||
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
|
||||
auto scheme1 = winrt::get_self<ColorScheme>(scheme1Proj);
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0, 0, 0), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 1, 1, 1), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
|
||||
}
|
||||
settings->_LayerOrCreateColorScheme(scheme2Json);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(2u, settings->_globals->ColorSchemes().Size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
|
||||
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
|
||||
auto scheme1 = winrt::get_self<ColorScheme>(scheme1Proj);
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
|
||||
}
|
||||
settings->_LayerOrCreateColorScheme(scheme3Json);
|
||||
|
||||
{
|
||||
VERIFY_ARE_EQUAL(3u, settings->_globals->ColorSchemes().Size());
|
||||
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme0"));
|
||||
auto scheme0Proj = settings->_globals->ColorSchemes().Lookup(L"scheme0");
|
||||
auto scheme0 = winrt::get_self<ColorScheme>(scheme0Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L"scheme1"));
|
||||
auto scheme1Proj = settings->_globals->ColorSchemes().Lookup(L"scheme1");
|
||||
auto scheme1 = winrt::get_self<ColorScheme>(scheme1Proj);
|
||||
VERIFY_IS_TRUE(settings->_globals->ColorSchemes().HasKey(L""));
|
||||
auto scheme2Proj = settings->_globals->ColorSchemes().Lookup(L"");
|
||||
auto scheme2 = winrt::get_self<ColorScheme>(scheme2Proj);
|
||||
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingColorScheme(scheme2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingColorScheme(scheme3Json));
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 4, 4, 4), scheme0->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 5, 5, 5), scheme0->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 2, 2, 2), scheme1->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 3, 3, 3), scheme1->_Background);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 6, 6, 6), scheme2->_Foreground);
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 7, 7, 7), scheme2->_Background);
|
||||
}
|
||||
const auto scheme1 = winrt::get_self<ColorScheme>(colorSchemes.Lookup(L"Campbell Powershell"));
|
||||
VERIFY_ARE_EQUAL(rgb(0xCC, 0xCC, 0xCC), scheme1->Foreground());
|
||||
VERIFY_ARE_EQUAL(rgb(0x01, 0x24, 0x56), scheme1->Background());
|
||||
}
|
||||
|
||||
void ColorSchemeTests::UpdateSchemeReferences()
|
||||
{
|
||||
const std::string settingsString{ R"json({
|
||||
"defaultProfile": "Inherited reference",
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"colorScheme": "Scheme 1"
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"name": "Explicit scheme reference",
|
||||
"colorScheme": "Scheme 1"
|
||||
},
|
||||
{
|
||||
"name": "Explicit reference; hidden",
|
||||
"colorScheme": "Scheme 1",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"name": "Inherited reference"
|
||||
},
|
||||
{
|
||||
"name": "Different reference",
|
||||
"colorScheme": "Scheme 2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemes": [
|
||||
{ "name": "Scheme 1" },
|
||||
{ "name": "Scheme 2" },
|
||||
{ "name": "Scheme 1 (renamed)" }
|
||||
]
|
||||
})json" };
|
||||
static constexpr std::string_view settingsString{ R"json({
|
||||
"defaultProfile": "Inherited reference",
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"colorScheme": "Campbell"
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"name": "Explicit scheme reference",
|
||||
"colorScheme": "Campbell"
|
||||
},
|
||||
{
|
||||
"name": "Explicit reference; hidden",
|
||||
"colorScheme": "Campbell",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"name": "Inherited reference"
|
||||
},
|
||||
{
|
||||
"name": "Different reference",
|
||||
"colorScheme": "One Half Dark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemes": [
|
||||
{
|
||||
"background": "#0C0C0C",
|
||||
"black": "#0C0C0C",
|
||||
"blue": "#0037DA",
|
||||
"brightBlack": "#767676",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightRed": "#E74856",
|
||||
"brightWhite": "#F2F2F2",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"cyan": "#3A96DD",
|
||||
"foreground": "#CCCCCC",
|
||||
"green": "#13A10E",
|
||||
"name": "Campbell",
|
||||
"purple": "#881798",
|
||||
"red": "#C50F1F",
|
||||
"selectionBackground": "#FFFFFF",
|
||||
"white": "#CCCCCC",
|
||||
"yellow": "#C19C00"
|
||||
},
|
||||
{
|
||||
"background": "#0C0C0C",
|
||||
"black": "#0C0C0C",
|
||||
"blue": "#0037DA",
|
||||
"brightBlack": "#767676",
|
||||
"brightBlue": "#3B78FF",
|
||||
"brightCyan": "#61D6D6",
|
||||
"brightGreen": "#16C60C",
|
||||
"brightPurple": "#B4009E",
|
||||
"brightRed": "#E74856",
|
||||
"brightWhite": "#F2F2F2",
|
||||
"brightYellow": "#F9F1A5",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"cyan": "#3A96DD",
|
||||
"foreground": "#CCCCCC",
|
||||
"green": "#13A10E",
|
||||
"name": "Campbell (renamed)",
|
||||
"purple": "#881798",
|
||||
"red": "#C50F1F",
|
||||
"selectionBackground": "#FFFFFF",
|
||||
"white": "#CCCCCC",
|
||||
"yellow": "#C19C00"
|
||||
},
|
||||
{
|
||||
"background": "#282C34",
|
||||
"black": "#282C34",
|
||||
"blue": "#61AFEF",
|
||||
"brightBlack": "#5A6374",
|
||||
"brightBlue": "#61AFEF",
|
||||
"brightCyan": "#56B6C2",
|
||||
"brightGreen": "#98C379",
|
||||
"brightPurple": "#C678DD",
|
||||
"brightRed": "#E06C75",
|
||||
"brightWhite": "#DCDFE4",
|
||||
"brightYellow": "#E5C07B",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"cyan": "#56B6C2",
|
||||
"foreground": "#DCDFE4",
|
||||
"green": "#98C379",
|
||||
"name": "One Half Dark",
|
||||
"purple": "#C678DD",
|
||||
"red": "#E06C75",
|
||||
"selectionBackground": "#FFFFFF",
|
||||
"white": "#DCDFE4",
|
||||
"yellow": "#E5C07B"
|
||||
}
|
||||
]
|
||||
})json" };
|
||||
|
||||
auto settings{ winrt::make_self<CascadiaSettings>(false) };
|
||||
settings->_ParseJsonString(settingsString, false);
|
||||
settings->_ApplyDefaultsFromUserSettings();
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
settings->_ValidateSettings();
|
||||
const auto settings{ winrt::make_self<CascadiaSettings>(settingsString) };
|
||||
|
||||
// update all references to "Scheme 1"
|
||||
const auto newName{ L"Scheme 1 (renamed)" };
|
||||
settings->UpdateColorSchemeReferences(L"Scheme 1", newName);
|
||||
const auto newName{ L"Campbell (renamed)" };
|
||||
settings->UpdateColorSchemeReferences(L"Campbell", newName);
|
||||
|
||||
// verify profile defaults
|
||||
Log::Comment(L"Profile Defaults");
|
||||
VERIFY_ARE_EQUAL(newName, settings->ProfileDefaults().DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(settings->ProfileDefaults().DefaultAppearance().HasColorSchemeName());
|
||||
|
||||
// verify all other profiles
|
||||
const auto& profiles{ settings->AllProfiles() };
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(0) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(1) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(2) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(newName, prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_FALSE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
{
|
||||
const auto& prof{ profiles.GetAt(3) };
|
||||
Log::Comment(prof.Name().c_str());
|
||||
VERIFY_ARE_EQUAL(L"Scheme 2", prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_ARE_EQUAL(L"One Half Dark", prof.DefaultAppearance().ColorSchemeName());
|
||||
VERIFY_IS_TRUE(prof.DefaultAppearance().HasColorSchemeName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,6 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(TestLayerOnAutogeneratedName);
|
||||
|
||||
TEST_METHOD(TestGenerateCommandline);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void CommandTests::ManyCommandsSameAction()
|
||||
|
@ -471,6 +465,10 @@ namespace SettingsModelLocalTests
|
|||
"name":"action7_startingDirectoryWithTrailingSlash",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\", "commandline": "bar.exe" }
|
||||
},
|
||||
{
|
||||
"name":"action8_tabTitleEscaping",
|
||||
"command": { "action": "newWindow", "tabTitle":"\\\";foo\\" }
|
||||
}
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
@ -479,7 +477,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(8u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(9u, commands.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action0");
|
||||
|
@ -592,5 +590,20 @@ namespace SettingsModelLocalTests
|
|||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,44 +7,34 @@ Module Name:
|
|||
|
||||
Abstract:
|
||||
- This class is a helper that can be used to quickly create tests that need to
|
||||
read & parse json data. Test classes that need to read JSON should make sure
|
||||
to derive from this class, and also make sure to call InitializeJsonReader()
|
||||
in the TEST_CLASS_SETUP().
|
||||
read & parse json data.
|
||||
|
||||
Author(s):
|
||||
Mike Griese (migrie) August-2019
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class JsonTestClass
|
||||
{
|
||||
public:
|
||||
void InitializeJsonReader()
|
||||
static Json::Value VerifyParseSucceeded(const std::string_view& content)
|
||||
{
|
||||
_reader = std::unique_ptr<Json::CharReader>(Json::CharReaderBuilder::CharReaderBuilder().newCharReader());
|
||||
};
|
||||
static const std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
|
||||
|
||||
void InitializeJsonWriter()
|
||||
{
|
||||
_writer = std::unique_ptr<Json::StreamWriter>(Json::StreamWriterBuilder::StreamWriterBuilder().newStreamWriter());
|
||||
}
|
||||
|
||||
Json::Value VerifyParseSucceeded(std::string content)
|
||||
{
|
||||
Json::Value root;
|
||||
std::string errs;
|
||||
const bool parseResult = _reader->parse(content.c_str(), content.c_str() + content.size(), &root, &errs);
|
||||
const bool parseResult = reader->parse(content.data(), content.data() + content.size(), &root, &errs);
|
||||
VERIFY_IS_TRUE(parseResult, winrt::to_hstring(errs).c_str());
|
||||
return root;
|
||||
};
|
||||
|
||||
std::string toString(const Json::Value& json)
|
||||
static std::string toString(const Json::Value& json)
|
||||
{
|
||||
static const std::unique_ptr<Json::StreamWriter> writer{ Json::StreamWriterBuilder::StreamWriterBuilder().newStreamWriter() };
|
||||
|
||||
std::stringstream s;
|
||||
_writer->write(json, &s);
|
||||
writer->write(json, &s);
|
||||
return s.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Json::CharReader> _reader;
|
||||
std::unique_ptr<Json::StreamWriter> _writer;
|
||||
};
|
||||
|
|
|
@ -59,12 +59,6 @@ namespace SettingsModelLocalTests
|
|||
|
||||
TEST_METHOD(TestGetKeyBindingForAction);
|
||||
TEST_METHOD(KeybindingsWithoutVkey);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void KeyBindingsTests::KeyChords()
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "JsonTestClass.h"
|
||||
|
||||
#include <defaults.h>
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace WEX::Logging;
|
||||
|
@ -32,81 +34,86 @@ namespace SettingsModelLocalTests
|
|||
TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml")
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(CanLayerProfile);
|
||||
TEST_METHOD(ProfileGeneratesGuid);
|
||||
TEST_METHOD(LayerProfileProperties);
|
||||
TEST_METHOD(LayerProfileIcon);
|
||||
TEST_METHOD(LayerProfilesOnArray);
|
||||
TEST_METHOD(DuplicateProfileTest);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
return true;
|
||||
}
|
||||
TEST_METHOD(TestGenGuidsForProfiles);
|
||||
};
|
||||
|
||||
void ProfileTests::CanLayerProfile()
|
||||
void ProfileTests::ProfileGeneratesGuid()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name" : "profile1",
|
||||
"guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name" : "profile2",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name" : "profile3"
|
||||
})" };
|
||||
// Parse some profiles without guids. We should NOT generate new guids
|
||||
// for them. If a profile doesn't have a GUID, we'll leave its _guid
|
||||
// set to nullopt. The Profile::Guid() getter will
|
||||
// ensure all profiles have a GUID that's actually set.
|
||||
// The null guid _is_ a valid guid, so we won't re-generate that
|
||||
// guid. null is _not_ a valid guid, so we'll leave that nullopt
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
// See SettingsTests::ValidateProfilesGenerateGuids for a version of
|
||||
// this test that includes synthesizing GUIDS for profiles without GUIDs
|
||||
// set
|
||||
|
||||
const std::string profileWithoutGuid{ R"({
|
||||
"name" : "profile0"
|
||||
})" };
|
||||
const std::string secondProfileWithoutGuid{ R"({
|
||||
"name" : "profile1"
|
||||
})" };
|
||||
const std::string profileWithNullForGuid{ R"({
|
||||
"name" : "profile2",
|
||||
"guid" : null
|
||||
})" };
|
||||
const std::string profileWithNullGuid{ R"({
|
||||
"name" : "profile3",
|
||||
"guid" : "{00000000-0000-0000-0000-000000000000}"
|
||||
})" };
|
||||
const std::string profileWithGuid{ R"({
|
||||
"name" : "profile4",
|
||||
"guid" : "{6239a42c-1de4-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profileWithoutGuid);
|
||||
const auto profile1Json = VerifyParseSucceeded(secondProfileWithoutGuid);
|
||||
const auto profile2Json = VerifyParseSucceeded(profileWithNullForGuid);
|
||||
const auto profile3Json = VerifyParseSucceeded(profileWithNullGuid);
|
||||
const auto profile4Json = VerifyParseSucceeded(profileWithGuid);
|
||||
|
||||
const auto profile0 = implementation::Profile::FromJson(profile0Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile0->ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_TRUE(profile0->ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile0->ShouldBeLayered(profile3Json));
|
||||
|
||||
const auto profile1 = implementation::Profile::FromJson(profile1Json);
|
||||
|
||||
VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile0Json));
|
||||
// A profile _can_ be layered with itself, though what's the point?
|
||||
VERIFY_IS_TRUE(profile1->ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_FALSE(profile1->ShouldBeLayered(profile3Json));
|
||||
|
||||
const auto profile2 = implementation::Profile::FromJson(profile2Json);
|
||||
const auto profile3 = implementation::Profile::FromJson(profile3Json);
|
||||
const auto profile4 = implementation::Profile::FromJson(profile4Json);
|
||||
const winrt::guid cmdGuid = Utils::GuidFromString(L"{6239a42c-1de4-49a3-80bd-e8fdd045185c}");
|
||||
const winrt::guid nullGuid{};
|
||||
|
||||
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile0Json));
|
||||
// A profile _can_ be layered with itself, though what's the point?
|
||||
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile1Json));
|
||||
VERIFY_IS_FALSE(profile3->ShouldBeLayered(profile2Json));
|
||||
VERIFY_IS_TRUE(profile3->ShouldBeLayered(profile3Json));
|
||||
VERIFY_IS_FALSE(profile0->HasGuid());
|
||||
VERIFY_IS_FALSE(profile1->HasGuid());
|
||||
VERIFY_IS_FALSE(profile2->HasGuid());
|
||||
VERIFY_IS_TRUE(profile3->HasGuid());
|
||||
VERIFY_IS_TRUE(profile4->HasGuid());
|
||||
|
||||
VERIFY_ARE_EQUAL(profile3->Guid(), nullGuid);
|
||||
VERIFY_ARE_EQUAL(profile4->Guid(), cmdGuid);
|
||||
}
|
||||
|
||||
void ProfileTests::LayerProfileProperties()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
static constexpr std::string_view profile0String{ R"({
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#000000",
|
||||
"background": "#010101",
|
||||
"selectionBackground": "#010101"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
static constexpr std::string_view profile1String{ R"({
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#020202",
|
||||
"startingDirectory": "C:/"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
static constexpr std::string_view profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"foreground": "#030303",
|
||||
|
@ -172,21 +179,21 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void ProfileTests::LayerProfileIcon()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
static constexpr std::string_view profile0String{ R"({
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": "not-null.png"
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
static constexpr std::string_view profile1String{ R"({
|
||||
"name": "profile1",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": null
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
static constexpr std::string_view profile2String{ R"({
|
||||
"name": "profile2",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
static constexpr std::string_view profile3String{ R"({
|
||||
"name": "profile3",
|
||||
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"icon": "another-real.png"
|
||||
|
@ -228,102 +235,116 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void ProfileTests::LayerProfilesOnArray()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
static constexpr std::string_view inboxProfiles{ R"({
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile0",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
}, {
|
||||
"name" : "profile1",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
}, {
|
||||
"name" : "profile2",
|
||||
"guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
const std::string profile1String{ R"({
|
||||
"name" : "profile1",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile2String{ R"({
|
||||
"name" : "profile2",
|
||||
"guid" : "{6239a42c-2222-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile3String{ R"({
|
||||
"name" : "profile3",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
})" };
|
||||
const std::string profile4String{ R"({
|
||||
"name" : "profile4",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
static constexpr std::string_view userProfiles{ R"({
|
||||
"profiles": [
|
||||
{
|
||||
"name" : "profile3",
|
||||
"guid" : "{6239a42c-0000-49a3-80bd-e8fdd045185c}"
|
||||
}, {
|
||||
"name" : "profile4",
|
||||
"guid" : "{6239a42c-1111-49a3-80bd-e8fdd045185c}"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto profile1Json = VerifyParseSucceeded(profile1String);
|
||||
const auto profile2Json = VerifyParseSucceeded(profile2String);
|
||||
const auto profile3Json = VerifyParseSucceeded(profile3String);
|
||||
const auto profile4Json = VerifyParseSucceeded(profile4String);
|
||||
|
||||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings->_allProfiles.Size());
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
|
||||
settings->_LayerOrCreateProfile(profile0Json);
|
||||
VERIFY_ARE_EQUAL(1u, settings->_allProfiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
|
||||
settings->_LayerOrCreateProfile(profile1Json);
|
||||
VERIFY_ARE_EQUAL(2u, settings->_allProfiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
|
||||
settings->_LayerOrCreateProfile(profile2Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile0", settings->_allProfiles.GetAt(0).Name());
|
||||
|
||||
settings->_LayerOrCreateProfile(profile3Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile3", settings->_allProfiles.GetAt(0).Name());
|
||||
|
||||
settings->_LayerOrCreateProfile(profile4Json);
|
||||
VERIFY_ARE_EQUAL(3u, settings->_allProfiles.Size());
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile0Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile1Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile2Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile3Json));
|
||||
VERIFY_IS_NOT_NULL(settings->_FindMatchingProfile(profile4Json));
|
||||
VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(0).Name());
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userProfiles, inboxProfiles);
|
||||
const auto allProfiles = settings->AllProfiles();
|
||||
VERIFY_ARE_EQUAL(3u, allProfiles.Size());
|
||||
VERIFY_ARE_EQUAL(L"profile3", allProfiles.GetAt(0).Name());
|
||||
VERIFY_ARE_EQUAL(L"profile4", allProfiles.GetAt(1).Name());
|
||||
VERIFY_ARE_EQUAL(L"profile2", allProfiles.GetAt(2).Name());
|
||||
}
|
||||
|
||||
void ProfileTests::DuplicateProfileTest()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"backgroundImage" : "some//path"
|
||||
static constexpr std::string_view userProfiles{ R"({
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"font": {
|
||||
"size": 123
|
||||
}
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"backgroundImage": "file:///some/path",
|
||||
"hidden": false,
|
||||
}
|
||||
]
|
||||
}
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userProfiles);
|
||||
const auto profile = settings->AllProfiles().GetAt(0);
|
||||
const auto duplicatedProfile = settings->DuplicateProfile(profile);
|
||||
|
||||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
// GH#11392: Ensure duplicated profiles properly inherit the base layer, even for nested objects.
|
||||
VERIFY_ARE_EQUAL(123, duplicatedProfile.FontInfo().FontSize());
|
||||
|
||||
settings->_LayerOrCreateProfile(profile0Json);
|
||||
auto duplicatedProfile = settings->DuplicateProfile(*settings->_FindMatchingProfile(profile0Json));
|
||||
duplicatedProfile.Name(L"profile0");
|
||||
duplicatedProfile.Guid(profile.Guid());
|
||||
duplicatedProfile.Name(profile.Name());
|
||||
|
||||
const auto json = winrt::get_self<implementation::Profile>(profile)->ToJson();
|
||||
const auto duplicatedJson = winrt::get_self<implementation::Profile>(duplicatedProfile)->ToJson();
|
||||
VERIFY_ARE_EQUAL(profile0Json, duplicatedJson);
|
||||
VERIFY_ARE_EQUAL(json, duplicatedJson, til::u8u16(toString(duplicatedJson)).c_str());
|
||||
}
|
||||
|
||||
void ProfileTests::TestGenGuidsForProfiles()
|
||||
{
|
||||
// We'll generate GUIDs in the Profile::Guid getter. We should make sure that
|
||||
// the GUID generated for a dynamic profile (with a source) is different
|
||||
// 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"({
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile0",
|
||||
"source": "Terminal.App.UnitTest.0",
|
||||
},
|
||||
{
|
||||
"name": "profile0"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(userSettings, inboxSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings->AllProfiles().Size());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(0).Name());
|
||||
VERIFY_IS_TRUE(settings->AllProfiles().GetAt(0).HasGuid());
|
||||
VERIFY_IS_FALSE(settings->AllProfiles().GetAt(0).Source().empty());
|
||||
|
||||
VERIFY_ARE_EQUAL(L"profile0", settings->AllProfiles().GetAt(1).Name());
|
||||
VERIFY_IS_TRUE(settings->AllProfiles().GetAt(1).HasGuid());
|
||||
VERIFY_IS_TRUE(settings->AllProfiles().GetAt(1).Source().empty());
|
||||
|
||||
VERIFY_ARE_NOT_EQUAL(settings->AllProfiles().GetAt(0).Guid(), settings->AllProfiles().GetAt(1).Guid());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "JsonTestClass.h"
|
||||
#include "TestUtils.h"
|
||||
#include <defaults.h>
|
||||
#include "../ut_app/TestDynamicProfileGenerator.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace WEX::Logging;
|
||||
|
@ -43,13 +42,6 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(CascadiaSettings);
|
||||
TEST_METHOD(LegacyFontSettings);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
InitializeJsonWriter();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Method Description:
|
||||
// - deserializes and reserializes a json string representing a settings object model of type T
|
||||
|
@ -61,7 +53,7 @@ namespace SettingsModelLocalTests
|
|||
// Return Value:
|
||||
// - the JsonObject representing this instance
|
||||
template<typename T>
|
||||
void RoundtripTest(const std::string& jsonString)
|
||||
void RoundtripTest(const std::string_view& jsonString)
|
||||
{
|
||||
const auto json{ VerifyParseSucceeded(jsonString) };
|
||||
const auto settings{ T::FromJson(json) };
|
||||
|
@ -77,7 +69,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::GlobalSettings()
|
||||
{
|
||||
const std::string globalsString{ R"(
|
||||
static constexpr std::string_view globalsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
|
||||
|
@ -105,6 +97,7 @@ namespace SettingsModelLocalTests
|
|||
"confirmCloseAllTabs": true,
|
||||
"largePasteWarning": true,
|
||||
"multiLinePasteWarning": true,
|
||||
"trimPaste": true,
|
||||
|
||||
"experimental.input.forceVT": false,
|
||||
"experimental.rendering.forceFullRepaint": false,
|
||||
|
@ -113,7 +106,7 @@ namespace SettingsModelLocalTests
|
|||
"actions": []
|
||||
})" };
|
||||
|
||||
const std::string smallGlobalsString{ R"(
|
||||
static constexpr std::string_view smallGlobalsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
"actions": []
|
||||
|
@ -125,7 +118,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::Profile()
|
||||
{
|
||||
const std::string profileString{ R"(
|
||||
static constexpr std::string_view profileString{ R"(
|
||||
{
|
||||
"name": "Windows PowerShell",
|
||||
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
|
||||
|
@ -160,7 +153,7 @@ namespace SettingsModelLocalTests
|
|||
"selectionBackground": "#CCAABB",
|
||||
|
||||
"useAcrylic": false,
|
||||
"acrylicOpacity": 0.5,
|
||||
"opacity": 50,
|
||||
|
||||
"backgroundImage": "made_you_look.jpeg",
|
||||
"backgroundImageStretchMode": "uniformToFill",
|
||||
|
@ -175,7 +168,7 @@ namespace SettingsModelLocalTests
|
|||
"experimental.retroTerminalEffect": false
|
||||
})" };
|
||||
|
||||
const std::string smallProfileString{ R"(
|
||||
static constexpr std::string_view smallProfileString{ R"(
|
||||
{
|
||||
"name": "Custom Profile"
|
||||
})" };
|
||||
|
@ -183,7 +176,7 @@ namespace SettingsModelLocalTests
|
|||
// 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 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}",
|
||||
"name": "Weird Profile",
|
||||
|
@ -200,7 +193,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::ColorScheme()
|
||||
{
|
||||
const std::string schemeString{ R"({
|
||||
static constexpr std::string_view schemeString{ R"({
|
||||
"name": "Campbell",
|
||||
|
||||
"cursorColor": "#FFFFFF",
|
||||
|
@ -233,56 +226,56 @@ namespace SettingsModelLocalTests
|
|||
void SerializationTests::Actions()
|
||||
{
|
||||
// simple command
|
||||
const std::string actionsString1{ R"([
|
||||
static constexpr std::string_view actionsString1{ R"([
|
||||
{ "command": "paste" }
|
||||
])" };
|
||||
|
||||
// complex command
|
||||
const std::string actionsString2A{ R"([
|
||||
static constexpr std::string_view actionsString2A{ R"([
|
||||
{ "command": { "action": "setTabColor" } }
|
||||
])" };
|
||||
const std::string actionsString2B{ R"([
|
||||
static constexpr std::string_view actionsString2B{ R"([
|
||||
{ "command": { "action": "setTabColor", "color": "#112233" } }
|
||||
])" };
|
||||
const std::string actionsString2C{ R"([
|
||||
static constexpr std::string_view actionsString2C{ R"([
|
||||
{ "command": { "action": "copy" } },
|
||||
{ "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } }
|
||||
])" };
|
||||
|
||||
// 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+b" }
|
||||
])" };
|
||||
|
||||
// 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+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": "prev" }, "keys": "ctrl+shift+r" }
|
||||
])" };
|
||||
|
||||
// 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" },
|
||||
{ "command": "scrollToTop", "keys": "ctrl+f" }
|
||||
])" };
|
||||
|
||||
// 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" },
|
||||
])" };
|
||||
|
||||
// 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" }
|
||||
])" };
|
||||
|
||||
// nested command
|
||||
const std::string actionsString8{ R"([
|
||||
static constexpr std::string_view actionsString8{ R"([
|
||||
{
|
||||
"name": "Change font size...",
|
||||
"commands": [
|
||||
|
@ -294,7 +287,7 @@ namespace SettingsModelLocalTests
|
|||
])" };
|
||||
|
||||
// iterable command
|
||||
const std::string actionsString9A{ R"([
|
||||
static constexpr std::string_view actionsString9A{ R"([
|
||||
{
|
||||
"name": "New tab",
|
||||
"commands": [
|
||||
|
@ -307,12 +300,12 @@ namespace SettingsModelLocalTests
|
|||
]
|
||||
}
|
||||
])" };
|
||||
const std::string actionsString9B{ R"([
|
||||
static constexpr std::string_view actionsString9B{ R"([
|
||||
{
|
||||
"commands":
|
||||
"commands":
|
||||
[
|
||||
{
|
||||
"command":
|
||||
"command":
|
||||
{
|
||||
"action": "sendInput",
|
||||
"input": "${profile.name}"
|
||||
|
@ -323,15 +316,15 @@ namespace SettingsModelLocalTests
|
|||
"name": "Send Input ..."
|
||||
}
|
||||
])" };
|
||||
const std::string actionsString9C{ R""([
|
||||
static constexpr std::string_view actionsString9C{ R""([
|
||||
{
|
||||
"commands":
|
||||
"commands":
|
||||
[
|
||||
{
|
||||
"commands":
|
||||
"commands":
|
||||
[
|
||||
{
|
||||
"command":
|
||||
"command":
|
||||
{
|
||||
"action": "sendInput",
|
||||
"input": "${profile.name} ${scheme.name}"
|
||||
|
@ -346,9 +339,9 @@ namespace SettingsModelLocalTests
|
|||
"name": "Send Input (Evil) ..."
|
||||
}
|
||||
])"" };
|
||||
const std::string actionsString9D{ R""([
|
||||
static constexpr std::string_view actionsString9D{ R""([
|
||||
{
|
||||
"command":
|
||||
"command":
|
||||
{
|
||||
"action": "newTab",
|
||||
"profile": "${profile.name}"
|
||||
|
@ -360,7 +353,7 @@ namespace SettingsModelLocalTests
|
|||
])"" };
|
||||
|
||||
// unbound command
|
||||
const std::string actionsString10{ R"([
|
||||
static constexpr std::string_view actionsString10{ R"([
|
||||
{ "command": "unbound", "keys": "ctrl+c" }
|
||||
])" };
|
||||
|
||||
|
@ -403,81 +396,77 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void SerializationTests::CascadiaSettings()
|
||||
{
|
||||
const std::string settingsString{ R"({
|
||||
"$schema": "https://aka.ms/terminal-profiles-schema",
|
||||
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
|
||||
"disabledProfileSources": [ "Windows.Terminal.Wsl" ],
|
||||
static constexpr std::string_view settingsString{ R"({
|
||||
"$help" : "https://aka.ms/terminal-documentation",
|
||||
"$schema" : "https://aka.ms/terminal-profiles-schema",
|
||||
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
|
||||
"disabledProfileSources": [ "Windows.Terminal.Wsl" ],
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"font": {
|
||||
"face": "Zamora Code"
|
||||
}
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"font": { "face": "Cascadia Code" },
|
||||
"guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
|
||||
"name": "HowettShell"
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"guid": "{c08b0496-e71c-5503-b84e-3af7a7a6d2a7}",
|
||||
"name": "BhojwaniShell"
|
||||
},
|
||||
{
|
||||
"antialiasingMode": "aliased",
|
||||
"guid": "{fe9df758-ac22-5c20-922d-c7766cdd13af}",
|
||||
"name": "NiksaShell"
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemes": [
|
||||
{
|
||||
"name": "Cinnamon Roll",
|
||||
|
||||
"profiles": {
|
||||
"defaults": {
|
||||
"font": {
|
||||
"face": "Zamora Code"
|
||||
}
|
||||
},
|
||||
"list": [
|
||||
{
|
||||
"font": { "face": "Cascadia Code" },
|
||||
"guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
|
||||
"name": "HowettShell"
|
||||
},
|
||||
{
|
||||
"hidden": true,
|
||||
"name": "BhojwaniShell"
|
||||
},
|
||||
{
|
||||
"antialiasingMode": "aliased",
|
||||
"name": "NiksaShell"
|
||||
}
|
||||
]
|
||||
},
|
||||
"schemes": [
|
||||
{
|
||||
"name": "Cinnamon Roll",
|
||||
"cursorColor": "#FFFFFD",
|
||||
"selectionBackground": "#FFFFFF",
|
||||
|
||||
"cursorColor": "#FFFFFD",
|
||||
"selectionBackground": "#FFFFFF",
|
||||
"background": "#3C0315",
|
||||
"foreground": "#FFFFFD",
|
||||
|
||||
"background": "#3C0315",
|
||||
"foreground": "#FFFFFD",
|
||||
"black": "#282A2E",
|
||||
"blue": "#0170C5",
|
||||
"cyan": "#3F8D83",
|
||||
"green": "#76AB23",
|
||||
"purple": "#7D498F",
|
||||
"red": "#BD0940",
|
||||
"white": "#FFFFFD",
|
||||
"yellow": "#E0DE48",
|
||||
"brightBlack": "#676E7A",
|
||||
"brightBlue": "#5C98C5",
|
||||
"brightCyan": "#8ABEB7",
|
||||
"brightGreen": "#B5D680",
|
||||
"brightPurple": "#AC79BB",
|
||||
"brightRed": "#BD6D85",
|
||||
"brightWhite": "#FFFFFD",
|
||||
"brightYellow": "#FFFD76"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{ "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" }
|
||||
]
|
||||
})" };
|
||||
|
||||
"black": "#282A2E",
|
||||
"blue": "#0170C5",
|
||||
"cyan": "#3F8D83",
|
||||
"green": "#76AB23",
|
||||
"purple": "#7D498F",
|
||||
"red": "#BD0940",
|
||||
"white": "#FFFFFD",
|
||||
"yellow": "#E0DE48",
|
||||
"brightBlack": "#676E7A",
|
||||
"brightBlue": "#5C98C5",
|
||||
"brightCyan": "#8ABEB7",
|
||||
"brightGreen": "#B5D680",
|
||||
"brightPurple": "#AC79BB",
|
||||
"brightRed": "#BD6D85",
|
||||
"brightWhite": "#FFFFFD",
|
||||
"brightYellow": "#FFFD76"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{ "command": { "action": "renameTab", "title": "Liang Tab" }, "keys": "ctrl+t" },
|
||||
{ "command": { "action": "sendInput", "input": "VT Griese Mode" }, "keys": "ctrl+k" },
|
||||
{ "command": { "action": "renameWindow", "name": "Hecker Window" }, "keys": "ctrl+l" }
|
||||
]
|
||||
})" };
|
||||
|
||||
auto settings{ winrt::make_self<implementation::CascadiaSettings>(false) };
|
||||
settings->_ParseJsonString(settingsString, false);
|
||||
settings->_ApplyDefaultsFromUserSettings();
|
||||
settings->LayerJson(settings->_userSettings);
|
||||
settings->_ValidateSettings();
|
||||
const auto settings{ winrt::make_self<implementation::CascadiaSettings>(settingsString) };
|
||||
|
||||
const auto result{ settings->ToJson() };
|
||||
VERIFY_ARE_EQUAL(toString(settings->_userSettings), toString(result));
|
||||
VERIFY_ARE_EQUAL(toString(VerifyParseSucceeded(settingsString)), toString(result));
|
||||
}
|
||||
|
||||
void SerializationTests::LegacyFontSettings()
|
||||
{
|
||||
const std::string profileString{ R"(
|
||||
static constexpr std::string_view profileString{ R"(
|
||||
{
|
||||
"name": "Profile with legacy font settings",
|
||||
|
||||
|
@ -486,7 +475,7 @@ namespace SettingsModelLocalTests
|
|||
"fontWeight": "normal"
|
||||
})" };
|
||||
|
||||
const std::string expectedOutput{ R"(
|
||||
static constexpr std::string_view expectedOutput{ R"(
|
||||
{
|
||||
"name": "Profile with legacy font settings",
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "pch.h"
|
||||
|
||||
#include <til/rand.h>
|
||||
|
||||
#include "../TerminalSettingsModel/CascadiaSettings.h"
|
||||
#include "../TerminalSettingsModel/TerminalSettings.h"
|
||||
#include "TestUtils.h"
|
||||
|
@ -34,14 +36,12 @@ namespace SettingsModelLocalTests
|
|||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(TryCreateWinRTType);
|
||||
|
||||
TEST_METHOD(TestTerminalArgsForBinding);
|
||||
|
||||
TEST_METHOD(CommandLineToArgvW);
|
||||
TEST_METHOD(GetProfileForArgsWithCommandline);
|
||||
TEST_METHOD(MakeSettingsForProfile);
|
||||
TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist);
|
||||
|
||||
TEST_METHOD(TestLayerProfileOnColorScheme);
|
||||
|
||||
TEST_METHOD(TestCommandlineToTitlePromotion);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
|
@ -60,9 +60,142 @@ namespace SettingsModelLocalTests
|
|||
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()
|
||||
{
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::string_view settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": { "list": [
|
||||
|
@ -106,12 +239,12 @@ namespace SettingsModelLocalTests
|
|||
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
|
||||
const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}") };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
|
||||
|
||||
auto actionMap = settings.GlobalSettings().ActionMap();
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
auto actionMap = settings->GlobalSettings().ActionMap();
|
||||
VERIFY_ARE_EQUAL(3u, settings->ActiveProfiles().Size());
|
||||
|
||||
const auto profile2Guid = settings.ActiveProfiles().GetAt(2).Guid();
|
||||
const auto profile2Guid = settings->ActiveProfiles().GetAt(2).Guid();
|
||||
VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
|
||||
|
||||
const auto& actionMapImpl{ winrt::get_self<implementation::ActionMap>(actionMap) };
|
||||
|
@ -131,8 +264,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
|
@ -153,8 +286,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
|
@ -175,8 +308,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
|
||||
|
@ -197,8 +330,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
|
@ -219,13 +352,13 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
|
||||
{
|
||||
// This action specified a command but no profile; it gets reassigned to the base profile
|
||||
VERIFY_ARE_EQUAL(settings.ProfileDefaults(), profile);
|
||||
VERIFY_ARE_EQUAL(settings->ProfileDefaults(), profile);
|
||||
VERIFY_ARE_EQUAL(29, termSettings.HistorySize());
|
||||
}
|
||||
else
|
||||
|
@ -251,8 +384,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", realArgs.TerminalArgs().Commandline());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
|
@ -271,8 +404,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
|
@ -292,8 +425,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
|
@ -315,8 +448,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"c:\\foo", realArgs.TerminalArgs().StartingDirectory());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
|
@ -337,8 +470,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid0, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
|
||||
|
@ -360,8 +493,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(profile2Guid, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
|
||||
|
@ -385,8 +518,8 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"bar", realArgs.TerminalArgs().TabTitle());
|
||||
VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
|
||||
|
||||
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto profile{ settings->GetProfileForArgs(realArgs.TerminalArgs()) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, realArgs.TerminalArgs(), nullptr) };
|
||||
const auto termSettings = settingsStruct.DefaultSettings();
|
||||
VERIFY_ARE_EQUAL(guid1, profile.Guid());
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
|
||||
|
@ -399,7 +532,7 @@ namespace SettingsModelLocalTests
|
|||
void TerminalSettingsTests::MakeSettingsForProfile()
|
||||
{
|
||||
// Test that making settings generally works.
|
||||
const std::string settingsString{ R"(
|
||||
static constexpr std::string_view settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -415,17 +548,17 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
]
|
||||
})" };
|
||||
CascadiaSettings settings{ til::u8u16(settingsString) };
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsString);
|
||||
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid2 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
const auto profile1 = settings.FindProfile(guid1);
|
||||
const auto profile2 = settings.FindProfile(guid2);
|
||||
const auto profile1 = settings->FindProfile(guid1);
|
||||
const auto profile2 = settings->FindProfile(guid2);
|
||||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile1, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(*settings, profile1, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(1, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
|
@ -436,7 +569,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
try
|
||||
{
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(settings, profile2, nullptr) };
|
||||
auto terminalSettings{ TerminalSettings::CreateWithProfile(*settings, profile2, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, terminalSettings);
|
||||
VERIFY_ARE_EQUAL(2, terminalSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
|
@ -447,7 +580,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
try
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
|
@ -463,7 +596,7 @@ namespace SettingsModelLocalTests
|
|||
// defaultProfile that's not in the list, we validate the settings, and
|
||||
// then call MakeSettings(nullopt). The validation should ensure that
|
||||
// the default profile is something reasonable
|
||||
const std::string settingsString{ R"(
|
||||
static constexpr std::string_view settingsString{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -479,14 +612,14 @@ namespace SettingsModelLocalTests
|
|||
}
|
||||
]
|
||||
})" };
|
||||
CascadiaSettings settings{ til::u8u16(settingsString) };
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsString);
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings.ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(settings.GlobalSettings().DefaultProfile(), settings.ActiveProfiles().GetAt(0).Guid());
|
||||
VERIFY_ARE_EQUAL(2u, settings->Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings->ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(settings->GlobalSettings().DefaultProfile(), settings->ActiveProfiles().GetAt(0).Guid());
|
||||
try
|
||||
{
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(settings, nullptr, nullptr) };
|
||||
const auto termSettings{ TerminalSettings::CreateWithNewTerminalArgs(*settings, nullptr, nullptr) };
|
||||
VERIFY_ARE_NOT_EQUAL(nullptr, termSettings);
|
||||
VERIFY_ARE_EQUAL(1, termSettings.DefaultSettings().HistorySize());
|
||||
}
|
||||
|
@ -501,7 +634,7 @@ namespace SettingsModelLocalTests
|
|||
Log::Comment(NoThrowString().Format(
|
||||
L"Ensure that setting (or not) a property in the profile that should override a property of the color scheme works correctly."));
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
static constexpr std::string_view settings0String{ R"(
|
||||
{
|
||||
"defaultProfile": "profile5",
|
||||
"profiles": [
|
||||
|
@ -534,18 +667,50 @@ namespace SettingsModelLocalTests
|
|||
"schemes": [
|
||||
{
|
||||
"name": "schemeWithCursorColor",
|
||||
"cursorColor": "#123456"
|
||||
"cursorColor": "#123456",
|
||||
"black": "#121314",
|
||||
"red": "#121314",
|
||||
"green": "#121314",
|
||||
"yellow": "#121314",
|
||||
"blue": "#121314",
|
||||
"purple": "#121314",
|
||||
"cyan": "#121314",
|
||||
"white": "#121314",
|
||||
"brightBlack": "#121314",
|
||||
"brightRed": "#121314",
|
||||
"brightGreen": "#121314",
|
||||
"brightYellow": "#121314",
|
||||
"brightBlue": "#121314",
|
||||
"brightPurple": "#121314",
|
||||
"brightCyan": "#121314",
|
||||
"brightWhite": "#121314"
|
||||
},
|
||||
{
|
||||
"name": "schemeWithoutCursorColor"
|
||||
"name": "schemeWithoutCursorColor",
|
||||
"black": "#121314",
|
||||
"red": "#121314",
|
||||
"green": "#121314",
|
||||
"yellow": "#121314",
|
||||
"blue": "#121314",
|
||||
"purple": "#121314",
|
||||
"cyan": "#121314",
|
||||
"white": "#121314",
|
||||
"brightBlack": "#121314",
|
||||
"brightRed": "#121314",
|
||||
"brightGreen": "#121314",
|
||||
"brightYellow": "#121314",
|
||||
"brightBlue": "#121314",
|
||||
"brightPurple": "#121314",
|
||||
"brightCyan": "#121314",
|
||||
"brightWhite": "#121314"
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settings0String) };
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settings0String);
|
||||
|
||||
VERIFY_ARE_EQUAL(6u, settings.ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings.GlobalSettings().ColorSchemes().Size());
|
||||
VERIFY_ARE_EQUAL(6u, settings->ActiveProfiles().Size());
|
||||
VERIFY_ARE_EQUAL(2u, settings->GlobalSettings().ColorSchemes().Size());
|
||||
|
||||
auto createTerminalSettings = [&](const auto& profile, const auto& schemes) {
|
||||
auto terminalSettings{ winrt::make_self<implementation::TerminalSettings>() };
|
||||
|
@ -554,12 +719,14 @@ namespace SettingsModelLocalTests
|
|||
return terminalSettings;
|
||||
};
|
||||
|
||||
auto terminalSettings0 = createTerminalSettings(settings.ActiveProfiles().GetAt(0), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings1 = createTerminalSettings(settings.ActiveProfiles().GetAt(1), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings2 = createTerminalSettings(settings.ActiveProfiles().GetAt(2), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings3 = createTerminalSettings(settings.ActiveProfiles().GetAt(3), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings4 = createTerminalSettings(settings.ActiveProfiles().GetAt(4), settings.GlobalSettings().ColorSchemes());
|
||||
auto terminalSettings5 = createTerminalSettings(settings.ActiveProfiles().GetAt(5), settings.GlobalSettings().ColorSchemes());
|
||||
const auto activeProfiles = settings->ActiveProfiles();
|
||||
const auto colorSchemes = settings->GlobalSettings().ColorSchemes();
|
||||
const auto terminalSettings0 = createTerminalSettings(activeProfiles.GetAt(0), colorSchemes);
|
||||
const auto terminalSettings1 = createTerminalSettings(activeProfiles.GetAt(1), colorSchemes);
|
||||
const auto terminalSettings2 = createTerminalSettings(activeProfiles.GetAt(2), colorSchemes);
|
||||
const auto terminalSettings3 = createTerminalSettings(activeProfiles.GetAt(3), colorSchemes);
|
||||
const auto terminalSettings4 = createTerminalSettings(activeProfiles.GetAt(4), colorSchemes);
|
||||
const auto terminalSettings5 = createTerminalSettings(activeProfiles.GetAt(5), colorSchemes);
|
||||
|
||||
VERIFY_ARE_EQUAL(ARGB(0, 0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme
|
||||
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1->CursorColor()); // default
|
||||
|
@ -571,7 +738,7 @@ namespace SettingsModelLocalTests
|
|||
|
||||
void TerminalSettingsTests::TestCommandlineToTitlePromotion()
|
||||
{
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::string_view settingsJson{ R"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": { "list": [
|
||||
|
@ -587,65 +754,63 @@ namespace SettingsModelLocalTests
|
|||
} }
|
||||
})" };
|
||||
|
||||
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
const auto settings = winrt::make_self<implementation::CascadiaSettings>(settingsJson);
|
||||
|
||||
{ // just a profile (profile wins)
|
||||
NewTerminalArgs args{};
|
||||
args.Profile(L"profile0");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // profile and command line -> no promotion (profile wins)
|
||||
NewTerminalArgs args{};
|
||||
args.Profile(L"profile0");
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // just a title -> it is propagated
|
||||
NewTerminalArgs args{};
|
||||
args.TabTitle(L"Analog Kid");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // title and command line -> no promotion
|
||||
NewTerminalArgs args{};
|
||||
args.TabTitle(L"Digital Man");
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{ // just a commandline -> promotion
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"foo.exe");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
// various typesof commandline follow
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"foo.exe bar");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"\"foo exe.exe\" bar");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L"\"\" grand designs");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
args.Commandline(L" imagine a man");
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
|
||||
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(*settings, args, nullptr) };
|
||||
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1031,9 +1031,11 @@ namespace TerminalAppLocalTests
|
|||
// The first action is going to always be a new-tab 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_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{};
|
||||
|
@ -1047,7 +1049,9 @@ namespace TerminalAppLocalTests
|
|||
|
||||
auto actionAndArgs = appArgs._startupActions.at(1);
|
||||
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{};
|
||||
|
|
|
@ -16,6 +16,31 @@ using namespace winrt::Microsoft::Terminal::Control;
|
|||
|
||||
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:
|
||||
// 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
|
||||
|
@ -79,7 +104,7 @@ namespace TerminalAppLocalTests
|
|||
// containing a ${profile.name} to replace. When we expand it, it should
|
||||
// have created one command for each profile.
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -107,14 +132,10 @@ namespace TerminalAppLocalTests
|
|||
"iterateOn": "profiles",
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}" }
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto guid0 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
||||
|
@ -207,7 +228,7 @@ namespace TerminalAppLocalTests
|
|||
// For this test, put an iterable command without a given `name` to
|
||||
// replace. When we expand it, it should still work.
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -234,14 +255,10 @@ namespace TerminalAppLocalTests
|
|||
"iterateOn": "profiles",
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}" }
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto guid0 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
||||
|
@ -335,7 +352,7 @@ namespace TerminalAppLocalTests
|
|||
// cause bad json to be filled in. Something like a profile with a name
|
||||
// of "Foo\"", so the trailing '"' might break the json parsing.
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -363,14 +380,10 @@ namespace TerminalAppLocalTests
|
|||
"iterateOn": "profiles",
|
||||
"command": { "action": "splitPane", "profile": "${profile.name}" }
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto guid0 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}");
|
||||
const auto guid1 = ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
|
||||
|
@ -468,7 +481,7 @@ namespace TerminalAppLocalTests
|
|||
// ├─ first.com
|
||||
// └─ second.com
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -504,11 +517,10 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -558,7 +570,7 @@ namespace TerminalAppLocalTests
|
|||
// ├─ child1
|
||||
// └─ child2
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -599,11 +611,10 @@ namespace TerminalAppLocalTests
|
|||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -691,7 +702,7 @@ namespace TerminalAppLocalTests
|
|||
// ├─ Split pane, direction: right, profile: profile2
|
||||
// └─ Split pane, direction: down, profile: profile2
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -723,11 +734,10 @@ namespace TerminalAppLocalTests
|
|||
{ "command": { "action": "splitPane", "profile": "${profile.name}", "split": "down" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -828,7 +838,7 @@ namespace TerminalAppLocalTests
|
|||
// ├─ Profile 2
|
||||
// └─ Profile 3
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -860,11 +870,10 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -926,7 +935,7 @@ namespace TerminalAppLocalTests
|
|||
// ├─ Split right
|
||||
// └─ Split down
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -963,11 +972,10 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemes": [ { "name": "Campbell" } ] // This is included here to prevent settings validation errors.
|
||||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(settingsJson) };
|
||||
CascadiaSettings settings{ settingsJson, inboxSettings };
|
||||
|
||||
VERIFY_ARE_EQUAL(0u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
@ -1071,7 +1079,7 @@ namespace TerminalAppLocalTests
|
|||
// containing a ${profile.name} to replace. When we expand it, it should
|
||||
// have created one command for each profile.
|
||||
|
||||
const std::string settingsJson{ R"(
|
||||
static constexpr std::wstring_view settingsJson{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -1094,9 +1102,72 @@ namespace TerminalAppLocalTests
|
|||
}
|
||||
],
|
||||
"schemes": [
|
||||
{ "name": "scheme_0" },
|
||||
{ "name": "scheme_1" },
|
||||
{ "name": "scheme_2" },
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
|
@ -1107,11 +1178,7 @@ namespace TerminalAppLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings{ til::u8u16(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());
|
||||
CascadiaSettings settings{ settingsJson, {} };
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
|
||||
|
||||
|
@ -1139,8 +1206,6 @@ namespace TerminalAppLocalTests
|
|||
auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes());
|
||||
_logCommandNames(expandedCommands.GetView());
|
||||
|
||||
// This is the same warning as above
|
||||
VERIFY_ARE_EQUAL(1u, settings.Warnings().Size());
|
||||
VERIFY_ARE_EQUAL(3u, expandedCommands.Size());
|
||||
|
||||
// Yes, this test is testing splitPane with profiles named after each
|
||||
|
@ -1148,7 +1213,7 @@ namespace TerminalAppLocalTests
|
|||
// 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);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
@ -1162,11 +1227,11 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().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);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
@ -1180,11 +1245,11 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().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);
|
||||
auto actionAndArgs = command.ActionAndArgs();
|
||||
VERIFY_IS_NOT_NULL(actionAndArgs);
|
||||
|
@ -1198,7 +1263,7 @@ namespace TerminalAppLocalTests
|
|||
VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -311,7 +311,7 @@ namespace TerminalAppLocalTests
|
|||
// TerminalPage and not only create them successfully, but also create a
|
||||
// tab using those settings successfully.
|
||||
|
||||
const std::string settingsJson0{ R"(
|
||||
static constexpr std::wstring_view settingsJson0{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -328,7 +328,7 @@ namespace TerminalAppLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
||||
CascadiaSettings settings0{ settingsJson0, {} };
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
|
||||
// This is super wacky, but we can't just initialize the
|
||||
|
@ -357,7 +357,7 @@ namespace TerminalAppLocalTests
|
|||
//
|
||||
// Created to test GH#2455
|
||||
|
||||
const std::string settingsJson0{ R"(
|
||||
static constexpr std::wstring_view settingsJson0{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -374,7 +374,7 @@ namespace TerminalAppLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
const std::string settingsJson1{ R"(
|
||||
static constexpr std::wstring_view settingsJson1{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -386,10 +386,10 @@ namespace TerminalAppLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
||||
CascadiaSettings settings0{ settingsJson0, {} };
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
|
||||
CascadiaSettings settings1{ til::u8u16(settingsJson1) };
|
||||
CascadiaSettings settings1{ settingsJson1, {} };
|
||||
VERIFY_IS_NOT_NULL(settings1);
|
||||
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
|
@ -444,7 +444,7 @@ namespace TerminalAppLocalTests
|
|||
//
|
||||
// Created to test GH#2455
|
||||
|
||||
const std::string settingsJson0{ R"(
|
||||
static constexpr std::wstring_view settingsJson0{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -461,7 +461,7 @@ namespace TerminalAppLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
const std::string settingsJson1{ R"(
|
||||
static constexpr std::wstring_view settingsJson1{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"profiles": [
|
||||
|
@ -473,10 +473,10 @@ namespace TerminalAppLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
||||
CascadiaSettings settings0{ settingsJson0, {} };
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
|
||||
CascadiaSettings settings1{ til::u8u16(settingsJson1) };
|
||||
CascadiaSettings settings1{ settingsJson1, {} };
|
||||
VERIFY_IS_NOT_NULL(settings1);
|
||||
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
|
@ -508,7 +508,7 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(NoThrowString().Format(L"Duplicate the first pane"));
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr);
|
||||
page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, true, nullptr));
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
|
@ -526,7 +526,7 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash"));
|
||||
result = RunOnUIThread([&page]() {
|
||||
page->_SplitPane(SplitDirection::Automatic, SplitType::Duplicate, 0.5f, nullptr);
|
||||
page->_SplitPane(SplitDirection::Automatic, 0.5f, page->_MakePane(nullptr, true, nullptr));
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
|
@ -558,7 +558,7 @@ namespace TerminalAppLocalTests
|
|||
// - The initialized TerminalPage, ready to use.
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> TabTests::_commonSetup()
|
||||
{
|
||||
const std::string settingsJson0{ R"(
|
||||
static constexpr std::wstring_view settingsJson0{ LR"(
|
||||
{
|
||||
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
|
||||
"showTabsInTitlebar": false,
|
||||
|
@ -659,7 +659,7 @@ namespace TerminalAppLocalTests
|
|||
]
|
||||
})" };
|
||||
|
||||
CascadiaSettings settings0{ til::u8u16(settingsJson0) };
|
||||
CascadiaSettings settings0{ settingsJson0, {} };
|
||||
VERIFY_IS_NOT_NULL(settings0);
|
||||
|
||||
const auto guid1 = Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}");
|
||||
|
@ -751,7 +751,7 @@ namespace TerminalAppLocalTests
|
|||
});
|
||||
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]() {
|
||||
// Set up action
|
||||
MoveFocusArgs args{ FocusDirection::Left };
|
||||
|
@ -761,7 +761,7 @@ namespace TerminalAppLocalTests
|
|||
|
||||
auto firstTab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
VERIFY_IS_TRUE(firstTab->IsZoomed());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
}
|
||||
|
@ -844,7 +844,7 @@ namespace TerminalAppLocalTests
|
|||
// | 1 | 2 |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitDirection::Right, SplitType::Duplicate, 0.5f, nullptr);
|
||||
page->_SplitPane(SplitDirection::Right, 0.5f, page->_MakePane(nullptr, true, nullptr));
|
||||
secondId = tab->_activePane->Id().value();
|
||||
});
|
||||
Sleep(250);
|
||||
|
@ -862,7 +862,7 @@ namespace TerminalAppLocalTests
|
|||
// | 3 | |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr);
|
||||
page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, true, nullptr));
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
// Split again to make the 3rd tab
|
||||
thirdId = tab->_activePane->Id().value();
|
||||
|
@ -882,7 +882,7 @@ namespace TerminalAppLocalTests
|
|||
// | 3 | 4 |
|
||||
// | | |
|
||||
// -------------------
|
||||
page->_SplitPane(SplitDirection::Down, SplitType::Duplicate, 0.5f, nullptr);
|
||||
page->_SplitPane(SplitDirection::Down, 0.5f, page->_MakePane(nullptr, true, nullptr));
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
fourthId = tab->_activePane->Id().value();
|
||||
});
|
||||
|
@ -1357,7 +1357,8 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(L"Color should be changed to the preview");
|
||||
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]() {
|
||||
|
@ -1383,7 +1384,8 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(L"Color should be changed");
|
||||
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");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
|
@ -1451,7 +1452,6 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(L"Color should be the same as it originally was");
|
||||
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");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
|
@ -1522,7 +1521,6 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(L"Color should be changed to the preview");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(originalSettings, page->_originalSettings);
|
||||
});
|
||||
|
||||
TestOnUIThread([&page]() {
|
||||
|
@ -1548,7 +1546,6 @@ namespace TerminalAppLocalTests
|
|||
|
||||
Log::Comment(L"Color should be changed");
|
||||
VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground());
|
||||
VERIFY_ARE_EQUAL(nullptr, page->_originalSettings);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.0" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug" MinVersion="14.0.27023.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
<PackageDependency Name="Microsoft.VCLibs.140.00.Debug.UWPDesktop" MinVersion="14.0.27027.1" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" />
|
||||
</Dependencies>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<uap:SupportedUsers>multiple</uap:SupportedUsers>
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.22000.0" />
|
||||
</Dependencies>
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
|
|
|
@ -239,8 +239,8 @@ HRESULT HwndTerminal::Initialize()
|
|||
_terminal->SetBackgroundCallback([](auto) {});
|
||||
|
||||
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
|
||||
_terminal->SetDefaultBackground(RGB(12, 12, 12));
|
||||
_terminal->SetDefaultForeground(RGB(204, 204, 204));
|
||||
_terminal->SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, RGB(12, 12, 12));
|
||||
_terminal->SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, RGB(204, 204, 204));
|
||||
_terminal->SetWriteInputCallback([=](std::wstring& input) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
|
@ -549,11 +549,11 @@ try
|
|||
|
||||
if (multiClickMapper == 3)
|
||||
{
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansionMode::Line);
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansion::Line);
|
||||
}
|
||||
else if (multiClickMapper == 2)
|
||||
{
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansionMode::Word);
|
||||
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansion::Word);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -781,8 +781,8 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
|||
{
|
||||
auto lock = publicTerminal->_terminal->LockForWriting();
|
||||
|
||||
publicTerminal->_terminal->SetDefaultForeground(theme.DefaultForeground);
|
||||
publicTerminal->_terminal->SetDefaultBackground(theme.DefaultBackground);
|
||||
publicTerminal->_terminal->SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, theme.DefaultForeground);
|
||||
publicTerminal->_terminal->SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, theme.DefaultBackground);
|
||||
publicTerminal->_renderEngine->SetSelectionBackground(theme.DefaultSelectionBackground, theme.SelectionBackgroundAlpha);
|
||||
|
||||
// Set the font colors
|
||||
|
@ -977,7 +977,7 @@ void HwndTerminal::_StringPaste(const wchar_t* const pData) noexcept
|
|||
CATCH_LOG();
|
||||
}
|
||||
|
||||
COORD HwndTerminal::GetFontSize() const
|
||||
COORD HwndTerminal::GetFontSize() const noexcept
|
||||
{
|
||||
return _actualFont.GetSize();
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ private:
|
|||
void _SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept;
|
||||
|
||||
// Inherited via IControlAccessibilityInfo
|
||||
COORD GetFontSize() const override;
|
||||
COORD GetFontSize() const noexcept override;
|
||||
RECT GetBounds() const noexcept override;
|
||||
double GetScaleFactor() const noexcept override;
|
||||
void ChangeViewport(const SMALL_RECT NewWindow) override;
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
<ProjectReference Include="$(SolutionDir)src\types\lib\types.vcxproj">
|
||||
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\atlas\atlas.vcxproj">
|
||||
<Project>{8222900C-8B6C-452A-91AC-BE95DB04B95F}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\base\lib\base.vcxproj">
|
||||
<Project>{af0a096a-8b3a-4949-81ef-7df8f0fee91f}</Project>
|
||||
</ProjectReference>
|
||||
|
|
5
src/cascadia/Remoting/GetWindowLayoutArgs.cpp
Normal file
5
src/cascadia/Remoting/GetWindowLayoutArgs.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
#include "pch.h"
|
||||
#include "GetWindowLayoutArgs.h"
|
||||
#include "GetWindowLayoutArgs.g.cpp"
|
32
src/cascadia/Remoting/GetWindowLayoutArgs.h
Normal file
32
src/cascadia/Remoting/GetWindowLayoutArgs.h
Normal 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);
|
||||
}
|
|
@ -12,7 +12,6 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Monarch.h">
|
||||
|
@ -36,6 +35,12 @@
|
|||
<ClInclude Include="WindowActivatedArgs.h">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</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="MonarchFactory.h" />
|
||||
<ClInclude Include="Peasant.h">
|
||||
|
@ -71,6 +76,12 @@
|
|||
<ClCompile Include="WindowActivatedArgs.cpp">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GetWindowLayoutArgs.cpp">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QuitAllRequestedArgs.cpp">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
|
@ -128,6 +139,5 @@
|
|||
</ItemDefinitionGroup>
|
||||
<!-- ========================= Globals ======================== -->
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -6,6 +6,7 @@
|
|||
#include "Monarch.h"
|
||||
#include "CommandlineArgs.h"
|
||||
#include "FindTargetWindowArgs.h"
|
||||
#include "QuitAllRequestedArgs.h"
|
||||
#include "ProposeCommandlineResult.h"
|
||||
|
||||
#include "Monarch.g.cpp"
|
||||
|
@ -50,6 +51,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// - Add the given peasant to the list of peasants we're tracking. This
|
||||
// Peasant may have already been assigned an ID. If it hasn't, then give
|
||||
// it an ID.
|
||||
// - NB: this takes a unique_lock on _peasantsMutex.
|
||||
// Arguments:
|
||||
// - peasant: the new Peasant to track.
|
||||
// Return Value:
|
||||
|
@ -71,10 +73,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
{
|
||||
// Peasant already had an ID (from an older monarch). Leave that one
|
||||
// be. Make sure that the next peasant's ID is higher than it.
|
||||
_nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID;
|
||||
// If multiple peasants are added concurrently we keep trying to update
|
||||
// until we get to set the new id.
|
||||
uint64_t current;
|
||||
do
|
||||
{
|
||||
current = _nextPeasantID.load(std::memory_order_relaxed);
|
||||
} while (current <= providedID && !_nextPeasantID.compare_exchange_weak(current, providedID + 1, std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
auto newPeasantsId = peasant.GetID();
|
||||
|
||||
// Keep track of which peasant we are
|
||||
// SAFETY: this is only true for one peasant, and each peasant
|
||||
// is only added to a monarch once, so we do not need synchronization here.
|
||||
if (peasant.GetPID() == _ourPID)
|
||||
{
|
||||
_ourPeasantId = newPeasantsId;
|
||||
}
|
||||
// Add an event listener to the peasant's WindowActivated event.
|
||||
peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated });
|
||||
peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows });
|
||||
|
@ -84,7 +100,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
peasant.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); });
|
||||
peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll });
|
||||
|
||||
_peasants[newPeasantsId] = peasant;
|
||||
{
|
||||
std::unique_lock lock{ _peasantsMutex };
|
||||
_peasants[newPeasantsId] = peasant;
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_AddPeasant",
|
||||
|
@ -117,16 +136,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// - <none> used
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
winrt::fire_and_forget Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
// Let the process hosting the monarch run any needed logic before
|
||||
// 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);
|
||||
// Tell all peasants to exit.
|
||||
const auto callback = [&](const auto& /*id*/, const auto& p) {
|
||||
p.Quit();
|
||||
const auto callback = [&](const auto& id, const auto& p) {
|
||||
// We want to tell our peasant to quit last, so that we don't try
|
||||
// to perform a bunch of elections on quit.
|
||||
if (id != _ourPeasantId)
|
||||
{
|
||||
p.Quit();
|
||||
}
|
||||
};
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
|
@ -137,18 +168,46 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
};
|
||||
|
||||
_forEachPeasant(callback, onError);
|
||||
|
||||
{
|
||||
std::shared_lock lock{ _peasantsMutex };
|
||||
const auto peasantSearch = _peasants.find(_ourPeasantId);
|
||||
if (peasantSearch != _peasants.end())
|
||||
{
|
||||
peasantSearch->second.Quit();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Somehow we don't have our own peasant, this should never happen.
|
||||
// We are trying to quit anyways so just fail here.
|
||||
assert(peasantSearch != _peasants.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Tells the monarch that a peasant is being closed.
|
||||
// - NB: this (separately) takes unique locks on _peasantsMutex and
|
||||
// _mruPeasantsMutex.
|
||||
// Arguments:
|
||||
// - peasantId: the id of the peasant
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::SignalClose(const uint64_t peasantId)
|
||||
{
|
||||
_clearOldMruEntries(peasantId);
|
||||
_peasants.erase(peasantId);
|
||||
// If we are quitting we don't care about maintaining our list of
|
||||
// peasants anymore, and don't need to notify the host that something
|
||||
// changed.
|
||||
if (_quitting.load(std::memory_order_acquire))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_clearOldMruEntries({ peasantId });
|
||||
{
|
||||
std::unique_lock lock{ _peasantsMutex };
|
||||
_peasants.erase(peasantId);
|
||||
}
|
||||
_WindowClosedHandlers(nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
@ -160,23 +219,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// - the number of active peasants.
|
||||
uint64_t Monarch::GetNumberOfPeasants()
|
||||
{
|
||||
auto num = 0;
|
||||
auto callback = [&](const auto& /*id*/, const auto& p) {
|
||||
// Check that the peasant is alive, and if so increment the count
|
||||
p.GetID();
|
||||
num += 1;
|
||||
};
|
||||
auto onError = [](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_GetNumberOfPeasants_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not enumerate"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(callback, onError);
|
||||
|
||||
return num;
|
||||
std::shared_lock lock{ _peasantsMutex };
|
||||
return _peasants.size();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -197,16 +241,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// Method Description:
|
||||
// - Lookup a peasant by its ID. If the peasant has died, this will also
|
||||
// remove the peasant from our list of peasants.
|
||||
// - NB: this (separately) takes unique locks on _peasantsMutex and
|
||||
// _mruPeasantsMutex.
|
||||
// Arguments:
|
||||
// - peasantID: The ID Of the peasant to find
|
||||
// - clearMruPeasantOnFailure: When true this function will handle clearing
|
||||
// from _mruPeasants if a peasant was not found, otherwise the caller is
|
||||
// expected to handle that cleanup themselves.
|
||||
// Return Value:
|
||||
// - the peasant if it exists in our map, otherwise null
|
||||
Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID)
|
||||
Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID, bool clearMruPeasantOnFailure)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto peasantSearch = _peasants.find(peasantID);
|
||||
auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second;
|
||||
IPeasant maybeThePeasant = nullptr;
|
||||
{
|
||||
std::shared_lock lock{ _peasantsMutex };
|
||||
const auto peasantSearch = _peasants.find(peasantID);
|
||||
maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second;
|
||||
}
|
||||
// Ask the peasant for their PID. This will validate that they're
|
||||
// actually still alive.
|
||||
if (maybeThePeasant)
|
||||
|
@ -218,12 +271,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
// Remove the peasant from the list of peasants
|
||||
_peasants.erase(peasantID);
|
||||
|
||||
// Remove the peasant from the list of MRU windows. They're dead.
|
||||
// They can't be the MRU anymore.
|
||||
_clearOldMruEntries(peasantID);
|
||||
// Remove the peasant from the list of peasants
|
||||
{
|
||||
std::unique_lock lock{ _peasantsMutex };
|
||||
_peasants.erase(peasantID);
|
||||
}
|
||||
|
||||
if (clearMruPeasantOnFailure)
|
||||
{
|
||||
// Remove the peasant from the list of MRU windows. They're dead.
|
||||
// They can't be the MRU anymore.
|
||||
_clearOldMruEntries({ peasantID });
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -244,39 +304,27 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> peasantsToErase{};
|
||||
uint64_t result = 0;
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto otherName = p.WindowName();
|
||||
if (otherName == name)
|
||||
{
|
||||
result = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
// Normally, we'd just erase the peasant here. However, we can't
|
||||
// erase from the map while we're iterating over it like this.
|
||||
// Instead, pull a good ole Java and collect this id for removal
|
||||
// later.
|
||||
peasantsToErase.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the dead peasants we came across while iterating.
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
// Remove the peasant from the list of peasants
|
||||
_peasants.erase(id);
|
||||
// Remove the peasant from the list of MRU windows. They're dead.
|
||||
// They can't be the MRU anymore.
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
const auto callback = [&](const auto& id, const auto& p) {
|
||||
auto otherName = p.WindowName();
|
||||
if (otherName == name)
|
||||
{
|
||||
result = id;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_lookupPeasantIdForName_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not get the name of"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(callback, onError);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_lookupPeasantIdForName",
|
||||
|
@ -328,33 +376,42 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// - Helper for removing a peasant from the list of MRU peasants. We want to
|
||||
// do this both when the peasant dies, and also when the peasant is newly
|
||||
// activated (so that we don't leave an old entry for it in the list).
|
||||
// - NB: This takes a unique lock on _mruPeasantsMutex.
|
||||
// Arguments:
|
||||
// - peasantID: The ID of the peasant to remove from the MRU list
|
||||
// - peasantIds: The list of peasant IDs to remove from the MRU list
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_clearOldMruEntries(const uint64_t peasantID)
|
||||
void Monarch::_clearOldMruEntries(const std::unordered_set<uint64_t>& peasantIds)
|
||||
{
|
||||
auto result = std::find_if(_mruPeasants.begin(),
|
||||
_mruPeasants.end(),
|
||||
[peasantID](auto&& other) {
|
||||
return peasantID == other.PeasantID();
|
||||
});
|
||||
|
||||
if (result != std::end(_mruPeasants))
|
||||
if (peasantIds.size() == 0)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_RemovedPeasantFromDesktop",
|
||||
TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"),
|
||||
TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
_mruPeasants.erase(result);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock lock{ _mruPeasantsMutex };
|
||||
auto partition = std::remove_if(_mruPeasants.begin(), _mruPeasants.end(), [&](const auto& p) {
|
||||
const auto id = p.PeasantID();
|
||||
// remove the element if it was found in the list to erase.
|
||||
if (peasantIds.count(id) == 1)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_RemovedPeasantFromDesktop",
|
||||
TraceLoggingUInt64(id, "peasantID", "The ID of the peasant"),
|
||||
TraceLoggingGuid(p.DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Remove everything that was in the list
|
||||
_mruPeasants.erase(partition, _mruPeasants.end());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Actually handle inserting the WindowActivatedArgs into our list of MRU windows.
|
||||
// - NB: this takes a unique_lock on _mruPeasantsMutex.
|
||||
// Arguments:
|
||||
// - localArgs: an in-proc WindowActivatedArgs that we should add to our list of MRU windows.
|
||||
// Return Value:
|
||||
|
@ -365,19 +422,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
|
||||
// * Check all the current lists to look for this peasant.
|
||||
// remove it from any where it exists.
|
||||
_clearOldMruEntries(localArgs->PeasantID());
|
||||
_clearOldMruEntries({ localArgs->PeasantID() });
|
||||
|
||||
// * If the current desktop doesn't have a vector, add one.
|
||||
const auto desktopGuid{ localArgs->DesktopID() };
|
||||
|
||||
// * Add this args list. By using lower_bound with insert, we can get it
|
||||
// into exactly the right spot, without having to re-sort the whole
|
||||
// array.
|
||||
_mruPeasants.insert(std::lower_bound(_mruPeasants.begin(),
|
||||
_mruPeasants.end(),
|
||||
*localArgs,
|
||||
[](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }),
|
||||
*localArgs);
|
||||
{
|
||||
std::unique_lock lock{ _mruPeasantsMutex };
|
||||
// * Add this args list. By using lower_bound with insert, we can get it
|
||||
// into exactly the right spot, without having to re-sort the whole
|
||||
// array.
|
||||
_mruPeasants.insert(std::lower_bound(_mruPeasants.begin(),
|
||||
_mruPeasants.end(),
|
||||
*localArgs,
|
||||
[](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }),
|
||||
*localArgs);
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SetMostRecentPeasant",
|
||||
|
@ -391,6 +451,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// Method Description:
|
||||
// - Retrieves the ID of the MRU peasant window. If requested, will limit
|
||||
// the search to windows that are on the current desktop.
|
||||
// - NB: This method will hold a shared lock on _mruPeasantsMutex and
|
||||
// potentially a unique_lock on _peasantsMutex at the same time.
|
||||
// Separately it might hold a unique_lock on _mruPeasantsMutex.
|
||||
// Arguments:
|
||||
// - limitToCurrentDesktop: if true, only return the MRU peasant that's
|
||||
// actually on the current desktop.
|
||||
|
@ -403,8 +466,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// - the ID of the most recent peasant, otherwise 0 if we could not find one.
|
||||
uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop, const bool ignoreQuakeWindow)
|
||||
{
|
||||
std::shared_lock lock{ _mruPeasantsMutex };
|
||||
if (_mruPeasants.empty())
|
||||
{
|
||||
// unlock the mruPeasants mutex to make sure we can't deadlock here.
|
||||
lock.unlock();
|
||||
// Only need a shared lock for read
|
||||
std::shared_lock peasantsLock{ _peasantsMutex };
|
||||
// We haven't yet been told the MRU peasant. Just use the first one.
|
||||
// This is just gonna be a random one, but really shouldn't happen
|
||||
// in practice. The WindowManager should set the MRU peasant
|
||||
|
@ -445,15 +513,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// - If it isn't on the current desktop, we'll loop again, on the
|
||||
// following peasant.
|
||||
// * If we don't care, then we'll just return that one.
|
||||
//
|
||||
// We're not just using an iterator because the contents of the list
|
||||
// might change while we're iterating here (if the peasant is dead we'll
|
||||
// remove it from the list).
|
||||
int positionInList = 0;
|
||||
while (_mruPeasants.cbegin() + positionInList < _mruPeasants.cend())
|
||||
uint64_t result = 0;
|
||||
std::unordered_set<uint64_t> peasantsToErase{};
|
||||
for (const auto& mruWindowArgs : _mruPeasants)
|
||||
{
|
||||
const auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) };
|
||||
const auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) };
|
||||
// Try to get the peasant, but do not have _getPeasant clean up old
|
||||
// _mruPeasants because we are iterating here.
|
||||
// SAFETY: _getPeasant can take a unique_lock on _peasantsMutex if
|
||||
// it detects a peasant is dead. Currently _getMostRecentPeasantId
|
||||
// is the only method that holds a lock on both _mruPeasantsMutex and
|
||||
// _peasantsMutex at the same time so there cannot be a deadlock here.
|
||||
const auto peasant{ _getPeasant(mruWindowArgs.PeasantID(), false) };
|
||||
if (!peasant)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
|
@ -467,6 +537,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// We'll go through the loop again. We removed the current one
|
||||
// at positionInList, so the next one in positionInList will be
|
||||
// a new, different peasant.
|
||||
peasantsToErase.emplace(mruWindowArgs.PeasantID());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -504,7 +575,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
"true if this window was in fact on the current desktop"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return mruWindowArgs.PeasantID();
|
||||
result = mruWindowArgs.PeasantID();
|
||||
break;
|
||||
}
|
||||
// If this window wasn't on the current desktop, another one
|
||||
// might be. We'll increment positionInList below, and try
|
||||
|
@ -518,20 +590,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
return mruWindowArgs.PeasantID();
|
||||
result = mruWindowArgs.PeasantID();
|
||||
break;
|
||||
}
|
||||
positionInList++;
|
||||
}
|
||||
|
||||
// Here, we've checked all the windows, and none of them was both alive
|
||||
// and the most recent (on this desktop). Just return 0 - the caller
|
||||
// will use this to create a new window.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_NotFound",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
lock.unlock();
|
||||
|
||||
return 0;
|
||||
if (peasantsToErase.size() > 0)
|
||||
{
|
||||
_clearOldMruEntries(peasantsToErase);
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
// Here, we've checked all the windows, and none of them was both alive
|
||||
// and the most recent (on this desktop). Just return 0 - the caller
|
||||
// will use this to create a new window.
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_getMostRecentPeasantID_NotFound",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -855,7 +937,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
Windows::Foundation::Collections::IVectorView<PeasantInfo> Monarch::GetPeasantInfos()
|
||||
{
|
||||
std::vector<PeasantInfo> names;
|
||||
names.reserve(_peasants.size());
|
||||
{
|
||||
std::shared_lock lock{ _peasantsMutex };
|
||||
names.reserve(_peasants.size());
|
||||
}
|
||||
|
||||
const auto func = [&](const auto& id, const auto& p) -> void {
|
||||
names.push_back({ id, p.WindowName(), p.ActiveTabTitle() });
|
||||
|
@ -916,4 +1001,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
|
||||
_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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "Peasant.h"
|
||||
#include "../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "WindowActivatedArgs.h"
|
||||
#include <atomic>
|
||||
|
||||
// We sure different GUIDs here depending on whether we're running a Release,
|
||||
// Preview, or Dev build. This ensures that different installs don't
|
||||
|
@ -58,34 +59,41 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void SummonAllWindows();
|
||||
bool DoesQuakeWindowExist();
|
||||
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(ShowNotificationIconRequested, 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(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:
|
||||
uint64_t _ourPID;
|
||||
|
||||
uint64_t _nextPeasantID{ 1 };
|
||||
uint64_t _thisPeasantID{ 0 };
|
||||
std::atomic<uint64_t> _nextPeasantID{ 1 };
|
||||
uint64_t _ourPeasantId{ 0 };
|
||||
|
||||
// When we're quitting we do not care as much about handling some events that we know will be triggered
|
||||
std::atomic<bool> _quitting{ false };
|
||||
|
||||
winrt::com_ptr<IVirtualDesktopManager> _desktopManager{ nullptr };
|
||||
|
||||
std::unordered_map<uint64_t, winrt::Microsoft::Terminal::Remoting::IPeasant> _peasants;
|
||||
|
||||
std::vector<Remoting::WindowActivatedArgs> _mruPeasants;
|
||||
// These should not be locked at the same time to prevent deadlocks
|
||||
// unless they are both shared_locks.
|
||||
std::shared_mutex _peasantsMutex{};
|
||||
std::shared_mutex _mruPeasantsMutex{};
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID);
|
||||
winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID, bool clearMruPeasantOnFailure = true);
|
||||
uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop, const bool ignoreQuakeWindow);
|
||||
uint64_t _lookupPeasantIdForName(std::wstring_view name);
|
||||
|
||||
void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
void _doHandleActivatePeasant(const winrt::com_ptr<winrt::Microsoft::Terminal::Remoting::implementation::WindowActivatedArgs>& args);
|
||||
void _clearOldMruEntries(const uint64_t peasantID);
|
||||
void _clearOldMruEntries(const std::unordered_set<uint64_t>& peasantIds);
|
||||
|
||||
void _forAllPeasantsIgnoringTheDead(std::function<void(const winrt::Microsoft::Terminal::Remoting::IPeasant&, const uint64_t)> callback,
|
||||
std::function<void(const uint64_t)> errorCallback);
|
||||
|
@ -96,8 +104,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
|
||||
void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
winrt::fire_and_forget _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
// Method Description:
|
||||
// - Helper for doing something on each and every peasant.
|
||||
|
@ -107,6 +115,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// returns false.
|
||||
// - If any single peasant is dead, then we'll call onError and then add it to a
|
||||
// list of peasants to clean up once the loop ends.
|
||||
// - NB: this (separately) takes unique locks on _peasantsMutex and
|
||||
// _mruPeasantsMutex.
|
||||
// Arguments:
|
||||
// - func: The function to call on each peasant
|
||||
// - onError: The function to call if a peasant is dead.
|
||||
|
@ -119,44 +129,59 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
using R = std::invoke_result_t<F, Map::key_type, Map::mapped_type>;
|
||||
static constexpr auto IsVoid = std::is_void_v<R>;
|
||||
|
||||
std::vector<uint64_t> peasantsToErase;
|
||||
|
||||
for (const auto& [id, p] : _peasants)
|
||||
std::unordered_set<uint64_t> peasantsToErase;
|
||||
{
|
||||
try
|
||||
std::shared_lock lock{ _peasantsMutex };
|
||||
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
if constexpr (IsVoid)
|
||||
try
|
||||
{
|
||||
func(id, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!func(id, p))
|
||||
if constexpr (IsVoid)
|
||||
{
|
||||
break;
|
||||
func(id, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!func(id, p))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error& exception)
|
||||
{
|
||||
onError(id);
|
||||
catch (const winrt::hresult_error& exception)
|
||||
{
|
||||
onError(id);
|
||||
|
||||
if (exception.code() == 0x800706ba) // The RPC server is unavailable.
|
||||
{
|
||||
peasantsToErase.emplace_back(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
throw;
|
||||
if (exception.code() == 0x800706ba) // The RPC server is unavailable.
|
||||
{
|
||||
peasantsToErase.emplace(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& id : peasantsToErase)
|
||||
if (peasantsToErase.size() > 0)
|
||||
{
|
||||
_peasants.erase(id);
|
||||
_clearOldMruEntries(id);
|
||||
// Don't hold a lock on _peasants and _mruPeasants at the same
|
||||
// time to avoid deadlocks.
|
||||
{
|
||||
std::unique_lock lock{ _peasantsMutex };
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
_peasants.erase(id);
|
||||
}
|
||||
}
|
||||
_clearOldMruEntries(peasantsToErase);
|
||||
|
||||
// A peasant died, let the app host know that the number of
|
||||
// windows has changed.
|
||||
_WindowClosedHandlers(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,12 @@ namespace Microsoft.Terminal.Remoting
|
|||
Windows.Foundation.IReference<UInt64> WindowID;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass QuitAllRequestedArgs
|
||||
{
|
||||
QuitAllRequestedArgs();
|
||||
Windows.Foundation.IAsyncAction BeforeQuitAllAction;
|
||||
}
|
||||
|
||||
struct PeasantInfo
|
||||
{
|
||||
UInt64 Id;
|
||||
|
@ -52,12 +58,13 @@ namespace Microsoft.Terminal.Remoting
|
|||
void SummonAllWindows();
|
||||
Boolean DoesQuakeWindowExist();
|
||||
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, Object> ShowNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, QuitAllRequestedArgs> QuitAllRequested;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Peasant.h"
|
||||
#include "CommandlineArgs.h"
|
||||
#include "SummonWindowBehavior.h"
|
||||
#include "GetWindowLayoutArgs.h"
|
||||
#include "Peasant.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
|
@ -289,4 +290,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
|
||||
|
||||
winrt::hstring GetWindowLayout();
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
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(QuitAllRequested, 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:
|
||||
Peasant(const uint64_t testPID);
|
||||
|
|
|
@ -30,6 +30,11 @@ namespace Microsoft.Terminal.Remoting
|
|||
Windows.Foundation.DateTime ActivatedTime { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass GetWindowLayoutArgs {
|
||||
GetWindowLayoutArgs();
|
||||
String WindowLayoutJson;
|
||||
Windows.Foundation.IAsyncOperation<String> WindowLayoutJsonAsync;
|
||||
}
|
||||
|
||||
enum MonitorBehavior
|
||||
{
|
||||
|
@ -69,6 +74,7 @@ namespace Microsoft.Terminal.Remoting
|
|||
void RequestHideNotificationIcon();
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
String GetWindowLayout();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
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, Object> ShowNotificationIconRequested;
|
||||
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> QuitRequested;
|
||||
};
|
||||
|
|
5
src/cascadia/Remoting/QuitAllRequestedArgs.cpp
Normal file
5
src/cascadia/Remoting/QuitAllRequestedArgs.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
#include "pch.h"
|
||||
#include "QuitAllRequestedArgs.h"
|
||||
#include "QuitAllRequestedArgs.g.cpp"
|
30
src/cascadia/Remoting/QuitAllRequestedArgs.h
Normal file
30
src/cascadia/Remoting/QuitAllRequestedArgs.h
Normal 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);
|
||||
}
|
|
@ -271,7 +271,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
|
||||
_monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*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);
|
||||
}
|
||||
|
@ -318,12 +318,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
}
|
||||
}
|
||||
|
||||
_peasant.GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers });
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"WindowManager_CreateOurPeasant",
|
||||
TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
|
||||
// If the peasant asks us to quit we should not try to act in future elections.
|
||||
_peasant.QuitRequested([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto wm = weakThis.get())
|
||||
{
|
||||
wm->_monarchWaitInterrupt.SetEvent();
|
||||
}
|
||||
});
|
||||
|
||||
return _peasant;
|
||||
}
|
||||
|
||||
|
@ -602,4 +612,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
winrt::fire_and_forget RequestQuitAll();
|
||||
bool DoesQuakeWindowExist();
|
||||
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(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(ShowNotificationIconRequested, 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:
|
||||
bool _shouldCreateWindow{ false };
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace Microsoft.Terminal.Remoting
|
|||
void SummonAllWindows();
|
||||
void RequestShowNotificationIcon();
|
||||
void RequestHideNotificationIcon();
|
||||
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
|
||||
|
||||
UInt64 GetNumberOfPeasants();
|
||||
void RequestQuitAll();
|
||||
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> WindowCreated;
|
||||
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> HideNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -55,9 +55,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
|
|||
STARTUPINFOEX siEx{ 0 };
|
||||
siEx.StartupInfo.cb = sizeof(STARTUPINFOEX);
|
||||
|
||||
// Append a "\." to the given path, so that this will work in "C:\"
|
||||
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()) };
|
||||
auto cmdline{ wil::str_printf<std::wstring>(LR"-("%s" -d %s)-", GetWtExePath().c_str(), QuoteAndEscapeCommandlineArg(pszName.get()).c_str()) };
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
|
||||
nullptr, // lpApplicationName
|
||||
cmdline.data(),
|
||||
|
@ -66,7 +64,7 @@ HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray,
|
|||
false, // bInheritHandles
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
||||
nullptr, // lpEnvironment
|
||||
path.data(),
|
||||
pszName.get(),
|
||||
&siEx.StartupInfo, // lpStartupInfo
|
||||
&_piClient // lpProcessInformation
|
||||
));
|
||||
|
|
|
@ -67,41 +67,17 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::_EndPreviewColorScheme()
|
||||
{
|
||||
// Get the focused control
|
||||
if (const auto& activeControl{ _GetActiveControl() })
|
||||
for (const auto& f : _restorePreviewFuncs)
|
||||
{
|
||||
// Get the runtime settings of the focused control
|
||||
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();
|
||||
f();
|
||||
}
|
||||
_originalSettings = nullptr;
|
||||
_restorePreviewFuncs.clear();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Preview handler for the SetColorScheme action.
|
||||
// - This method will stash the settings of the current control in
|
||||
// _originalSettings. Then it will create a new TerminalSettings object
|
||||
// - This method will stash functions to reset the settings of the selected controls in
|
||||
// _restorePreviewFuncs. Then it will create a new TerminalSettings object
|
||||
// with only the properties from the ColorScheme set. It'll _insert_ a
|
||||
// TerminalSettings between the control's root settings (built from
|
||||
// CascadiaSettings) and the control's runtime settings. That'll cause the
|
||||
|
@ -112,33 +88,63 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::_PreviewColorScheme(const Settings::Model::SetColorSchemeArgs& args)
|
||||
{
|
||||
// Get the focused control
|
||||
if (const auto& activeControl{ _GetActiveControl() })
|
||||
if (const auto& scheme{ _settings.GlobalSettings().ColorSchemes().TryLookup(args.SchemeName()) })
|
||||
{
|
||||
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
|
||||
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
|
||||
// this while you're currently previewing a SetColorScheme
|
||||
// action, then the parent of the control's settings is _the
|
||||
// last preview TerminalSettings we inserted! We don't want
|
||||
// to save that one!
|
||||
_originalSettings = controlSettings.GetParent();
|
||||
while (_originalSettings.GetParent() != nullptr)
|
||||
auto originalSettings = controlSettings.GetParent();
|
||||
while (originalSettings.GetParent() != nullptr)
|
||||
{
|
||||
_originalSettings = _originalSettings.GetParent();
|
||||
originalSettings = originalSettings.GetParent();
|
||||
}
|
||||
// Create a new child for those settings
|
||||
TerminalSettingsCreateResult fake{ _originalSettings };
|
||||
TerminalSettingsCreateResult fake{ originalSettings };
|
||||
const auto& childStruct = TerminalSettings::CreateWithParent(fake);
|
||||
// Modify the child to have the applied color scheme
|
||||
childStruct.DefaultSettings().ApplyColorScheme(scheme);
|
||||
|
||||
// Insert that new child as the parent of the control's settings
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
CloseWindow(false);
|
||||
_CloseRequestedHandlers(nullptr, nullptr);
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
|
@ -174,10 +174,9 @@ namespace winrt::TerminalApp::implementation
|
|||
else if (const auto& realArgs = args.ActionArgs().try_as<SplitPaneArgs>())
|
||||
{
|
||||
_SplitPane(realArgs.SplitDirection(),
|
||||
realArgs.SplitMode(),
|
||||
// This is safe, we're already filtering so the value is (0, 1)
|
||||
::base::saturated_cast<float>(realArgs.SplitSize()),
|
||||
realArgs.TerminalArgs());
|
||||
_MakePane(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
|
@ -377,11 +376,10 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<AdjustFontSizeArgs>())
|
||||
{
|
||||
if (const auto& termControl{ _GetActiveControl() })
|
||||
{
|
||||
termControl.AdjustFontSize(realArgs.Delta());
|
||||
args.Handled(true);
|
||||
}
|
||||
const auto res = _ApplyToActiveControls([&](auto& control) {
|
||||
control.AdjustFontSize(realArgs.Delta());
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,21 +393,19 @@ namespace winrt::TerminalApp::implementation
|
|||
void TerminalPage::_HandleResetFontSize(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& termControl{ _GetActiveControl() })
|
||||
{
|
||||
termControl.ResetFontSize();
|
||||
args.Handled(true);
|
||||
}
|
||||
const auto res = _ApplyToActiveControls([](auto& control) {
|
||||
control.ResetFontSize();
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleShaderEffects(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto& termControl{ _GetActiveControl() })
|
||||
{
|
||||
termControl.ToggleShaderEffects();
|
||||
args.Handled(true);
|
||||
}
|
||||
const auto res = _ApplyToActiveControls([](auto& control) {
|
||||
control.ToggleShaderEffects();
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleFocusMode(const IInspectable& /*sender*/,
|
||||
|
@ -452,37 +448,33 @@ namespace winrt::TerminalApp::implementation
|
|||
args.Handled(false);
|
||||
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())
|
||||
{
|
||||
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
|
||||
const auto res = _ApplyToActiveControls([&](auto& control) {
|
||||
// Start by getting the current settings of the control
|
||||
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
|
||||
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);
|
||||
parentSettings = controlSettings.GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyColorScheme(nullptr) will clear the old color scheme.
|
||||
controlSettings.ApplyColorScheme(nullptr);
|
||||
parentSettings.ApplyColorScheme(scheme);
|
||||
|
||||
control.UpdateSettings();
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +555,7 @@ namespace winrt::TerminalApp::implementation
|
|||
auto actions = winrt::single_threaded_vector<ActionAndArgs>(std::move(
|
||||
TerminalPage::ConvertExecuteCommandlineToActions(realArgs)));
|
||||
|
||||
if (_startupActions.Size() != 0)
|
||||
if (actions.Size() != 0)
|
||||
{
|
||||
actionArgs.Handled(true);
|
||||
ProcessStartupActions(actions, false);
|
||||
|
@ -896,11 +888,10 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>())
|
||||
{
|
||||
if (const auto termControl{ _GetActiveControl() })
|
||||
{
|
||||
termControl.ClearBuffer(realArgs.Clear());
|
||||
args.Handled(true);
|
||||
}
|
||||
const auto res = _ApplyToActiveControls([&](auto& control) {
|
||||
control.ClearBuffer(realArgs.Clear());
|
||||
});
|
||||
args.Handled(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,6 +187,10 @@ void AppCommandlineArgs::_buildParser()
|
|||
_windowTarget,
|
||||
RS_A(L"CmdWindowTargetArgDesc"));
|
||||
|
||||
_app.add_option("-s,--saved",
|
||||
_loadPersistedLayoutIdx,
|
||||
RS_A(L"CmdSavedLayoutArgDesc"));
|
||||
|
||||
// Subcommands
|
||||
_buildNewTabParser();
|
||||
_buildSplitPaneParser();
|
||||
|
@ -700,6 +704,7 @@ void AppCommandlineArgs::_resetStateToDefault()
|
|||
_swapPaneDirection = FocusDirection::None;
|
||||
|
||||
_focusPaneTarget = -1;
|
||||
_loadPersistedLayoutIdx = -1;
|
||||
|
||||
// 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
|
||||
|
@ -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
|
||||
{
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
const std::string& GetExitMessage();
|
||||
bool ShouldExitEarly() const noexcept;
|
||||
|
||||
std::optional<uint32_t> GetPersistedLayoutIdx() const noexcept;
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> GetLaunchMode() const noexcept;
|
||||
|
||||
int ParseArgs(const winrt::Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
|
||||
|
@ -123,6 +124,7 @@ private:
|
|||
std::string _exitMessage;
|
||||
bool _shouldExitEarly{ false };
|
||||
|
||||
int _loadPersistedLayoutIdx{};
|
||||
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
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <WtExeUtils.h>
|
||||
#include <wil/token_helpers.h >
|
||||
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
@ -29,12 +31,12 @@ namespace winrt
|
|||
using IInspectable = Windows::Foundation::IInspectable;
|
||||
}
|
||||
|
||||
static const winrt::hstring StartupTaskName = L"StartTerminalOnLoginTask";
|
||||
static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask";
|
||||
// clang-format off
|
||||
// !!! IMPORTANT !!!
|
||||
// Make sure that these keys are in the same order as the
|
||||
// SettingsLoadWarnings/Errors enum is!
|
||||
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWarnings::WARNINGS_SIZE)> settingsLoadWarningsLabels {
|
||||
static const std::array settingsLoadWarningsLabels {
|
||||
USES_RESOURCE(L"MissingDefaultProfileText"),
|
||||
USES_RESOURCE(L"DuplicateProfileText"),
|
||||
USES_RESOURCE(L"UnknownColorSchemeText"),
|
||||
|
@ -43,7 +45,6 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWar
|
|||
USES_RESOURCE(L"AtLeastOneKeybindingWarning"),
|
||||
USES_RESOURCE(L"TooManyKeysForChord"),
|
||||
USES_RESOURCE(L"MissingRequiredParameter"),
|
||||
USES_RESOURCE(L"LegacyGlobalsProperty"),
|
||||
USES_RESOURCE(L"FailedToParseCommandJson"),
|
||||
USES_RESOURCE(L"FailedToWriteToSettings"),
|
||||
USES_RESOURCE(L"InvalidColorSchemeInCmd"),
|
||||
|
@ -51,12 +52,15 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadWar
|
|||
USES_RESOURCE(L"FailedToParseStartupActions"),
|
||||
USES_RESOURCE(L"FailedToParseSubCommands"),
|
||||
};
|
||||
static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels {
|
||||
static const std::array settingsLoadErrorsLabels {
|
||||
USES_RESOURCE(L"NoProfilesText"),
|
||||
USES_RESOURCE(L"AllProfilesHiddenText")
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static_assert(settingsLoadWarningsLabels.size() == static_cast<size_t>(SettingsLoadWarnings::WARNINGS_SIZE));
|
||||
static_assert(settingsLoadErrorsLabels.size() == static_cast<size_t>(SettingsLoadErrors::ERRORS_SIZE));
|
||||
|
||||
// Function Description:
|
||||
// - General-purpose helper for looking up a localized string for a
|
||||
// warning/error. First will look for the given key in the provided map of
|
||||
|
@ -68,12 +72,12 @@ static const std::array<std::wstring_view, static_cast<uint32_t>(SettingsLoadErr
|
|||
// - map: A map of keys->Resource keys.
|
||||
// Return Value:
|
||||
// - the localized string for the given type, if it exists.
|
||||
template<std::size_t N>
|
||||
static winrt::hstring _GetMessageText(uint32_t index, std::array<std::wstring_view, N> keys)
|
||||
template<typename T>
|
||||
winrt::hstring _GetMessageText(uint32_t index, const T& keys)
|
||||
{
|
||||
if (index < keys.size())
|
||||
{
|
||||
return GetLibraryResourceString(keys.at(index));
|
||||
return GetLibraryResourceString(til::at(keys, index));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -129,38 +133,6 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD
|
|||
return textRun;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns whether the user is either a member of the Administrators group or
|
||||
// is currently elevated.
|
||||
// - This will return **FALSE** if the user has UAC disabled entirely, because
|
||||
// there's no separation of power between the user and an admin in that case.
|
||||
// Return Value:
|
||||
// - true if the user is an administrator
|
||||
static bool _isUserAdmin() noexcept
|
||||
try
|
||||
{
|
||||
wil::unique_handle processToken{ GetCurrentProcessToken() };
|
||||
const auto elevationType = wil::get_token_information<TOKEN_ELEVATION_TYPE>(processToken.get());
|
||||
const auto elevationState = wil::get_token_information<TOKEN_ELEVATION>(processToken.get());
|
||||
if (elevationType == TokenElevationTypeDefault && elevationState.TokenIsElevated)
|
||||
{
|
||||
// In this case, the user has UAC entirely disabled. This is sort of
|
||||
// weird, we treat this like the user isn't an admin at all. There's no
|
||||
// separation of powers, so the things we normally want to gate on
|
||||
// "having special powers" doesn't apply.
|
||||
//
|
||||
// See GH#7754, GH#11096
|
||||
return false;
|
||||
}
|
||||
|
||||
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Function Description:
|
||||
|
@ -212,7 +184,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// The TerminalPage has to be constructed during our construction, to
|
||||
// make sure that there's a terminal page for callers of
|
||||
// SetTitleBarContent
|
||||
_isElevated = _isUserAdmin();
|
||||
_isElevated = ::Microsoft::Console::Utils::IsElevated();
|
||||
_root = winrt::make_self<TerminalPage>();
|
||||
|
||||
_reloadSettings = std::make_shared<ThrottledFuncTrailing<>>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() {
|
||||
|
@ -332,6 +304,9 @@ namespace winrt::TerminalApp::implementation
|
|||
_RefreshThemeRoutine();
|
||||
_ApplyStartupTaskStateChange();
|
||||
|
||||
auto args = winrt::make_self<SystemMenuChangeArgs>(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &AppLogic::_OpenSettingsUI));
|
||||
_SystemMenuChangeRequestedHandlers(*this, *args);
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"AppCreated",
|
||||
|
@ -373,6 +348,8 @@ namespace winrt::TerminalApp::implementation
|
|||
co_return ContentDialogResult::None;
|
||||
}
|
||||
|
||||
_dialog = dialog;
|
||||
|
||||
// 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
|
||||
// xaml tree somehow.
|
||||
|
@ -410,6 +387,16 @@ namespace winrt::TerminalApp::implementation
|
|||
// 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:
|
||||
// - Displays a dialog for errors found while loading or validating the
|
||||
// settings. Uses the resources under the provided title and content keys
|
||||
|
@ -488,27 +475,6 @@ namespace winrt::TerminalApp::implementation
|
|||
if (!warningText.empty())
|
||||
{
|
||||
warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources()));
|
||||
|
||||
// The "LegacyGlobalsProperty" warning is special - it has a URL
|
||||
// that goes with it. So we need to manually construct a
|
||||
// Hyperlink and insert it along with the warning text.
|
||||
if (warning == SettingsLoadWarnings::LegacyGlobalsProperty)
|
||||
{
|
||||
// Add the URL here too
|
||||
const auto legacyGlobalsLinkLabel = RS_(L"LegacyGlobalsPropertyHrefLabel");
|
||||
const auto legacyGlobalsLinkUriValue = RS_(L"LegacyGlobalsPropertyHrefUrl");
|
||||
|
||||
winrt::Windows::UI::Xaml::Documents::Run legacyGlobalsLinkText;
|
||||
winrt::Windows::UI::Xaml::Documents::Hyperlink legacyGlobalsLink;
|
||||
winrt::Windows::Foundation::Uri legacyGlobalsLinkUri{ legacyGlobalsLinkUriValue };
|
||||
|
||||
legacyGlobalsLinkText.Text(legacyGlobalsLinkLabel);
|
||||
legacyGlobalsLink.NavigateUri(legacyGlobalsLinkUri);
|
||||
legacyGlobalsLink.Inlines().Append(legacyGlobalsLinkText);
|
||||
|
||||
warningsTextBlock.Inlines().Append(legacyGlobalsLink);
|
||||
}
|
||||
|
||||
warningsTextBlock.Inlines().Append(Documents::LineBreak{});
|
||||
}
|
||||
}
|
||||
|
@ -619,13 +585,11 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::Windows::Foundation::Size proposedSize{};
|
||||
|
||||
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 (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialSize())
|
||||
if (layout.InitialSize())
|
||||
{
|
||||
proposedSize = layouts.GetAt(0).InitialSize().Value();
|
||||
proposedSize = layout.InitialSize().Value();
|
||||
// The size is saved as a non-scaled real pixel size,
|
||||
// so we need to scale it appropriately.
|
||||
proposedSize.Height = proposedSize.Height * scale;
|
||||
|
@ -723,13 +687,11 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
|
||||
|
||||
if (_root->ShouldUsePersistedLayout(_settings))
|
||||
if (const auto layout = _root->LoadPersistedLayout(_settings))
|
||||
{
|
||||
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
|
||||
|
||||
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialPosition())
|
||||
if (layout.InitialPosition())
|
||||
{
|
||||
initialPosition = layouts.GetAt(0).InitialPosition().Value();
|
||||
initialPosition = layout.InitialPosition().Value();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -918,8 +880,6 @@ namespace winrt::TerminalApp::implementation
|
|||
void AppLogic::_RegisterSettingsChange()
|
||||
{
|
||||
const std::filesystem::path settingsPath{ std::wstring_view{ CascadiaSettings::SettingsPath() } };
|
||||
const std::filesystem::path statePath{ std::wstring_view{ ApplicationState::SharedInstance().FilePath() } };
|
||||
|
||||
_reader.create(
|
||||
settingsPath.parent_path().c_str(),
|
||||
false,
|
||||
|
@ -928,14 +888,29 @@ namespace winrt::TerminalApp::implementation
|
|||
// editors, who will write a temp file, then rename it to be the
|
||||
// actual file you wrote. So listen for that too.
|
||||
wil::FolderChangeEvents::FileName | wil::FolderChangeEvents::LastWriteTime,
|
||||
[this, settingsBasename = settingsPath.filename(), stateBasename = statePath.filename()](wil::FolderChangeEvent, PCWSTR fileModified) {
|
||||
const auto modifiedBasename = std::filesystem::path{ fileModified }.filename();
|
||||
[this, settingsBasename = settingsPath.filename()](wil::FolderChangeEvent, PCWSTR fileModified) {
|
||||
// DO NOT create a static reference to ApplicationState::SharedInstance here.
|
||||
//
|
||||
// ApplicationState::SharedInstance already caches its own
|
||||
// static ref. If _we_ keep a static ref to the member in
|
||||
// AppState, then our reference will keep ApplicationState alive
|
||||
// after the `ActionToStringMap` gets cleaned up. Then, when we
|
||||
// try to persist the actions in the window state, we won't be
|
||||
// able to. We'll try to look up the action and the map just
|
||||
// won't exist. We'll explode, even though the Terminal is
|
||||
// tearing down anyways. So we'll just die, but still invoke
|
||||
// WinDBG's post-mortem debugger, who won't be able to attach to
|
||||
// the process that's already exiting.
|
||||
//
|
||||
// So DON'T ~give a mouse a cookie~ take a static ref here.
|
||||
|
||||
const winrt::hstring modifiedBasename{ std::filesystem::path{ fileModified }.filename().c_str() };
|
||||
|
||||
if (modifiedBasename == settingsBasename)
|
||||
{
|
||||
_reloadSettings->Run();
|
||||
}
|
||||
else if (modifiedBasename == stateBasename)
|
||||
else if (ApplicationState::SharedInstance().IsStatePath(modifiedBasename))
|
||||
{
|
||||
_reloadState();
|
||||
}
|
||||
|
@ -1062,6 +1037,11 @@ namespace winrt::TerminalApp::implementation
|
|||
_SettingsChangedHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
void AppLogic::_OpenSettingsUI()
|
||||
{
|
||||
_root->OpenSettingsUI();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns a pointer to the global shared settings.
|
||||
[[nodiscard]] CascadiaSettings AppLogic::GetSettings() const noexcept
|
||||
|
@ -1170,10 +1150,22 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppLogic::WindowCloseButtonClicked()
|
||||
void AppLogic::CloseWindow(LaunchPosition pos)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1187,6 +1179,16 @@ namespace winrt::TerminalApp::implementation
|
|||
return {};
|
||||
}
|
||||
|
||||
bool AppLogic::HasCommandlineArguments() const noexcept
|
||||
{
|
||||
return _hasCommandLineArguments;
|
||||
}
|
||||
|
||||
bool AppLogic::HasSettingsStartupActions() const noexcept
|
||||
{
|
||||
return _hasSettingsStartupActions;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the initial commandline to process on startup, and attempts to
|
||||
// parse it. Commands will be parsed into a list of ShortcutActions that
|
||||
|
@ -1210,6 +1212,10 @@ namespace winrt::TerminalApp::implementation
|
|||
// then it contains only the executable name and no other arguments.
|
||||
_hasCommandLineArguments = args.size() > 1;
|
||||
_appArgs.ValidateStartupCommands();
|
||||
if (const auto idx = _appArgs.GetPersistedLayoutIdx())
|
||||
{
|
||||
_root->SetPersistedLayoutIdx(idx.value());
|
||||
}
|
||||
_root->SetStartupActions(_appArgs.GetStartupActions());
|
||||
|
||||
// Check if we were started as a COM server for inbound connections of console sessions
|
||||
|
@ -1447,6 +1453,40 @@ namespace winrt::TerminalApp::implementation
|
|||
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()
|
||||
{
|
||||
if (_root)
|
||||
|
@ -1478,8 +1518,17 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void AppLogic::SetPersistedLayoutIdx(const uint32_t idx)
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
_root->SetPersistedLayoutIdx(idx);
|
||||
}
|
||||
}
|
||||
|
||||
void AppLogic::SetNumberOfOpenWindows(const uint64_t num)
|
||||
{
|
||||
_numOpenWindows = num;
|
||||
if (_root)
|
||||
{
|
||||
_root->SetNumberOfOpenWindows(num);
|
||||
|
@ -1499,6 +1548,11 @@ namespace winrt::TerminalApp::implementation
|
|||
return _root->IsQuakeWindow();
|
||||
}
|
||||
|
||||
void AppLogic::RequestExitFullscreen()
|
||||
{
|
||||
_root->SetFullscreen(false);
|
||||
}
|
||||
|
||||
bool AppLogic::GetMinimizeToNotificationArea()
|
||||
{
|
||||
if constexpr (Feature_NotificationIcon::IsEnabled())
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "AppLogic.g.h"
|
||||
#include "FindTargetWindowResult.g.h"
|
||||
#include "SystemMenuChangeArgs.g.h"
|
||||
#include "Jumplist.h"
|
||||
#include "LanguageProfileNotifier.h"
|
||||
#include "TerminalPage.h"
|
||||
|
@ -35,6 +36,17 @@ namespace winrt::TerminalApp::implementation
|
|||
FindTargetWindowResult(id, L""){};
|
||||
};
|
||||
|
||||
struct SystemMenuChangeArgs : SystemMenuChangeArgsT<SystemMenuChangeArgs>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::hstring, Name, L"");
|
||||
WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add);
|
||||
WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr);
|
||||
|
||||
public:
|
||||
SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) :
|
||||
_Name{ name }, _Action{ action }, _Handler{ handler } {};
|
||||
};
|
||||
|
||||
struct AppLogic : AppLogicT<AppLogic, IInitializeWithWindow>
|
||||
{
|
||||
public:
|
||||
|
@ -55,6 +67,8 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void Quit();
|
||||
|
||||
bool HasCommandlineArguments() const noexcept;
|
||||
bool HasSettingsStartupActions() const noexcept;
|
||||
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
|
||||
int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd);
|
||||
TerminalApp::FindTargetWindowResult FindTargetWindow(array_view<const winrt::hstring> actions);
|
||||
|
@ -65,14 +79,19 @@ namespace winrt::TerminalApp::implementation
|
|||
bool Fullscreen() 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 RenameFailed();
|
||||
winrt::hstring WindowName();
|
||||
void WindowName(const winrt::hstring& name);
|
||||
uint64_t WindowId();
|
||||
void WindowId(const uint64_t& id);
|
||||
void SetPersistedLayoutIdx(const uint32_t idx);
|
||||
void SetNumberOfOpenWindows(const uint64_t num);
|
||||
bool IsQuakeWindow() const noexcept;
|
||||
void RequestExitFullscreen();
|
||||
|
||||
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
|
||||
bool CenterOnLaunch();
|
||||
|
@ -91,7 +110,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void TitlebarClicked();
|
||||
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();
|
||||
|
||||
|
@ -100,12 +119,14 @@ namespace winrt::TerminalApp::implementation
|
|||
bool GetShowTitleInTitlebar();
|
||||
|
||||
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();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
|
||||
|
||||
private:
|
||||
bool _isUwp{ false };
|
||||
|
@ -123,7 +144,10 @@ namespace winrt::TerminalApp::implementation
|
|||
HRESULT _settingsLoadedResult = S_OK;
|
||||
bool _loadedInitialSettings = false;
|
||||
|
||||
uint64_t _numOpenWindows{ 0 };
|
||||
|
||||
std::shared_mutex _dialogLock;
|
||||
winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog;
|
||||
|
||||
::TerminalApp::AppCommandlineArgs _appArgs;
|
||||
::TerminalApp::AppCommandlineArgs _settingsAppArgs;
|
||||
|
@ -153,6 +177,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void _RegisterSettingsChange();
|
||||
fire_and_forget _DispatchReloadSettings();
|
||||
void _ReloadSettings();
|
||||
void _OpenSettingsUI();
|
||||
|
||||
void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme);
|
||||
|
||||
|
@ -175,6 +200,7 @@ namespace winrt::TerminalApp::implementation
|
|||
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(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(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
|
||||
|
||||
|
|
|
@ -19,6 +19,20 @@ namespace TerminalApp
|
|||
String WindowName { get; };
|
||||
};
|
||||
|
||||
delegate void SystemMenuItemHandler();
|
||||
|
||||
enum SystemMenuChangeAction
|
||||
{
|
||||
Add = 0,
|
||||
Remove = 1
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SystemMenuChangeArgs {
|
||||
String Name { get; };
|
||||
SystemMenuChangeAction Action { get; };
|
||||
SystemMenuItemHandler Handler { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter
|
||||
{
|
||||
AppLogic();
|
||||
|
@ -34,6 +48,8 @@ namespace TerminalApp
|
|||
void RunAsUwp();
|
||||
Boolean IsElevated();
|
||||
|
||||
Boolean HasCommandlineArguments();
|
||||
Boolean HasSettingsStartupActions();
|
||||
Int32 SetStartupCommandline(String[] commands);
|
||||
Int32 ExecuteCommandline(String[] commands, String cwd);
|
||||
String ParseCommandlineMessage { get; };
|
||||
|
@ -55,8 +71,10 @@ namespace TerminalApp
|
|||
void IdentifyWindow();
|
||||
String WindowName;
|
||||
UInt64 WindowId;
|
||||
void SetPersistedLayoutIdx(UInt32 idx);
|
||||
void SetNumberOfOpenWindows(UInt64 num);
|
||||
void RenameFailed();
|
||||
void RequestExitFullscreen();
|
||||
Boolean IsQuakeWindow();
|
||||
|
||||
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);
|
||||
|
@ -69,10 +87,14 @@ namespace TerminalApp
|
|||
Boolean GetInitialAlwaysOnTop();
|
||||
Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension);
|
||||
void TitlebarClicked();
|
||||
void WindowCloseButtonClicked();
|
||||
void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position);
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
|
||||
Boolean ShouldUsePersistedLayout();
|
||||
String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position);
|
||||
void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector<String> layouts);
|
||||
|
||||
Boolean GetMinimizeToNotificationArea();
|
||||
Boolean GetAlwaysShowNotificationIcon();
|
||||
Boolean GetShowTitleInTitlebar();
|
||||
|
@ -84,6 +106,7 @@ namespace TerminalApp
|
|||
// See IDialogPresenter and TerminalPage's DialogPresenter for more
|
||||
// information.
|
||||
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, String> TitleChanged;
|
||||
|
@ -99,7 +122,9 @@ namespace TerminalApp
|
|||
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
|
||||
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> QuitRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,22 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
|||
}
|
||||
void Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) {}
|
||||
~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();
|
||||
}
|
||||
void WriteInput(hstring const& data)
|
||||
|
@ -59,6 +73,9 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
|||
void DebugTapConnection::Start()
|
||||
{
|
||||
// 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)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include "../../inc/cppwinrt_utils.h"
|
||||
#include <til/latch.h>
|
||||
|
||||
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> _inputSide;
|
||||
|
||||
til::latch _start{ 1 };
|
||||
|
||||
friend class DebugInputTapConnection;
|
||||
};
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -40,7 +40,8 @@ enum class Borders : int
|
|||
Top = 0x1,
|
||||
Bottom = 0x2,
|
||||
Left = 0x4,
|
||||
Right = 0x8
|
||||
Right = 0x8,
|
||||
All = 0xF
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(Borders);
|
||||
|
||||
|
@ -58,7 +59,14 @@ public:
|
|||
const winrt::Microsoft::Terminal::Control::TermControl& control,
|
||||
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();
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl();
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
|
||||
|
||||
|
@ -102,8 +110,7 @@ public:
|
|||
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
const float splitSize,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
std::shared_ptr<Pane> pane);
|
||||
bool ToggleSplitOrientation();
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
|
||||
|
@ -142,25 +149,43 @@ public:
|
|||
// - true if the predicate returned true on any pane.
|
||||
template<typename F>
|
||||
//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()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
using R = std::invoke_result_t<F, std::shared_ptr<Pane>>;
|
||||
static constexpr auto IsVoid = std::is_void_v<R>;
|
||||
|
||||
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);
|
||||
|
||||
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(PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler<bool>);
|
||||
DECLARE_EVENT(Detached, _PaneDetachedHandlers, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
@ -173,7 +198,8 @@ private:
|
|||
struct LayoutSizeNode;
|
||||
|
||||
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::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
|
||||
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
|
||||
|
@ -185,6 +211,7 @@ private:
|
|||
float _desiredSplitPosition;
|
||||
|
||||
std::optional<uint32_t> _id;
|
||||
std::weak_ptr<Pane> _parentChildPath{};
|
||||
|
||||
bool _lastActive{ false };
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
|
||||
|
@ -205,6 +232,7 @@ private:
|
|||
bool _IsLeaf() const noexcept;
|
||||
bool _HasFocusedChild() const noexcept;
|
||||
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,
|
||||
const float splitSize,
|
||||
|
@ -232,6 +260,7 @@ private:
|
|||
void _CloseChild(const bool closeFirst, const bool isDetaching);
|
||||
winrt::fire_and_forget _CloseChildRoutine(const bool closeFirst);
|
||||
|
||||
void _Focus();
|
||||
void _FocusFirstChild();
|
||||
void _ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
|
||||
void _ControlWarningBellHandler(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
|
|
|
@ -190,6 +190,9 @@
|
|||
<data name="CloseWindowWarningTitle" xml:space="preserve">
|
||||
<value>Do you want to close all tabs?</value>
|
||||
</data>
|
||||
<data name="MultiplePanes" xml:space="preserve">
|
||||
<value>Multiple panes</value>
|
||||
</data>
|
||||
<data name="TabCloseSubMenu" xml:space="preserve">
|
||||
<value>Close...</value>
|
||||
</data>
|
||||
|
@ -381,6 +384,9 @@
|
|||
<data name="CmdFocusDesc" xml:space="preserve">
|
||||
<value>Launch the window in focus mode</value>
|
||||
</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">
|
||||
<value>Specify a terminal window to run the given commandline in. "0" always refers to the current window. </value>
|
||||
</data>
|
||||
|
@ -712,7 +718,17 @@
|
|||
<data name="CloseOnExitInfoBar.Message" xml:space="preserve">
|
||||
<value>Termination behavior can be configured in advanced profile settings.</value>
|
||||
</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">
|
||||
<value>Don't show again</value>
|
||||
</data>
|
||||
<data name="ElevationShield.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>This Terminal window is running as Admin</value>
|
||||
</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>
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
#include "ColorHelper.h"
|
||||
#include "DebugTapConnection.h"
|
||||
#include "SettingsTab.h"
|
||||
#include "..\TerminalSettingsModel\FileUtils.h"
|
||||
|
||||
#include <shlobj.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
@ -65,7 +68,7 @@ namespace winrt::TerminalApp::implementation
|
|||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
|
||||
_CreateNewTabWithProfileAndSettings(profile, settings, existingConnection);
|
||||
_CreateNewTabFromPane(_MakePane(newTerminalArgs, false, existingConnection));
|
||||
|
||||
const uint32_t tabCount = _tabs.Size();
|
||||
const bool usedManualProfile = (newTerminalArgs != nullptr) &&
|
||||
|
@ -241,58 +244,6 @@ namespace winrt::TerminalApp::implementation
|
|||
_InitializeTab(newTabImpl);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new tab with the given settings. If the tab bar is not being
|
||||
// currently displayed, it will be shown.
|
||||
// Arguments:
|
||||
// - profile: profile settings for this connection
|
||||
// - settings: the TerminalSettings object to use to create the TerminalControl with.
|
||||
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
|
||||
void TerminalPage::_CreateNewTabWithProfileAndSettings(const Profile& profile, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
|
||||
{
|
||||
// Initialize the new tab
|
||||
// Create a connection based on the values in our settings object if we weren't given one.
|
||||
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, settings.DefaultSettings());
|
||||
|
||||
// If we had an `existingConnection`, then this is an inbound handoff from somewhere else.
|
||||
// We need to tell it about our size information so it can match the dimensions of what
|
||||
// we are about to present.
|
||||
if (existingConnection)
|
||||
{
|
||||
connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols());
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
|
||||
if (_settings.GlobalSettings().DebugFeaturesEnabled())
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
if (bothAltsPressed)
|
||||
{
|
||||
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
// Give term control a child of the settings so that any overrides go in the child
|
||||
// This way, when we do a settings reload we just update the parent and the overrides remain
|
||||
auto term = _InitControl(settings, connection);
|
||||
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(profile, term);
|
||||
_RegisterTerminalEvents(term);
|
||||
_InitializeTab(newTabImpl);
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
auto newControl = _InitControl(settings, debugConnection);
|
||||
_RegisterTerminalEvents(newControl);
|
||||
// Split (auto) with the debug tap.
|
||||
newTabImpl->SplitPane(SplitDirection::Automatic, 0.5f, profile, newControl);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the icon of the currently focused terminal control, and set its
|
||||
// tab's icon to that icon.
|
||||
|
@ -362,28 +313,14 @@ namespace winrt::TerminalApp::implementation
|
|||
// In the future, it may be preferable to just duplicate the
|
||||
// current control's live settings (which will include changes
|
||||
// made through VT).
|
||||
_CreateNewTabFromPane(_MakePane(nullptr, true, nullptr));
|
||||
|
||||
if (auto profile = tab.GetFocusedProfile())
|
||||
const auto runtimeTabText{ tab.GetTabText() };
|
||||
if (!runtimeTabText.empty())
|
||||
{
|
||||
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
const auto settingsCreateResult{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
if (auto newTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
settingsCreateResult.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
_CreateNewTabWithProfileAndSettings(profile, settingsCreateResult);
|
||||
|
||||
const auto runtimeTabText{ tab.GetTabText() };
|
||||
if (!runtimeTabText.empty())
|
||||
{
|
||||
if (auto newTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
newTab->SetTabText(runtimeTabText);
|
||||
}
|
||||
newTab->SetTabText(runtimeTabText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +336,7 @@ namespace winrt::TerminalApp::implementation
|
|||
try
|
||||
{
|
||||
_SetFocusedTab(tab);
|
||||
_SplitPane(tab, SplitDirection::Automatic, SplitType::Duplicate);
|
||||
_SplitPane(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, true));
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
@ -410,33 +347,45 @@ namespace winrt::TerminalApp::implementation
|
|||
// - tab: tab to export
|
||||
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
|
||||
{
|
||||
if (const auto control{ tab.GetActiveTerminalControl() })
|
||||
{
|
||||
const FileSavePicker savePicker;
|
||||
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd);
|
||||
savePicker.SuggestedStartLocation(PickerLocationId::Downloads);
|
||||
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" });
|
||||
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices);
|
||||
savePicker.SuggestedFileName(control.Title());
|
||||
// GH#11356 - we can't use the UWP apis for writing the file,
|
||||
// because they don't work elevated (shocker) So just use the
|
||||
// shell32 file picker manually.
|
||||
auto path = co_await SaveFilePicker(*_hostingHwnd, [control](auto&& dialog) {
|
||||
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExportFile));
|
||||
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();
|
||||
if (file != nullptr)
|
||||
// Default to using the tab title as the file name
|
||||
THROW_IF_FAILED(dialog->SetFileName((control.Title() + L".txt").c_str()));
|
||||
});
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
const auto buffer = control.ReadEntireBuffer();
|
||||
CachedFileManager::DeferUpdates(file);
|
||||
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"));
|
||||
}
|
||||
CascadiaSettings::ExportFile(path, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -501,7 +450,9 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
// If we are supposed to save state, make sure we clear it out
|
||||
// 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();
|
||||
state.PersistedWindowLayouts(nullptr);
|
||||
|
@ -734,31 +685,32 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
_UnZoomIfNeeded();
|
||||
|
||||
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();
|
||||
|
||||
// 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();
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
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() })
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "winrt/Microsoft.UI.Xaml.Controls.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
#include "TabRowControl.g.h"
|
||||
|
||||
|
@ -16,6 +17,9 @@ namespace winrt::TerminalApp::implementation
|
|||
void OnNewTabButtonClick(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::Controls::SplitButtonClickEventArgs const& args);
|
||||
void OnNewTabButtonDrop(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::DragEventArgs const& e);
|
||||
void OnNewTabButtonDragOver(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::DragEventArgs const& e);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, ShowElevationShield, _PropertyChangedHandlers, false);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TabRowControl : Windows.UI.Xaml.Controls.ContentPresenter
|
||||
[default_interface] runtimeclass TabRowControl : Windows.UI.Xaml.Controls.ContentPresenter,
|
||||
Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
TabRowControl();
|
||||
Microsoft.UI.Xaml.Controls.TabView TabView { get; };
|
||||
Boolean ShowElevationShield;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,17 @@
|
|||
IsAddTabButtonVisible="false"
|
||||
TabWidthMode="Equal">
|
||||
|
||||
<mux:TabView.TabStripHeader>
|
||||
<!-- EA18 is the "Shield" glyph -->
|
||||
<FontIcon x:Uid="ElevationShield"
|
||||
Margin="9,4,0,4"
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind ShowElevationShield, Mode=OneWay}" />
|
||||
</mux:TabView.TabStripHeader>
|
||||
|
||||
<mux:TabView.TabStripFooter>
|
||||
<mux:SplitButton x:Name="NewTabButton"
|
||||
x:Uid="NewTabSplitButton"
|
||||
|
|
|
@ -318,7 +318,29 @@
|
|||
</ProjectReference>
|
||||
<!-- For whatever reason, we can't include the TerminalControl and
|
||||
TerminalSettings projects' winmds via project references. So we'll have to
|
||||
manually include the winmds as References below -->
|
||||
manually include the winmds as References below
|
||||
|
||||
BODGY: we do need to add a ProjectReference to TerminalControl.vcxproj,
|
||||
with Private=true, ReferenceOutputAssembly=false, so that Visual Studio's
|
||||
"Fast Up-to-date Check" will work with this project. If we don't, the Fast
|
||||
Up-to-date Check will look for the .xaml files from that project in our
|
||||
output, which won't actually be there.
|
||||
|
||||
We do still need to separately reference the winmds manually below, which is annoying.
|
||||
-->
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\dll\TerminalControl.vcxproj">
|
||||
<!-- Private:true and ReferenceOutputAssembly:false, in combination with
|
||||
the manual reference to TerminalControl.winmd below make sure that this
|
||||
project will compile correct, and that we won't roll up the TermControl
|
||||
xbf's into the packaging project twice. -->
|
||||
<Private>true</Private>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettingsEditor\Microsoft.Terminal.Settings.Editor.vcxproj">
|
||||
<Private>true</Private>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- This is a hack to get the ARM64 CI build working. See
|
||||
|
|
|
@ -3,38 +3,38 @@
|
|||
|
||||
#include "pch.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "Utils.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "TerminalPage.g.cpp"
|
||||
#include "RenameWindowRequestedArgs.g.cpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <inc/WindowingBehavior.h>
|
||||
#include <LibraryResources.h>
|
||||
#include <TerminalCore/ControlKeyStates.hpp>
|
||||
#include <til/latch.h>
|
||||
|
||||
#include "TerminalPage.g.cpp"
|
||||
#include <winrt/Windows.Storage.h>
|
||||
|
||||
#include "TabRowControl.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "ColorHelper.h"
|
||||
#include "DebugTapConnection.h"
|
||||
#include "SettingsTab.h"
|
||||
#include "RenameWindowRequestedArgs.g.cpp"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
|
||||
#include <til/latch.h>
|
||||
#include "TabRowControl.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace ::TerminalApp;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#define HOOKUP_ACTION(action) _actionDispatch->action({ this, &TerminalPage::_Handle##action });
|
||||
|
@ -118,6 +118,29 @@ namespace winrt::TerminalApp::implementation
|
|||
_systemRowsToScroll = _ReadSystemRowsToScroll();
|
||||
}
|
||||
|
||||
bool TerminalPage::IsElevated() const noexcept
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
// This won't change over the lifetime of the application
|
||||
|
||||
static const bool isElevated = []() {
|
||||
// *** THIS IS A SINGLETON ***
|
||||
auto result = false;
|
||||
|
||||
// GH#2455 - Make sure to try/catch calls to Application::Current,
|
||||
// because that _won't_ be an instance of TerminalApp::App in the
|
||||
// LocalTests
|
||||
try
|
||||
{
|
||||
result = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsElevated();
|
||||
}
|
||||
CATCH_LOG();
|
||||
return result;
|
||||
}();
|
||||
|
||||
return isElevated;
|
||||
}
|
||||
|
||||
void TerminalPage::Create()
|
||||
{
|
||||
// Hookup the key bindings
|
||||
|
@ -128,19 +151,7 @@ namespace winrt::TerminalApp::implementation
|
|||
_tabView = _tabRow.TabView();
|
||||
_rearranging = false;
|
||||
|
||||
// GH#2455 - Make sure to try/catch calls to Application::Current,
|
||||
// because that _won't_ be an instance of TerminalApp::App in the
|
||||
// LocalTests
|
||||
auto isElevated = false;
|
||||
try
|
||||
{
|
||||
// GH#3581 - There's a platform limitation that causes us to crash when we rearrange tabs.
|
||||
// Xaml tries to send a drag visual (to wit: a screenshot) to the drag hosting process,
|
||||
// but that process is running at a different IL than us.
|
||||
// For now, we're disabling elevated drag.
|
||||
isElevated = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsElevated();
|
||||
}
|
||||
CATCH_LOG();
|
||||
const auto isElevated = IsElevated();
|
||||
|
||||
if (_settings.GlobalSettings().UseAcrylicInTabRow())
|
||||
{
|
||||
|
@ -207,7 +218,7 @@ namespace winrt::TerminalApp::implementation
|
|||
_RegisterActionCallbacks();
|
||||
|
||||
// Hook up inbound connection event handler
|
||||
TerminalConnection::ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
|
||||
ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection });
|
||||
|
||||
//Event Bindings (Early)
|
||||
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
|
@ -267,6 +278,8 @@ namespace winrt::TerminalApp::implementation
|
|||
// Setup mouse vanish attributes
|
||||
SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &_shouldMouseVanish, false);
|
||||
|
||||
_tabRow.ShowElevationShield(IsElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
|
||||
// Store cursor, so we can restore it, e.g., after mouse vanishing
|
||||
// (we'll need to adapt this logic once we make cursor context aware)
|
||||
try
|
||||
|
@ -274,6 +287,8 @@ namespace winrt::TerminalApp::implementation
|
|||
_defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
ShowSetAsDefaultInfoBar();
|
||||
}
|
||||
|
||||
// Method Description;
|
||||
|
@ -285,10 +300,34 @@ namespace winrt::TerminalApp::implementation
|
|||
// - true if the ApplicationState should be used.
|
||||
bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const
|
||||
{
|
||||
// If the setting is enabled, and we are the only window.
|
||||
return Feature_PersistedWindowLayout::IsEnabled() &&
|
||||
settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout &&
|
||||
_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)
|
||||
|
@ -374,30 +413,13 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
_startupState = StartupState::InStartup;
|
||||
|
||||
// If the user selected to save their tab layout, we are the first
|
||||
// window opened, and wt was not run with any other arguments, then
|
||||
// we should use the saved 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)))
|
||||
// If we are provided with an index, the cases where we have
|
||||
// commandline args and startup actions are already handled.
|
||||
if (const auto layout = LoadPersistedLayout(_settings))
|
||||
{
|
||||
auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
|
||||
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).TabLayout() && layouts.GetAt(0).TabLayout().Size() > 0)
|
||||
if (layout.TabLayout().Size() > 0)
|
||||
{
|
||||
_startupActions = layouts.GetAt(0).TabLayout();
|
||||
_startupActions = layout.TabLayout();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -825,14 +847,7 @@ namespace winrt::TerminalApp::implementation
|
|||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
this->_SplitPane(SplitDirection::Automatic,
|
||||
SplitType::Manual,
|
||||
0.5f,
|
||||
newTerminalArgs);
|
||||
}
|
||||
else if (shiftPressed && !debugTap)
|
||||
if (shiftPressed && !debugTap)
|
||||
{
|
||||
// Manually fill in the evaluated profile.
|
||||
if (newTerminalArgs.ProfileIndex() != nullptr)
|
||||
|
@ -848,7 +863,17 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
else
|
||||
{
|
||||
LOG_IF_FAILED(this->_OpenNewTab(newTerminalArgs));
|
||||
const auto newPane = _MakePane(newTerminalArgs);
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
this->_SplitPane(SplitDirection::Automatic,
|
||||
0.5f,
|
||||
newPane);
|
||||
}
|
||||
else
|
||||
{
|
||||
_CreateNewTabFromPane(newPane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1013,29 +1038,153 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
|
||||
// Method Description:
|
||||
// Called when the users pressed keyBindings while CommandPalette is open.
|
||||
// - Called when the users pressed keyBindings while CommandPalette is open.
|
||||
// - This method is effectively an extract of TermControl::_KeyHandler and TermControl::_TryHandleKeyBinding.
|
||||
// Arguments:
|
||||
// - e: the KeyRoutedEventArgs containing info about the keystroke.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_KeyDownHandler(Windows::Foundation::IInspectable const& /*sender*/, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
const auto key = e.OriginalKey();
|
||||
const auto scanCode = e.KeyStatus().ScanCode;
|
||||
const auto coreWindow = CoreWindow::GetForCurrentThread();
|
||||
const auto ctrlDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
|
||||
const auto altDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
|
||||
const auto shiftDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
|
||||
const auto keyStatus = e.KeyStatus();
|
||||
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(keyStatus.ScanCode);
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
|
||||
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, false, static_cast<int32_t>(key), static_cast<int32_t>(scanCode) };
|
||||
if (const auto cmd{ _settings.ActionMap().GetActionByKeyChord(kc) })
|
||||
// GH#11076:
|
||||
// For some weird reason we sometimes receive a WM_KEYDOWN
|
||||
// message without vkey or scanCode if a user drags a tab.
|
||||
// The KeyChord constructor has a debug assertion ensuring that all KeyChord
|
||||
// either have a valid vkey/scanCode. This is important, because this prevents
|
||||
// accidential insertion of invalid KeyChords into classes like ActionMap.
|
||||
if (!vkey && !scanCode)
|
||||
{
|
||||
if (CommandPalette().Visibility() == Visibility::Visible && cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
|
||||
return;
|
||||
}
|
||||
|
||||
// Alt-Numpad# input will send us a character once the user releases
|
||||
// Alt, so we should be ignoring the individual keydowns. The character
|
||||
// will be sent through the TSFInputControl. See GH#1401 for more
|
||||
// details
|
||||
if (modifiers.IsAltPressed() && (vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// GH#2235: Terminal::Settings hasn't been modified to differentiate
|
||||
// between AltGr and Ctrl+Alt yet.
|
||||
// -> Don't check for key bindings if this is an AltGr key combination.
|
||||
if (modifiers.IsAltGrPressed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto actionMap = _settings.ActionMap();
|
||||
if (!actionMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cmd = actionMap.GetActionByKeyChord({
|
||||
modifiers.IsCtrlPressed(),
|
||||
modifiers.IsAltPressed(),
|
||||
modifiers.IsShiftPressed(),
|
||||
modifiers.IsWinPressed(),
|
||||
vkey,
|
||||
scanCode,
|
||||
});
|
||||
if (!cmd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_actionDispatch->DoAction(cmd.ActionAndArgs()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto p = CommandPalette(); p.Visibility() == Visibility::Visible && cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
|
||||
{
|
||||
p.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
|
||||
// Let's assume the user has bound the dead key "^" to a sendInput command that sends "b".
|
||||
// If the user presses the two keys "^a" it'll produce "bâ", despite us marking the key event as handled.
|
||||
// The following is used to manually "consume" such dead keys and clear them from the keyboard state.
|
||||
_ClearKeyboardState(vkey, scanCode);
|
||||
e.Handled(true);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the modifier keys that are currently pressed. This can be used to
|
||||
// find out which modifiers (ctrl, alt, shift) are pressed in events that
|
||||
// don't necessarily include that state.
|
||||
// - This is a copy of TermControl::_GetPressedModifierKeys.
|
||||
// Return Value:
|
||||
// - The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
||||
ControlKeyStates TerminalPage::_GetPressedModifierKeys() noexcept
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
// DONT USE
|
||||
// != CoreVirtualKeyStates::None
|
||||
// OR
|
||||
// == CoreVirtualKeyStates::Down
|
||||
// Sometimes with the key down, the state is Down | Locked.
|
||||
// Sometimes with the key up, the state is Locked.
|
||||
// IsFlagSet(Down) is the only correct solution.
|
||||
|
||||
struct KeyModifier
|
||||
{
|
||||
VirtualKey vkey;
|
||||
ControlKeyStates flags;
|
||||
};
|
||||
|
||||
constexpr std::array<KeyModifier, 7> modifiers{ {
|
||||
{ VirtualKey::RightMenu, ControlKeyStates::RightAltPressed },
|
||||
{ VirtualKey::LeftMenu, ControlKeyStates::LeftAltPressed },
|
||||
{ VirtualKey::RightControl, ControlKeyStates::RightCtrlPressed },
|
||||
{ VirtualKey::LeftControl, ControlKeyStates::LeftCtrlPressed },
|
||||
{ VirtualKey::Shift, ControlKeyStates::ShiftPressed },
|
||||
{ VirtualKey::RightWindows, ControlKeyStates::RightWinPressed },
|
||||
{ VirtualKey::LeftWindows, ControlKeyStates::LeftWinPressed },
|
||||
} };
|
||||
|
||||
ControlKeyStates flags;
|
||||
|
||||
for (const auto& mod : modifiers)
|
||||
{
|
||||
const auto state = window.GetKeyState(mod.vkey);
|
||||
const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (isDown)
|
||||
{
|
||||
CommandPalette().Visibility(Visibility::Collapsed);
|
||||
flags |= mod.flags;
|
||||
}
|
||||
_actionDispatch->DoAction(cmd.ActionAndArgs());
|
||||
e.Handled(true);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Discards currently pressed dead keys.
|
||||
// - This is a copy of TermControl::_ClearKeyboardState.
|
||||
// Arguments:
|
||||
// - vkey: The vkey of the key pressed.
|
||||
// - scanCode: The scan code of the key pressed.
|
||||
void TerminalPage::_ClearKeyboardState(const WORD vkey, const WORD scanCode) noexcept
|
||||
{
|
||||
std::array<BYTE, 256> keyState;
|
||||
if (!GetKeyboardState(keyState.data()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// As described in "Sometimes you *want* to interfere with the keyboard's state buffer":
|
||||
// http://archives.miloush.net/michkap/archive/2006/09/10/748775.html
|
||||
// > "The key here is to keep trying to pass stuff to ToUnicode until -1 is not returned."
|
||||
std::array<wchar_t, 16> buffer;
|
||||
while (ToUnicodeEx(vkey, scanCode, keyState.data(), buffer.data(), gsl::narrow_cast<int>(buffer.size()), 0b1, nullptr) < 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1276,12 +1425,19 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
// Method Description:
|
||||
// - 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:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::PersistWindowLayout()
|
||||
// - the window layout
|
||||
WindowLayout TerminalPage::GetWindowLayout()
|
||||
{
|
||||
if (_startupState != StartupState::Initialized)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<ActionAndArgs> actions;
|
||||
|
||||
for (auto tab : _tabs)
|
||||
|
@ -1289,7 +1445,7 @@ namespace winrt::TerminalApp::implementation
|
|||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
{
|
||||
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>())
|
||||
{
|
||||
|
@ -1298,7 +1454,7 @@ namespace winrt::TerminalApp::implementation
|
|||
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
|
||||
action.Args(args);
|
||||
|
||||
actions.push_back(action);
|
||||
actions.emplace_back(std::move(action));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1311,7 +1467,18 @@ namespace winrt::TerminalApp::implementation
|
|||
SwitchToTabArgs switchToTabArgs{ idx.value() };
|
||||
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{};
|
||||
|
@ -1324,33 +1491,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
layout.InitialSize(windowSize);
|
||||
|
||||
if (_hostingHwnd)
|
||||
{
|
||||
// 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 }));
|
||||
return layout;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1379,8 +1520,9 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1467,18 +1609,15 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
// Method Description:
|
||||
// - Split the focused pane either horizontally or vertically, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
// given pane accordingly in the tree
|
||||
// Arguments:
|
||||
// - newPane: the pane to add to our tree of panes
|
||||
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
|
||||
// new pane should be split from its parent.
|
||||
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
|
||||
// - newTerminalArgs: An object that may contain a blob of parameters to
|
||||
// control which profile is created and with possible other
|
||||
// configurations. See CascadiaSettings::BuildSettings for more details.
|
||||
// - splitSize: the size of the split
|
||||
void TerminalPage::_SplitPane(const SplitDirection splitDirection,
|
||||
const SplitType splitMode,
|
||||
const float splitSize,
|
||||
const NewTerminalArgs& newTerminalArgs)
|
||||
std::shared_ptr<Pane> newPane)
|
||||
{
|
||||
const auto focusedTab{ _GetFocusedTabImpl() };
|
||||
|
||||
|
@ -1488,101 +1627,52 @@ namespace winrt::TerminalApp::implementation
|
|||
return;
|
||||
}
|
||||
|
||||
_SplitPane(*focusedTab, splitDirection, splitMode, splitSize, newTerminalArgs);
|
||||
_SplitPane(*focusedTab, splitDirection, splitSize, newPane);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Split the focused pane of the given tab, either horizontally or vertically, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
// given pane accordingly
|
||||
// Arguments:
|
||||
// - tab: The tab that is going to be split.
|
||||
// - newPane: the pane to add to our tree of panes
|
||||
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
|
||||
// new pane should be split from its parent.
|
||||
// - splitMode: value from TerminalApp::SplitType enum, indicating the profile to be used in the newly split pane.
|
||||
// - newTerminalArgs: An object that may contain a blob of parameters to
|
||||
// control which profile is created and with possible other
|
||||
// configurations. See CascadiaSettings::BuildSettings for more details.
|
||||
// - splitSize: the size of the split
|
||||
void TerminalPage::_SplitPane(TerminalTab& tab,
|
||||
const SplitDirection splitDirection,
|
||||
const SplitType splitMode,
|
||||
const float splitSize,
|
||||
const NewTerminalArgs& newTerminalArgs)
|
||||
std::shared_ptr<Pane> newPane)
|
||||
{
|
||||
try
|
||||
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
|
||||
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
|
||||
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
|
||||
|
||||
auto realSplitType = splitDirection;
|
||||
if (realSplitType == SplitDirection::Automatic)
|
||||
{
|
||||
TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
Profile profile{ nullptr };
|
||||
realSplitType = tab.PreCalculateAutoSplit(availableSpace);
|
||||
}
|
||||
|
||||
if (splitMode == SplitType::Duplicate)
|
||||
const auto canSplit = tab.PreCalculateCanSplit(realSplitType, splitSize, availableSpace);
|
||||
if (!canSplit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_UnZoomIfNeeded();
|
||||
tab.SplitPane(realSplitType, splitSize, newPane);
|
||||
|
||||
// After GH#6586, the control will no longer focus itself
|
||||
// automatically when it's finished being laid out. Manually focus
|
||||
// the control here instead.
|
||||
if (_startupState == StartupState::Initialized)
|
||||
{
|
||||
if (const auto control = _GetActiveControl())
|
||||
{
|
||||
profile = tab.GetFocusedProfile();
|
||||
if (profile)
|
||||
{
|
||||
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
|
||||
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
}
|
||||
// TODO: GH#5047 - In the future, we should get the Profile of
|
||||
// the focused pane, and use that to build a new instance of the
|
||||
// settings so we can duplicate this tab/pane.
|
||||
//
|
||||
// Currently, if the profile doesn't exist anymore in our
|
||||
// settings, we'll silently do nothing.
|
||||
//
|
||||
// In the future, it will be preferable to just duplicate the
|
||||
// current control's settings, but we can't do that currently,
|
||||
// because we won't be able to create a new instance of the
|
||||
// connection without keeping an instance of the original Profile
|
||||
// object around.
|
||||
}
|
||||
if (!profile)
|
||||
{
|
||||
profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
|
||||
}
|
||||
|
||||
const auto controlConnection = _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
|
||||
|
||||
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
|
||||
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
|
||||
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
|
||||
|
||||
auto realSplitType = splitDirection;
|
||||
if (realSplitType == SplitDirection::Automatic)
|
||||
{
|
||||
realSplitType = tab.PreCalculateAutoSplit(availableSpace);
|
||||
}
|
||||
|
||||
const auto canSplit = tab.PreCalculateCanSplit(realSplitType, splitSize, availableSpace);
|
||||
if (!canSplit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto newControl = _InitControl(controlSettings, controlConnection);
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(newControl);
|
||||
|
||||
_UnZoomIfNeeded();
|
||||
|
||||
tab.SplitPane(realSplitType, splitSize, profile, newControl);
|
||||
|
||||
// After GH#6586, the control will no longer focus itself
|
||||
// automatically when it's finished being laid out. Manually focus
|
||||
// the control here instead.
|
||||
if (_startupState == StartupState::Initialized)
|
||||
{
|
||||
_GetActiveControl().Focus(FocusState::Programmatic);
|
||||
control.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1851,6 +1941,22 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
if (_settings.GlobalSettings().TrimPaste())
|
||||
{
|
||||
std::wstring_view textView{ text };
|
||||
const auto pos = textView.find_last_not_of(L"\t\n\v\f\r ");
|
||||
if (pos == textView.npos)
|
||||
{
|
||||
// Text is all white space, nothing to paste
|
||||
co_return;
|
||||
}
|
||||
else if (const auto toRemove = textView.size() - 1 - pos; toRemove > 0)
|
||||
{
|
||||
textView.remove_suffix(toRemove);
|
||||
text = { textView };
|
||||
}
|
||||
}
|
||||
|
||||
bool warnMultiLine = _settings.GlobalSettings().WarnAboutMultiLinePaste();
|
||||
if (warnMultiLine)
|
||||
{
|
||||
|
@ -2074,7 +2180,7 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (target == SettingsTarget::SettingsUI)
|
||||
{
|
||||
_OpenSettingsUI();
|
||||
OpenSettingsUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2151,6 +2257,96 @@ namespace winrt::TerminalApp::implementation
|
|||
return term;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a pane and returns a shared_ptr to it
|
||||
// - The caller should handle where the pane goes after creation,
|
||||
// either to split an already existing pane or to create a new tab with it
|
||||
// Arguments:
|
||||
// - newTerminalArgs: an object that may contain a blob of parameters to
|
||||
// control which profile is created and with possible other
|
||||
// configurations. See CascadiaSettings::BuildSettings for more details.
|
||||
// - duplicate: a boolean to indicate whether the pane we create should be
|
||||
// a duplicate of the currently focused pane
|
||||
// - existingConnection: optionally receives a connection from the outside
|
||||
// world instead of attempting to create one
|
||||
std::shared_ptr<Pane> TerminalPage::_MakePane(const NewTerminalArgs& newTerminalArgs, const bool duplicate, TerminalConnection::ITerminalConnection existingConnection)
|
||||
{
|
||||
TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
Profile profile{ nullptr };
|
||||
|
||||
if (duplicate)
|
||||
{
|
||||
const auto focusedTab{ _GetFocusedTabImpl() };
|
||||
if (focusedTab)
|
||||
{
|
||||
profile = focusedTab->GetFocusedProfile();
|
||||
if (profile)
|
||||
{
|
||||
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
|
||||
const auto workingDirectory = focusedTab->GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!profile)
|
||||
{
|
||||
profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
|
||||
}
|
||||
|
||||
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
|
||||
if (existingConnection)
|
||||
{
|
||||
connection.Resize(controlSettings.DefaultSettings().InitialRows(), controlSettings.DefaultSettings().InitialCols());
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
|
||||
if (_settings.GlobalSettings().DebugFeaturesEnabled())
|
||||
{
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool bothAltsPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
if (bothAltsPressed)
|
||||
{
|
||||
std::tie(connection, debugConnection) = OpenDebugTapConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
const auto control = _InitControl(controlSettings, connection);
|
||||
_RegisterTerminalEvents(control);
|
||||
|
||||
auto resultPane = std::make_shared<Pane>(profile, control);
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
auto newControl = _InitControl(controlSettings, debugConnection);
|
||||
_RegisterTerminalEvents(newControl);
|
||||
// Split (auto) with the debug tap.
|
||||
auto debugPane = std::make_shared<Pane>(profile, newControl);
|
||||
|
||||
// Since we're doing this split directly on the pane (instead of going through TerminalTab,
|
||||
// we need to handle the panes 'active' states
|
||||
|
||||
// Set the pane we're splitting to active (otherwise Split will not do anything)
|
||||
resultPane->SetActive();
|
||||
auto [original, _] = resultPane->Split(SplitDirection::Automatic, 0.5f, debugPane);
|
||||
|
||||
// Set the non-debug pane as active
|
||||
resultPane->ClearActive();
|
||||
original->SetActive();
|
||||
}
|
||||
|
||||
return resultPane;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Hook up keybindings, and refresh the UI of the terminal.
|
||||
// This includes update the settings of all the tabs according
|
||||
|
@ -2242,6 +2438,8 @@ namespace winrt::TerminalApp::implementation
|
|||
// enabled application-wide, so we don't need to check it each time we
|
||||
// want to create an animation.
|
||||
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_settings.GlobalSettings().DisableAnimations());
|
||||
|
||||
_tabRow.ShowElevationShield(IsElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
}
|
||||
|
||||
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
|
||||
|
@ -2458,9 +2656,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::ToggleFullscreen()
|
||||
{
|
||||
_isFullscreen = !_isFullscreen;
|
||||
_UpdateTabView();
|
||||
_FullscreenChangedHandlers(*this, nullptr);
|
||||
SetFullscreen(!_isFullscreen);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -2677,52 +2873,51 @@ namespace winrt::TerminalApp::implementation
|
|||
return _isAlwaysOnTop;
|
||||
}
|
||||
|
||||
HRESULT TerminalPage::_OnNewConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection)
|
||||
void TerminalPage::SetFullscreen(bool newFullscreen)
|
||||
{
|
||||
if (_isFullscreen == newFullscreen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isFullscreen = newFullscreen;
|
||||
_UpdateTabView();
|
||||
_FullscreenChangedHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection)
|
||||
{
|
||||
// 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.
|
||||
// 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.
|
||||
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
|
||||
if (!Dispatcher().HasThreadAccess())
|
||||
{
|
||||
til::latch latch{ 1 };
|
||||
HRESULT finalVal = S_OK;
|
||||
|
||||
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() {
|
||||
// Re-running ourselves under the dispatcher will cause us to take the first branch above.
|
||||
finalVal = _OnNewConnection(connection);
|
||||
|
||||
latch.count_down();
|
||||
});
|
||||
|
||||
latch.wait();
|
||||
return finalVal;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
NewTerminalArgs newTerminalArgs;
|
||||
newTerminalArgs.Commandline(connection.Commandline());
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
|
||||
|
||||
_CreateNewTabFromPane(_MakePane(newTerminalArgs, false, connection));
|
||||
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -2732,7 +2927,7 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_OpenSettingsUI()
|
||||
void TerminalPage::OpenSettingsUI()
|
||||
{
|
||||
// If we're holding the settings tab's switch command, don't create a new one, switch to the existing one.
|
||||
if (!_settingsTab)
|
||||
|
@ -2875,6 +3070,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:
|
||||
// - 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
|
||||
|
@ -3091,6 +3310,11 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx)
|
||||
{
|
||||
_loadFromPersistedLayoutIdx = idx;
|
||||
}
|
||||
|
||||
void TerminalPage::SetNumberOfOpenWindows(const uint64_t num)
|
||||
{
|
||||
_numOpenWindows = num;
|
||||
|
@ -3290,6 +3514,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:
|
||||
// - Checks whether information bar message was dismissed earlier (in the application state)
|
||||
// Arguments:
|
||||
|
@ -3319,13 +3577,20 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalPage::_DismissMessage(const InfoBarMessage& message)
|
||||
{
|
||||
auto dismissedMessages = ApplicationState::SharedInstance().DismissedMessages();
|
||||
if (!dismissedMessages)
|
||||
const auto applicationState = ApplicationState::SharedInstance();
|
||||
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);
|
||||
ApplicationState::SharedInstance().DismissedMessages(dismissedMessages);
|
||||
if (std::none_of(messages.begin(), messages.end(), [&](const auto& m) { return m == message; }))
|
||||
{
|
||||
messages.emplace_back(message);
|
||||
}
|
||||
|
||||
applicationState.DismissedMessages(std::move(messages));
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue