Compare commits
96 commits
main
...
dev/migrie
Author | SHA1 | Date | |
---|---|---|---|
bfc943aebf | |||
30a3621ec6 | |||
f3c7d9c471 | |||
530903219e | |||
c78c7aefa2 | |||
5936bd13b3 | |||
c95a4297cf | |||
045a8db5e1 | |||
91372ea1f9 | |||
26d5194b0c | |||
c215a97d09 | |||
4eb1d3a8d6 | |||
8370789462 | |||
15a8a9cec0 | |||
2eec961a87 | |||
51617cf6db | |||
f71c948554 | |||
9f9eacb5d9 | |||
305e62708a | |||
1b2e7a7ca1 | |||
717db8130b | |||
4166afaa98 | |||
f892752da7 | |||
71577fcd01 | |||
2a2f5cb9a5 | |||
9d76c62dae | |||
b20222f2c0 | |||
784ec731f8 | |||
1fdb6b1a3c | |||
ac8fef0e39 | |||
7c2a514e50 | |||
25b31ff854 | |||
10779ca310 | |||
5c039eaa1a | |||
eff18d1ec9 | |||
5052d31831 | |||
342d3f2026 | |||
fee6473737 | |||
1dcb4cb777 | |||
848682a27f | |||
91b52d4e6d | |||
3e5d9276f1 | |||
18099d2aa7 | |||
6e7ea615d2 | |||
a41bee653b | |||
18d1a205bb | |||
71f6b581e2 | |||
1c2f8e5d6a | |||
a65f3419e3 | |||
d2a34389a8 | |||
b2db3170dd | |||
f02969b70f | |||
59deca1c2e | |||
689c38519b | |||
5b8ace276b | |||
c34e4ce90f | |||
81c09d9650 | |||
2a7bc94a8f | |||
be74b2ee2d | |||
88ffc6fc0e | |||
bc492f1815 | |||
52b2cb6d3f | |||
3e39ab9e71 | |||
c02f25aba3 | |||
a75da0a104 | |||
65376865b5 | |||
9fc2f0ee3a | |||
0f0df5e8cf | |||
813dbc6671 | |||
bcbef340ba | |||
977db464fa | |||
658db6b568 | |||
00184e7ef6 | |||
921d91582b | |||
0f5c24f661 | |||
fa2df47898 | |||
c08889585b | |||
5939636182 | |||
f978a9c52c | |||
e1402d834f | |||
e101efd11d | |||
d08e65ce03 | |||
b4fe1bffbf | |||
0103331987 | |||
9c6eac4e86 | |||
a3faed6b7d | |||
0579b2417b | |||
590b9ff0c7 | |||
03bfc6e8a9 | |||
5cabcfb452 | |||
9a41647ffe | |||
27ace16652 | |||
36539cfa47 | |||
5a9cdc8b0b | |||
1f52d35833 | |||
3bef7bbb38 |
|
@ -1,3 +1,4 @@
|
|||
<!-- markdownlint-disable MD033 MD041 -->
|
||||
<details>
|
||||
<summary>
|
||||
:pencil2: Contributor please read this
|
||||
|
@ -8,9 +9,10 @@ By default the command suggestion will generate a file named based on your commi
|
|||
:warning: The command is written for posix shells. You can copy the contents of each `perl` command excluding the outer `'` marks and dropping any `'"`/`"'` quotation mark pairs into a file and then run `perl file.pl` from the root of the repository to run the code. Alternatively, you can manually insert the items...
|
||||
|
||||
If the listed items are:
|
||||
|
||||
* ... **misspelled**, then please *correct* them instead of using the command.
|
||||
* ... *names*, please add them to `.github/actions/spelling/dictionary/names.txt`.
|
||||
* ... APIs, you can add them to a file in `.github/actions/spelling/dictionary/`.
|
||||
* ... *names*, please add them to `.github/actions/spelling/allow/names.txt`.
|
||||
* ... APIs, you can add them to a file in `.github/actions/spelling/allow/`.
|
||||
* ... just things you're using, please add them to an appropriate file in `.github/actions/spelling/expect/`.
|
||||
* ... tokens you only need in one place and shouldn't *generally be used*, you can add an item in an appropriate file in `.github/actions/spelling/patterns/`.
|
||||
|
||||
|
@ -18,10 +20,31 @@ See the `README.md` in each directory for more information.
|
|||
|
||||
:microscope: You can test your commits **without** *appending* to a PR by creating a new branch with that extra change and pushing it to your fork. The [check-spelling](https://github.com/marketplace/actions/check-spelling) action will run in response to your **push** -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. :wink:
|
||||
|
||||
:clamp: If you see a bunch of garbage and it relates to a binary-ish string, please add a file path to the `.github/actions/spelling/excludes.txt` file instead of just accepting the garbage.
|
||||
<details><summary>:clamp: If you see a bunch of garbage</summary>
|
||||
|
||||
If it relates to a ...
|
||||
<details><summary>well-formed pattern</summary>
|
||||
|
||||
See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
|
||||
|
||||
If not, try writing one and adding it to a `patterns/{file}.txt`.
|
||||
|
||||
Patterns are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
|
||||
|
||||
Note that patterns can't match multiline strings.
|
||||
</details>
|
||||
<details><summary>binary-ish string</summary>
|
||||
|
||||
Please add a file path to the `excludes.txt` file instead of just accepting the garbage.
|
||||
|
||||
File paths are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
|
||||
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](https://github.com/microsoft/terminal/blob/main/README.md) (on whichever branch you're using).
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
|
||||
../tree/HEAD/README.md) (on whichever branch you're using).
|
||||
</details>
|
||||
|
||||
</details>
|
||||
|
||||
</details>
|
|
@ -1,4 +1,4 @@
|
|||
# Dictionaries are lists of words to accept unconditionally
|
||||
# Allow files are lists of words to accept unconditionally
|
||||
|
||||
While check spelling will complain about an expected word
|
||||
which is no longer present, you can include things here even if
|
||||
|
@ -8,11 +8,11 @@ E.g., you could include a list of system APIs here, or potential
|
|||
contributors (so that if a future commit includes their name,
|
||||
it'll be accepted).
|
||||
|
||||
### Files
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
| ---- | ----------- |
|
||||
| [Dictionary](dictionary.txt) | Primary US English dictionary |
|
||||
| [Allow](allow.txt) | Supplements to the dictionary |
|
||||
| [Chinese](chinese.txt) | Chinese words |
|
||||
| [Japanese](japanese.txt) | Japanese words |
|
||||
| [Microsoft](microsoft.txt) | Microsoft brand items |
|
77
.github/actions/spelling/allow/allow.txt
vendored
Normal file
77
.github/actions/spelling/allow/allow.txt
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
apc
|
||||
calt
|
||||
ccmp
|
||||
changelog
|
||||
cybersecurity
|
||||
Apc
|
||||
clickable
|
||||
clig
|
||||
copyable
|
||||
dalet
|
||||
dcs
|
||||
Dcs
|
||||
dialytika
|
||||
dje
|
||||
downside
|
||||
downsides
|
||||
dze
|
||||
dzhe
|
||||
Enum'd
|
||||
Fitt
|
||||
formattings
|
||||
ftp
|
||||
fvar
|
||||
geeksforgeeks
|
||||
ghe
|
||||
gje
|
||||
hostname
|
||||
hostnames
|
||||
hyperlink
|
||||
hyperlinking
|
||||
hyperlinks
|
||||
img
|
||||
It'd
|
||||
kje
|
||||
liga
|
||||
lje
|
||||
locl
|
||||
lorem
|
||||
Llast
|
||||
Lmid
|
||||
Lorigin
|
||||
maxed
|
||||
mkmk
|
||||
mru
|
||||
noreply
|
||||
nje
|
||||
ogonek
|
||||
ok'd
|
||||
overlined
|
||||
postmodern
|
||||
ptys
|
||||
qof
|
||||
qps
|
||||
rclt
|
||||
reimplementation
|
||||
reserialization
|
||||
reserialize
|
||||
reserializes
|
||||
rlig
|
||||
runtimes
|
||||
shcha
|
||||
slnt
|
||||
Sos
|
||||
timestamped
|
||||
TLDR
|
||||
tokenizes
|
||||
tonos
|
||||
tshe
|
||||
uiatextrange
|
||||
UIs
|
||||
und
|
||||
unregister
|
||||
versioned
|
||||
We'd
|
||||
wildcards
|
||||
yeru
|
||||
zhe
|
|
@ -1,23 +1,35 @@
|
|||
ACCEPTFILES
|
||||
ACCESSDENIED
|
||||
alignas
|
||||
alignof
|
||||
APPLYTOSUBMENUS
|
||||
bitfield
|
||||
bitfields
|
||||
BUILDBRANCH
|
||||
BUILDMSG
|
||||
BUILDNUMBER
|
||||
BYPOSITION
|
||||
charconv
|
||||
CLASSNOTAVAILABLE
|
||||
cmdletbinding
|
||||
colspan
|
||||
COLORPROPERTY
|
||||
colspan
|
||||
COMDLG
|
||||
comparand
|
||||
cstdint
|
||||
CXICON
|
||||
CYICON
|
||||
D2DERR_SHADER_COMPILE_FAILED
|
||||
dataobject
|
||||
dcomp
|
||||
DERR
|
||||
dlldata
|
||||
DONTADDTORECENT
|
||||
DWORDLONG
|
||||
enumset
|
||||
environstrings
|
||||
EXPCMDFLAGS
|
||||
EXPCMDSTATE
|
||||
filetime
|
||||
FILTERSPEC
|
||||
FORCEFILESYSTEM
|
||||
FORCEMINIMIZE
|
||||
|
@ -31,6 +43,7 @@ HIGHCONTRASTON
|
|||
HIGHCONTRASTW
|
||||
hotkeys
|
||||
href
|
||||
hrgn
|
||||
IActivation
|
||||
IApp
|
||||
IAppearance
|
||||
|
@ -39,97 +52,143 @@ IBind
|
|||
IBox
|
||||
IClass
|
||||
IComparable
|
||||
IComparer
|
||||
IConnection
|
||||
ICustom
|
||||
IDialog
|
||||
IDirect
|
||||
IExplorer
|
||||
IFACEMETHOD
|
||||
IFile
|
||||
IInheritable
|
||||
IMap
|
||||
IObject
|
||||
iosfwd
|
||||
IPackage
|
||||
IPeasant
|
||||
isspace
|
||||
ISetup
|
||||
IStorage
|
||||
istream
|
||||
IStringable
|
||||
ITab
|
||||
ITaskbar
|
||||
IUri
|
||||
IVirtual
|
||||
KEYSELECT
|
||||
LCID
|
||||
llabs
|
||||
llu
|
||||
localtime
|
||||
lround
|
||||
LSHIFT
|
||||
MULTIPLEUSE
|
||||
MENUCOMMAND
|
||||
MENUDATA
|
||||
MENUINFO
|
||||
memicmp
|
||||
mptt
|
||||
mov
|
||||
msappx
|
||||
MULTIPLEUSE
|
||||
NCHITTEST
|
||||
NCLBUTTONDBLCLK
|
||||
NCRBUTTONDBLCLK
|
||||
NIF
|
||||
NIN
|
||||
NOAGGREGATION
|
||||
NOASYNC
|
||||
NOCHANGEDIR
|
||||
NOPROGRESS
|
||||
NOREDIRECTIONBITMAP
|
||||
NOREPEAT
|
||||
NOTIFYBYPOS
|
||||
NOTIFYICON
|
||||
NOTIFYICONDATA
|
||||
ntprivapi
|
||||
oaidl
|
||||
ocidl
|
||||
ODR
|
||||
offsetof
|
||||
osver
|
||||
OSVERSIONINFOEXW
|
||||
otms
|
||||
OUTLINETEXTMETRICW
|
||||
overridable
|
||||
PAGESCROLL
|
||||
PICKFOLDERS
|
||||
pmr
|
||||
REGCLS
|
||||
rcx
|
||||
REGCLS
|
||||
RETURNCMD
|
||||
rfind
|
||||
roundf
|
||||
RSHIFT
|
||||
rx
|
||||
schandle
|
||||
semver
|
||||
serializer
|
||||
SETVERSION
|
||||
SHELLEXECUTEINFOW
|
||||
shobjidl
|
||||
SINGLEUSE
|
||||
SHOWMINIMIZED
|
||||
SHOWTIP
|
||||
SINGLEUSE
|
||||
SIZENS
|
||||
smoothstep
|
||||
GETDESKWALLPAPER
|
||||
SHELLEXECUTEINFOW
|
||||
snprintf
|
||||
spsc
|
||||
sregex
|
||||
SRWLOC
|
||||
SRWLOCK
|
||||
STDCPP
|
||||
strchr
|
||||
STDMETHOD
|
||||
strchr
|
||||
strcpy
|
||||
streambuf
|
||||
strtoul
|
||||
Stubless
|
||||
Subheader
|
||||
Subpage
|
||||
UPDATEINIFILE
|
||||
syscall
|
||||
TASKBARCREATED
|
||||
TBPF
|
||||
THEMECHANGED
|
||||
tlg
|
||||
tmp
|
||||
tolower
|
||||
toupper
|
||||
TTask
|
||||
TVal
|
||||
tx
|
||||
UChar
|
||||
UPDATEINIFILE
|
||||
userenv
|
||||
wcsstr
|
||||
wcstoui
|
||||
winmain
|
||||
wmemcmp
|
||||
wpc
|
||||
wsregex
|
||||
wwinmain
|
||||
xchg
|
||||
XDocument
|
||||
XElement
|
||||
xfacet
|
||||
xhash
|
||||
XIcon
|
||||
xiosbase
|
||||
xlocale
|
||||
xlocbuf
|
||||
xlocinfo
|
||||
xlocmes
|
||||
xlocmon
|
||||
xlocnum
|
||||
xloctime
|
||||
XMax
|
||||
xmemory
|
||||
XParse
|
||||
xpath
|
||||
xstddef
|
||||
xstring
|
||||
xtree
|
||||
xutility
|
||||
YIcon
|
||||
YMax
|
117
.github/actions/spelling/allow/colors.txt
vendored
Normal file
117
.github/actions/spelling/allow/colors.txt
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
alice
|
||||
aliceblue
|
||||
antiquewhite
|
||||
blanchedalmond
|
||||
blueviolet
|
||||
burlywood
|
||||
cadetblue
|
||||
cornflowerblue
|
||||
cornsilk
|
||||
cyan
|
||||
darkblue
|
||||
darkcyan
|
||||
darkgoldenrod
|
||||
darkgray
|
||||
darkgreen
|
||||
darkgrey
|
||||
darkkhaki
|
||||
darkmagenta
|
||||
darkolivegreen
|
||||
darkorange
|
||||
darkorchid
|
||||
darkred
|
||||
darksalmon
|
||||
darkseagreen
|
||||
darkslateblue
|
||||
darkslategray
|
||||
darkslategrey
|
||||
darkturquoise
|
||||
darkviolet
|
||||
deeppink
|
||||
deepskyblue
|
||||
dimgray
|
||||
dimgrey
|
||||
dodgerblue
|
||||
firebrick
|
||||
floralwhite
|
||||
forestgreen
|
||||
gainsboro
|
||||
ghostwhite
|
||||
greenyellow
|
||||
hotpink
|
||||
indian
|
||||
indianred
|
||||
lavenderblush
|
||||
lawngreen
|
||||
lemonchiffon
|
||||
lightblue
|
||||
lightcoral
|
||||
lightcyan
|
||||
lightgoldenrod
|
||||
lightgoldenrodyellow
|
||||
lightgray
|
||||
lightgreen
|
||||
lightgrey
|
||||
lightpink
|
||||
lightsalmon
|
||||
lightseagreen
|
||||
lightskyblue
|
||||
lightslateblue
|
||||
lightslategray
|
||||
lightslategrey
|
||||
lightsteelblue
|
||||
lightyellow
|
||||
limegreen
|
||||
mediumaquamarine
|
||||
mediumblue
|
||||
mediumorchid
|
||||
mediumpurple
|
||||
mediumseagreen
|
||||
mediumslateblue
|
||||
mediumspringgreen
|
||||
mediumturquoise
|
||||
mediumvioletred
|
||||
midnightblue
|
||||
mintcream
|
||||
mistyrose
|
||||
navajo
|
||||
navajowhite
|
||||
navyblue
|
||||
oldlace
|
||||
olivedrab
|
||||
orangered
|
||||
palegoldenrod
|
||||
palegreen
|
||||
paleturquoise
|
||||
palevioletred
|
||||
papayawhip
|
||||
peachpuff
|
||||
peru
|
||||
powderblue
|
||||
rebecca
|
||||
rebeccapurple
|
||||
rosybrown
|
||||
royalblue
|
||||
saddlebrown
|
||||
sandybrown
|
||||
seagreen
|
||||
sienna
|
||||
skyblue
|
||||
slateblue
|
||||
slategray
|
||||
slategrey
|
||||
springgreen
|
||||
steelblue
|
||||
violetred
|
||||
webgray
|
||||
webgreen
|
||||
webgrey
|
||||
webmaroon
|
||||
webpurple
|
||||
whitesmoke
|
||||
xaroon
|
||||
xray
|
||||
xreen
|
||||
xrey
|
||||
xurple
|
||||
yellowgreen
|
|
@ -7,3 +7,4 @@ Iosevka
|
|||
MDL
|
||||
Monofur
|
||||
Segoe
|
||||
wght
|
11
.github/actions/spelling/allow/math.txt
vendored
Normal file
11
.github/actions/spelling/allow/math.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
atan
|
||||
CPrime
|
||||
HBar
|
||||
HPrime
|
||||
isnan
|
||||
LPrime
|
||||
LStep
|
||||
powf
|
||||
RSub
|
||||
sqrtf
|
||||
ULP
|
|
@ -1,15 +1,22 @@
|
|||
ACLs
|
||||
ADMINS
|
||||
advapi
|
||||
altform
|
||||
altforms
|
||||
appendwttlogging
|
||||
appx
|
||||
appxbundle
|
||||
appxerror
|
||||
appxmanifest
|
||||
ATL
|
||||
backplating
|
||||
bitmaps
|
||||
BOMs
|
||||
CPLs
|
||||
CPRs
|
||||
cpptools
|
||||
cppvsdbg
|
||||
CPRs
|
||||
cryptbase
|
||||
DACL
|
||||
DACLs
|
||||
diffs
|
||||
|
@ -19,14 +26,20 @@ DTDs
|
|||
DWINRT
|
||||
enablewttlogging
|
||||
Intelli
|
||||
IVisual
|
||||
LKG
|
||||
LOCKFILE
|
||||
Lxss
|
||||
mfcribbon
|
||||
microsoft
|
||||
microsoftonline
|
||||
MSAA
|
||||
msixbundle
|
||||
MSVC
|
||||
muxc
|
||||
netcore
|
||||
osgvsowi
|
||||
PFILETIME
|
||||
pgc
|
||||
pgo
|
||||
pgosweep
|
||||
|
@ -34,20 +47,29 @@ powerrename
|
|||
powershell
|
||||
propkey
|
||||
pscustomobject
|
||||
QWORD
|
||||
regedit
|
||||
robocopy
|
||||
SACLs
|
||||
sdkddkver
|
||||
Shobjidl
|
||||
Skype
|
||||
SRW
|
||||
sxs
|
||||
Sysinternals
|
||||
sysnative
|
||||
systemroot
|
||||
taskkill
|
||||
tasklist
|
||||
tdbuildteamid
|
||||
unvirtualized
|
||||
VCRT
|
||||
vcruntime
|
||||
Virtualization
|
||||
visualstudio
|
||||
vscode
|
||||
VSTHRD
|
||||
winsdkver
|
||||
wlk
|
||||
wslpath
|
||||
wtl
|
|
@ -13,6 +13,7 @@ ethanschoonover
|
|||
Firefox
|
||||
Gatta
|
||||
glsl
|
||||
Gravell
|
||||
Grie
|
||||
Griese
|
||||
Hernan
|
||||
|
@ -30,6 +31,7 @@ Kourosh
|
|||
kowalczyk
|
||||
leonmsft
|
||||
Lepilleur
|
||||
lhecker
|
||||
lukesampson
|
||||
Manandhar
|
||||
mbadolato
|
||||
|
@ -51,6 +53,7 @@ oldnewthing
|
|||
opengl
|
||||
osgwiki
|
||||
pabhojwa
|
||||
panos
|
||||
paulcam
|
||||
pauldotknopf
|
||||
PGP
|
||||
|
@ -65,12 +68,13 @@ sonpham
|
|||
stakx
|
||||
thereses
|
||||
Walisch
|
||||
Wellons
|
||||
Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
zamora
|
||||
Zamora
|
||||
zamora
|
||||
Zoey
|
||||
zorio
|
||||
Zverovich
|
782
.github/actions/spelling/dictionary/colors.txt
vendored
782
.github/actions/spelling/dictionary/colors.txt
vendored
|
@ -1,782 +0,0 @@
|
|||
snow
|
||||
ghost-white
|
||||
ghostwhite
|
||||
white-smoke
|
||||
whitesmoke
|
||||
gainsboro
|
||||
floral-white
|
||||
floralwhite
|
||||
old-lace
|
||||
oldlace
|
||||
linen
|
||||
antique-white
|
||||
antiquewhite
|
||||
papaya-whip
|
||||
papayawhip
|
||||
blanched-almond
|
||||
blanchedalmond
|
||||
bisque
|
||||
peach-puff
|
||||
peachpuff
|
||||
navajo-white
|
||||
navajowhite
|
||||
moccasin
|
||||
cornsilk
|
||||
ivory
|
||||
lemon-chiffon
|
||||
lemonchiffon
|
||||
seashell
|
||||
honeydew
|
||||
mint-cream
|
||||
mintcream
|
||||
azure
|
||||
alice-blue
|
||||
aliceblue
|
||||
lavender
|
||||
lavender-blush
|
||||
lavenderblush
|
||||
misty-rose
|
||||
mistyrose
|
||||
white
|
||||
black
|
||||
dark-slate-gray
|
||||
darkslategray
|
||||
dark-slate-grey
|
||||
darkslategrey
|
||||
dim-gray
|
||||
dimgray
|
||||
dim-grey
|
||||
dimgrey
|
||||
slate-gray
|
||||
slategray
|
||||
slate-grey
|
||||
slategrey
|
||||
light-slate-gray
|
||||
lightslategray
|
||||
light-slate-grey
|
||||
lightslategrey
|
||||
gray
|
||||
grey
|
||||
xray
|
||||
x11gray
|
||||
xrey
|
||||
x11grey
|
||||
web-gray
|
||||
webgray
|
||||
web-grey
|
||||
webgrey
|
||||
light-grey
|
||||
lightgrey
|
||||
light-gray
|
||||
lightgray
|
||||
midnight-blue
|
||||
midnightblue
|
||||
navy
|
||||
navy-blue
|
||||
navyblue
|
||||
cornflower-blue
|
||||
cornflowerblue
|
||||
dark-slate-blue
|
||||
darkslateblue
|
||||
slate-blue
|
||||
slateblue
|
||||
medium-slate-blue
|
||||
mediumslateblue
|
||||
light-slate-blue
|
||||
lightslateblue
|
||||
medium-blue
|
||||
mediumblue
|
||||
royal-blue
|
||||
royalblue
|
||||
blue
|
||||
dodger-blue
|
||||
dodgerblue
|
||||
deep-sky-blue
|
||||
deepskyblue
|
||||
sky-blue
|
||||
skyblue
|
||||
light-sky-blue
|
||||
lightskyblue
|
||||
steel-blue
|
||||
steelblue
|
||||
light-steel-blue
|
||||
lightsteelblue
|
||||
light-blue
|
||||
lightblue
|
||||
powder-blue
|
||||
powderblue
|
||||
pale-turquoise
|
||||
paleturquoise
|
||||
dark-turquoise
|
||||
darkturquoise
|
||||
medium-turquoise
|
||||
mediumturquoise
|
||||
turquoise
|
||||
cyan
|
||||
aqua
|
||||
light-cyan
|
||||
lightcyan
|
||||
cadet-blue
|
||||
cadetblue
|
||||
medium-aquamarine
|
||||
mediumaquamarine
|
||||
aquamarine
|
||||
dark-green
|
||||
darkgreen
|
||||
dark-olive-green
|
||||
darkolivegreen
|
||||
dark-sea-green
|
||||
darkseagreen
|
||||
sea-green
|
||||
seagreen
|
||||
medium-sea-green
|
||||
mediumseagreen
|
||||
light-sea-green
|
||||
lightseagreen
|
||||
pale-green
|
||||
palegreen
|
||||
spring-green
|
||||
springgreen
|
||||
lawn-green
|
||||
lawngreen
|
||||
green
|
||||
lime
|
||||
xreen
|
||||
x11green
|
||||
web-green
|
||||
webgreen
|
||||
chartreuse
|
||||
medium-spring-green
|
||||
mediumspringgreen
|
||||
green-yellow
|
||||
greenyellow
|
||||
lime-green
|
||||
limegreen
|
||||
yellow-green
|
||||
yellowgreen
|
||||
forest-green
|
||||
forestgreen
|
||||
olive-drab
|
||||
olivedrab
|
||||
dark-khaki
|
||||
darkkhaki
|
||||
khaki
|
||||
pale-goldenrod
|
||||
palegoldenrod
|
||||
light-goldenrod-yellow
|
||||
lightgoldenrodyellow
|
||||
light-yellow
|
||||
lightyellow
|
||||
yellow
|
||||
gold
|
||||
light-goldenrod
|
||||
lightgoldenrod
|
||||
goldenrod
|
||||
dark-goldenrod
|
||||
darkgoldenrod
|
||||
rosy-brown
|
||||
rosybrown
|
||||
indian-red
|
||||
indianred
|
||||
saddle-brown
|
||||
saddlebrown
|
||||
sienna
|
||||
peru
|
||||
burlywood
|
||||
beige
|
||||
wheat
|
||||
sandy-brown
|
||||
sandybrown
|
||||
tan
|
||||
chocolate
|
||||
firebrick
|
||||
brown
|
||||
dark-salmon
|
||||
darksalmon
|
||||
salmon
|
||||
light-salmon
|
||||
lightsalmon
|
||||
orange
|
||||
dark-orange
|
||||
darkorange
|
||||
coral
|
||||
light-coral
|
||||
lightcoral
|
||||
tomato
|
||||
orange-red
|
||||
orangered
|
||||
red
|
||||
hot-pink
|
||||
hotpink
|
||||
deep-pink
|
||||
deeppink
|
||||
pink
|
||||
light-pink
|
||||
lightpink
|
||||
pale-violet-red
|
||||
palevioletred
|
||||
maroon
|
||||
xaroon
|
||||
x11maroon
|
||||
web-maroon
|
||||
webmaroon
|
||||
medium-violet-red
|
||||
mediumvioletred
|
||||
violet-red
|
||||
violetred
|
||||
magenta
|
||||
fuchsia
|
||||
violet
|
||||
plum
|
||||
orchid
|
||||
medium-orchid
|
||||
mediumorchid
|
||||
dark-orchid
|
||||
darkorchid
|
||||
dark-violet
|
||||
darkviolet
|
||||
blue-violet
|
||||
blueviolet
|
||||
purple
|
||||
xurple
|
||||
x11purple
|
||||
web-purple
|
||||
webpurple
|
||||
medium-purple
|
||||
mediumpurple
|
||||
thistle
|
||||
snow1
|
||||
snow2
|
||||
snow3
|
||||
snow4
|
||||
seashell1
|
||||
seashell2
|
||||
seashell3
|
||||
seashell4
|
||||
antiquewhite1
|
||||
antiquewhite2
|
||||
antiquewhite3
|
||||
antiquewhite4
|
||||
bisque1
|
||||
bisque2
|
||||
bisque3
|
||||
bisque4
|
||||
peachpuff1
|
||||
peachpuff2
|
||||
peachpuff3
|
||||
peachpuff4
|
||||
navajowhite1
|
||||
navajowhite2
|
||||
navajowhite3
|
||||
navajowhite4
|
||||
lemonchiffon1
|
||||
lemonchiffon2
|
||||
lemonchiffon3
|
||||
lemonchiffon4
|
||||
cornsilk1
|
||||
cornsilk2
|
||||
cornsilk3
|
||||
cornsilk4
|
||||
ivory1
|
||||
ivory2
|
||||
ivory3
|
||||
ivory4
|
||||
honeydew1
|
||||
honeydew2
|
||||
honeydew3
|
||||
honeydew4
|
||||
lavenderblush1
|
||||
lavenderblush2
|
||||
lavenderblush3
|
||||
lavenderblush4
|
||||
mistyrose1
|
||||
mistyrose2
|
||||
mistyrose3
|
||||
mistyrose4
|
||||
azure1
|
||||
azure2
|
||||
azure3
|
||||
azure4
|
||||
slateblue1
|
||||
slateblue2
|
||||
slateblue3
|
||||
slateblue4
|
||||
royalblue1
|
||||
royalblue2
|
||||
royalblue3
|
||||
royalblue4
|
||||
blue1
|
||||
blue2
|
||||
blue3
|
||||
blue4
|
||||
dodgerblue1
|
||||
dodgerblue2
|
||||
dodgerblue3
|
||||
dodgerblue4
|
||||
steelblue1
|
||||
steelblue2
|
||||
steelblue3
|
||||
steelblue4
|
||||
deepskyblue1
|
||||
deepskyblue2
|
||||
deepskyblue3
|
||||
deepskyblue4
|
||||
skyblue1
|
||||
skyblue2
|
||||
skyblue3
|
||||
skyblue4
|
||||
lightskyblue1
|
||||
lightskyblue2
|
||||
lightskyblue3
|
||||
lightskyblue4
|
||||
slategray1
|
||||
slategray2
|
||||
slategray3
|
||||
slategray4
|
||||
lightsteelblue1
|
||||
lightsteelblue2
|
||||
lightsteelblue3
|
||||
lightsteelblue4
|
||||
lightblue1
|
||||
lightblue2
|
||||
lightblue3
|
||||
lightblue4
|
||||
lightcyan1
|
||||
lightcyan2
|
||||
lightcyan3
|
||||
lightcyan4
|
||||
paleturquoise1
|
||||
paleturquoise2
|
||||
paleturquoise3
|
||||
paleturquoise4
|
||||
cadetblue1
|
||||
cadetblue2
|
||||
cadetblue3
|
||||
cadetblue4
|
||||
turquoise1
|
||||
turquoise2
|
||||
turquoise3
|
||||
turquoise4
|
||||
cyan1
|
||||
cyan2
|
||||
cyan3
|
||||
cyan4
|
||||
darkslategray1
|
||||
darkslategray2
|
||||
darkslategray3
|
||||
darkslategray4
|
||||
aquamarine1
|
||||
aquamarine2
|
||||
aquamarine3
|
||||
aquamarine4
|
||||
darkseagreen1
|
||||
darkseagreen2
|
||||
darkseagreen3
|
||||
darkseagreen4
|
||||
seagreen1
|
||||
seagreen2
|
||||
seagreen3
|
||||
seagreen4
|
||||
palegreen1
|
||||
palegreen2
|
||||
palegreen3
|
||||
palegreen4
|
||||
springgreen1
|
||||
springgreen2
|
||||
springgreen3
|
||||
springgreen4
|
||||
green1
|
||||
green2
|
||||
green3
|
||||
green4
|
||||
chartreuse1
|
||||
chartreuse2
|
||||
chartreuse3
|
||||
chartreuse4
|
||||
olivedrab1
|
||||
olivedrab2
|
||||
olivedrab3
|
||||
olivedrab4
|
||||
darkolivegreen1
|
||||
darkolivegreen2
|
||||
darkolivegreen3
|
||||
darkolivegreen4
|
||||
khaki1
|
||||
khaki2
|
||||
khaki3
|
||||
khaki4
|
||||
lightgoldenrod1
|
||||
lightgoldenrod2
|
||||
lightgoldenrod3
|
||||
lightgoldenrod4
|
||||
lightyellow1
|
||||
lightyellow2
|
||||
lightyellow3
|
||||
lightyellow4
|
||||
yellow1
|
||||
yellow2
|
||||
yellow3
|
||||
yellow4
|
||||
gold1
|
||||
gold2
|
||||
gold3
|
||||
gold4
|
||||
goldenrod1
|
||||
goldenrod2
|
||||
goldenrod3
|
||||
goldenrod4
|
||||
darkgoldenrod1
|
||||
darkgoldenrod2
|
||||
darkgoldenrod3
|
||||
darkgoldenrod4
|
||||
rosybrown1
|
||||
rosybrown2
|
||||
rosybrown3
|
||||
rosybrown4
|
||||
indianred1
|
||||
indianred2
|
||||
indianred3
|
||||
indianred4
|
||||
sienna1
|
||||
sienna2
|
||||
sienna3
|
||||
sienna4
|
||||
burlywood1
|
||||
burlywood2
|
||||
burlywood3
|
||||
burlywood4
|
||||
wheat1
|
||||
wheat2
|
||||
wheat3
|
||||
wheat4
|
||||
tan1
|
||||
tan2
|
||||
tan3
|
||||
tan4
|
||||
chocolate1
|
||||
chocolate2
|
||||
chocolate3
|
||||
chocolate4
|
||||
firebrick1
|
||||
firebrick2
|
||||
firebrick3
|
||||
firebrick4
|
||||
brown1
|
||||
brown2
|
||||
brown3
|
||||
brown4
|
||||
salmon1
|
||||
salmon2
|
||||
salmon3
|
||||
salmon4
|
||||
lightsalmon1
|
||||
lightsalmon2
|
||||
lightsalmon3
|
||||
lightsalmon4
|
||||
orange1
|
||||
orange2
|
||||
orange3
|
||||
orange4
|
||||
darkorange1
|
||||
darkorange2
|
||||
darkorange3
|
||||
darkorange4
|
||||
coral1
|
||||
coral2
|
||||
coral3
|
||||
coral4
|
||||
tomato1
|
||||
tomato2
|
||||
tomato3
|
||||
tomato4
|
||||
orangered1
|
||||
orangered2
|
||||
orangered3
|
||||
orangered4
|
||||
red1
|
||||
red2
|
||||
red3
|
||||
red4
|
||||
deeppink1
|
||||
deeppink2
|
||||
deeppink3
|
||||
deeppink4
|
||||
hotpink1
|
||||
hotpink2
|
||||
hotpink3
|
||||
hotpink4
|
||||
pink1
|
||||
pink2
|
||||
pink3
|
||||
pink4
|
||||
lightpink1
|
||||
lightpink2
|
||||
lightpink3
|
||||
lightpink4
|
||||
palevioletred1
|
||||
palevioletred2
|
||||
palevioletred3
|
||||
palevioletred4
|
||||
maroon1
|
||||
maroon2
|
||||
maroon3
|
||||
maroon4
|
||||
violetred1
|
||||
violetred2
|
||||
violetred3
|
||||
violetred4
|
||||
magenta1
|
||||
magenta2
|
||||
magenta3
|
||||
magenta4
|
||||
orchid1
|
||||
orchid2
|
||||
orchid3
|
||||
orchid4
|
||||
plum1
|
||||
plum2
|
||||
plum3
|
||||
plum4
|
||||
mediumorchid1
|
||||
mediumorchid2
|
||||
mediumorchid3
|
||||
mediumorchid4
|
||||
darkorchid1
|
||||
darkorchid2
|
||||
darkorchid3
|
||||
darkorchid4
|
||||
purple1
|
||||
purple2
|
||||
purple3
|
||||
purple4
|
||||
mediumpurple1
|
||||
mediumpurple2
|
||||
mediumpurple3
|
||||
mediumpurple4
|
||||
thistle1
|
||||
thistle2
|
||||
thistle3
|
||||
thistle4
|
||||
gray0
|
||||
grey0
|
||||
gray1
|
||||
grey1
|
||||
gray2
|
||||
grey2
|
||||
gray3
|
||||
grey3
|
||||
gray4
|
||||
grey4
|
||||
gray5
|
||||
grey5
|
||||
gray6
|
||||
grey6
|
||||
gray7
|
||||
grey7
|
||||
gray8
|
||||
grey8
|
||||
gray9
|
||||
grey9
|
||||
gray10
|
||||
grey10
|
||||
gray11
|
||||
grey11
|
||||
gray12
|
||||
grey12
|
||||
gray13
|
||||
grey13
|
||||
gray14
|
||||
grey14
|
||||
gray15
|
||||
grey15
|
||||
gray16
|
||||
grey16
|
||||
gray17
|
||||
grey17
|
||||
gray18
|
||||
grey18
|
||||
gray19
|
||||
grey19
|
||||
gray20
|
||||
grey20
|
||||
gray21
|
||||
grey21
|
||||
gray22
|
||||
grey22
|
||||
gray23
|
||||
grey23
|
||||
gray24
|
||||
grey24
|
||||
gray25
|
||||
grey25
|
||||
gray26
|
||||
grey26
|
||||
gray27
|
||||
grey27
|
||||
gray28
|
||||
grey28
|
||||
gray29
|
||||
grey29
|
||||
gray30
|
||||
grey30
|
||||
gray31
|
||||
grey31
|
||||
gray32
|
||||
grey32
|
||||
gray33
|
||||
grey33
|
||||
gray34
|
||||
grey34
|
||||
gray35
|
||||
grey35
|
||||
gray36
|
||||
grey36
|
||||
gray37
|
||||
grey37
|
||||
gray38
|
||||
grey38
|
||||
gray39
|
||||
grey39
|
||||
gray40
|
||||
grey40
|
||||
gray41
|
||||
grey41
|
||||
gray42
|
||||
grey42
|
||||
gray43
|
||||
grey43
|
||||
gray44
|
||||
grey44
|
||||
gray45
|
||||
grey45
|
||||
gray46
|
||||
grey46
|
||||
gray47
|
||||
grey47
|
||||
gray48
|
||||
grey48
|
||||
gray49
|
||||
grey49
|
||||
gray50
|
||||
grey50
|
||||
gray51
|
||||
grey51
|
||||
gray52
|
||||
grey52
|
||||
gray53
|
||||
grey53
|
||||
gray54
|
||||
grey54
|
||||
gray55
|
||||
grey55
|
||||
gray56
|
||||
grey56
|
||||
gray57
|
||||
grey57
|
||||
gray58
|
||||
grey58
|
||||
gray59
|
||||
grey59
|
||||
gray60
|
||||
grey60
|
||||
gray61
|
||||
grey61
|
||||
gray62
|
||||
grey62
|
||||
gray63
|
||||
grey63
|
||||
gray64
|
||||
grey64
|
||||
gray65
|
||||
grey65
|
||||
gray66
|
||||
grey66
|
||||
gray67
|
||||
grey67
|
||||
gray68
|
||||
grey68
|
||||
gray69
|
||||
grey69
|
||||
gray70
|
||||
grey70
|
||||
gray71
|
||||
grey71
|
||||
gray72
|
||||
grey72
|
||||
gray73
|
||||
grey73
|
||||
gray74
|
||||
grey74
|
||||
gray75
|
||||
grey75
|
||||
gray76
|
||||
grey76
|
||||
gray77
|
||||
grey77
|
||||
gray78
|
||||
grey78
|
||||
gray79
|
||||
grey79
|
||||
gray80
|
||||
grey80
|
||||
gray81
|
||||
grey81
|
||||
gray82
|
||||
grey82
|
||||
gray83
|
||||
grey83
|
||||
gray84
|
||||
grey84
|
||||
gray85
|
||||
grey85
|
||||
gray86
|
||||
grey86
|
||||
gray87
|
||||
grey87
|
||||
gray88
|
||||
grey88
|
||||
gray89
|
||||
grey89
|
||||
gray90
|
||||
grey90
|
||||
gray91
|
||||
grey91
|
||||
gray92
|
||||
grey92
|
||||
gray93
|
||||
grey93
|
||||
gray94
|
||||
grey94
|
||||
gray95
|
||||
grey95
|
||||
gray96
|
||||
grey96
|
||||
gray97
|
||||
grey97
|
||||
gray98
|
||||
grey98
|
||||
gray99
|
||||
grey99
|
||||
gray100
|
||||
grey100
|
||||
dark-grey
|
||||
darkgrey
|
||||
dark-gray
|
||||
darkgray
|
||||
dark-blue
|
||||
darkblue
|
||||
dark-cyan
|
||||
darkcyan
|
||||
dark-magenta
|
||||
darkmagenta
|
||||
dark-red
|
||||
darkred
|
||||
light-green
|
||||
lightgreen
|
||||
crimson
|
||||
indigo
|
||||
olive
|
||||
rebecca-purple
|
||||
rebeccapurple
|
||||
silver
|
||||
teal
|
479851
.github/actions/spelling/dictionary/dictionary.txt
vendored
479851
.github/actions/spelling/dictionary/dictionary.txt
vendored
File diff suppressed because it is too large
Load diff
3
.github/actions/spelling/dictionary/math.txt
vendored
3
.github/actions/spelling/dictionary/math.txt
vendored
|
@ -1,3 +0,0 @@
|
|||
powf
|
||||
sqrtf
|
||||
isnan
|
20
.github/actions/spelling/excludes.txt
vendored
20
.github/actions/spelling/excludes.txt
vendored
|
@ -1,3 +1,4 @@
|
|||
(?:(?i)\.png$)
|
||||
(?:^|/)dirs$
|
||||
(?:^|/)go\.mod$
|
||||
(?:^|/)go\.sum$
|
||||
|
@ -35,7 +36,6 @@ SUMS$
|
|||
\.pbxproj$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
(?:(?i)\.png$)
|
||||
\.psd$
|
||||
\.runsettings$
|
||||
\.sig$
|
||||
|
@ -54,14 +54,26 @@ SUMS$
|
|||
\.zip$
|
||||
^consolegit2gitfilters\.json$
|
||||
^dep/
|
||||
^oss/
|
||||
^doc/reference/master-sequence-list.csv$
|
||||
^doc/reference/UTF8-torture-test\.txt$
|
||||
^oss/
|
||||
^src/host/ft_uia/run\.bat$
|
||||
^src/host/runft\.bat$
|
||||
^src/host/runut\.bat$
|
||||
^src/interactivity/onecore/BgfxEngine\.
|
||||
^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/types/ut_types/UtilsTests.cpp$
|
||||
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
|
||||
^src/terminal/parser/ut_parser/run\.bat$
|
||||
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
|
||||
^src/tools/lnkd/lnkd\.bat$
|
||||
^src/tools/pixels/pixels\.bat$
|
||||
^src/tools/texttests/fira\.txt$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^\.github/actions/spelling/
|
||||
^\.gitignore$
|
||||
^doc/reference/master-sequence-list.csv$
|
||||
^\XamlStyler.json$
|
||||
|
|
16
.github/actions/spelling/expect/alphabet.txt
vendored
16
.github/actions/spelling/expect/alphabet.txt
vendored
|
@ -1,4 +1,9 @@
|
|||
AAAa
|
||||
AAAAA
|
||||
AAAAAAAAAAAAA
|
||||
AAAAAABBBBBBCCC
|
||||
AAAAABBBBBBCCC
|
||||
abcd
|
||||
abcd
|
||||
abcde
|
||||
abcdef
|
||||
|
@ -6,12 +11,20 @@ ABCDEFG
|
|||
ABCDEFGH
|
||||
ABCDEFGHIJ
|
||||
abcdefghijk
|
||||
ABCDEFGHIJKLMNO
|
||||
abcdefghijklmnop
|
||||
ABCDEFGHIJKLMNOPQRST
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
ABCG
|
||||
ABE
|
||||
BBGGRR
|
||||
abf
|
||||
BBBBB
|
||||
BBBBBBBB
|
||||
BBBBBBBBBBBBBBDDDD
|
||||
BBBBBCCC
|
||||
BBBBCCCCC
|
||||
BBGGRR
|
||||
CCE
|
||||
EFG
|
||||
EFGh
|
||||
QQQQQQQQQQABCDEFGHIJ
|
||||
|
@ -32,3 +45,4 @@ ZYXWVUT
|
|||
ZZBBZ
|
||||
ZZZBB
|
||||
ZZZBZ
|
||||
ZZZZZ
|
||||
|
|
244
.github/actions/spelling/expect/expect.txt
vendored
244
.github/actions/spelling/expect/expect.txt
vendored
File diff suppressed because it is too large
Load diff
6
.github/actions/spelling/expect/web.txt
vendored
6
.github/actions/spelling/expect/web.txt
vendored
|
@ -1,6 +1,7 @@
|
|||
http
|
||||
td
|
||||
www
|
||||
easyrgb
|
||||
php
|
||||
ecma
|
||||
rapidtables
|
||||
WCAG
|
||||
|
@ -10,10 +11,9 @@ robertelder
|
|||
kovidgoyal
|
||||
leonerd
|
||||
fixterms
|
||||
uk
|
||||
winui
|
||||
appshellintegration
|
||||
mdtauk
|
||||
cppreference
|
||||
gfycat
|
||||
what3words
|
||||
Guake
|
||||
|
|
|
@ -22,3 +22,6 @@ Base64::s_(?:En|De)code\(L"[^"]+"
|
|||
VERIFY_ARE_EQUAL\(L"[^"]+"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/"
|
||||
std::memory_order_[\w]+
|
||||
D2DERR_SHADER_COMPILE_FAILED
|
||||
TIL_FEATURE_[0-9A-Z_]+
|
||||
vcvars\w*
|
||||
|
|
22
.github/actions/spelling/reject.txt
vendored
Normal file
22
.github/actions/spelling/reject.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
^attache$
|
||||
^attacher$
|
||||
^attachers$
|
||||
^spae$
|
||||
^spaebook$
|
||||
^spaecraft$
|
||||
^spaed$
|
||||
^spaedom$
|
||||
^spaeing$
|
||||
^spaeings$
|
||||
^spae-man$
|
||||
^spaeman$
|
||||
^spaer$
|
||||
^Spaerobee$
|
||||
^spaes$
|
||||
^spaewife$
|
||||
^spaewoman$
|
||||
^spaework$
|
||||
^spaewright$
|
||||
^wether$
|
||||
^wethers$
|
||||
^wetherteg$
|
15
.github/workflows/spelling.yml
vendored
15
.github/workflows/spelling.yml
vendored
|
@ -1,15 +0,0 @@
|
|||
name: Spell checking
|
||||
on:
|
||||
pull_request_target:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Spell checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.0.0
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 5
|
||||
- uses: check-spelling/check-spelling@0.0.17-alpha
|
20
.github/workflows/spelling2.yml
vendored
Normal file
20
.github/workflows/spelling2.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
# spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p
|
||||
name: Spell checking
|
||||
on:
|
||||
pull_request_target:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Spell checking
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout-merge
|
||||
if: "contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: refs/pull/${{github.event.pull_request.number}}/merge
|
||||
- name: checkout
|
||||
if: "!contains(github.event_name, 'pull_request')"
|
||||
uses: actions/checkout@v2
|
||||
- uses: check-spelling/check-spelling@v0.0.19
|
30
NOTICE.md
30
NOTICE.md
|
@ -251,3 +251,33 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
```
|
||||
|
||||
## `VirtualDesktopUtils`
|
||||
|
||||
**Source**: [https://github.com/microsoft/PowerToys](https://github.com/microsoft/PowerToys)
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
The MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
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.
|
||||
```
|
||||
|
|
|
@ -25,6 +25,12 @@
|
|||
<ClInclude Include="ProposeCommandlineResult.h">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SummonWindowSelectionArgs.h">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SummonWindowBehavior.h">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RenameRequestArgs.h">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -54,6 +60,12 @@
|
|||
<ClCompile Include="ProposeCommandlineResult.cpp">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SummonWindowSelectionArgs.cpp">
|
||||
<DependentUpon>Monarch.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SummonWindowBehavior.cpp">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RenameRequestArgs.cpp">
|
||||
<DependentUpon>Peasant.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
|
|
@ -680,4 +680,54 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to summon a window. `args` contains information about which
|
||||
// window we should try to summon:
|
||||
// * if a WindowName is provided, we'll try to find a window with exactly
|
||||
// that name, and fail if there isn't one.
|
||||
// - Calls Peasant::Summon on the matching peasant (which might be an RPC call)
|
||||
// - This should only ever be called by the WindowManager in the monarch
|
||||
// process itself. The monarch is the one registering for global hotkeys,
|
||||
// so it's the one calling this method.
|
||||
// Arguments:
|
||||
// - args: contains information about the window that should be summoned.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
// - Sets args.FoundMatch when a window matching args is found successfully.
|
||||
void Monarch::SummonWindow(const Remoting::SummonWindowSelectionArgs& args)
|
||||
{
|
||||
const auto searchedForName{ args.WindowName() };
|
||||
try
|
||||
{
|
||||
args.FoundMatch(false);
|
||||
uint64_t windowId = 0;
|
||||
// If no name was provided, then just summon the MRU window.
|
||||
if (searchedForName.empty())
|
||||
{
|
||||
// Use the value of the `desktop` arg to determine if we should
|
||||
// limit to the current desktop (desktop:onCurrent) or not
|
||||
// (desktop:any or desktop:toCurrent)
|
||||
windowId = _getMostRecentPeasantID(args.OnCurrentDesktop());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find a peasant that currently has this name
|
||||
windowId = _lookupPeasantIdForName(searchedForName);
|
||||
}
|
||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||
{
|
||||
targetPeasant.Summon(args.SummonBehavior());
|
||||
args.FoundMatch(true);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonWindow_Failed",
|
||||
TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
|
||||
winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
|
||||
void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
|
||||
|
|
|
@ -18,6 +18,19 @@ namespace Microsoft.Terminal.Remoting
|
|||
Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass SummonWindowSelectionArgs {
|
||||
SummonWindowSelectionArgs();
|
||||
SummonWindowSelectionArgs(String windowName);
|
||||
String WindowName;
|
||||
Boolean OnCurrentDesktop;
|
||||
// TODO GH#8888 Other options:
|
||||
// * CurrentMonitor
|
||||
|
||||
Boolean FoundMatch;
|
||||
SummonWindowBehavior SummonBehavior;
|
||||
}
|
||||
|
||||
|
||||
[default_interface] runtimeclass Monarch {
|
||||
Monarch();
|
||||
|
||||
|
@ -25,6 +38,7 @@ namespace Microsoft.Terminal.Remoting
|
|||
UInt64 AddPeasant(IPeasant peasant);
|
||||
ProposeCommandlineResult ProposeCommandline(CommandlineArgs args);
|
||||
void HandleActivatePeasant(WindowActivatedArgs args);
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "pch.h"
|
||||
#include "Peasant.h"
|
||||
#include "CommandlineArgs.h"
|
||||
#include "SummonWindowBehavior.h"
|
||||
#include "Peasant.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
|
@ -117,6 +118,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
return _lastActivatedArgs;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Summon this peasant to become the active window. Currently, it just
|
||||
// causes the peasant to become the active window wherever the window
|
||||
// already was.
|
||||
// - Will raise a SummonRequested event to ask the hosting window to handle for us.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Peasant::Summon(const Remoting::SummonWindowBehavior& summonBehavior)
|
||||
{
|
||||
auto localCopy = winrt::make_self<implementation::SummonWindowBehavior>(summonBehavior);
|
||||
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_Summon",
|
||||
TraceLoggingUInt64(GetID(), "peasantID", "Our ID"),
|
||||
TraceLoggingUInt64(localCopy->MoveToCurrentDesktop(), "MoveToCurrentDesktop", "true if we should move to the current desktop"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
_SummonRequestedHandlers(*this, *localCopy);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Tell this window to display it's window ID. We'll raise a
|
||||
// DisplayWindowIdRequested event, which will get handled in the AppHost,
|
||||
|
|
|
@ -23,6 +23,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
|
||||
bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
|
||||
void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
|
||||
|
||||
void Summon(const Remoting::SummonWindowBehavior& summonBehavior);
|
||||
void RequestIdentifyWindows();
|
||||
void DisplayWindowId();
|
||||
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
|
@ -37,6 +39,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs);
|
||||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
|
||||
|
||||
private:
|
||||
Peasant(const uint64_t testPID);
|
||||
|
|
|
@ -30,6 +30,13 @@ namespace Microsoft.Terminal.Remoting
|
|||
Windows.Foundation.DateTime ActivatedTime { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SummonWindowBehavior {
|
||||
SummonWindowBehavior();
|
||||
Boolean MoveToCurrentDesktop;
|
||||
// Other options:
|
||||
// * CurrentMonitor
|
||||
}
|
||||
|
||||
interface IPeasant
|
||||
{
|
||||
CommandlineArgs InitialArgs { get; };
|
||||
|
@ -40,17 +47,20 @@ namespace Microsoft.Terminal.Remoting
|
|||
Boolean ExecuteCommandline(CommandlineArgs args);
|
||||
void ActivateWindow(WindowActivatedArgs args);
|
||||
WindowActivatedArgs GetLastActivatedArgs();
|
||||
String WindowName { get; };
|
||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
|
||||
void DisplayWindowId(); // Tells us to display its own ID (which causes a DisplayWindowIdRequested to be raised)
|
||||
|
||||
String WindowName { get; };
|
||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
||||
void Summon(SummonWindowBehavior behavior);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> DisplayWindowIdRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameRequestArgs> RenameRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Peasant : IPeasant
|
||||
|
|
5
src/cascadia/Remoting/SummonWindowBehavior.cpp
Normal file
5
src/cascadia/Remoting/SummonWindowBehavior.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
#include "pch.h"
|
||||
#include "SummonWindowBehavior.h"
|
||||
#include "SummonWindowBehavior.g.cpp"
|
39
src/cascadia/Remoting/SummonWindowBehavior.h
Normal file
39
src/cascadia/Remoting/SummonWindowBehavior.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Class Name:
|
||||
- SummonWindowBehavior.h
|
||||
|
||||
Abstract:
|
||||
- A helper class for encapsulating the various properties that control _how_ a
|
||||
window should be summoned, when it is summoned. This will be created from the
|
||||
properties in a GlobalSummonArgs, and passed into a SummonWindowSelectionArgs.
|
||||
When the window is summoned, the Monarch will use these args in the
|
||||
SummonWindowSelectionArgs to tell the peasant how to be summoned.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SummonWindowBehavior.g.h"
|
||||
#include "../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct SummonWindowBehavior : public SummonWindowBehaviorT<SummonWindowBehavior>
|
||||
{
|
||||
public:
|
||||
SummonWindowBehavior() = default;
|
||||
WINRT_PROPERTY(bool, MoveToCurrentDesktop, true);
|
||||
|
||||
public:
|
||||
SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) :
|
||||
_MoveToCurrentDesktop{ other.MoveToCurrentDesktop() } {};
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(SummonWindowBehavior);
|
||||
}
|
5
src/cascadia/Remoting/SummonWindowSelectionArgs.cpp
Normal file
5
src/cascadia/Remoting/SummonWindowSelectionArgs.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
#include "pch.h"
|
||||
#include "SummonWindowSelectionArgs.h"
|
||||
#include "SummonWindowSelectionArgs.g.cpp"
|
43
src/cascadia/Remoting/SummonWindowSelectionArgs.h
Normal file
43
src/cascadia/Remoting/SummonWindowSelectionArgs.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Class Name:
|
||||
- SummonWindowSelectionArgs.h
|
||||
|
||||
Abstract:
|
||||
- This is a helper class for determining which window a should be summoned when
|
||||
a global hotkey is pressed. Parameters from a GlobalSummon action will be
|
||||
filled in here. The Monarch will use these to find the window that matches
|
||||
these args, and Summon() that Peasant.
|
||||
- When the monarch finds a match, it will set FoundMatch to true. If it doesn't,
|
||||
then the Monarch window might need to create a new window matching these args
|
||||
instead.
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SummonWindowSelectionArgs.g.h"
|
||||
#include "../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct SummonWindowSelectionArgs : public SummonWindowSelectionArgsT<SummonWindowSelectionArgs>
|
||||
{
|
||||
public:
|
||||
SummonWindowSelectionArgs() = default;
|
||||
SummonWindowSelectionArgs(winrt::hstring name) :
|
||||
_WindowName{ name } {};
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
|
||||
WINRT_PROPERTY(bool, FoundMatch, false);
|
||||
WINRT_PROPERTY(bool, OnCurrentDesktop, false);
|
||||
WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(SummonWindowSelectionArgs);
|
||||
}
|
|
@ -247,6 +247,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// window, and when the current monarch dies.
|
||||
|
||||
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
|
||||
|
||||
_BecameMonarchHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
bool WindowManager::_areWeTheKing()
|
||||
|
@ -478,4 +480,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
{
|
||||
_FindTargetWindowRequestedHandlers(sender, args);
|
||||
}
|
||||
|
||||
bool WindowManager::IsMonarch()
|
||||
{
|
||||
return _isKing;
|
||||
}
|
||||
|
||||
void WindowManager::SummonWindow(const Remoting::SummonWindowSelectionArgs& args)
|
||||
{
|
||||
// We should only ever get called when we are the monarch, because only
|
||||
// the monarch ever registers for the global hotkey. So the monarch is
|
||||
// the only window that will be calling this.
|
||||
_monarch.SummonWindow(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,8 +37,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
bool ShouldCreateWindow();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow();
|
||||
bool IsMonarch();
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
||||
private:
|
||||
bool _shouldCreateWindow{ false };
|
||||
|
|
|
@ -10,6 +10,9 @@ namespace Microsoft.Terminal.Remoting
|
|||
void ProposeCommandline(CommandlineArgs args);
|
||||
Boolean ShouldCreateWindow { get; };
|
||||
IPeasant CurrentWindow();
|
||||
Boolean IsMonarch { get; };
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -771,4 +771,23 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleGlobalSummon(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
// Manually return false. These shouldn't ever get here, except for when
|
||||
// we fail to register for the global hotkey. In that case, returning
|
||||
// false here will let the underlying terminal still process the key, as
|
||||
// if it wasn't bound at all.
|
||||
args.Handled(false);
|
||||
}
|
||||
void TerminalPage::_HandleQuakeMode(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
// Manually return false. These shouldn't ever get here, except for when
|
||||
// we fail to register for the global hotkey. In that case, returning
|
||||
// false here will let the underlying terminal still process the key, as
|
||||
// if it wasn't bound at all.
|
||||
args.Handled(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -988,7 +988,7 @@ namespace winrt::TerminalApp::implementation
|
|||
CATCH_LOG();
|
||||
|
||||
// Method Description:
|
||||
// - Reloads the settings from the profile.json.
|
||||
// - Reloads the settings from the settings.json file.
|
||||
void AppLogic::_ReloadSettings()
|
||||
{
|
||||
// Attempt to load our settings.
|
||||
|
@ -1018,6 +1018,8 @@ namespace winrt::TerminalApp::implementation
|
|||
_ApplyStartupTaskStateChange();
|
||||
|
||||
Jumplist::UpdateJumplist(_settings);
|
||||
|
||||
_SettingsChangedHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1400,6 +1402,11 @@ namespace winrt::TerminalApp::implementation
|
|||
return _root ? _root->AlwaysOnTop() : false;
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IMap<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::ActionAndArgs> AppLogic::GlobalHotkeys()
|
||||
{
|
||||
return _settings.GlobalSettings().KeyMap().FetchGlobalHotkeys();
|
||||
}
|
||||
|
||||
void AppLogic::IdentifyWindow()
|
||||
{
|
||||
if (_root)
|
||||
|
|
|
@ -89,8 +89,11 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
|
||||
Windows::Foundation::Collections::IMap<Microsoft::Terminal::Control::KeyChord, Microsoft::Terminal::Settings::Model::ActionAndArgs> 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);
|
||||
|
||||
private:
|
||||
bool _isUwp{ false };
|
||||
|
|
|
@ -70,6 +70,8 @@ namespace TerminalApp
|
|||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
|
||||
Windows.Foundation.Collections.IMap<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.ActionAndArgs> GlobalHotkeys();
|
||||
|
||||
// See IDialogPresenter and TerminalPage's DialogPresenter for more
|
||||
// information.
|
||||
Windows.Foundation.IAsyncOperation<Windows.UI.Xaml.Controls.ContentDialogResult> ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog);
|
||||
|
@ -85,5 +87,6 @@ namespace TerminalApp
|
|||
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" };
|
|||
static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" };
|
||||
static constexpr std::string_view RenameWindowKey{ "renameWindow" };
|
||||
static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
|
||||
static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
|
||||
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
|
||||
|
||||
static constexpr std::string_view ActionKey{ "action" };
|
||||
|
||||
|
@ -127,6 +129,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{ IdentifyWindowsKey, ShortcutAction::IdentifyWindows },
|
||||
{ RenameWindowKey, ShortcutAction::RenameWindow },
|
||||
{ OpenWindowRenamerKey, ShortcutAction::OpenWindowRenamer },
|
||||
{ GlobalSummonKey, ShortcutAction::GlobalSummon },
|
||||
{ QuakeModeKey, ShortcutAction::QuakeMode },
|
||||
};
|
||||
|
||||
using ParseResult = std::tuple<IActionArgs, std::vector<SettingsLoadWarnings>>;
|
||||
|
@ -162,6 +166,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{ ShortcutAction::PrevTab, PrevTabArgs::FromJson },
|
||||
{ ShortcutAction::NextTab, NextTabArgs::FromJson },
|
||||
{ ShortcutAction::RenameWindow, RenameWindowArgs::FromJson },
|
||||
{ ShortcutAction::GlobalSummon, GlobalSummonArgs::FromJson },
|
||||
|
||||
{ ShortcutAction::Invalid, nullptr },
|
||||
};
|
||||
|
@ -337,6 +342,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{ ShortcutAction::IdentifyWindows, RS_(L"IdentifyWindowsCommandKey") },
|
||||
{ ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") },
|
||||
{ ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") },
|
||||
{ ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
|
||||
};
|
||||
}();
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "PrevTabArgs.g.cpp"
|
||||
#include "NextTabArgs.g.cpp"
|
||||
#include "RenameWindowArgs.g.cpp"
|
||||
#include "GlobalSummonArgs.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
|
@ -582,4 +583,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
return RS_(L"ResetWindowNameCommandKey");
|
||||
}
|
||||
|
||||
winrt::hstring GlobalSummonArgs::GenerateName() const
|
||||
{
|
||||
std::wstringstream ss;
|
||||
ss << std::wstring_view(RS_(L"GlobalSummonCommandKey"));
|
||||
|
||||
// "Summon the Terminal window"
|
||||
// "Summon the Terminal window, name:\"{_Name}\""
|
||||
if (!_Name.empty())
|
||||
{
|
||||
ss << L", name: ";
|
||||
ss << std::wstring_view(_Name);
|
||||
}
|
||||
return winrt::hstring{ ss.str() };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "PrevTabArgs.g.h"
|
||||
#include "NextTabArgs.g.h"
|
||||
#include "RenameWindowArgs.g.h"
|
||||
#include "GlobalSummonArgs.g.h"
|
||||
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "JsonUtils.h"
|
||||
|
@ -1037,6 +1038,45 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
return *copy;
|
||||
}
|
||||
};
|
||||
|
||||
struct GlobalSummonArgs : public GlobalSummonArgsT<GlobalSummonArgs>
|
||||
{
|
||||
GlobalSummonArgs() = default;
|
||||
WINRT_PROPERTY(winrt::hstring, Name, L"");
|
||||
WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent);
|
||||
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view DesktopKey{ "desktop" };
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
if (auto otherAsUs = other.try_as<GlobalSummonArgs>())
|
||||
{
|
||||
return otherAsUs->_Name == _Name &&
|
||||
otherAsUs->_Desktop == _Desktop;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static FromJsonResult FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<GlobalSummonArgs>();
|
||||
JsonUtils::GetValueForKey(json, NameKey, args->_Name);
|
||||
JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop);
|
||||
return { *args, {} };
|
||||
}
|
||||
IActionArgs Copy() const
|
||||
{
|
||||
auto copy{ winrt::make_self<GlobalSummonArgs>() };
|
||||
copy->_Name = _Name;
|
||||
copy->_Desktop = _Desktop;
|
||||
return *copy;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
|
|
|
@ -84,6 +84,13 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
Disabled,
|
||||
};
|
||||
|
||||
enum DesktopBehavior
|
||||
{
|
||||
Any,
|
||||
ToCurrent,
|
||||
OnCurrent,
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass NewTerminalArgs {
|
||||
NewTerminalArgs();
|
||||
NewTerminalArgs(Int32 profileIndex);
|
||||
|
@ -252,4 +259,10 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
{
|
||||
String Name { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass GlobalSummonArgs : IActionArgs
|
||||
{
|
||||
String Name { get; };
|
||||
DesktopBehavior Desktop { get; };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -72,4 +72,6 @@
|
|||
ON_ALL_ACTIONS(IdentifyWindow) \
|
||||
ON_ALL_ACTIONS(IdentifyWindows) \
|
||||
ON_ALL_ACTIONS(RenameWindow) \
|
||||
ON_ALL_ACTIONS(OpenWindowRenamer)
|
||||
ON_ALL_ACTIONS(OpenWindowRenamer) \
|
||||
ON_ALL_ACTIONS(GlobalSummon) \
|
||||
ON_ALL_ACTIONS(QuakeMode)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "../../inc/DefaultSettings.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
#include "KeyChordSerialization.h"
|
||||
|
||||
#include "GlobalAppSettings.g.cpp"
|
||||
|
||||
|
|
|
@ -141,4 +141,44 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
return keyModifiers;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Build a map of all the globalSummon actions.
|
||||
// - quakeMode actions are included in this, but expanded to the equivalent
|
||||
// set of GlobalSummonArgs
|
||||
// - This is only ever called in two scenarios:
|
||||
// - on becoming the monarch (which only happens once per window)
|
||||
// - when the settings reload (and the cache would inevitably be dirty)
|
||||
// So it's perfectly reasonable to not cache these results.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a map of KeyChord -> ActionAndArgs containing all globally bindable actions.
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::ActionAndArgs> KeyMapping::FetchGlobalHotkeys()
|
||||
{
|
||||
std::unordered_map<Control::KeyChord, Model::ActionAndArgs, KeyChordHash, KeyChordEquality> justGlobals;
|
||||
|
||||
for (const auto& [k, v] : _keyShortcuts)
|
||||
{
|
||||
if (v.Action() == ShortcutAction::GlobalSummon)
|
||||
{
|
||||
justGlobals[k] = v;
|
||||
}
|
||||
else if (v.Action() == ShortcutAction::QuakeMode)
|
||||
{
|
||||
// Manually replace the QuakeMode action with a globalSummon
|
||||
// that has the appropriate action args.
|
||||
auto args = winrt::make_self<GlobalSummonArgs>();
|
||||
|
||||
// We want to summon the window with the name "_quake" specifically.
|
||||
args->Name(L"_quake");
|
||||
|
||||
Model::ActionAndArgs actionAndArgs{ ShortcutAction::GlobalSummon, *args };
|
||||
justGlobals[k] = actionAndArgs;
|
||||
}
|
||||
}
|
||||
|
||||
return winrt::single_threaded_map<Control::KeyChord, Model::ActionAndArgs>(std::move(justGlobals));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
std::vector<Model::SettingsLoadWarnings> LayerJson(const Json::Value& json);
|
||||
Json::Value ToJson();
|
||||
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::ActionAndArgs> FetchGlobalHotkeys();
|
||||
|
||||
private:
|
||||
std::unordered_map<Control::KeyChord, Model::ActionAndArgs, KeyChordHash, KeyChordEquality> _keyShortcuts;
|
||||
std::vector<std::pair<Control::KeyChord, Model::ActionAndArgs>> _keyShortcutsByInsertionOrder;
|
||||
|
|
|
@ -35,5 +35,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
|
||||
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action);
|
||||
Microsoft.Terminal.Control.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs);
|
||||
|
||||
Windows.Foundation.Collections.IMap<Microsoft.Terminal.Control.KeyChord, ActionAndArgs> FetchGlobalHotkeys();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -391,4 +391,10 @@
|
|||
<data name="OpenWindowRenamerCommandKey" xml:space="preserve">
|
||||
<value>Rename window...</value>
|
||||
</data>
|
||||
<data name="GlobalSummonCommandKey" xml:space="preserve">
|
||||
<value>Summon the Terminal window</value>
|
||||
</data>
|
||||
<data name="QuakeModeCommandKey" xml:space="preserve">
|
||||
<value>Summon Quake window</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -448,3 +448,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode)
|
|||
pair_type{ "useExisting", ValueType::UseExisting },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::DesktopBehavior)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "any", ValueType::Any },
|
||||
pair_type{ "toCurrent", ValueType::ToCurrent },
|
||||
pair_type{ "onCurrent", ValueType::OnCurrent },
|
||||
};
|
||||
};
|
||||
|
|
|
@ -36,6 +36,31 @@ using namespace winrt::Microsoft::Terminal;
|
|||
|
||||
namespace RemotingUnitTests
|
||||
{
|
||||
struct MockDesktopManager : implements<MockDesktopManager, IVirtualDesktopManager>
|
||||
{
|
||||
HRESULT GetWindowDesktopId(HWND /*topLevelWindow*/, GUID* /*desktopId*/)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests.");
|
||||
return E_FAIL;
|
||||
}
|
||||
HRESULT MoveWindowToDesktop(HWND /*topLevelWindow*/, REFGUID /*desktopId*/)
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests.");
|
||||
return E_FAIL;
|
||||
}
|
||||
HRESULT IsWindowOnCurrentVirtualDesktop(HWND topLevelWindow, BOOL* onCurrentDesktop)
|
||||
{
|
||||
if (pfnIsWindowOnCurrentVirtualDesktop)
|
||||
{
|
||||
return pfnIsWindowOnCurrentVirtualDesktop(topLevelWindow, onCurrentDesktop);
|
||||
}
|
||||
VERIFY_IS_TRUE(false, L"You didn't set up the pfnIsWindowOnCurrentVirtualDesktop for this test!");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
std::function<HRESULT(HWND, BOOL*)> pfnIsWindowOnCurrentVirtualDesktop;
|
||||
};
|
||||
|
||||
// This is a silly helper struct.
|
||||
// It will always throw an hresult_error on any of its methods.
|
||||
//
|
||||
|
@ -60,11 +85,13 @@ namespace RemotingUnitTests
|
|||
Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; }
|
||||
Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; }
|
||||
void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error{}; }
|
||||
void Summon(const Remoting::SummonWindowBehavior& /*args*/) { throw winrt::hresult_error{}; };
|
||||
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs);
|
||||
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs);
|
||||
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs);
|
||||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior);
|
||||
};
|
||||
|
||||
class RemotingTests
|
||||
|
@ -105,6 +132,15 @@ namespace RemotingUnitTests
|
|||
TEST_METHOD(TestRenameSameNameAsAnother);
|
||||
TEST_METHOD(TestRenameSameNameAsADeadPeasant);
|
||||
|
||||
TEST_METHOD(TestSummonMostRecentWindow);
|
||||
TEST_METHOD(TestSummonNamedWindow);
|
||||
TEST_METHOD(TestSummonNamedDeadWindow);
|
||||
TEST_METHOD(TestSummonMostRecentDeadWindow);
|
||||
|
||||
TEST_METHOD(TestSummonOnCurrent);
|
||||
TEST_METHOD(TestSummonOnCurrentWithName);
|
||||
TEST_METHOD(TestSummonOnCurrentDeadWindow);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
return true;
|
||||
|
@ -466,7 +502,6 @@ namespace RemotingUnitTests
|
|||
VERIFY_ARE_EQUAL(false, (bool)result.Id());
|
||||
}
|
||||
}
|
||||
|
||||
void RemotingTests::ProposeCommandlineCurrentWindow()
|
||||
{
|
||||
Log::Comment(L"Test proposing a commandline for the current window (ID=0)");
|
||||
|
@ -547,7 +582,6 @@ namespace RemotingUnitTests
|
|||
VERIFY_ARE_EQUAL(false, (bool)result.Id());
|
||||
}
|
||||
}
|
||||
|
||||
void RemotingTests::ProposeCommandlineNonExistentWindow()
|
||||
{
|
||||
Log::Comment(L"Test proposing a commandline for an ID that doesn't have a current peasant");
|
||||
|
@ -1506,4 +1540,884 @@ namespace RemotingUnitTests
|
|||
VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"two"));
|
||||
}
|
||||
|
||||
void RemotingTests::TestSummonMostRecentWindow()
|
||||
{
|
||||
Log::Comment(L"Attempt to summon the most recent window");
|
||||
|
||||
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
|
||||
|
||||
const auto monarch0PID = 12345u;
|
||||
const auto peasant1PID = 23456u;
|
||||
const auto peasant2PID = 34567u;
|
||||
|
||||
com_ptr<Remoting::implementation::Monarch> m0;
|
||||
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p1;
|
||||
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p2;
|
||||
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
|
||||
|
||||
VERIFY_IS_NOT_NULL(m0);
|
||||
VERIFY_IS_NOT_NULL(p1);
|
||||
VERIFY_IS_NOT_NULL(p2);
|
||||
p1->WindowName(L"one");
|
||||
p2->WindowName(L"two");
|
||||
|
||||
VERIFY_ARE_EQUAL(0, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p2->GetID());
|
||||
|
||||
m0->AddPeasant(*p1);
|
||||
m0->AddPeasant(*p2);
|
||||
|
||||
VERIFY_ARE_EQUAL(1, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(2, p2->GetID());
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
|
||||
|
||||
bool p1ExpectedToBeSummoned = false;
|
||||
bool p2ExpectedToBeSummoned = false;
|
||||
|
||||
p1->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p1 summoned");
|
||||
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
|
||||
});
|
||||
p2->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p2 summoned");
|
||||
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
|
||||
});
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the second peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p2->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
p2ExpectedToBeSummoned = true;
|
||||
Remoting::SummonWindowSelectionArgs args;
|
||||
// Without setting the WindowName, SummonWindowSelectionArgs defaults to
|
||||
// the MRU window
|
||||
Log::Comment(L"Summon the MRU window, which is window two");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
Log::Comment(L"Now that one is the MRU, summon it");
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p1ExpectedToBeSummoned = true;
|
||||
args.FoundMatch(false);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
}
|
||||
|
||||
void RemotingTests::TestSummonNamedWindow()
|
||||
{
|
||||
Log::Comment(L"Attempt to summon a window by name. When there isn't a "
|
||||
L"window with that name, set FoundMatch to false, so the "
|
||||
L"caller can handle that case.");
|
||||
|
||||
const auto monarch0PID = 12345u;
|
||||
const auto peasant1PID = 23456u;
|
||||
const auto peasant2PID = 34567u;
|
||||
|
||||
com_ptr<Remoting::implementation::Monarch> m0;
|
||||
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p1;
|
||||
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p2;
|
||||
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
|
||||
|
||||
VERIFY_IS_NOT_NULL(m0);
|
||||
VERIFY_IS_NOT_NULL(p1);
|
||||
VERIFY_IS_NOT_NULL(p2);
|
||||
p1->WindowName(L"one");
|
||||
p2->WindowName(L"two");
|
||||
|
||||
VERIFY_ARE_EQUAL(0, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p2->GetID());
|
||||
|
||||
m0->AddPeasant(*p1);
|
||||
m0->AddPeasant(*p2);
|
||||
|
||||
VERIFY_ARE_EQUAL(1, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(2, p2->GetID());
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
|
||||
|
||||
bool p1ExpectedToBeSummoned = false;
|
||||
bool p2ExpectedToBeSummoned = false;
|
||||
|
||||
p1->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p1 summoned");
|
||||
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
|
||||
});
|
||||
p2->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p2 summoned");
|
||||
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
|
||||
});
|
||||
|
||||
Remoting::SummonWindowSelectionArgs args;
|
||||
|
||||
Log::Comment(L"Summon window two by name");
|
||||
p2ExpectedToBeSummoned = true;
|
||||
args.WindowName(L"two");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Summon window one by name");
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p1ExpectedToBeSummoned = true;
|
||||
args.FoundMatch(false);
|
||||
args.WindowName(L"one");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Fail to summon window three by name");
|
||||
p1ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.WindowName(L"three");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_FALSE(args.FoundMatch());
|
||||
}
|
||||
|
||||
void RemotingTests::TestSummonNamedDeadWindow()
|
||||
{
|
||||
Log::Comment(L"Attempt to summon a dead window by name. This will fail, but not crash.");
|
||||
|
||||
const auto monarch0PID = 12345u;
|
||||
const auto peasant1PID = 23456u;
|
||||
const auto peasant2PID = 34567u;
|
||||
|
||||
com_ptr<Remoting::implementation::Monarch> m0;
|
||||
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p1;
|
||||
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p2;
|
||||
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
|
||||
|
||||
VERIFY_IS_NOT_NULL(m0);
|
||||
VERIFY_IS_NOT_NULL(p1);
|
||||
VERIFY_IS_NOT_NULL(p2);
|
||||
p1->WindowName(L"one");
|
||||
p2->WindowName(L"two");
|
||||
|
||||
VERIFY_ARE_EQUAL(0, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p2->GetID());
|
||||
|
||||
m0->AddPeasant(*p1);
|
||||
m0->AddPeasant(*p2);
|
||||
|
||||
VERIFY_ARE_EQUAL(1, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(2, p2->GetID());
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
|
||||
|
||||
bool p1ExpectedToBeSummoned = false;
|
||||
bool p2ExpectedToBeSummoned = false;
|
||||
|
||||
p1->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p1 summoned");
|
||||
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
|
||||
});
|
||||
p2->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p2 summoned");
|
||||
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
|
||||
});
|
||||
|
||||
Remoting::SummonWindowSelectionArgs args;
|
||||
|
||||
Log::Comment(L"Summon window two by name");
|
||||
p2ExpectedToBeSummoned = true;
|
||||
args.WindowName(L"two");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Summon window one by name");
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p1ExpectedToBeSummoned = true;
|
||||
args.FoundMatch(false);
|
||||
args.WindowName(L"one");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Kill peasant one.");
|
||||
RemotingTests::_killPeasant(m0, p1->GetID());
|
||||
|
||||
Log::Comment(L"Fail to summon window one by name");
|
||||
p1ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.WindowName(L"one");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_FALSE(args.FoundMatch());
|
||||
}
|
||||
|
||||
void RemotingTests::TestSummonMostRecentDeadWindow()
|
||||
{
|
||||
Log::Comment(L"Attempt to summon the MRU window, when the MRU window "
|
||||
L"has died. This will fall back to the next MRU window.");
|
||||
|
||||
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
|
||||
|
||||
const auto monarch0PID = 12345u;
|
||||
const auto peasant1PID = 23456u;
|
||||
const auto peasant2PID = 34567u;
|
||||
|
||||
com_ptr<Remoting::implementation::Monarch> m0;
|
||||
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p1;
|
||||
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p2;
|
||||
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
|
||||
|
||||
VERIFY_IS_NOT_NULL(m0);
|
||||
VERIFY_IS_NOT_NULL(p1);
|
||||
VERIFY_IS_NOT_NULL(p2);
|
||||
p1->WindowName(L"one");
|
||||
p2->WindowName(L"two");
|
||||
|
||||
VERIFY_ARE_EQUAL(0, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p2->GetID());
|
||||
|
||||
m0->AddPeasant(*p1);
|
||||
m0->AddPeasant(*p2);
|
||||
|
||||
VERIFY_ARE_EQUAL(1, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(2, p2->GetID());
|
||||
|
||||
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
|
||||
|
||||
bool p1ExpectedToBeSummoned = false;
|
||||
bool p2ExpectedToBeSummoned = false;
|
||||
|
||||
p1->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p1 summoned");
|
||||
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
|
||||
});
|
||||
p2->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p2 summoned");
|
||||
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
|
||||
});
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the second peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p2->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
p2ExpectedToBeSummoned = true;
|
||||
Remoting::SummonWindowSelectionArgs args;
|
||||
// Without setting the WindowName, SummonWindowSelectionArgs defaults to
|
||||
// the MRU window
|
||||
Log::Comment(L"Summon the MRU window, which is window two");
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
Log::Comment(L"Now that one is the MRU, summon it");
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p1ExpectedToBeSummoned = true;
|
||||
args.FoundMatch(false);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Kill peasant one.");
|
||||
RemotingTests::_killPeasant(m0, p1->GetID());
|
||||
|
||||
Log::Comment(L"We now expect to summon two, since the MRU peasant (one) is actually dead.");
|
||||
p2ExpectedToBeSummoned = true;
|
||||
p1ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
}
|
||||
|
||||
void RemotingTests::TestSummonOnCurrent()
|
||||
{
|
||||
Log::Comment(L"Tests summoning a window, using OnCurrentDesktop to only"
|
||||
L"select windows on the current desktop.");
|
||||
|
||||
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
|
||||
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
|
||||
|
||||
constexpr auto monarch0PID = 12345u;
|
||||
constexpr auto peasant1PID = 23456u;
|
||||
constexpr auto peasant2PID = 34567u;
|
||||
constexpr auto peasant3PID = 45678u;
|
||||
|
||||
com_ptr<Remoting::implementation::Monarch> m0;
|
||||
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p1;
|
||||
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p2;
|
||||
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p3;
|
||||
p3.attach(new Remoting::implementation::Peasant(peasant3PID));
|
||||
|
||||
VERIFY_IS_NOT_NULL(m0);
|
||||
VERIFY_IS_NOT_NULL(p1);
|
||||
VERIFY_IS_NOT_NULL(p2);
|
||||
VERIFY_IS_NOT_NULL(p3);
|
||||
p1->WindowName(L"one");
|
||||
p2->WindowName(L"two");
|
||||
p3->WindowName(L"three");
|
||||
|
||||
VERIFY_ARE_EQUAL(0, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p2->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p3->GetID());
|
||||
|
||||
m0->AddPeasant(*p1);
|
||||
m0->AddPeasant(*p2);
|
||||
m0->AddPeasant(*p3);
|
||||
|
||||
VERIFY_ARE_EQUAL(1, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(2, p2->GetID());
|
||||
VERIFY_ARE_EQUAL(3, p3->GetID());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
|
||||
|
||||
bool p1ExpectedToBeSummoned = false;
|
||||
bool p2ExpectedToBeSummoned = false;
|
||||
bool p3ExpectedToBeSummoned = false;
|
||||
|
||||
p1->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p1 summoned");
|
||||
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
|
||||
});
|
||||
p2->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p2 summoned");
|
||||
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
|
||||
});
|
||||
p3->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p3 summoned");
|
||||
VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
|
||||
});
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the second peasant, second desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
|
||||
p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid2,
|
||||
winrt::clock().now() };
|
||||
p2->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the third peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
|
||||
p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p3->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
|
||||
winrt::com_ptr<MockDesktopManager> manager;
|
||||
manager.attach(new MockDesktopManager());
|
||||
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
|
||||
|
||||
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
|
||||
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
|
||||
|
||||
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
|
||||
if (hwnd == peasant1PID || hwnd == peasant3PID)
|
||||
{
|
||||
*result = true;
|
||||
}
|
||||
else if (hwnd == peasant2PID)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
|
||||
}
|
||||
return S_OK;
|
||||
};
|
||||
manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
|
||||
|
||||
Remoting::SummonWindowSelectionArgs args;
|
||||
|
||||
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
|
||||
p3ExpectedToBeSummoned = true;
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
Log::Comment(L"Summon window one - it is the MRU on desktop 1");
|
||||
p1ExpectedToBeSummoned = true;
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p3ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Now we'll pretend we switched to desktop 2");
|
||||
|
||||
auto secondCallback = [&](HWND h, BOOL* result) -> HRESULT {
|
||||
Log::Comment(L"secondCallback: Checking if window is on desktop 2");
|
||||
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
|
||||
if (hwnd == peasant1PID || hwnd == peasant3PID)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
else if (hwnd == peasant2PID)
|
||||
{
|
||||
*result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
|
||||
}
|
||||
return S_OK;
|
||||
};
|
||||
manager->pfnIsWindowOnCurrentVirtualDesktop = secondCallback;
|
||||
|
||||
Log::Comment(L"Summon window one - it is the MRU on desktop 2");
|
||||
p1ExpectedToBeSummoned = false;
|
||||
p2ExpectedToBeSummoned = true;
|
||||
p3ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the third peasant, second desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
|
||||
p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid2,
|
||||
winrt::clock().now() };
|
||||
p3->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
auto thirdCallback = [&](HWND h, BOOL* result) -> HRESULT {
|
||||
Log::Comment(L"thirdCallback: Checking if window is on desktop 2. (windows 2 and 3 are)");
|
||||
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
|
||||
if (hwnd == peasant1PID)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
else if (hwnd == peasant2PID || hwnd == peasant3PID)
|
||||
{
|
||||
*result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
|
||||
}
|
||||
return S_OK;
|
||||
};
|
||||
manager->pfnIsWindowOnCurrentVirtualDesktop = thirdCallback;
|
||||
|
||||
Log::Comment(L"Summon window three - it is the MRU on desktop 2");
|
||||
p1ExpectedToBeSummoned = false;
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p3ExpectedToBeSummoned = true;
|
||||
args.FoundMatch(false);
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Now we'll pretend we switched to desktop 1");
|
||||
|
||||
auto fourthCallback = [&](HWND h, BOOL* result) -> HRESULT {
|
||||
Log::Comment(L"fourthCallback: Checking if window is on desktop 1. (window 1 is)");
|
||||
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
|
||||
if (hwnd == peasant1PID)
|
||||
{
|
||||
*result = true;
|
||||
}
|
||||
else if (hwnd == peasant2PID || hwnd == peasant3PID)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
|
||||
}
|
||||
return S_OK;
|
||||
};
|
||||
manager->pfnIsWindowOnCurrentVirtualDesktop = fourthCallback;
|
||||
|
||||
Log::Comment(L"Summon window one - it is the only window on desktop 1");
|
||||
p1ExpectedToBeSummoned = true;
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p3ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Now we'll pretend we switched to desktop 3");
|
||||
|
||||
auto fifthCallback = [&](HWND h, BOOL* result) -> HRESULT {
|
||||
Log::Comment(L"fifthCallback: Checking if window is on desktop 3. (none are)");
|
||||
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
|
||||
if (hwnd == peasant1PID || hwnd == peasant2PID || hwnd == peasant3PID)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
|
||||
}
|
||||
return S_OK;
|
||||
};
|
||||
manager->pfnIsWindowOnCurrentVirtualDesktop = fifthCallback;
|
||||
|
||||
Log::Comment(L"This summon won't find a window.");
|
||||
p1ExpectedToBeSummoned = false;
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p3ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_FALSE(args.FoundMatch());
|
||||
}
|
||||
|
||||
void RemotingTests::TestSummonOnCurrentWithName()
|
||||
{
|
||||
Log::Comment(L"Test that specifying a WindowName forces us to ignore OnCurrentDesktop");
|
||||
|
||||
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
|
||||
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
|
||||
|
||||
constexpr auto monarch0PID = 12345u;
|
||||
constexpr auto peasant1PID = 23456u;
|
||||
constexpr auto peasant2PID = 34567u;
|
||||
constexpr auto peasant3PID = 45678u;
|
||||
|
||||
com_ptr<Remoting::implementation::Monarch> m0;
|
||||
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p1;
|
||||
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p2;
|
||||
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p3;
|
||||
p3.attach(new Remoting::implementation::Peasant(peasant3PID));
|
||||
|
||||
VERIFY_IS_NOT_NULL(m0);
|
||||
VERIFY_IS_NOT_NULL(p1);
|
||||
VERIFY_IS_NOT_NULL(p2);
|
||||
VERIFY_IS_NOT_NULL(p3);
|
||||
p1->WindowName(L"one");
|
||||
p2->WindowName(L"two");
|
||||
p3->WindowName(L"three");
|
||||
|
||||
VERIFY_ARE_EQUAL(0, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p2->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p3->GetID());
|
||||
|
||||
m0->AddPeasant(*p1);
|
||||
m0->AddPeasant(*p2);
|
||||
m0->AddPeasant(*p3);
|
||||
|
||||
VERIFY_ARE_EQUAL(1, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(2, p2->GetID());
|
||||
VERIFY_ARE_EQUAL(3, p3->GetID());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
|
||||
|
||||
bool p1ExpectedToBeSummoned = false;
|
||||
bool p2ExpectedToBeSummoned = false;
|
||||
bool p3ExpectedToBeSummoned = false;
|
||||
|
||||
p1->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p1 summoned");
|
||||
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
|
||||
});
|
||||
p2->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p2 summoned");
|
||||
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
|
||||
});
|
||||
p3->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p3 summoned");
|
||||
VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
|
||||
});
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the second peasant, second desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
|
||||
p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid2,
|
||||
winrt::clock().now() };
|
||||
p2->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the third peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
|
||||
p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p3->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
|
||||
winrt::com_ptr<MockDesktopManager> manager;
|
||||
manager.attach(new MockDesktopManager());
|
||||
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
|
||||
|
||||
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
|
||||
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
|
||||
|
||||
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
|
||||
if (hwnd == peasant1PID || hwnd == peasant3PID)
|
||||
{
|
||||
*result = true;
|
||||
}
|
||||
else if (hwnd == peasant2PID)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
|
||||
}
|
||||
return S_OK;
|
||||
};
|
||||
manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
|
||||
|
||||
Remoting::SummonWindowSelectionArgs args;
|
||||
|
||||
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
|
||||
p3ExpectedToBeSummoned = true;
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Look for window 1 by name. When given a name, we don't care about OnCurrentDesktop.");
|
||||
p1ExpectedToBeSummoned = true;
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p3ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.WindowName(L"one");
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Look for window 2 by name. When given a name, we don't care about OnCurrentDesktop.");
|
||||
p1ExpectedToBeSummoned = false;
|
||||
p2ExpectedToBeSummoned = true;
|
||||
p3ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.WindowName(L"two");
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Look for window 3 by name. When given a name, we don't care about OnCurrentDesktop.");
|
||||
p1ExpectedToBeSummoned = false;
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p3ExpectedToBeSummoned = true;
|
||||
args.FoundMatch(false);
|
||||
args.WindowName(L"three");
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
}
|
||||
|
||||
void RemotingTests::TestSummonOnCurrentDeadWindow()
|
||||
{
|
||||
Log::Comment(L"Test that we can summon a window on the current desktop,"
|
||||
L" when the MRU window on that desktop dies.");
|
||||
|
||||
const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") };
|
||||
const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") };
|
||||
|
||||
constexpr auto monarch0PID = 12345u;
|
||||
constexpr auto peasant1PID = 23456u;
|
||||
constexpr auto peasant2PID = 34567u;
|
||||
constexpr auto peasant3PID = 45678u;
|
||||
|
||||
com_ptr<Remoting::implementation::Monarch> m0;
|
||||
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p1;
|
||||
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p2;
|
||||
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
|
||||
|
||||
com_ptr<Remoting::implementation::Peasant> p3;
|
||||
p3.attach(new Remoting::implementation::Peasant(peasant3PID));
|
||||
|
||||
VERIFY_IS_NOT_NULL(m0);
|
||||
VERIFY_IS_NOT_NULL(p1);
|
||||
VERIFY_IS_NOT_NULL(p2);
|
||||
VERIFY_IS_NOT_NULL(p3);
|
||||
p1->WindowName(L"one");
|
||||
p2->WindowName(L"two");
|
||||
p3->WindowName(L"three");
|
||||
|
||||
VERIFY_ARE_EQUAL(0, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p2->GetID());
|
||||
VERIFY_ARE_EQUAL(0, p3->GetID());
|
||||
|
||||
m0->AddPeasant(*p1);
|
||||
m0->AddPeasant(*p2);
|
||||
m0->AddPeasant(*p3);
|
||||
|
||||
VERIFY_ARE_EQUAL(1, p1->GetID());
|
||||
VERIFY_ARE_EQUAL(2, p2->GetID());
|
||||
VERIFY_ARE_EQUAL(3, p3->GetID());
|
||||
|
||||
VERIFY_ARE_EQUAL(3u, m0->_peasants.size());
|
||||
|
||||
bool p1ExpectedToBeSummoned = false;
|
||||
bool p2ExpectedToBeSummoned = false;
|
||||
bool p3ExpectedToBeSummoned = false;
|
||||
|
||||
p1->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p1 summoned");
|
||||
VERIFY_IS_TRUE(p1ExpectedToBeSummoned);
|
||||
});
|
||||
p2->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p2 summoned");
|
||||
VERIFY_IS_TRUE(p2ExpectedToBeSummoned);
|
||||
});
|
||||
p3->SummonRequested([&](auto&&, auto&&) {
|
||||
Log::Comment(L"p3 summoned");
|
||||
VERIFY_IS_TRUE(p3ExpectedToBeSummoned);
|
||||
});
|
||||
|
||||
{
|
||||
Log::Comment(L"Activate the first peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(),
|
||||
p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p1->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the second peasant, second desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(),
|
||||
p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid2,
|
||||
winrt::clock().now() };
|
||||
p2->ActivateWindow(activatedArgs);
|
||||
}
|
||||
{
|
||||
Log::Comment(L"Activate the third peasant, first desktop");
|
||||
Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(),
|
||||
p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter
|
||||
guid1,
|
||||
winrt::clock().now() };
|
||||
p3->ActivateWindow(activatedArgs);
|
||||
}
|
||||
|
||||
Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop");
|
||||
winrt::com_ptr<MockDesktopManager> manager;
|
||||
manager.attach(new MockDesktopManager());
|
||||
m0->_desktopManager = manager.try_as<IVirtualDesktopManager>();
|
||||
|
||||
auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT {
|
||||
Log::Comment(L"firstCallback: Checking if window is on desktop 1");
|
||||
|
||||
const uint64_t hwnd = reinterpret_cast<uint64_t>(h);
|
||||
if (hwnd == peasant1PID || hwnd == peasant3PID)
|
||||
{
|
||||
*result = true;
|
||||
}
|
||||
else if (hwnd == peasant2PID)
|
||||
{
|
||||
*result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value");
|
||||
}
|
||||
return S_OK;
|
||||
};
|
||||
manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback;
|
||||
|
||||
Remoting::SummonWindowSelectionArgs args;
|
||||
|
||||
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
|
||||
p3ExpectedToBeSummoned = true;
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
|
||||
Log::Comment(L"Kill window 3. Window 1 is now the MRU on desktop 1.");
|
||||
RemotingTests::_killPeasant(m0, p3->GetID());
|
||||
|
||||
Log::Comment(L"Summon window three - it is the MRU on desktop 1");
|
||||
p1ExpectedToBeSummoned = true;
|
||||
p2ExpectedToBeSummoned = false;
|
||||
p3ExpectedToBeSummoned = false;
|
||||
args.FoundMatch(false);
|
||||
args.OnCurrentDesktop(true);
|
||||
m0->SummonWindow(args);
|
||||
VERIFY_IS_TRUE(args.FoundMatch());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
#include "../types/inc/Viewport.hpp"
|
||||
#include "../types/inc/utils.hpp"
|
||||
#include "../types/inc/User32Utils.hpp"
|
||||
#include "../WinRTUtils/inc/WtExeUtils.h"
|
||||
#include "resource.h"
|
||||
#include "VirtualDesktopUtils.h"
|
||||
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Composition;
|
||||
|
@ -22,6 +24,8 @@ using namespace ::Microsoft::Console::Types;
|
|||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
static const wchar_t* const PSEUDO_WINDOW_CLASS = L"PseudoWindow";
|
||||
|
||||
AppHost::AppHost() noexcept :
|
||||
_app{},
|
||||
_windowManager{},
|
||||
|
@ -71,8 +75,15 @@ AppHost::AppHost() noexcept :
|
|||
std::placeholders::_2));
|
||||
_window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled });
|
||||
_window->WindowActivated({ this, &AppHost::_WindowActivated });
|
||||
_window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed });
|
||||
_window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop());
|
||||
_window->MakeWindow();
|
||||
|
||||
_windowManager.BecameMonarch({ this, &AppHost::_BecomeMonarch });
|
||||
if (_windowManager.IsMonarch())
|
||||
{
|
||||
_BecomeMonarch(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
AppHost::~AppHost()
|
||||
|
@ -194,6 +205,7 @@ void AppHost::_HandleCommandlineArgs()
|
|||
// commandline (in the future), it'll trigger this callback, that we'll
|
||||
// use to send the actions to the app.
|
||||
peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline });
|
||||
peasant.SummonRequested({ this, &AppHost::_HandleSummon });
|
||||
|
||||
peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId });
|
||||
|
||||
|
@ -251,6 +263,7 @@ void AppHost::Initialize()
|
|||
_logic.SetTaskbarProgress({ this, &AppHost::SetTaskbarProgress });
|
||||
_logic.IdentifyWindowsRequested({ this, &AppHost::_IdentifyWindowsRequested });
|
||||
_logic.RenameWindowRequested({ this, &AppHost::_RenameWindowRequested });
|
||||
_logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged });
|
||||
|
||||
_window->UpdateTitle(_logic.Title());
|
||||
|
||||
|
@ -555,6 +568,8 @@ bool AppHost::HasWindow()
|
|||
void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/,
|
||||
Remoting::CommandlineArgs args)
|
||||
{
|
||||
// Summon the window whenever we dispatch a commandline to it. This will
|
||||
// make it obvious when a new tab/pane is created in a window.
|
||||
_window->SummonWindow();
|
||||
_logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory());
|
||||
}
|
||||
|
@ -596,18 +611,271 @@ winrt::fire_and_forget AppHost::_WindowActivated()
|
|||
}
|
||||
}
|
||||
|
||||
void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
_setupGlobalHotkeys();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget AppHost::_setupGlobalHotkeys()
|
||||
{
|
||||
// The hotkey MUST be registered on the main thread. It will fail otherwise!
|
||||
co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(),
|
||||
winrt::Windows::UI::Core::CoreDispatcherPriority::Normal);
|
||||
|
||||
// Remove all the already registered hotkeys before setting up the new ones.
|
||||
_window->UnsetHotkeys(_hotkeys);
|
||||
|
||||
_hotkeyActions = _logic.GlobalHotkeys();
|
||||
_hotkeys.clear();
|
||||
for (const auto& [k, v] : _hotkeyActions)
|
||||
{
|
||||
if (k != nullptr)
|
||||
{
|
||||
_hotkeys.push_back(k);
|
||||
}
|
||||
}
|
||||
|
||||
_window->SetGlobalHotkeys(_hotkeys);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called whenever a registered hotkey is pressed. We'll look up the
|
||||
// GlobalSummonArgs for the specified hotkey, then dispatch a call to the
|
||||
// Monarch with the selection information.
|
||||
// - If the monarch finds a match for the window name (or no name was provided),
|
||||
// it'll set FoundMatch=true.
|
||||
// - If FoundMatch is false, and a name was provided, then we should create a
|
||||
// new window with the given name.
|
||||
// Arguments:
|
||||
// - hotkeyIndex: the index of the entry in _hotkeys that was pressed.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex)
|
||||
{
|
||||
if (hotkeyIndex < 0 || static_cast<size_t>(hotkeyIndex) > _hotkeys.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Lookup the matching keychord
|
||||
Control::KeyChord kc = _hotkeys.at(hotkeyIndex);
|
||||
// Get the stored ActionAndArgs for that chord
|
||||
if (const auto& actionAndArgs{ _hotkeyActions.Lookup(kc) })
|
||||
{
|
||||
if (const auto& summonArgs{ actionAndArgs.Args().try_as<Settings::Model::GlobalSummonArgs>() })
|
||||
{
|
||||
Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() };
|
||||
|
||||
// desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false
|
||||
// desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false
|
||||
// desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true
|
||||
args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent);
|
||||
args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent);
|
||||
|
||||
_windowManager.SummonWindow(args);
|
||||
if (args.FoundMatch())
|
||||
{
|
||||
// Excellent, the window was found. We have nothing else to do here.
|
||||
}
|
||||
else
|
||||
{
|
||||
// We should make the window ourselves.
|
||||
_createNewTerminalWindow(summonArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the monarch failed to summon a window for a given set of
|
||||
// SummonWindowSelectionArgs. In this case, we should create the specified
|
||||
// window ourselves.
|
||||
// - This is to support the scenario like `globalSummon(Name="_quake")` being
|
||||
// used to summon the window if it already exists, or create it if it doesn't.
|
||||
// Arguments:
|
||||
// - args: Contains information on how we should name the window
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::GlobalSummonArgs args)
|
||||
{
|
||||
// Hop to the BG thread
|
||||
co_await winrt::resume_background();
|
||||
|
||||
// This will get us the correct exe for dev/preview/release. If you
|
||||
// don't stick this in a local, it'll get mangled by ShellExecute. I
|
||||
// have no idea why.
|
||||
const auto exePath{ GetWtExePath() };
|
||||
|
||||
// If we weren't given a name, then just use new to force the window to be
|
||||
// unnamed.
|
||||
winrt::hstring cmdline{
|
||||
fmt::format(L"-w {}",
|
||||
args.Name().empty() ? L"new" :
|
||||
args.Name())
|
||||
};
|
||||
// Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we
|
||||
// can pass the SEE_MASK_NOASYNC flag. That flag allows us to safely
|
||||
// call this on the background thread, and have ShellExecute _not_ call
|
||||
// back to us on the main thread. Without this, if you close the
|
||||
// Terminal quickly after the UAC prompt, the elevated WT will never
|
||||
// actually spawn.
|
||||
SHELLEXECUTEINFOW seInfo{ 0 };
|
||||
seInfo.cbSize = sizeof(seInfo);
|
||||
seInfo.fMask = SEE_MASK_NOASYNC;
|
||||
seInfo.lpVerb = L"open";
|
||||
seInfo.lpFile = exePath.c_str();
|
||||
seInfo.lpParameters = cmdline.c_str();
|
||||
seInfo.nShow = SW_SHOWNORMAL;
|
||||
LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo));
|
||||
|
||||
co_return;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper to initialize our instance of IVirtualDesktopManager. If we already
|
||||
// got one, then this will just return true. Otherwise, we'll try and init a
|
||||
// new instance of one, and store that.
|
||||
// - This will return false if we weren't able to initialize one, which I'm not
|
||||
// sure is actually possible.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - true iff _desktopManager points to a non-null instance of IVirtualDesktopManager
|
||||
bool AppHost::_LazyLoadDesktopManager()
|
||||
{
|
||||
if (_desktopManager == nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
_desktopManager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
return _desktopManager != nullptr;
|
||||
}
|
||||
|
||||
static wil::unique_event windowCreated;
|
||||
GUID AppHost::_fakeGetCurrentDesktop()
|
||||
{
|
||||
GUID currentlyActiveDesktop;
|
||||
|
||||
static bool createEvent{ []() {
|
||||
windowCreated.create();
|
||||
return true;
|
||||
}() };
|
||||
static auto fakeWndProc = [](HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) -> LRESULT {
|
||||
switch (message)
|
||||
{
|
||||
case WM_SHOWWINDOW:
|
||||
{
|
||||
OutputDebugString(L"WM_SHOWWINDOW\n");
|
||||
break;
|
||||
}
|
||||
case WM_ACTIVATE:
|
||||
{
|
||||
OutputDebugString(L"WM_ACTIVATE\n");
|
||||
|
||||
break;
|
||||
}
|
||||
case WM_CREATE:
|
||||
{
|
||||
OutputDebugString(L"WM_CREATE\n");
|
||||
// PostMessage(window, WM_USER + 100, 0, 0);
|
||||
break;
|
||||
}
|
||||
case WM_USER + 100:
|
||||
{
|
||||
OutputDebugString(L"WM_USER + 100\n");
|
||||
windowCreated.SetEvent();
|
||||
}
|
||||
}
|
||||
|
||||
// if (message == WM_ACTIVATE)
|
||||
// {
|
||||
// windowCreated.SetEvent();
|
||||
// }
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
};
|
||||
|
||||
// *** magic static ***
|
||||
// Only initialize the psuedo class once over the lifetime of the process
|
||||
static ATOM pseudoClassAtom{ []() {
|
||||
WNDCLASS pseudoClass{ 0 };
|
||||
pseudoClass.lpszClassName = PSEUDO_WINDOW_CLASS;
|
||||
// pseudoClass.lpfnWndProc = DefWindowProc;
|
||||
pseudoClass.lpfnWndProc = fakeWndProc;
|
||||
return RegisterClass(&pseudoClass);
|
||||
}() };
|
||||
|
||||
wil::unique_hwnd hwnd{ CreateWindowExW(0,
|
||||
PSEUDO_WINDOW_CLASS,
|
||||
nullptr,
|
||||
WS_POPUP, //WS_TILED, // WS_CHILD,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
HWND_DESKTOP,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr) };
|
||||
|
||||
{
|
||||
// // Attempt 1:
|
||||
// ShowWindow(hwnd.get(), SW_SHOWNORMAL);
|
||||
// Sleep(1); // !! LOAD BEARING !!
|
||||
}
|
||||
|
||||
OutputDebugString(L"after CreateWindowExW");
|
||||
windowCreated.ResetEvent();
|
||||
OutputDebugString(L"after ResetEvent");
|
||||
ShowWindow(hwnd.get(), SW_SHOWNORMAL);
|
||||
// PostMessage(hwnd.get(), WM_USER + 100, 0, 0);
|
||||
WaitForSingleObject(windowCreated.get(), INFINITE);
|
||||
OutputDebugString(L"after WaitForSingleObject");
|
||||
|
||||
{
|
||||
// // Attempt 3
|
||||
// BOOL onCurrent = false;
|
||||
// while (!onCurrent)
|
||||
// {
|
||||
// ShowWindow(hwnd.get(), SW_HIDE);
|
||||
// ShowWindow(hwnd.get(), SW_RESTORE);
|
||||
// _desktopManager->IsWindowOnCurrentVirtualDesktop(hwnd.get(), &onCurrent);
|
||||
// }
|
||||
}
|
||||
|
||||
LOG_IF_FAILED(_desktopManager->GetWindowDesktopId(hwnd.get(), ¤tlyActiveDesktop));
|
||||
|
||||
return currentlyActiveDesktop;
|
||||
}
|
||||
|
||||
void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const Remoting::SummonWindowBehavior& args)
|
||||
{
|
||||
_window->SummonWindow();
|
||||
|
||||
if (args != nullptr && args.MoveToCurrentDesktop())
|
||||
{
|
||||
if (_LazyLoadDesktopManager())
|
||||
{
|
||||
// GUID currentlyActiveDesktop;
|
||||
// VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop);
|
||||
auto currentlyActiveDesktop = _fakeGetCurrentDesktop();
|
||||
LOG_IF_FAILED(_desktopManager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUID AppHost::_CurrentDesktopGuid()
|
||||
{
|
||||
GUID currentDesktopGuid{ 0 };
|
||||
try
|
||||
{
|
||||
const auto manager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
|
||||
if (manager)
|
||||
if (_LazyLoadDesktopManager())
|
||||
{
|
||||
LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid));
|
||||
LOG_IF_FAILED(_desktopManager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid));
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
return currentDesktopGuid;
|
||||
}
|
||||
|
||||
|
@ -674,3 +942,9 @@ winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Fou
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
_setupGlobalHotkeys();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,11 @@ private:
|
|||
bool _shouldCreateWindow{ false };
|
||||
winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr };
|
||||
|
||||
std::vector<winrt::Microsoft::Terminal::Control::KeyChord> _hotkeys{};
|
||||
winrt::Windows::Foundation::Collections::IMap<winrt::Microsoft::Terminal::Control::KeyChord, winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> _hotkeyActions{ nullptr };
|
||||
|
||||
winrt::com_ptr<IVirtualDesktopManager> _desktopManager{ nullptr };
|
||||
|
||||
void _HandleCommandlineArgs();
|
||||
|
||||
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
|
||||
|
@ -51,6 +56,13 @@ private:
|
|||
|
||||
void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args);
|
||||
|
||||
void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
void _GlobalHotkeyPressed(const long hotkeyIndex);
|
||||
void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args);
|
||||
|
||||
winrt::fire_and_forget _IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable sender,
|
||||
const winrt::Windows::Foundation::IInspectable args);
|
||||
void _DisplayWindowId(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
|
@ -59,4 +71,11 @@ private:
|
|||
const winrt::TerminalApp::RenameWindowRequestedArgs args);
|
||||
|
||||
GUID _CurrentDesktopGuid();
|
||||
bool _LazyLoadDesktopManager();
|
||||
GUID _fakeGetCurrentDesktop();
|
||||
|
||||
winrt::fire_and_forget _setupGlobalHotkeys();
|
||||
winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args);
|
||||
void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ using namespace winrt::Windows::UI::Xaml;
|
|||
using namespace winrt::Windows::UI::Xaml::Hosting;
|
||||
using namespace winrt::Windows::Foundation::Numerics;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
|
||||
#define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS"
|
||||
|
@ -351,6 +352,11 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
|
|||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_HOTKEY:
|
||||
{
|
||||
_HotkeyPressedHandlers(static_cast<long>(wparam));
|
||||
return 0;
|
||||
}
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
_OnGetMinMaxInfo(wparam, lparam);
|
||||
|
@ -367,8 +373,9 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
|
|||
{
|
||||
// send focus to the child window
|
||||
SetFocus(_interopWindowHandle);
|
||||
return 0; // eat the message
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_ACTIVATE:
|
||||
{
|
||||
|
@ -895,6 +902,63 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled)
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Call UnregisterHotKey once for each entry in hotkeyList, to unset all the bound global hotkeys.
|
||||
// Arguments:
|
||||
// - hotkeyList: a list of hotkeys to unbind
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::UnsetHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList)
|
||||
{
|
||||
TraceLoggingWrite(g_hWindowsTerminalProvider,
|
||||
"UnsetHotkeys",
|
||||
TraceLoggingDescription("Emitted when clearing previously set hotkeys"),
|
||||
TraceLoggingInt64(hotkeyList.size(), "numHotkeys", "The number of hotkeys to unset"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
|
||||
for (int i = 0; i < ::base::saturated_cast<int>(hotkeyList.size()); i++)
|
||||
{
|
||||
LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), static_cast<int>(i)));
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Call RegisterHotKey once for each entry in hotkeyList, to attempt to
|
||||
// register that keybinding as a global hotkey.
|
||||
// - When these keys are pressed, we'll get a WM_HOTKEY message with the payload
|
||||
// containing the index we registered here.
|
||||
// Arguments:
|
||||
// - hotkeyList: a list of hotkeys to bind
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::SetGlobalHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList)
|
||||
{
|
||||
TraceLoggingWrite(g_hWindowsTerminalProvider,
|
||||
"SetGlobalHotkeys",
|
||||
TraceLoggingDescription("Emitted when setting hotkeys"),
|
||||
TraceLoggingInt64(hotkeyList.size(), "numHotkeys", "The number of hotkeys to set"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
|
||||
int index = 0;
|
||||
for (const auto& hotkey : hotkeyList)
|
||||
{
|
||||
const auto modifiers = hotkey.Modifiers();
|
||||
const auto hotkeyFlags = MOD_NOREPEAT |
|
||||
(WI_IsFlagSet(modifiers, KeyModifiers::Windows) ? MOD_WIN : 0) |
|
||||
(WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? MOD_ALT : 0) |
|
||||
(WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? MOD_CONTROL : 0) |
|
||||
(WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? MOD_SHIFT : 0);
|
||||
|
||||
// TODO GH#8888: We should display a warning of some kind if this fails.
|
||||
// This can fail if something else already bound this hotkey.
|
||||
LOG_IF_WIN32_BOOL_FALSE(RegisterHotKey(_window.get(),
|
||||
index,
|
||||
hotkeyFlags,
|
||||
hotkey.Vkey()));
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Force activate this window. This method will bring us to the foreground and
|
||||
// activate us. If the window is minimized, it will restore the window. If the
|
||||
|
@ -929,6 +993,10 @@ winrt::fire_and_forget IslandWindow::SummonWindow()
|
|||
});
|
||||
LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get()));
|
||||
LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW));
|
||||
|
||||
// Activate the window too. This will force us to the virtual desktop this
|
||||
// window is on, if it's on another virtual desktop.
|
||||
LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get()));
|
||||
}
|
||||
|
||||
DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
|
||||
|
|
|
@ -38,6 +38,9 @@ public:
|
|||
void FlashTaskbar();
|
||||
void SetTaskbarProgress(const size_t state, const size_t progress);
|
||||
|
||||
void UnsetHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList);
|
||||
void SetGlobalHotkeys(const std::vector<winrt::Microsoft::Terminal::Control::KeyChord>& hotkeyList);
|
||||
|
||||
winrt::fire_and_forget SummonWindow();
|
||||
|
||||
#pragma endregion
|
||||
|
@ -46,6 +49,7 @@ public:
|
|||
DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
|
||||
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
|
||||
WINRT_CALLBACK(WindowActivated, winrt::delegate<void()>);
|
||||
WINRT_CALLBACK(HotkeyPressed, winrt::delegate<void(long)>);
|
||||
|
||||
protected:
|
||||
void ForceResize()
|
||||
|
|
145
src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp
Normal file
145
src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// Shamelessly copied from microsoft/PowerToys, at
|
||||
// https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/lib/VirtualDesktopUtils.cpp
|
||||
//
|
||||
// The code style is left untouched, as to make contributions from/to the
|
||||
// upstream source easier.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "VirtualDesktopUtils.h"
|
||||
|
||||
// Non-Localizable strings
|
||||
namespace NonLocalizable
|
||||
{
|
||||
const wchar_t RegCurrentVirtualDesktop[] = L"CurrentVirtualDesktop";
|
||||
const wchar_t RegVirtualDesktopIds[] = L"VirtualDesktopIDs";
|
||||
const wchar_t RegKeyVirtualDesktops[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops";
|
||||
const wchar_t RegKeyVirtualDesktopsFromSession[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d\\VirtualDesktops";
|
||||
}
|
||||
|
||||
namespace VirtualDesktopUtils
|
||||
{
|
||||
bool GetDesktopIdFromCurrentSession(GUID* desktopId)
|
||||
{
|
||||
DWORD sessionId;
|
||||
if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wchar_t sessionKeyPath[256]{};
|
||||
if (FAILED(StringCchPrintfW(sessionKeyPath, ARRAYSIZE(sessionKeyPath), NonLocalizable::RegKeyVirtualDesktopsFromSession, sessionId)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
wil::unique_hkey key{};
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionKeyPath, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS)
|
||||
{
|
||||
GUID value{};
|
||||
DWORD size = sizeof(GUID);
|
||||
if (RegQueryValueExW(key.get(), NonLocalizable::RegCurrentVirtualDesktop, 0, nullptr, reinterpret_cast<BYTE*>(&value), &size) == ERROR_SUCCESS)
|
||||
{
|
||||
*desktopId = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetVirtualDesktopIds(HKEY hKey, std::vector<GUID>& ids)
|
||||
{
|
||||
if (!hKey)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
DWORD bufferCapacity;
|
||||
// request regkey binary buffer capacity only
|
||||
if (RegQueryValueExW(hKey, NonLocalizable::RegVirtualDesktopIds, 0, nullptr, nullptr, &bufferCapacity) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<BYTE[]> buffer = std::make_unique<BYTE[]>(bufferCapacity);
|
||||
// request regkey binary content
|
||||
if (RegQueryValueExW(hKey, NonLocalizable::RegVirtualDesktopIds, 0, nullptr, buffer.get(), &bufferCapacity) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const size_t guidSize = sizeof(GUID);
|
||||
std::vector<GUID> temp;
|
||||
temp.reserve(bufferCapacity / guidSize);
|
||||
for (size_t i = 0; i < bufferCapacity; i += guidSize)
|
||||
{
|
||||
GUID* guid = reinterpret_cast<GUID*>(buffer.get() + i);
|
||||
temp.push_back(*guid);
|
||||
}
|
||||
ids = std::move(temp);
|
||||
return true;
|
||||
}
|
||||
|
||||
HKEY OpenVirtualDesktopsRegKey()
|
||||
{
|
||||
HKEY hKey{ nullptr };
|
||||
if (RegOpenKeyEx(HKEY_CURRENT_USER, NonLocalizable::RegKeyVirtualDesktops, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
|
||||
{
|
||||
return hKey;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HKEY GetVirtualDesktopsRegKey()
|
||||
{
|
||||
static wil::unique_hkey virtualDesktopsKey{ OpenVirtualDesktopsRegKey() };
|
||||
return virtualDesktopsKey.get();
|
||||
}
|
||||
bool GetVirtualDesktopIds(std::vector<GUID>& ids)
|
||||
{
|
||||
return GetVirtualDesktopIds(GetVirtualDesktopsRegKey(), ids);
|
||||
}
|
||||
|
||||
bool GetVirtualDesktopIds(std::vector<std::wstring>& ids)
|
||||
{
|
||||
std::vector<GUID> guids{};
|
||||
if (GetVirtualDesktopIds(guids))
|
||||
{
|
||||
for (auto& guid : guids)
|
||||
{
|
||||
wil::unique_cotaskmem_string guidString;
|
||||
if (SUCCEEDED(StringFromCLSID(guid, &guidString)))
|
||||
{
|
||||
ids.push_back(guidString.get());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetCurrentVirtualDesktopId(GUID* desktopId)
|
||||
{
|
||||
// Explorer persists current virtual desktop identifier to registry on a per session basis, but only
|
||||
// after first virtual desktop switch happens. If the user hasn't switched virtual desktops in this
|
||||
// session, value in registry will be empty.
|
||||
if (GetDesktopIdFromCurrentSession(desktopId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Fallback scenario is to get array of virtual desktops stored in registry, but not kept per session.
|
||||
// Note that we are taking first element from virtual desktop array, which is primary desktop.
|
||||
// If user has more than one virtual desktop, previous function should return correct value, as desktop
|
||||
// switch occurred in current session.
|
||||
else
|
||||
{
|
||||
std::vector<GUID> ids{};
|
||||
if (GetVirtualDesktopIds(ids) && ids.size() > 0)
|
||||
{
|
||||
*desktopId = ids[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
11
src/cascadia/WindowsTerminal/VirtualDesktopUtils.h
Normal file
11
src/cascadia/WindowsTerminal/VirtualDesktopUtils.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// A helper function for determining the GUID of the current Virtual Desktop.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace VirtualDesktopUtils
|
||||
{
|
||||
bool GetCurrentVirtualDesktopId(GUID* desktopId);
|
||||
}
|
|
@ -48,6 +48,7 @@
|
|||
<ClInclude Include="BaseWindow.h" />
|
||||
<ClInclude Include="IslandWindow.h" />
|
||||
<ClInclude Include="NonClientIslandWindow.h" />
|
||||
<ClInclude Include="VirtualDesktopUtils.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
|
@ -57,6 +58,7 @@
|
|||
<ClCompile Include="AppHost.cpp" />
|
||||
<ClCompile Include="IslandWindow.cpp" />
|
||||
<ClCompile Include="NonClientIslandWindow.cpp" />
|
||||
<ClCompile Include="VirtualDesktopUtils.cpp" />
|
||||
<ClCompile Include="icon.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -56,12 +56,14 @@ Abstract:
|
|||
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
|
||||
|
||||
// Additional headers for various xaml features. We need:
|
||||
// * Core so we can resume_foreground
|
||||
// * Core so we can resume_foreground with CoreDispatcher
|
||||
// * Controls for grid
|
||||
// * Media for ScaleTransform
|
||||
// * ApplicationModel for finding the path to wt.exe
|
||||
#include <winrt/Windows.UI.Core.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.ui.xaml.media.h>
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
|
||||
#include <winrt/TerminalApp.h>
|
||||
#include <winrt/Microsoft.Terminal.Settings.Model.h>
|
||||
|
|
Loading…
Reference in a new issue