Compare commits

...

96 commits

Author SHA1 Message Date
Dustin L. Howett bfc943aebf Migrate spelling-0.0.19 changes from main 2021-04-27 09:48:59 -05:00
Mike Griese 30a3621ec6 Well this certainly wasn't it 2021-04-27 09:48:59 -05:00
Mike Griese f3c7d9c471 This obviously works but is somehow just as dumb as a Sleep(1) 2021-04-27 09:23:18 -05:00
Mike Griese 530903219e This is now working less well 2021-04-27 09:12:22 -05:00
Mike Griese c78c7aefa2 This is a load bearing call to Sleep(1), which makes me unreasonably unhappy 2021-04-27 08:22:03 -05:00
Mike Griese 5936bd13b3 This works better than dev/migrie/f/quake-toCurrent-experiments 2021-04-27 06:15:07 -05:00
Mike Griese c95a4297cf Give Clint the credit they deserce 2021-04-26 09:21:40 -05:00
Mike Griese 045a8db5e1 fix a build break 2021-04-23 12:26:26 -05:00
Mike Griese 91372ea1f9 more tests, because I like being thorough 2021-04-23 11:14:39 -05:00
Mike Griese 26d5194b0c this test is _fantastic_ 2021-04-23 09:01:51 -05:00
Mike Griese c215a97d09 Merge branch 'dev/migrie/f/653-QUAKE-MODE' into dev/migrie/f/quake-toCurrent-desktop 2021-04-23 06:33:46 -05:00
Mike Griese 4eb1d3a8d6 Merge remote-tracking branch 'origin/main' into dev/migrie/f/653-QUAKE-MODE 2021-04-23 06:02:23 -05:00
Mike Griese 8370789462 replace todo with comment 2021-04-21 12:02:56 -05:00
Mike Griese 15a8a9cec0 !! THIS NEEDS TO GO TO THE PARENT BRANCH !!
(cherry picked from commit 9f9eacb5d9)
2021-04-21 12:01:46 -05:00
Mike Griese 2eec961a87 All of carlos's feedback 2021-04-21 08:42:15 -05:00
Mike Griese 51617cf6db cache these results 2021-04-20 15:10:04 -05:00
Mike Griese f71c948554 Add support for summoning to the current virtual desktop
Tested with

  ```json
        { "keys": "ctrl+`", "command": { "action": "quakeMode" } },
        { "keys": "ctrl+1", "command": { "action": "globalSummon" } },
        { "keys": "ctrl+2", "command": { "action": "globalSummon", "desktop": "toCurrent" } },
        { "keys": "ctrl+3", "command": { "action": "globalSummon", "desktop": "onCurrent" } },
        { "keys": "ctrl+4", "command": { "action": "globalSummon", "desktop": "any" } },
  ```

  And holy god it works so well
2021-04-19 14:44:26 -05:00
Mike Griese 9f9eacb5d9 !! THIS NEEDS TO GO TO THE PARENT BRANCH !! 2021-04-19 14:41:48 -05:00
Mike Griese 305e62708a oh man, this will magic summon the window to the current desktop. I'm shocked that it works, thanks powertoys 2021-04-19 12:05:43 -05:00
Mike Griese 1b2e7a7ca1 Merge remote-tracking branch 'origin/main' into dev/migrie/f/653-QUAKE-MODE 2021-04-19 10:11:06 -05:00
Mike Griese 717db8130b Again, nobody's got disk space to build x86. This is a guess 2021-04-19 10:10:54 -05:00
Mike Griese 4166afaa98 Okay that was bad pre-emptive spelling 2021-04-16 09:22:40 -05:00
Mike Griese f892752da7 Add some tracelogging to find a heisenbug that might not have ever been there 2021-04-16 09:02:46 -05:00
Mike Griese 71577fcd01 Merge remote-tracking branch 'origin/main' into dev/migrie/f/653-QUAKE-MODE 2021-04-16 06:33:19 -05:00
Mike Griese 2a2f5cb9a5 pre emptive spellbot 2021-04-14 16:37:24 -05:00
Mike Griese 9d76c62dae Some dead code cleanup 2021-04-14 16:21:17 -05:00
Mike Griese b20222f2c0 More comments 2021-04-14 16:16:12 -05:00
Mike Griese 784ec731f8 Merge remote-tracking branch 'origin/main' into dev/migrie/f/653-QUAKE-MODE 2021-04-14 16:03:21 -05:00
Mike Griese 1fdb6b1a3c Tests and doc comments 2021-04-14 16:02:12 -05:00
Mike Griese ac8fef0e39 THATS RIGHT, IT HOT-RELOADS 2021-04-12 16:50:42 -05:00
Mike Griese 7c2a514e50 if there is no window with the name, then make a new window with the name 2021-04-12 15:27:43 -05:00
Mike Griese 25b31ff854 Allow the user to set multiple globalSummon keys, and quakeMode for summoning the _quake window 2021-04-12 14:15:28 -05:00
Mike Griese 10779ca310 We caan now summon based on window name or just use the MRU one. Note: I haven't tested this at all 2021-04-12 09:49:32 -05:00
Mike Griese 5c039eaa1a Merge remote-tracking branch 'origin/main' into dev/migrie/f/653-QUAKE-MODE 2021-04-12 09:09:19 -05:00
Mike Griese eff18d1ec9 more cleanup 2021-01-29 07:15:38 -06:00
Mike Griese 5052d31831 deal with minimized windows too 2021-01-29 06:54:30 -06:00
Mike Griese 342d3f2026 This works if the window _isn't_ minimized 2021-01-29 06:25:01 -06:00
Mike Griese fee6473737 Still trying to figure this out exactly. For linking: #653 2021-01-28 17:14:51 -06:00
Mike Griese 1dcb4cb777 man this is crisp 2021-01-28 17:02:24 -06:00
Mike Griese 848682a27f Oh hey SetForegroundWindow might be the thing 2021-01-28 16:56:19 -06:00
Mike Griese 91b52d4e6d !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!!
> The **low-order word** specifies whether the window is being activated or deactivated.
2021-01-28 14:25:04 -06:00
Mike Griese 3e5d9276f1 Cleanup, now that this isn't needed 2021-01-28 12:32:48 -06:00
Mike Griese 18099d2aa7 Summon the most recent window
This roundtrips through the monarch to find the MRU peasant.
  Also ensures that we register for the hotkey on the right thread. The monarch will get elected on the BG thread, but registering the hotkey must be on the main thread
2021-01-28 12:26:07 -06:00
Mike Griese 6e7ea615d2 !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! 2021-01-28 12:24:52 -06:00
Mike Griese a41bee653b !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! 2021-01-28 12:24:09 -06:00
Mike Griese 18d1a205bb Add support for the win key in keybindings 2021-01-28 12:23:37 -06:00
Mike Griese 71f6b581e2 RegisterHotKey will be more robust 2021-01-28 10:56:57 -06:00
Mike Griese 1c2f8e5d6a This will always summon the monarch window 2021-01-28 08:00:28 -06:00
Mike Griese a65f3419e3 trycatch the window manager. A lot more going on there. 2021-01-27 12:15:06 -06:00
Mike Griese d2a34389a8 Add try/catch's throughout Monarch.cpp 2021-01-27 10:23:47 -06:00
Mike Griese b2db3170dd man I can't spel 2021-01-26 11:51:06 -06:00
Mike Griese f02969b70f More tests, more redundancy 2021-01-26 11:07:15 -06:00
Mike Griese 59deca1c2e Merge branch 'main' into dev/migrie/f/the-first-galactic-empire 2021-01-26 06:53:47 -06:00
Mike Griese 689c38519b sure yea that's a doc comment 2021-01-08 13:00:33 -06:00
Mike Griese 5b8ace276b A bunch of tests for Monarch::ProposeCommandline 2021-01-08 12:58:43 -06:00
Mike Griese c34e4ce90f Merge remote-tracking branch 'origin/main' into dev/migrie/f/the-first-galactic-empire
# Conflicts:
#	OpenConsole.sln
#	src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj
#	src/cascadia/Remoting/Monarch.cpp
#	src/cascadia/Remoting/Monarch.h
#	src/cascadia/Remoting/Monarch.idl
#	src/cascadia/Remoting/Peasant.cpp
#	src/cascadia/Remoting/Peasant.h
#	src/cascadia/Remoting/Peasant.idl
#	src/cascadia/Remoting/WindowManager.cpp
#	src/cascadia/Remoting/WindowManager.h
#	src/cascadia/Remoting/WindowManager.idl
#	src/cascadia/Remoting/pch.h
#	src/cascadia/TerminalApp/AppLogic.h
#	src/cascadia/TerminalApp/AppLogic.idl
#	src/cascadia/TerminalApp/TerminalPage.cpp
#	src/cascadia/TerminalApp/TerminalPage.h
#	src/cascadia/UnitTests_Remoting/RemotingTests.cpp
#	src/cascadia/WindowsTerminal/AppHost.cpp
#	src/cascadia/WindowsTerminal/AppHost.h
#	src/cascadia/WindowsTerminal/main.cpp
2021-01-08 11:16:00 -06:00
Mike Griese 81c09d9650 Merge branch 'dev/migrie/f/remoting.dll' into dev/migrie/f/the-first-galactic-empire 2021-01-08 10:49:32 -06:00
Mike Griese 2a7bc94a8f branding 2021-01-07 16:40:49 -06:00
Mike Griese be74b2ee2d putting var in headers is bad, mkay? 2021-01-07 15:18:26 -06:00
Mike Griese 88ffc6fc0e Merge remote-tracking branch 'origin/main' into dev/migrie/f/remoting.dll 2021-01-07 14:59:41 -06:00
Mike Griese bc492f1815 Code cleanup 2021-01-07 10:45:46 -06:00
Mike Griese 52b2cb6d3f Allow the user to provide ids on the commandline 2021-01-07 10:39:07 -06:00
Mike Griese 3e39ab9e71 more comments 2021-01-07 08:29:35 -06:00
Mike Griese c02f25aba3 peasants now switch to the cwd that was requested 2021-01-07 08:00:51 -06:00
Mike Griese a75da0a104 mostly just notes 2021-01-07 06:33:44 -06:00
Mike Griese 65376865b5 The peasant will now correctly inform the monarch when it re-connects 2021-01-07 05:37:25 -06:00
Mike Griese 9fc2f0ee3a add some tracelogging 2021-01-06 15:41:58 -06:00
Mike Griese 0f0df5e8cf activate windows 2021-01-06 12:27:10 -06:00
Mike Griese 813dbc6671 notes, comments, cleanup 2021-01-06 11:00:06 -06:00
Mike Griese bcbef340ba Add a note about commiting suicide 2021-01-06 09:52:40 -06:00
Mike Griese 977db464fa Holy bajesus, this works like a charm 2021-01-06 09:50:00 -06:00
Mike Griese 658db6b568 Ask the TerminalApp to parse the commandline, and tell us what the window should be. It just always says 0 for now, but in the future it could actually give us useful info. 2021-01-06 07:10:54 -06:00
Mike Griese 00184e7ef6 Merge branch 'dev/migrie/f/remoting.dll' into dev/migrie/f/the-first-galactic-empire 2021-01-05 12:28:50 -06:00
Mike Griese 921d91582b nits 2021-01-05 11:28:10 -06:00
Mike Griese 0f5c24f661 Enable audit mode 2021-01-05 11:22:23 -06:00
Mike Griese fa2df47898 Merge remote-tracking branch 'origin/main' into dev/migrie/f/remoting.dll 2021-01-05 11:19:08 -06:00
Mike Griese c08889585b last commit before the holidays 2020-12-18 15:21:53 -06:00
Mike Griese 5939636182 Oh yea actually remove the thing 2020-12-18 12:48:23 -06:00
Mike Griese f978a9c52c it's hard to believe he's gone / were it so easy 2020-12-18 12:45:29 -06:00
Mike Griese e1402d834f This seems to work to create a thread listening for the monarch, and safely tear it down. It _seems_ like it. 2020-12-18 09:29:45 -06:00
Mike Griese e101efd11d pr nits 2020-12-18 07:12:12 -06:00
Mike Griese d08e65ce03 _final_ final cleanup for review 2020-12-17 14:33:50 -06:00
Mike Griese b4fe1bffbf Final cleanup for review 2020-12-17 14:26:06 -06:00
Mike Griese 0103331987 cleanup Peasant for review 2020-12-17 12:49:27 -06:00
Mike Griese 9c6eac4e86 Clean up a lot for review 2020-12-17 12:39:59 -06:00
Mike Griese a3faed6b7d finish renaming this test 2020-12-17 11:15:57 -06:00
Mike Griese 0579b2417b LocalTests_Remoting -> UnitTests_Remoting 2020-12-17 11:10:09 -06:00
Mike Griese 590b9ff0c7 this macro makes me feel dirty 2020-12-17 11:01:52 -06:00
Mike Griese 03bfc6e8a9 This works as a unittest, but not a local test. That's batty 2020-12-17 10:50:48 -06:00
Mike Griese 5cabcfb452 add a note to future me 2020-12-17 07:43:46 -06:00
Mike Griese 9a41647ffe HOLY SHIT I GOT THE COMANDLINE TO EXECUTE IN THE CURRENT WINDOW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 2020-12-17 07:24:16 -06:00
Mike Griese 27ace16652 whoop, we pass the commandline from the peasant, to the monarch, and then back! 2020-12-16 16:31:51 -06:00
Mike Griese 36539cfa47 This won't work, but I'm committing this becaus I finally got it to compile a String[] 2020-12-16 16:03:23 -06:00
Mike Griese 5a9cdc8b0b Shockingly, this works, it works elevated, and it works unpackaged 2020-12-16 09:51:24 -06:00
Mike Griese 1f52d35833 Yank all the M/P files, this builds?! 2020-12-16 08:08:55 -06:00
Mike Griese 3bef7bbb38 Get all the projects created and hooked up to the sln 2020-12-16 07:59:52 -06:00
61 changed files with 2398 additions and 480834 deletions

View file

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

View file

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

View 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

View file

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

View 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

View file

@ -7,3 +7,4 @@ Iosevka
MDL
Monofur
Segoe
wght

11
.github/actions/spelling/allow/math.txt vendored Normal file
View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,3 +0,0 @@
powf
sqrtf
isnan

View file

@ -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$

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -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
View 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$

View file

@ -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
View 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

View file

@ -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.
```

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

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

View file

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

View file

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

View 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);
}

View file

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

View 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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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") },
};
}();

View file

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

View file

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

View file

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

View file

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

View file

@ -7,6 +7,7 @@
#include "../../inc/DefaultSettings.h"
#include "JsonUtils.h"
#include "TerminalSettingsSerializationHelpers.h"
#include "KeyChordSerialization.h"
#include "GlobalAppSettings.g.cpp"

View file

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

View file

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

View file

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

View file

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

View file

@ -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 },
};
};

View file

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

View file

@ -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(), &currentlyActiveDesktop));
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(&currentlyActiveDesktop);
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 (_LazyLoadDesktopManager())
{
const auto manager = winrt::create_instance<IVirtualDesktopManager>(__uuidof(VirtualDesktopManager));
if (manager)
{
LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), &currentDesktopGuid));
}
LOG_IF_FAILED(_desktopManager->GetWindowDesktopId(_window->GetHandle(), &currentDesktopGuid));
}
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();
}

View file

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

View file

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

View file

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

View 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;
}
}

View 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);
}

View file

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

View file

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