Compare commits

...

94 Commits

Author SHA1 Message Date
Dustin L. Howett
0e301b010d Migrate spelling-0.0.21 changes from main 2022-07-15 14:28:10 -05:00
Mike Griese
27887603b4 tab drag/drop is actually kinda just... done 2022-07-15 14:28:10 -05:00
Mike Griese
87c840b381 Merge branch 'dev/migrie/oop/2/wandavision' into dev/migrie/oop/2/loki 2022-07-15 12:34:34 -05:00
Mike Griese
91616885db Actually, movePane is a lot harder, and I honestly think we can punt that 2022-07-15 12:33:23 -05:00
Mike Griese
089c015347 more notes to go with the parent commit 2022-07-15 11:57:45 -05:00
Mike Griese
dea94d51ad this and 83649075a should probably go to the parent branch 2022-07-15 11:49:16 -05:00
Mike Griese
83649075a7 the final_release thing Dustin was talking about. I think I still need to synchronously close the connection though 2022-07-15 11:11:31 -05:00
Mike Griese
e9375d67f1 Remove the tab from the previous window, as well.
When we move a tab to a new window, we'll hang on to a reference to all the
  content procs, and then remove the whole tab from the UI tree. We'll hang on
  to those refs until the new tab gets attached, and the controls take the next
  ContentProcess reference.
2022-07-15 10:35:02 -05:00
Mike Griese
4b4e057a75 one shot one opportunity 2022-07-15 06:20:19 -05:00
Mike Griese
35c82d37f0 this is important for moving, yknow, the panes 2022-07-14 16:47:29 -05:00
Mike Griese
4acbffb0e3 can I get a whoop whoop 2022-07-14 16:40:39 -05:00
Mike Griese
aaefdf4408 Add comments 2022-07-14 15:44:42 -05:00
Mike Griese
1f2bb760eb Pane officially opened via the serialized actions, via across the process boundary 2022-07-14 15:38:37 -05:00
Mike Griese
5cb4ab1a9e notes 2022-07-14 13:05:04 -05:00
Mike Griese
cfe879da96 Serialize the pane to a Content string, instead of just the guid.
There's a lot of renaming, signature changes here. But StartupActions is a good mechanism for passing panes around, more or less.
2022-07-14 13:01:00 -05:00
Mike Griese
ac62de78a2 IT ABSOLUTELY WORKED 2022-07-13 15:49:04 -05:00
Mike Griese
9870eb1d39 Holy shit all the plumbing worked on the first try 2022-07-13 15:11:26 -05:00
Mike Griese
e06ce39811 more cleanup 2022-07-13 13:08:56 -05:00
Mike Griese
997212f9c5 cleanup 2022-07-13 13:03:40 -05:00
Mike Griese
2ab0a50a11 thank god that horror is cleaned up 2022-07-13 12:40:52 -05:00
Mike Griese
376939cef8 Now panes run out of proc too 2022-07-13 12:25:33 -05:00
Mike Griese
15a7d49f4d Start using PreparedContent everywhere ; add _asyncSplitPane* methods
There were entirely too many places initializing a ControlSettings and Profile with evaluateSettings it and that was a chore. A PreparedContent is a handly little bundle of that, with the IAsyncOperation for starting the content.

We need that to pass to the pane operations, which aren't yet hooked up
2022-07-13 11:53:37 -05:00
Mike Griese
ef595b497a Convert duplicateTab to be out-of-proc 2022-07-13 09:38:12 -05:00
Mike Griese
97676cedd8 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/endgame 2022-07-13 06:28:55 -05:00
Mike Griese
ae5b04f7b2 Merge branch 'dev/migrie/oop/2/infinity-war' into dev/migrie/oop/2/endgame 2022-07-11 13:01:04 -05:00
Mike Griese
3434208947 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-07-11 13:00:38 -05:00
Mike Griese
f893f7516a Merge branch 'dev/migrie/oop/2/infinity-war' into dev/migrie/oop/2/endgame 2022-07-06 13:14:37 -05:00
Mike Griese
702e15ce5a Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-07-06 13:14:17 -05:00
Mike Griese
ff23726b7c Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/endgame 2022-07-06 07:04:33 -05:00
Mike Griese
7675354b2d notes 2022-07-06 07:04:22 -05:00
Mike Griese
19605bf75b A tab should be able to be created without a pane, and this works for that 2022-06-23 15:27:01 -05:00
Mike Griese
eb4b2a7534 The first tab now opens out-of-proc too
We're gonna definitely run into a lot of pain here. Like, the initialization
  finishes now bbefore the tabs are created, so the "no tabs got opened" check
  needs to be changed.

  I don't think I need both resume_bg's in CreateContent and also in AsyncCreateTab
2022-06-23 09:05:00 -05:00
Mike Griese
1f083cbd89 differentiate these two, for clarity 2022-06-22 16:45:38 -05:00
Mike Griese
797ebae6e1 guess what, this totally isn't used! 2022-06-22 14:45:44 -05:00
Mike Griese
4f7e99123a Merge branch 'dev/migrie/oop/2/infinity-war' into dev/migrie/oop/2/endgame 2022-06-22 11:20:16 -05:00
Mike Griese
b8cd2b239f that was vestigial anyways 2022-06-22 11:19:24 -05:00
Mike Griese
023eb75550 this was definitely a bug 2022-06-21 16:52:45 -05:00
Mike Griese
f7d93849a8 this was LOAD BEARING 2022-06-21 13:15:21 -05:00
Mike Griese
7707716b08 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-06-21 11:44:43 -05:00
Mike Griese
dfd345405a holy nuts it worked
LAWD this is dirty code. But POC, it does work for new tabs from the dropdown. Trick is that MakePane can't be used like that anymore, so gotta find a new way.
2022-05-25 12:36:35 -05:00
Mike Griese
daa2ef139f This is when I realized that asyncmakepane was never going to work 2022-05-24 16:36:40 -05:00
Mike Griese
44ac527f31 more nits, more a11y stuff 2022-05-24 12:09:50 -05:00
Mike Griese
52f7664d01 some nits 2022-05-24 10:47:45 -05:00
Mike Griese
223e270778 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-05-24 10:23:43 -05:00
Mike Griese
eac5cebbc6 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-05-05 09:21:49 -05:00
Mike Griese
7d4ee45e4e Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-04-28 05:56:45 -05:00
Mike Griese
02fa24cb12 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-04-20 06:36:40 -05:00
Mike Griese
f898855c82 yes I know this doesn't return that's the whole point 2022-04-20 06:36:31 -05:00
Mike Griese
a38fe5f1a6 woah typos 2022-04-19 16:10:15 -05:00
Mike Griese
bbe32f80e5 Reduce the diff size trivially 2022-04-19 14:37:05 -05:00
Mike Griese
446d07e79f add logging 2022-04-19 12:30:02 -05:00
Mike Griese
a74b45aa2c hey look this assert() doesn't hit anymore 2022-04-19 12:12:45 -05:00
Mike Griese
c588a9c75d cleanup from fixed a11y code 2022-04-19 11:18:11 -05:00
Mike Griese
082c63bde5 this seems to work with narrator. I think this is what I was trying to do 2022-04-18 17:03:39 -05:00
Mike Griese
91977bec5a For this commit, I went with what was in #11501. That seems to have been wrong. I get all sorts of errors. I should to the opposite.
(cherry picked from commit bf0f516634)
2022-04-18 16:17:30 -05:00
Mike Griese
07a1a07e47 When trying to use Narrator, this gets hit and throws an exception. Plumb it through instead 2022-04-18 16:12:01 -05:00
Mike Griese
ce8f8fb618 Make the window smaller because the default is HUUEG 2022-04-18 16:11:09 -05:00
Mike Griese
b1ee82ae18 Revert "start thinking about moving the timers to being threadpooltimers in the core. I think every x-proc call in TermControl logs that message, so _crap_"
This reverts commit 76d8fa7b7e.
2022-04-18 12:17:33 -05:00
Mike Griese
358edd520a Revert "For this commit, I went with what was in #11501. That seems to have been wrong. I get all sorts of errors. I should to the opposite."
This reverts commit bf0f516634.
2022-04-15 14:05:45 -05:00
Mike Griese
bf0f516634 For this commit, I went with what was in #11501. That seems to have been wrong. I get all sorts of errors. I should to the opposite. 2022-04-15 14:05:38 -05:00
Mike Griese
99308b43e2 This actually works for not loading the resources 2022-04-15 14:04:41 -05:00
Mike Griese
2a9eefc2d9 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/2/infinity-war 2022-04-15 12:02:16 -05:00
Mike Griese
33b1ab9e79 own the handle 2022-04-13 06:47:11 -05:00
Mike Griese
7b3ca83329 this reconnects the new window to the existing content process 2022-04-13 06:08:07 -05:00
Mike Griese
76d8fa7b7e start thinking about moving the timers to being threadpooltimers in the core. I think every x-proc call in TermControl logs that message, so _crap_ 2022-04-12 10:53:52 -05:00
Mike Griese
9141f0419a tried to make these blindli fire_and_forgets. Didn't work 2022-04-12 10:23:27 -05:00
Mike Griese
857fb399e1 add localization back 2022-04-12 09:42:37 -05:00
Mike Griese
b3129192ad derp 2022-04-12 09:42:22 -05:00
Mike Griese
b253440cf5 add a velocity flag for this 2022-04-12 09:34:36 -05:00
Mike Griese
5429fca422 The sample works again 2022-04-12 06:53:45 -05:00
Mike Griese
46e299d2b6 Definitely needed those 2022-04-11 14:47:51 -05:00
Mike Griese
34601f7e05 more cleanup 2022-04-11 13:28:46 -05:00
Mike Griese
cf70797083 BODGY, don't raise an event on destruction, that would be too... 2022-04-11 13:28:46 -05:00
Mike Griese
07ba33b893 simplify the interface here a bit 2022-04-11 13:28:46 -05:00
Mike Griese
f818885636 move the content process main to another file as well 2022-04-11 13:28:44 -05:00
Mike Griese
738d1910da Move the content process handling to a separate file in TermControl project 2022-04-11 13:28:06 -05:00
Mike Griese
651efe248b add a dialog internally to the bounds of the control, not outside of the control 2022-04-11 13:28:04 -05:00
Mike Griese
768d4c0df3 some cleanup 2022-04-11 13:27:13 -05:00
Mike Griese
8ceb9baf9a This doesn't immediately crash, but it does crash when you start asking for the actual text ranges. That's not what you want. 2022-04-11 13:26:11 -05:00
Mike Griese
35ca313b48 Revert "This too didn't work. Creating the XAML thing not on the XAML thing isn't going to work"
This reverts commit fd364db727.
2022-04-11 13:21:55 -05:00
Mike Griese
1a78557b61 This too didn't work. Creating the XAML thing not on the XAML thing isn't going to work 2022-04-11 13:21:08 -05:00
Mike Griese
dafb627278 Revert "this was the part where I realized I dun goofed"
This reverts commit 64533c838a.
2022-04-11 13:19:22 -05:00
Mike Griese
eeb01a139d this was the part where I realized I dun goofed 2022-04-11 13:19:22 -05:00
Mike Griese
4a8f0e9562 The sample app has a hard time loading TermControl resources so we're just going to disable this for now 2022-04-11 13:19:22 -05:00
Mike Griese
ea9d3cb5f7 a fix for a crash when closing 2022-04-11 13:19:22 -05:00
Mike Griese
3d15b097b7 This works to kill the content and have the app live 2022-04-11 13:19:10 -05:00
Mike Griese
5a07282d19 Add a kill button for manually killing the content 2022-04-11 13:18:41 -05:00
Mike Griese
7e792b2b5e You know, there's 0% chance that this is the right pattern for this, but it _works_ 2022-04-11 13:18:40 -05:00
Mike Griese
c3a94454e0 some short-circuits for these inits, to make them more stable 2022-04-11 13:18:40 -05:00
Mike Griese
2f23e1fc0c A close button, and more logging 2022-04-11 13:18:40 -05:00
Mike Griese
996c71a933 Add a signal that the content can use to tell the window it's ready 2022-04-11 13:18:40 -05:00
Mike Griese
7731f5943a Some comments because everything is hard 2022-04-11 13:18:40 -05:00
Mike Griese
bb49d5086c I believe this merges the buisness of connection-factory, though there are many issues. 2022-04-11 13:18:38 -05:00
Mike Griese
e168413e9f I think this merges the-whole-thing into this branch. The remote control doesn't render right, but I think that's because the actual HEAD of all this work is in connection-factory 2022-04-11 13:17:47 -05:00
79 changed files with 3162 additions and 1096 deletions

15
.github/actions/spelling/README.md vendored Normal file
View File

@@ -0,0 +1,15 @@
# check-spelling/check-spelling configuration
File | Purpose | Format | Info
-|-|-|-
[allow/*.txt](allow/) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
[patterns/*.txt](patterns/) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
[expect/*.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
Note: you can replace any of these files with a directory by the same name (minus the suffix)
and then include multiple files inside that directory (with that suffix) to merge multiple files together.

View File

@@ -1,4 +1,4 @@
<!-- markdownlint-disable MD033 MD041 -->
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
<details>
<summary>
:pencil2: Contributor please read this
@@ -6,7 +6,7 @@
By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.
: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...
:warning: The command is written for posix shells. If it doesn't work for you, you can manually _add_ (one word per line) / _remove_ items to `expect.txt` and the `excludes.txt` files.
If the listed items are:
@@ -20,31 +20,29 @@ 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:
<details><summary>:clamp: If you see a bunch of garbage</summary>
If it relates to a ...
<details><summary>well-formed pattern</summary>
<details><summary>If the flagged items are :exploding_head: false positives</summary>
See if there's a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it.
If items relate to a ...
* binary file (or some other file you wouldn't want to check at all).
If not, try writing one and adding it to a `patterns/{file}.txt`.
Please add a file path to the `excludes.txt` file matching the containing file.
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](
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](
`^` 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>
* well-formed pattern.
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
try adding it to the `patterns.txt` file.
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>

View File

@@ -1,7 +1,7 @@
admins
allcolors
apc
Apc
apc
breadcrumb
breadcrumbs
bsd
@@ -14,8 +14,8 @@ CMMI
copyable
cybersecurity
dalet
dcs
Dcs
dcs
dialytika
dje
downside
@@ -34,10 +34,12 @@ gantt
gcc
geeksforgeeks
ghe
github
gje
godbolt
hostname
hostnames
https
hyperlink
hyperlinking
hyperlinks
@@ -54,9 +56,11 @@ Llast
llvm
Lmid
locl
lol
lorem
Lorigin
maxed
minimalistic
mkmk
mnt
mru
@@ -80,6 +84,7 @@ runtimes
shcha
slnt
Sos
ssh
timeline
timelines
timestamped
@@ -88,6 +93,7 @@ tokenizes
tonos
toolset
tshe
ubuntu
uiatextrange
UIs
und
@@ -96,5 +102,7 @@ versioned
vsdevcmd
We'd
wildcards
XBox
YBox
yeru
zhe

View File

@@ -5,6 +5,7 @@ aclapi
alignas
alignof
APPLYTOSUBMENUS
appxrecipe
bitfield
bitfields
BUILDBRANCH
@@ -14,6 +15,7 @@ BYCOMMAND
BYPOSITION
charconv
CLASSNOTAVAILABLE
CLOSEAPP
cmdletbinding
COLORPROPERTY
colspan
@@ -28,11 +30,14 @@ dataobject
dcomp
DERR
dlldata
DNE
DONTADDTORECENT
DWORDLONG
DWMSBT
DWMWA
DWMWA
DWORDLONG
endfor
ENDSESSION
enumset
environstrings
EXPCMDFLAGS
@@ -72,6 +77,7 @@ IDirect
IExplorer
IFACEMETHOD
IFile
IGraphics
IInheritable
IMap
IMonarch
@@ -86,6 +92,7 @@ istream
IStringable
ITab
ITaskbar
itow
IUri
IVirtual
KEYSELECT
@@ -97,13 +104,14 @@ lround
Lsa
lsass
LSHIFT
LTGRAY
MAINWINDOW
memchr
memicmp
MENUCOMMAND
MENUDATA
MENUITEMINFOW
MENUINFO
MENUITEMINFOW
mmeapi
MOUSELEAVE
mov
@@ -140,16 +148,18 @@ OUTLINETEXTMETRICW
overridable
PACL
PAGESCROLL
PATINVERT
PEXPLICIT
PICKFOLDERS
pmr
ptstr
QUERYENDSESSION
rcx
REGCLS
RETURNCMD
rfind
roundf
ROOTOWNER
roundf
RSHIFT
SACL
schandle
@@ -200,6 +210,8 @@ UOI
UPDATEINIFILE
userenv
USEROBJECTFLAGS
Viewbox
virtualalloc
wcsstr
wcstoui
winmain

View File

@@ -69,6 +69,8 @@ Rincewind
rprichard
Schoonover
shadertoy
Shomnipotence
simioni
Somuah
sonph
sonpham

View File

@@ -0,0 +1,523 @@
# marker to ignore all code on line
^.*/\* #no-spell-check-line \*/.*$
# marker for ignoring a comment to the end of the line
// #no-spell-check.*$
# patch hunk comments
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
# git index header
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
# cid urls
(['"])cid:.*?\g{-1}
# data url in parens
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
# data url in quotes
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
# data url
data:[-a-zA-Z=;:/0-9+]*,\S*
# mailto urls
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
# magnet urls
magnet:[?=:\w]+
# magnet urls
"magnet:[^"]+"
# obs:
"obs:[^"]*"
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
# In this examples content, I'm using a number of different ways to match things to show various approaches
# asciinema
\basciinema\.org/a/[0-9a-zA-Z]+
# apple
\bdeveloper\.apple\.com/[-\w?=/]+
# Apple music
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
# appveyor api
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
# appveyor project
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
# Amazon
# Amazon
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
# AWS S3
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
# AWS execute-api
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
# AWS ELB
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
# AWS SNS
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
# AWS VPC
vpc-\w+
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
# YouTube url
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
# YouTube music
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
# YouTube tag
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
# YouTube image
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
# Google Accounts
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
# Google Analytics
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
# Google APIs
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
# Google Storage
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
# Google Calendar
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
\w+\@group\.calendar\.google\.com\b
# Google DataStudio
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
# The leading `/` here is as opposed to the `\b` above
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
# Google Docs
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
# Google Drive
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
# Google Groups
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
# Google Maps
\bmaps\.google\.com/maps\?[\w&;=]*
# Google themes
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
# Google CDN
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
# Goo.gl
/goo\.gl/[a-zA-Z0-9]+
# Google Chrome Store
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
# Google Books
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
# Google Fonts
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
# Google Forms
\bforms\.gle/\w+
# Google Scholar
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
# Google Colab Research Drive
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
# GitHub SHAs (api)
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
# GitHub SHAs (markdown)
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
# GitHub SHAs
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
# GitHub wiki
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
# githubusercontent
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
# githubassets
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
# gist github
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
# git.io
\bgit\.io/[0-9a-zA-Z]+
# GitHub JSON
"node_id": "[-a-zA-Z=;:/0-9+]*"
# Contributor
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
# GHSA
GHSA(?:-[0-9a-z]{4}){3}
# GitLab commit
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
# GitLab merge requests
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
# GitLab uploads
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
# GitLab commits
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
# binanace
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
# bitbucket diff
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
# bitbucket repositories commits
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
# bitbucket commits
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
# bit.ly
\bbit\.ly/\w+
# bitrise
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
# bootstrapcdn.com
\bbootstrapcdn\.com/[-./\w]+
# cdn.cloudflare.com
\bcdnjs\.cloudflare\.com/[./\w]+
# circleci
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
# gitter
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
# gravatar
\bgravatar\.com/avatar/[0-9a-f]+
# ibm
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
# imgur
\bimgur\.com/[^.]+
# Internet Archive
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
# discord
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
# Disqus
\bdisqus\.com/[-\w/%.()!?&=_]*
# medium link
\blink\.medium\.com/[a-zA-Z0-9]+
# medium
\bmedium\.com/\@?[^/\s"]+/[-\w]+
# microsoft
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
# powerbi
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
# vs devops
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
# microsoft store
\bmicrosoft\.com/store/apps/\w+
# mvnrepository.com
\bmvnrepository\.com/[-0-9a-z./]+
# now.sh
/[0-9a-z-.]+\.now\.sh\b
# oracle
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
# chromatic.com
/\S+.chromatic.com\S*[")]
# codacy
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
# compai
\bcompai\.pub/v1/png/[0-9a-f]+
# mailgun api
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
# mailgun
\b[0-9a-z]+.mailgun.org
# /message-id/
/message-id/[-\w@./%]+
# Reddit
\breddit\.com/r/[/\w_]*
# requestb.in
\brequestb\.in/[0-9a-z]+
# sched
\b[a-z0-9]+\.sched\.com\b
# Slack url
slack://[a-zA-Z0-9?&=]+
# Slack
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
# Slack edge
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
# Slack images
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
# shields.io
\bshields\.io/[-\w/%?=&.:+;,]*
# stackexchange -- https://stackexchange.com/feeds/sites
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
# Sentry
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
# Twitter markdown
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
# Twitter hashtag
\btwitter\.com/hashtag/[\w?_=&]*
# Twitter status
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
# Twitter profile images
\btwimg\.com/profile_images/[_\w./]*
# Twitter media
\btwimg\.com/media/[-_\w./?=]*
# Twitter link shortened
\bt\.co/\w+
# facebook
\bfburl\.com/[0-9a-z_]+
# facebook CDN
\bfbcdn\.net/[\w/.,]*
# facebook watch
\bfb\.watch/[0-9A-Za-z]+
# dropbox
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
# ipfs protocol
ipfs://[0-9a-z]*
# ipfs url
/ipfs/[0-9a-z]*
# w3
\bw3\.org/[-0-9a-zA-Z/#.]+
# loom
\bloom\.com/embed/[0-9a-f]+
# regex101
\bregex101\.com/r/[^/\s"]+/\d+
# figma
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
# freecodecamp.org
\bfreecodecamp\.org/[-\w/.]+
# image.tmdb.org
\bimage\.tmdb\.org/[/\w.]+
# mermaid
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
# Wikipedia
\ben\.wikipedia\.org/wiki/[-\w%.#]+
# gitweb
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
# HyperKitty lists
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
# lists
/thread\.html/[^"\s]+
# list-management
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
# kubectl.kubernetes.io/last-applied-configuration
"kubectl.kubernetes.io/last-applied-configuration": ".*"
# pgp
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
# Spotify
\bopen\.spotify\.com/embed/playlist/\w+
# Mastodon
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
# scastie
\bscastie\.scala-lang\.org/[^/]+/\w+
# images.unsplash.com
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
# pastebin
\bpastebin\.com/[\w/]+
# heroku
\b\w+\.heroku\.com/source/archive/\w+
# quip
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
# badgen.net
\bbadgen\.net/badge/[^")\]'\s]+
# statuspage.io
\w+\.statuspage\.io\b
# media.giphy.com
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
# tinyurl
\btinyurl\.com/\w+
# getopts
\bgetopts\s+(?:"[^"]+"|'[^']+')
# ANSI color codes
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
# URL escaped characters
\%[0-9A-F][A-F]
# IPv6
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
# c99 hex digits (not the full format, just one I've seen)
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
# Punycode
\bxn--[-0-9a-z]+
# sha
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
# sha-... -- uses a fancy capture
(['"]|&quot;)[0-9a-f]{40,}\g{-1}
# hex runs
\b[0-9a-fA-F]{16,}\b
# hex in url queries
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
# ssh
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
# PGP
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
# GPG keys
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
# Well known gpg keys
.well-known/openpgpkey/[\w./]+
# uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
# integrity
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
# https://www.gnu.org/software/groff/manual/groff.html
# man troff content
\\f[BCIPR]
# '
\\\(aq
# .desktop mime types
^MimeTypes?=.*$
# .desktop localized entries
^[A-Z][a-z]+\[[a-z]+\]=.*$
# Localized .desktop content
Name\[[^\]]+\]=.*
# IServiceProvider
\bI(?=(?:[A-Z][a-z]{2,})+\b)
# crypt
"\$2[ayb]\$.{56}"
# scrypt / argon
\$(?:scrypt|argon\d+[di]*)\$\S+
# Input to GitHub JSON
content: "[-a-zA-Z=;:/0-9+]*="
# Python stringprefix / binaryprefix
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
# Regular expressions for (P|p)assword
\([A-Z]\|[a-z]\)[a-z]+
# JavaScript regular expressions
# javascript test regex
/.*/[gim]*\.test\(
# javascript match regex
\.match\(/[^/\s"]*/[gim]*\s*
# javascript match regex
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
# javascript regex
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
# javascript replace regex
\.replace\(/[^/\s"]*/[gim]*\s*,
# Go regular expressions
regexp?\.MustCompile\(`[^`]*`\)
# sed regular expressions
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
# go install
go install(?:\s+[a-z]+\.[-@\w/.]+)+
# kubernetes pod status lists
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
# kubectl - pods in CrashLoopBackOff
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
# kubernetes object suffix
-[0-9a-f]{10}-\w{5}\s
# posthog secrets
posthog\.init\((['"])phc_[^"',]+\g{-1},
# xcode
# xcodeproject scenes
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
# xcode api botches
customObjectInstantitationMethod
# font awesome classes
\.fa-[-a-z0-9]+
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
## You could manually change `(?i)X...` to use `[Xx]...`
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
# Lorem
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
# Non-English
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
# French
# This corpus only had capital letters, but you probably want lowercase ones as well.
\b[LN]'+[a-z]{2,}\b
# latex
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b
# Note that the next example is no longer necessary if you are using
# to match a string starting with a `#`, use a character-class:
[#]backwards
# version suffix <word>v#
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
# Compiler flags (Scala)
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# Compiler flags
#(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# Compiler flags (linker)
,-B
# curl arguments
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
# set arguments
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
# tar arguments
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
# macOS temp folders
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/

View File

@@ -1,28 +1,39 @@
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
(?:(?i)\.png$)
(?:^|/)(?i)COPYRIGHT
(?:^|/)(?i)LICEN[CS]E
(?:^|/)3rdparty/
(?:^|/)dirs$
(?:^|/)go\.mod$
(?:^|/)go\.sum$
(?:^|/)package-lock\.json$
(?:^|/)package(?:-lock|)\.json$
(?:^|/)sources(?:|\.dep)$
SUMS$
(?:^|/)vendor/
\.a$
\.ai$
\.avi$
\.bmp$
\.bz2$
\.cer$
\.class$
\.crl$
\.crt$
\.csr$
\.dll$
\.docx?$
\.drawio$
\.DS_Store$
\.eot$
\.eps$
\.exe$
\.gif$
\.gitattributes$
\.graffle$
\.gz$
\.icns$
\.ico$
\.jar$
\.jks$
\.jpeg$
\.jpg$
\.key$
@@ -30,28 +41,52 @@ SUMS$
\.lock$
\.map$
\.min\..
\.mod$
\.mp3$
\.mp4$
\.o$
\.ocf$
\.otf$
\.pbxproj$
\.pdf$
\.pem$
\.png$
\.psd$
\.pyc$
\.runsettings$
\.s$
\.sig$
\.so$
\.svg$
\.svgz$
\.svgz?$
\.tar$
\.tgz$
\.tiff?$
\.ttf$
\.vsdx$
\.wav$
\.webm$
\.webp$
\.woff
\.woff2?$
\.xcf$
\.xls
\.xlsx?$
\.xpm$
\.yml$
\.zip$
^\.github/actions/spelling/
^\.github/fabricbot.json$
^\.gitignore$
^\Q.git-blame-ignore-revs\E$
^\Q.github/workflows/spelling.yml\E$
^\Qdoc/reference/windows-terminal-logo.ans\E$
^\Qsamples/ConPTY/EchoCon/EchoCon/EchoCon.vcxproj.filters\E$
^\Qsrc/host/exe/Host.EXE.vcxproj.filters\E$
^\Qsrc/host/ft_host/chafa.txt\E$
^\Qsrc/tools/closetest/CloseTest.vcxproj.filters\E$
^\XamlStyler.json$
^build/config/
^consolegit2gitfilters\.json$
^dep/
@@ -78,7 +113,5 @@ SUMS$
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
^src/types/ut_types/UtilsTests.cpp$
^tools/ReleaseEngineering/ServicingPipeline.ps1$
^\.github/actions/spelling/
^\.github/fabricbot.json$
^\.gitignore$
^\XamlStyler.json$
ignore$
SUMS$

View File

@@ -5,26 +5,19 @@ AAAAAABBBBBBCCC
AAAAABBBBBBCCC
abcd
abcd
abcde
abcdef
ABCDEFG
ABCDEFGH
ABCDEFGHIJ
abcdefghijk
ABCDEFGHIJKLMNO
abcdefghijklmnop
ABCDEFGHIJKLMNOPQRST
abcdefghijklmnopqrstuvwxyz
ABCG
ABE
abf
BBBBB
BBBBBBBB
BBBBBBBBBBBBBBDDDD
BBBBBCCC
BBBBCCCCC
BBGGRR
CCE
EFG
EFGh
QQQQQQQQQQABCDEFGHIJ
@@ -33,7 +26,6 @@ QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ
QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ
qrstuvwxyz
qwerty
QWERTYUIOP
qwertyuiopasdfg
YYYYYYYDDDDDDDDDDD
ZAAZZ

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,6 @@
http
www
easyrgb
php
ecma
rapidtables
WCAG
freedesktop
ycombinator
robertelder
kovidgoyal
leonerd
fixterms
winui
appshellintegration
mdtauk
cppreference
gfycat
Guake
azurewebsites
askubuntu
dostips
viewtopic
rosettacode
Rexx
tldp
HOWTO
uwspace
uwaterloo

View File

@@ -0,0 +1,62 @@
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
# \bm_data\b
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
# to use this:
#\bfit\(
# s.b. GitHub
\bGithub\b
# s.b. GitLab
\bGitlab\b
# s.b. JavaScript
\bJavascript\b
# s.b. Microsoft
\bMicroSoft\b
# s.b. another
\ban[- ]other\b
# s.b. greater than
\bgreater then\b
# s.b. into
#\sin to\s
# s.b. opt-in
\sopt in\s
# s.b. less than
\bless then\b
# s.b. otherwise
\bother[- ]wise\b
# s.b. nonexistent
\bnon existing\b
\b[Nn]o[nt][- ]existent\b
# s.b. preexisting
[Pp]re[- ]existing
# s.b. preempt
[Pp]re[- ]empt\b
# s.b. preemptively
[Pp]re[- ]emptively
# s.b. reentrancy
[Rr]e[- ]entrancy
# s.b. reentrant
[Rr]e[- ]entrant
# s.b. workaround(s)
#\bwork[- ]arounds?\b
# Reject duplicate words
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s

View File

@@ -1,3 +1,5 @@
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
https?://\S+
[Pp]ublicKeyToken="?[0-9a-fA-F]{16}"?
(?:[{"]|UniqueIdentifier>)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}(?:[}"]|</UniqueIdentifier)
@@ -21,3 +23,74 @@ vcvars\w*
ROY\sG\.\sBIV
!(?:(?i)ESC)!\[
!(?:(?i)CSI)!(?:\d+(?:;\d+|)m|[ABCDF])
# Python stringprefix / binaryprefix
\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'
# Automatically suggested patterns
# hit-count: 3831 file-count: 582
# IServiceProvider
\bI(?=(?:[A-Z][a-z]{2,})+\b)
# hit-count: 71 file-count: 35
# Compiler flags
(?:^|[\t ,"'`=(])-[D](?=[A-Z]{2,}|[A-Z][a-z])
(?:^|[\t ,"'`=(])-[X](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# hit-count: 41 file-count: 28
# version suffix <word>v#
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
# hit-count: 20 file-count: 9
# hex runs
\b[0-9a-fA-F]{16,}\b
# hit-count: 10 file-count: 7
# uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
# hit-count: 4 file-count: 4
# mailto urls
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
# hit-count: 4 file-count: 1
# ANSI color codes
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
# hit-count: 2 file-count: 1
# latex
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
# hit-count: 1 file-count: 1
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
# hit-count: 1 file-count: 1
# Non-English
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
# hit-count: 1 file-count: 1
# French
# This corpus only had capital letters, but you probably want lowercase ones as well.
\b[LN]'+[a-z]{2,}\b
# acceptable duplicates
# ls directory listings
[-bcdlpsw](?:[-r][-w][-sx]){3}\s+\d+\s+(\S+)\s+\g{-1}\s+\d+\s+
# C/idl types + English ...
\s(Guid|long|LONG|that) \g{-1}\s
# javadoc / .net
(?:[\\@](?:groupname|param)|(?:public|private)(?:\s+static|\s+readonly)*)\s+(\w+)\s+\g{-1}\s
# Commit message -- Signed-off-by and friends
^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$
# Autogenerated revert commit message
^This reverts commit [0-9a-f]{40}\.$
# vtmode
--vtmode\s+(\w+)\s+\g{-1}\s
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b

View File

@@ -1,22 +1,12 @@
^attache$
^attacher$
^attachers$
^spae$
^spaebook$
^spaecraft$
^spaed$
^spaedom$
^spaeing$
^spaeings$
^spae-man$
^spaeman$
^spaer$
^Spaerobee$
^spaes$
^spaewife$
^spaewoman$
^spaework$
^spaewright$
^wether$
^wethers$
^wetherteg$
benefitting
occurences?
^dependan.*
^oer$
Sorce
^[Ss]pae.*
^untill$
^untilling$
^wether.*

View File

@@ -1,20 +1,134 @@
# spelling.yml is blocked per https://github.com/check-spelling/check-spelling/security/advisories/GHSA-g86g-chm8-7r2p
name: Spell checking
# Comment management is handled through a secondary job, for details see:
# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions
#
# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment
# (in odd cases, it might actually run just to collapse a commment, but that's fairly rare)
# it needs `contents: write` in order to add a comment.
#
# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment
# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment)
# it needs `pull-requests: write` in order to manipulate those comments.
# Updating pull request branches is managed via comment handling.
# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list
#
# These elements work together to make it happen:
#
# `on.issue_comment`
# This event listens to comments by users asking to update the metadata.
#
# `jobs.update`
# This job runs in response to an issue_comment and will push a new commit
# to update the spelling metadata.
#
# `with.experimental_apply_changes_via_bot`
# Tells the action to support and generate messages that enable it
# to make a commit to update the spelling metadata.
#
# `with.ssh_key`
# In order to trigger workflows when the commit is made, you can provide a
# secret (typically, a write-enabled github deploy key).
#
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
on:
pull_request_target:
push:
branches:
- "**"
tags-ignore:
- "**"
pull_request_target:
branches:
- "**"
tags-ignore:
- "**"
types:
- 'opened'
- 'reopened'
- 'synchronize'
issue_comment:
types:
- 'created'
jobs:
spelling:
name: Spell checking
permissions:
contents: read
pull-requests: read
actions: read
outputs:
followup: ${{ steps.spelling.outputs.followup }}
runs-on: ubuntu-latest
if: "contains(github.event_name, 'pull_request') || github.event_name == 'push'"
concurrency:
group: spelling-${{ github.event.pull_request.number || github.ref }}
# note: If you use only_check_changed_files, you do not want cancel-in-progress
cancel-in-progress: true
steps:
- name: checkout-merge
if: "contains(github.event_name, 'pull_request')"
uses: actions/checkout@v2
- name: check-spelling
id: spelling
uses: check-spelling/check-spelling@v0.0.21
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
suppress_push_for_open_pull_request: 1
checkout: true
check_file_names: 1
spell_check_this: check-spelling/spell-check-this@prerelease
post_comment: 0
use_magic_file: 1
extra_dictionary_limit: 10
extra_dictionaries:
cspell:software-terms/src/software-terms.txt
cspell:python/src/python/python-lib.txt
cspell:node/node.txt
cspell:cpp/src/stdlib-c.txt
cspell:cpp/src/stdlib-cpp.txt
cspell:fullstack/fullstack.txt
cspell:filetypes/filetypes.txt
cspell:html/html.txt
cspell:cpp/src/compiler-msvc.txt
cspell:python/src/common/extra.txt
cspell:powershell/powershell.txt
cspell:aws/aws.txt
cspell:cpp/src/lang-keywords.txt
cspell:npm/npm.txt
cspell:dotnet/dotnet.txt
cspell:python/src/python/python.txt
cspell:css/css.txt
cspell:cpp/src/stdlib-cmath.txt
check_extra_dictionaries: ''
comment-push:
name: Report (Push)
# If your workflow isn't running on push, you can remove this job
runs-on: ubuntu-latest
needs: spelling
permissions:
contents: write
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
steps:
- name: comment
uses: check-spelling/check-spelling@v0.0.21
with:
checkout: true
spell_check_this: check-spelling/spell-check-this@prerelease
task: ${{ needs.spelling.outputs.followup }}
comment-pr:
name: Report (PR)
# If you workflow isn't running on pull_request*, you can remove this job
runs-on: ubuntu-latest
needs: spelling
permissions:
pull-requests: write
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
steps:
- name: comment
uses: check-spelling/check-spelling@v0.0.21
with:
checkout: true
spell_check_this: check-spelling/spell-check-this@prerelease
task: ${{ needs.spelling.outputs.followup }}

View File

@@ -3,9 +3,11 @@
#include "pch.h"
#include "MyPage.h"
#include "MySettings.h"
#include <LibraryResources.h>
#include "MyPage.g.cpp"
#include "MySettings.h"
#include "..\..\..\src\cascadia\UnitTests_Control\MockControlSettings.h"
#include "..\..\..\src\types\inc\utils.hpp"
using namespace std::chrono_literals;
using namespace winrt::Microsoft::Terminal;
@@ -26,7 +28,7 @@ namespace winrt::SampleApp::implementation
void MyPage::Create()
{
auto settings = winrt::make_self<implementation::MySettings>();
auto settings = winrt::make_self<MySettings>();
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
winrt::hstring{},
@@ -44,6 +46,216 @@ namespace winrt::SampleApp::implementation
Control::TermControl control{ *settings, *settings, conn };
InProcContent().Children().Append(control);
// Once the control loads (and not before that), write some text for debugging:
control.Initialized([conn](auto&&, auto&&) {
conn.WriteInput(L"This TermControl is hosted in-proc...");
});
}
static wil::unique_process_information _createHostClassProcess(const winrt::guid& g)
{
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(g) };
// Create an event that the content process will use to signal it is
// ready to go. We won't need the event after this function, so the
// unique_event will clean up our handle when we leave this scope. The
// ContentProcess is responsible for cleaning up its own handle.
wil::unique_event ev{ CreateEvent(nullptr, true, false, nullptr) };
// Make sure to mark this handle as inheritable! Even with
// bInheritHandles=true, this is only inherited when it's explicitly
// allowed to be.
SetHandleInformation(ev.get(), HANDLE_FLAG_INHERIT, 1);
// god bless, fmt::format will format a HANDLE like `0xa80`
std::wstring commandline{
fmt::format(L"WindowsTerminal.exe --content {} --signal {}", guidStr, ev.get())
};
STARTUPINFO siOne{ 0 };
siOne.cb = sizeof(STARTUPINFOW);
wil::unique_process_information piOne;
auto succeeded = CreateProcessW(
nullptr,
commandline.data(),
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
true, // bInheritHandles
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // startingDirectory
&siOne, // lpStartupInfo
&piOne // lpProcessInformation
);
THROW_IF_WIN32_BOOL_FALSE(succeeded);
// Wait for the child process to signal that they're ready.
WaitForSingleObject(ev.get(), INFINITE);
return piOne;
}
winrt::fire_and_forget MyPage::_writeToLog(std::wstring_view str)
{
winrt::hstring copy{ str };
// Switch back to the UI thread.
co_await resume_foreground(Dispatcher());
winrt::WUX::Controls::TextBlock block;
block.Text(copy);
Log().Children().Append(block);
}
winrt::fire_and_forget MyPage::CreateClicked(const IInspectable& sender,
const WUX::Input::TappedRoutedEventArgs& eventArgs)
{
auto guidString = GuidInput().Text();
// Capture calling context.
winrt::apartment_context ui_thread;
auto canConvert = guidString.size() == 38 &&
guidString.front() == '{' &&
guidString.back() == '}';
bool tryingToAttach = false;
winrt::guid contentGuid{ ::Microsoft::Console::Utils::CreateGuid() };
if (canConvert)
{
GUID result{};
if (SUCCEEDED(IIDFromString(guidString.c_str(), &result)))
{
contentGuid = result;
tryingToAttach = true;
}
}
_writeToLog(tryingToAttach ? L"Attaching to existing content process" : L"Creating new content process");
co_await winrt::resume_background();
if (!tryingToAttach)
{
// Spawn a wt.exe, with the guid on the commandline
piContentProcess = std::move(_createHostClassProcess(contentGuid));
}
// THIS MUST TAKE PLACE AFTER _createHostClassProcess.
// * If we're creating a new OOP control, _createHostClassProcess will
// spawn the process that will actually host the ContentProcess
// object.
// * If we're attaching, then that process already exists.
Control::ContentProcess content{ nullptr };
try
{
content = create_instance<Control::ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
}
catch (winrt::hresult_error hr)
{
_writeToLog(L"CreateInstance the ContentProcess object");
_writeToLog(fmt::format(L" HR ({}): {}", hr.code(), hr.message().c_str()));
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
}
if (content == nullptr)
{
_writeToLog(L"Failed to connect to the ContentProcess object. It may not have been started fast enough.");
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
}
TerminalConnection::ConnectionInformation connectInfo{ nullptr };
Control::IControlSettings settings{ *winrt::make_self<implementation::MySettings>() };
// When creating a terminal for the first time, pass it a connection
// info
//
// otherwise, when attaching to an existing one, just pass null, because
// we don't need the connection info.
if (!tryingToAttach)
{
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted out-of-proc...",
winrt::hstring{},
L"",
nullptr,
32,
80,
winrt::guid()) };
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
connectInfo = TerminalConnection::ConnectionInformation(myClass, connectionSettings);
if (!content.Initialize(settings, settings, connectInfo))
{
_writeToLog(L"Failed to Initialize the ContentProcess object.");
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
}
}
else
{
// If we're attaching, we don't really need to do anything special.
}
// Switch back to the UI thread.
co_await ui_thread;
// Create the XAML control that will be attached to the content process.
// We're not passing in a connection, because the contentGuid will be used instead.
Control::TermControl control{ contentGuid, settings, settings, nullptr };
auto weakControl = winrt::make_weak(control);
control.RaiseNotice([this](auto&&, auto& args) {
_writeToLog(L"Content process died, probably.");
_writeToLog(args.Message());
OutOfProcContent().Children().Clear();
GuidInput().Text(L"");
if (piContentProcess.hProcess)
{
piContentProcess.reset();
}
});
control.ConnectionStateChanged([this, weakControl](auto&&, auto&) {
if (auto strongControl{ weakControl.get() })
{
const auto newConnectionState = strongControl.ConnectionState();
if (newConnectionState == TerminalConnection::ConnectionState::Closed)
{
_writeToLog(L"Connection was closed");
OutOfProcContent().Children().Clear();
GuidInput().Text(L"");
if (piContentProcess.hProcess)
{
piContentProcess.reset();
}
}
}
});
Log().Children().Clear();
OutOfProcContent().Children().Append(control);
if (!tryingToAttach)
{
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(contentGuid) };
GuidInput().Text(guidStr);
}
}
void MyPage::CloseClicked(const IInspectable& /*sender*/,
const WUX::Input::TappedRoutedEventArgs& /*eventArgs*/)
{
OutOfProcContent().Children().Clear();
GuidInput().Text(L"");
if (piContentProcess.hProcess)
{
piContentProcess.reset();
}
}
void MyPage::KillClicked(const IInspectable& /*sender*/,
const WUX::Input::TappedRoutedEventArgs& /*eventArgs*/)
{
if (piContentProcess.hProcess)
{
TerminateProcess(piContentProcess.hProcess, (UINT)-1);
piContentProcess.reset();
}
}
// Method Description:

View File

@@ -14,11 +14,18 @@ namespace winrt::SampleApp::implementation
MyPage();
void Create();
hstring Title();
winrt::fire_and_forget CreateClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
void CloseClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
void KillClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
private:
friend struct MyPageT<MyPage>; // for Xaml to bind events
wil::unique_process_information piContentProcess;
winrt::fire_and_forget _writeToLog(std::wstring_view str);
};
}

View File

@@ -23,9 +23,23 @@
<TextBox x:Name="GuidInput"
Width="400"
PlaceholderText="{}{guid here}" />
<Button Grid.Row="0">
<Button x:Name="CreateOutOfProcControl"
Grid.Row="0"
Tapped="CreateClicked">
Create
</Button>
<Button x:Name="CloseOutOfProcControl"
Grid.Row="0"
Margin="4,0,0,0"
Tapped="CloseClicked">
Close
</Button>
<Button x:Name="KillOutOfProcControl"
Grid.Row="0"
Margin="4,0,0,0"
Tapped="KillClicked">
Kill
</Button>
</StackPanel>
@@ -46,14 +60,26 @@
VerticalAlignment="Stretch"
Background="#ff0000" />
<Grid x:Name="OutOfProcContent"
Grid.Column="1"
Padding="16"
<Grid Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#0000ff" />
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel x:Name="Log"
Grid.Row="0"
Orientation="Vertical" />
<Grid x:Name="OutOfProcContent"
Grid.Row="1"
Padding="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#0000ff" />
</Grid>
</Grid>

View File

@@ -17,9 +17,15 @@
<DisableEmbeddedXbf>false</DisableEmbeddedXbf>
<XamlComponentResourceLocation>nested</XamlComponentResourceLocation>
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
<ItemDefinitionGroup>
<ClCompile>
@@ -147,14 +153,15 @@
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!--

View File

@@ -11,9 +11,13 @@
<!-- sets a bunch of Windows Universal properties -->
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
</PropertyGroup>
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
<!-- ========================= XAML files ======================== -->
<ItemGroup>
<!-- DON'T PUT XAML FILES HERE! Put them in SampleAppLib.vcxproj -->
@@ -81,13 +85,11 @@
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>
<ItemDefinitionGroup>
@@ -102,4 +104,7 @@
</Link>
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
</Project>

View File

@@ -57,8 +57,8 @@ void SampleIslandWindow::MakeWindow() noexcept
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
1024,
860,
nullptr,
nullptr,
wc.hInstance,
@@ -104,8 +104,6 @@ void SampleIslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam)
void SampleIslandWindow::Initialize()
{
const bool initialized = (_interopWindowHandle != nullptr);
_source = DesktopWindowXamlSource{};
auto interop = _source.as<IDesktopWindowXamlSourceNative>();

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
<PropertyGroup Label="Globals">
<ProjectGuid>{b4427499-9fde-4208-b456-5bc580637633}</ProjectGuid>
@@ -16,7 +15,15 @@
<TargetPlatformIdentifier>Windows</TargetPlatformIdentifier>
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
<TerminalVCRTForwarders>true</TerminalVCRTForwarders>
<TerminalThemeHelpers>true</TerminalThemeHelpers>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
@@ -138,16 +145,11 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.7.1\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets'))" />
</Target>
<!-- Override GetPackagingOutputs to roll up all our dependencies.
@@ -225,13 +227,24 @@
<TargetPath>%(Filename)%(Extension)</TargetPath>
</PackagingOutputs>
</ItemGroup>
<!-- Same thing AGAIN here, with OpenConsole.exe If you forget this, then
the scratch app will use the inbox conpty with a newer conpty lib, causing
us to send the inbox conhost messages that will make it explode. -->
<ItemGroup>
<_OpenConsoleExe Include="$(OpenConsoleCommonOutDir)\OpenConsole.exe" />
<PackagingOutputs Include="@(_OpenConsoleExe)">
<ProjectName>$(ProjectName)</ProjectName>
<OutputGroup>BuiltProjectOutputGroup</OutputGroup>
<TargetPath>%(Filename)%(Extension)</TargetPath>
</PackagingOutputs>
</ItemGroup>
</Target>
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
<Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" />
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
</Project>

View File

@@ -1036,4 +1036,26 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return winrt::single_threaded_vector(std::move(vec));
}
void Monarch::RequestMoveContent(winrt::hstring window,
winrt::hstring content,
uint32_t tabIndex)
{
auto windowId = _lookupPeasantIdForName(window);
if (windowId == 0)
{
/* TODO! try the name as an integer ID */
return;
}
if (auto targetPeasant{ _getPeasant(windowId) })
{
auto request = winrt::make_self<implementation::AttachRequest>(content, tabIndex);
targetPeasant.AttachContentToWindow(*request);
}
else
{
/*TODO! log */
}
}
}

View File

@@ -60,6 +60,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex);
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);

View File

@@ -60,6 +60,8 @@ namespace Microsoft.Terminal.Remoting
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
Windows.Foundation.Collections.IVector<String> GetAllWindowLayouts();
void RequestMoveContent(String window, String content, UInt32 tabIndex);
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;

View File

@@ -8,6 +8,7 @@
#include "GetWindowLayoutArgs.h"
#include "Peasant.g.cpp"
#include "../../types/inc/utils.hpp"
#include "AttachRequest.g.cpp"
using namespace winrt;
using namespace winrt::Microsoft::Terminal;
@@ -275,6 +276,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::AttachContentToWindow(Remoting::AttachRequest request)
{
try
{
_AttachRequestedHandlers(*this, request);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_AttachContentToWindow",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::Quit()
{
try

View File

@@ -5,6 +5,7 @@
#include "Peasant.g.h"
#include "RenameRequestArgs.h"
#include "AttachRequest.g.h"
namespace RemotingUnitTests
{
@@ -12,6 +13,18 @@ namespace RemotingUnitTests
};
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct AttachRequest : public AttachRequestT<AttachRequest>
{
WINRT_PROPERTY(winrt::hstring, Content);
WINRT_PROPERTY(uint32_t, TabIndex);
public:
AttachRequest(winrt::hstring content,
uint32_t tabIndex) :
_Content{ content },
_TabIndex{ tabIndex } {};
};
struct Peasant : public PeasantT<Peasant>
{
Peasant();
@@ -32,6 +45,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestQuitAll();
void Quit();
void AttachContentToWindow(Remoting::AttachRequest request);
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
@@ -47,12 +62,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
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);
TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs);
TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest);
private:
Peasant(const uint64_t testPID);
uint64_t _ourPID;

View File

@@ -52,6 +52,11 @@ namespace Microsoft.Terminal.Remoting
MonitorBehavior ToMonitor;
}
[default_interface] runtimeclass AttachRequest {
String Content { get; };
UInt32 TabIndex { get; };
}
interface IPeasant
{
CommandlineArgs InitialArgs { get; };
@@ -70,23 +75,31 @@ namespace Microsoft.Terminal.Remoting
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
void Summon(SummonWindowBehavior behavior);
void RequestShowNotificationIcon();
void RequestHideNotificationIcon();
void RequestQuitAll();
void Quit();
String GetWindowLayout();
void AttachContentToWindow(AttachRequest request);
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;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
event Windows.Foundation.TypedEventHandler<Object, GetWindowLayoutArgs> GetWindowLayoutRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, AttachRequest> AttachRequested;
};
[default_interface] runtimeclass Peasant : IPeasant

View File

@@ -788,4 +788,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
return nullptr;
}
winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window,
winrt::hstring content,
uint32_t tabIndex)
{
co_await winrt::resume_background();
_monarch.RequestMoveContent(window, content, tabIndex);
}
}

View File

@@ -48,9 +48,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::fire_and_forget RequestHideNotificationIcon();
winrt::fire_and_forget RequestQuitAll();
bool DoesQuakeWindowExist();
void UpdateActiveTabTitle(winrt::hstring title);
Windows::Foundation::Collections::IVector<winrt::hstring> GetAllWindowLayouts();
winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex);
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);

View File

@@ -22,7 +22,11 @@ namespace Microsoft.Terminal.Remoting
void RequestQuitAll();
void UpdateActiveTabTitle(String title);
Boolean DoesQuakeWindowExist();
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
void RequestMoveContent(String window, String content, UInt32 tabIndex);
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;

View File

@@ -175,7 +175,7 @@ namespace winrt::TerminalApp::implementation
}
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
{
auto moved = _MovePane(realArgs.TabIndex());
auto moved = _MovePane(realArgs);
args.Handled(moved);
}
}
@@ -201,10 +201,15 @@ namespace winrt::TerminalApp::implementation
}
}
_SplitPane(realArgs.SplitDirection(),
// This is safe, we're already filtering so the value is (0, 1)
::base::saturated_cast<float>(realArgs.SplitSize()),
_MakePane(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
// _SplitPaneActiveTab(realArgs.SplitDirection(),
// // This is safe, we're already filtering so the value is (0, 1)
// ::base::saturated_cast<float>(realArgs.SplitSize()),
// _MakePane(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
_asyncSplitPaneActiveTab(realArgs.SplitDirection(),
// This is safe, we're already filtering so the value is (0, 1)
::base::saturated_cast<float>(realArgs.SplitSize()),
_prepareContentProc(realArgs.TerminalArgs(), realArgs.SplitMode() == SplitType::Duplicate));
args.Handled(true);
}
}
@@ -751,17 +756,8 @@ namespace winrt::TerminalApp::implementation
{
if (const auto& realArgs = actionArgs.ActionArgs().try_as<MoveTabArgs>())
{
auto direction = realArgs.Direction();
if (direction != MoveTabDirection::None)
{
if (auto focusedTabIndex = _GetFocusedTabIndex())
{
auto currentTabIndex = focusedTabIndex.value();
auto delta = direction == MoveTabDirection::Forward ? 1 : -1;
_TryMoveTab(currentTabIndex, currentTabIndex + delta);
}
}
actionArgs.Handled(true);
auto moved = _MoveTab(realArgs);
actionArgs.Handled(moved);
}
}

View File

@@ -1660,4 +1660,11 @@ namespace winrt::TerminalApp::implementation
return _settings.GlobalSettings().CurrentTheme();
}
void AppLogic::AttachContent(winrt::hstring content, uint32_t tabIndex)
{
if (_root)
{
_root->AttachContent(content, tabIndex);
}
}
}

View File

@@ -124,6 +124,8 @@ namespace winrt::TerminalApp::implementation
bool GetAlwaysShowNotificationIcon();
bool GetShowTitleInTitlebar();
void AttachContent(winrt::hstring content, uint32_t tabIndex);
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
void DismissDialog();
@@ -216,11 +218,14 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested);
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged);
FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;
#endif

View File

@@ -110,6 +110,7 @@ namespace TerminalApp
Microsoft.Terminal.Settings.Model.Theme Theme { get; };
FindTargetWindowResult FindTargetWindow(String[] args);
void AttachContent(String content, UInt32 tabIndex);
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
@@ -138,5 +139,8 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
}
}

View File

@@ -119,7 +119,7 @@ Pane::Pane(std::shared_ptr<Pane> first,
// - <none>
// Return Value:
// - Arguments appropriate for a SplitPane or NewTab action
NewTerminalArgs Pane::GetTerminalArgsForPane() const
NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const
{
// Leaves are the only things that have controls
assert(_IsLeaf());
@@ -164,6 +164,11 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
// object. That would work for schemes set by the Terminal, but not ones set
// by VT, but that seems good enough.
if (asContent)
{
args.ContentGuid(_control.ContentGuid());
}
return args;
}
@@ -175,14 +180,15 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
// Arguments:
// - currentId: the id to use for the current/first pane
// - nextId: the id to use for a new pane if we split
// - asContent: TODO!
// Return Value:
// - The state from building the startup actions, includes a vector of commands,
// the original root pane, the id of the focused pane, and the number of panes
// created.
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId)
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent)
{
// if we are a leaf then all there is to do is defer to the parent.
if (_IsLeaf())
if (/*!asContent && */ _IsLeaf())
{
if (_lastActive)
{
@@ -195,16 +201,29 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n
auto buildSplitPane = [&](auto newPane) {
ActionAndArgs actionAndArgs;
actionAndArgs.Action(ShortcutAction::SplitPane);
const auto terminalArgs{ newPane->GetTerminalArgsForPane() };
const auto terminalArgs{ newPane->GetTerminalArgsForPane(asContent) };
// When creating a pane the split size is the size of the new pane
// and not position.
const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right;
SplitPaneArgs args{ SplitType::Manual, splitDirection, 1. - _desiredSplitPosition, terminalArgs };
// const auto splitSize = (asContent && _IsLeaf()) ? .5 : (1. - _desiredSplitPosition);
const auto splitSize = (1. - _desiredSplitPosition);
SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs };
actionAndArgs.Args(args);
return actionAndArgs;
};
// if (asContent && _IsLeaf())
// {
// // TODO! This probably won't work. We probably do need to ask the parent
// // of this pane to generate the action for us. Consider moving a pane
// // that's 25% of the parent - the pane doesn't know that. Only the
// // parent does. When we recieve it, we can determine if we're putting it
// // into a tab or a pane, and then parse the NewTerminalArgs out of
// // either the splitPane or the newTab action.
// return { { buildSplitPane(shared_from_this()) }, shared_from_this(), currentId, 1 };
// }
auto buildMoveFocus = [](auto direction) {
MoveFocusArgs args{ direction };

View File

@@ -91,8 +91,8 @@ public:
std::optional<uint32_t> focusedPaneId;
uint32_t panesCreated;
};
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId);
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane() const;
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false);
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const;
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);

View File

@@ -44,7 +44,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - The list of actions.
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions() const
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(const bool /*asContent*/) const
{
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);

View File

@@ -29,7 +29,7 @@ namespace winrt::TerminalApp::implementation
void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
private:
void _MakeTabViewItem() override;

View File

@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const = 0;
virtual std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const = 0;
WINRT_CALLBACK(RequestFocusActiveControl, winrt::delegate<void()>);

View File

@@ -62,7 +62,7 @@ namespace winrt::TerminalApp::implementation
// - existingConnection: An optional connection that is already established to a PTY
// for this tab to host instead of creating one.
// If not defined, the tab will create the connection.
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs)
try
{
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
@@ -72,10 +72,10 @@ namespace winrt::TerminalApp::implementation
{
return S_FALSE;
}
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
const auto controlSettings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
// Try to handle auto-elevation
if (_maybeElevate(newTerminalArgs, settings, profile))
if (_maybeElevate(newTerminalArgs, controlSettings, profile))
{
return S_OK;
}
@@ -83,35 +83,42 @@ namespace winrt::TerminalApp::implementation
// unfortunately. This seems to be due to Centennial quirks. It works
// unpackaged, but not packaged.
//
// This call to _MakePane won't return nullptr, we already checked that
// case above with the _maybeElevate call.
_CreateNewTabFromPane(_MakePane(newTerminalArgs, false, existingConnection));
// // This call to _MakePane won't return nullptr, we already checked that
// // case above with the _maybeElevate call.
// _CreateNewTabFromPane(_MakePane(newTerminalArgs, false, nullptr));
const auto tabCount = _tabs.Size();
const auto usedManualProfile = (newTerminalArgs != nullptr) &&
(newTerminalArgs.ProfileIndex() != nullptr ||
newTerminalArgs.Profile().empty());
// TerminalSettingsCreateResult controlSettings{ nullptr };
// Profile profile{ nullptr };
// _evaluateSettings(newTerminalArgs, false /*duplicate*/, controlSettings, profile);
auto initContentProc = (newTerminalArgs && newTerminalArgs.ContentGuid() != winrt::guid{}) ?
_AttachToContentProcess(newTerminalArgs.ContentGuid()) :
_CreateNewContentProcess(profile, controlSettings);
_createNewTabFromContent(PreparedContent{ initContentProc, controlSettings, profile });
// const auto tabCount = _tabs.Size();
// const auto usedManualProfile = (newTerminalArgs != nullptr) &&
// (newTerminalArgs.ProfileIndex() != nullptr ||
// newTerminalArgs.Profile().empty());
// Lookup the name of the color scheme used by this profile.
const auto scheme = _settings.GetColorSchemeForProfile(profile);
// If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
// that as the empty string.
const auto schemeName = scheme ? scheme.Name() : L"\0";
// // Lookup the name of the color scheme used by this profile.
// const auto scheme = _settings.GetColorSchemeForProfile(profile);
// // If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
// // that as the empty string.
// const auto schemeName = scheme ? scheme.Name() : L"\0";
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"TabInformation",
TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
TraceLoggingFloat64(settings.DefaultSettings().Opacity(), "TintOpacity", "Opacity preference from the settings"),
TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
// TraceLoggingWrite(
// g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
// "TabInformation",
// TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
// TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
// TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
// TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
// TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
// TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
// TraceLoggingFloat64(settings.DefaultSettings().Opacity(), "TintOpacity", "Opacity preference from the settings"),
// TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
// TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
// TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
return S_OK;
}
@@ -123,7 +130,7 @@ namespace winrt::TerminalApp::implementation
// - newTabImpl: the uninitialized tab.
void TerminalPage::_InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl)
{
newTabImpl->Initialize();
// newTabImpl->Initialize();
uint32_t insertPosition = _tabs.Size();
if (_settings.GlobalSettings().NewTabPosition() == NewTabPosition::AfterCurrentTab)
@@ -205,7 +212,7 @@ namespace winrt::TerminalApp::implementation
if (page && tab)
{
page->_SplitTab(*tab);
page->_SplitTab(tab);
}
});
@@ -235,13 +242,17 @@ namespace winrt::TerminalApp::implementation
_tabView.TabItems().InsertAt(insertPosition, tabViewItem);
// Set this tab's icon to the icon from the user's profile
if (const auto profile{ newTabImpl->GetFocusedProfile() })
{
if (!profile.Icon().empty())
{
newTabImpl->UpdateIcon(profile.Icon());
}
}
//
// TODO! This doesn't need ot live in TerminalPage like, at all. This
// should get moved inside TerminalTab::AttachRootPane, or
// TerminalTab::Initialize or something.
// if (const auto profile{ newTabImpl->GetFocusedProfile() })
// {
// if (!profile.Icon().empty())
// {
// newTabImpl->UpdateIcon(profile.Icon());
// }
// }
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
@@ -291,6 +302,41 @@ namespace winrt::TerminalApp::implementation
}
}
winrt::fire_and_forget TerminalPage::_createNewTabFromContent(PreparedContent preppedContent,
std::function<void(const winrt::com_ptr<TerminalTab>&)> postInitTab /* defaults to nullptr*/)
{
auto newTabImpl = winrt::make_self<TerminalTab>(nullptr);
// TODO! This tab should have a Content that's initialized with a blank
// grid that takes up the whole space, with the BG set to the BG color
// of the TerminalControl. So that the tab has something to show
// initially.
//
// Alternatively, the Control could be initialized with a
// AsyncAction<ContentProcess> and then when _that_ returns, the
// TermControl starts to initialize itself?
//
// That's an idea. We do already have the settings for the control, just not the content. Huh.
_InitializeTab(newTabImpl); // Adds tab to list, tabview
// If the caller requested additional setup for the tab, do that now.
// For example, DuplicateTab uses this to copy the runtime tab text from
// the old tab to the new one.
if (postInitTab)
{
postInitTab(newTabImpl);
}
// TODO! Do we need both this and the resume_background in _CreateNewContentProcess
co_await winrt::resume_background();
auto content = co_await preppedContent.initContentProc;
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High);
auto pane = _makePaneFromContent(content, preppedContent.controlSettings, preppedContent.profile);
newTabImpl->AttachRootPane(pane);
// TODO! DebugTap
}
// Method Description:
// - Get the icon of the currently focused terminal control, and set its
// tab's icon to that icon.
@@ -360,30 +406,40 @@ namespace winrt::TerminalApp::implementation
// In the future, it may be preferable to just duplicate the
// current control's live settings (which will include changes
// made through VT).
_CreateNewTabFromPane(_MakePane(nullptr, true, nullptr));
const auto runtimeTabText{ tab.GetTabText() };
if (!runtimeTabText.empty())
{
if (auto newTab{ _GetFocusedTabImpl() })
// _CreateNewTabFromPane(_MakePane(nullptr, true, nullptr));
auto initRuntimeTabTitle = [&tab](auto& newTab) {
const auto runtimeTabText{ tab.GetTabText() };
if (!runtimeTabText.empty())
{
newTab->SetTabText(runtimeTabText);
}
}
};
// TerminalSettingsCreateResult controlSettings{ nullptr };
// Profile profile{ nullptr };
// _evaluateSettings(nullptr, true, controlSettings, profile);
// auto initContentProc = _CreateNewContentProcess(profile, controlSettings);
auto preppedContent = _prepareContentProc(nullptr, true);
_createNewTabFromContent(preppedContent, initRuntimeTabTitle);
}
CATCH_LOG();
}
// Method Description:
// - Sets the specified tab as the focused tab and splits its active pane
// - Sets the specified tab as the focused tab and splits its active pane.
// This will duplicate the profile that's currently active in the given
// tab.
// Arguments:
// - tab: tab to split
void TerminalPage::_SplitTab(TerminalTab& tab)
void TerminalPage::_SplitTab(winrt::com_ptr<TerminalTab>& tab)
{
try
{
_SetFocusedTab(tab);
_SplitPane(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, true));
_SetFocusedTab(*tab);
// _SplitPaneOnTab(tab, SplitDirection::Automatic, 0.5f, _MakePane(nullptr, true));
_asyncSplitPaneOnTab(tab, SplitDirection::Automatic, 0.5f, _prepareContentProc(nullptr, true));
}
CATCH_LOG();
}

View File

@@ -6,6 +6,7 @@
#include "TerminalPage.h"
#include "TerminalPage.g.cpp"
#include "RenameWindowRequestedArgs.g.cpp"
#include "RequestMoveContentArgs.g.cpp"
#include <filesystem>
@@ -248,6 +249,10 @@ namespace winrt::TerminalApp::implementation
_tabView.TabCloseRequested({ this, &TerminalPage::_OnTabCloseRequested });
_tabView.TabItemsChanged({ this, &TerminalPage::_OnTabItemsChanged });
_tabView.TabDragStarting({ this, &TerminalPage::_onTabDragStarting });
_tabView.TabStripDragOver({ this, &TerminalPage::_onTabStripDragOver });
_tabView.TabStripDrop({ this, &TerminalPage::_onTabStripDrop });
_CreateNewTabFlyout();
_UpdateTabWidthMode();
@@ -681,7 +686,9 @@ namespace winrt::TerminalApp::implementation
// GH#12267: Make sure that we don't instantly close ourselves when
// we're readying to accept a defterm connection. In that case, we don't
// have a tab yet, but will once we're initialized.
if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener))
const bool startedElevated = false;
// TODO!
if ((_tabs.Size() == 0 && startedElevated) && !(_shouldStartInboundListener || _isEmbeddingInboundListener))
{
_LastTabClosedHandlers(*this, nullptr);
}
@@ -960,7 +967,7 @@ namespace winrt::TerminalApp::implementation
_newTabButton.Flyout().ShowAt(_newTabButton);
}
void TerminalPage::_OpenNewTerminalViaDropdown(const NewTerminalArgs newTerminalArgs)
winrt::fire_and_forget TerminalPage::_OpenNewTerminalViaDropdown(const NewTerminalArgs newTerminalArgs)
{
// if alt is pressed, open a pane
const auto window = CoreWindow::GetForCurrentThread();
@@ -1014,24 +1021,21 @@ namespace winrt::TerminalApp::implementation
}
else
{
const auto newPane = _MakePane(newTerminalArgs);
// If the newTerminalArgs caused us to open an elevated window
// instead of creating a pane, it may have returned nullptr. Just do
// nothing then.
if (!newPane)
{
return;
}
auto preppedContent = _prepareContentProc(newTerminalArgs, false /*duplicate*/);
if (altPressed && !debugTap)
{
this->_SplitPane(SplitDirection::Automatic,
0.5f,
newPane);
_asyncSplitPaneActiveTab(SplitDirection::Automatic,
0.5f,
preppedContent);
}
else
{
_CreateNewTabFromPane(newPane);
_createNewTabFromContent(preppedContent);
}
// TODO! just change the signature you lazy
co_await resume_foreground(Dispatcher());
}
}
@@ -1147,6 +1151,98 @@ namespace winrt::TerminalApp::implementation
return connection;
}
// TODO!: Merge this with _CreateConnectionFromSettings. We should be able
// to use this with ConnectionInformation::CreateConnection to just do the
// thing.
TerminalConnection::ConnectionInformation TerminalPage::_CreateConnectionInfoFromSettings(const Profile& profile,
const TerminalSettings& settings)
{
// TODO! My WIP branch had this, but the current code doesn't. I don't think this is an issue? We can probably yank this.
//if (!profile)
//{
// // Use the default profile if we didn't get one as an argument.
// profile = _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
//}
auto connectionType = profile.ConnectionType();
winrt::guid sessionGuid{};
Windows::Foundation::Collections::ValueSet connectionSettings{ nullptr };
winrt::hstring className;
if (connectionType == TerminalConnection::AzureConnection::ConnectionType() &&
TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
{
// TODO GH#4661: Replace this with directly using the AzCon when our VT is better
std::filesystem::path azBridgePath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
azBridgePath.replace_filename(L"TerminalAzBridge.exe");
className = winrt::name_of<TerminalConnection::ConptyConnection>();
connectionSettings = TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(),
L".",
L"Azure",
nullptr,
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
winrt::guid());
if constexpr (Feature_VtPassthroughMode::IsEnabled())
{
connectionSettings.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough()));
}
}
else
{
// profile is guaranteed to exist here
auto guidWString = Utils::GuidToString(profile.Guid());
StringMap envMap{};
envMap.Insert(L"WT_PROFILE_ID", guidWString);
envMap.Insert(L"WSLENV", L"WT_PROFILE_ID");
// Update the path to be relative to whatever our CWD is.
//
// Refer to the examples in
// https://en.cppreference.com/w/cpp/filesystem/path/append
//
// We need to do this here, to ensure we tell the ConptyConnection
// the correct starting path. If we're being invoked from another
// terminal instance (e.g. wt -w 0 -d .), then we have switched our
// CWD to the provided path. We should treat the StartingDirectory
// as relative to the current CWD.
//
// The connection must be informed of the current CWD on
// construction, because the connection might not spawn the child
// process until later, on another thread, after we've already
// restored the CWD to it's original value.
auto newWorkingDirectory{ settings.StartingDirectory() };
if (newWorkingDirectory.size() == 0 || newWorkingDirectory.size() == 1 &&
!(newWorkingDirectory[0] == L'~' || newWorkingDirectory[0] == L'/'))
{ // We only want to resolve the new WD against the CWD if it doesn't look like a Linux path (see GH#592)
auto cwdString{ wil::GetCurrentDirectoryW<std::wstring>() };
std::filesystem::path cwd{ cwdString };
cwd /= settings.StartingDirectory().c_str();
newWorkingDirectory = winrt::hstring{ cwd.wstring() };
}
className = winrt::name_of<TerminalConnection::ConptyConnection>();
connectionSettings = TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(),
newWorkingDirectory,
settings.StartingTitle(),
envMap.GetView(),
::base::saturated_cast<uint32_t>(settings.InitialRows()),
::base::saturated_cast<uint32_t>(settings.InitialCols()),
winrt::guid());
connectionSettings.Insert(L"passthroughMode", Windows::Foundation::PropertyValue::CreateBoolean(settings.VtPassthrough()));
// TODO!
// sessionGuid = conhostConn.Guid();
}
return TerminalConnection::ConnectionInformation(className, connectionSettings);
}
// Method Description:
// - Called when the settings button is clicked. Launches a background
// thread to open the settings file in the default JSON editor.
@@ -1720,11 +1816,13 @@ namespace winrt::TerminalApp::implementation
// - No move will occur if the tabIdx is the same as the current tab, or if
// the specified tab is not a host of terminals (such as the settings tab).
// Arguments:
// - tabIdx: The target tab index.
// - TODO!
// Return Value:
// - true if the pane was successfully moved to the new tab.
bool TerminalPage::_MovePane(const uint32_t tabIdx)
bool TerminalPage::_MovePane(MovePaneArgs args)
{
const auto tabIdx{ args.TabIndex() };
auto focusedTab{ _GetFocusedTabImpl() };
if (!focusedTab)
@@ -1762,6 +1860,74 @@ namespace winrt::TerminalApp::implementation
return true;
}
bool TerminalPage::_MoveTab(MoveTabArgs args)
{
const auto windowId{ args.Window() };
if (!windowId.empty())
{
if (const auto terminalTab{ _GetFocusedTabImpl() })
{
auto startupActions = terminalTab->BuildStartupActions(true);
// Collect all the content we're about to detach.
if (const auto rootPane = terminalTab->GetRootPane())
{
rootPane->WalkTree([&](auto p) {
if (const auto& control{ p->GetTerminalControl() })
{
if (auto content{ control.ContentProc() })
{
content.Attached({ get_weak(), &TerminalPage::_finalizeDetach });
_recentlyDetachedContent.insert({ content.Guid(), content });
}
}
});
}
auto winRtActions{ winrt::single_threaded_vector<ActionAndArgs>(std::move(startupActions)) };
auto str = ActionAndArgs::Serialize(winRtActions);
auto request = winrt::make_self<RequestMoveContentArgs>(args.Window(),
str,
0);
_RemoveTab(*terminalTab);
_RequestMoveContentHandlers(*this, *request); // This will return on another thread.
return true;
}
}
auto direction = args.Direction();
if (direction != MoveTabDirection::None)
{
if (auto focusedTabIndex = _GetFocusedTabIndex())
{
auto currentTabIndex = focusedTabIndex.value();
auto delta = direction == MoveTabDirection::Forward ? 1 : -1;
_TryMoveTab(currentTabIndex, currentTabIndex + delta);
}
}
return true;
}
winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content,
uint32_t /*tabIndex*/)
{
auto args = ActionAndArgs::Deserialize(content);
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); // TODO! may need to go to the top of _createNewTabFromContent
for (const auto& action : args)
{
_actionDispatch->DoAction(action);
}
}
void TerminalPage::_finalizeDetach(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable e)
{
if (const auto& content{ sender.try_as<winrt::Microsoft::Terminal::Control::ContentProcess>() })
{
_recentlyDetachedContent.erase(content.Guid());
}
}
// Method Description:
// - Split the focused pane either horizontally or vertically, and place the
// given pane accordingly in the tree
@@ -1770,11 +1936,11 @@ namespace winrt::TerminalApp::implementation
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
// new pane should be split from its parent.
// - splitSize: the size of the split
void TerminalPage::_SplitPane(const SplitDirection splitDirection,
const float splitSize,
std::shared_ptr<Pane> newPane)
void TerminalPage::_SplitPaneActiveTab(const SplitDirection splitDirection,
const float splitSize,
std::shared_ptr<Pane> newPane)
{
const auto focusedTab{ _GetFocusedTabImpl() };
auto focusedTab{ _GetFocusedTabImpl() };
// Clever hack for a crash in startup, with multiple sub-commands. Say
// you have the following commandline:
@@ -1805,7 +1971,7 @@ namespace winrt::TerminalApp::implementation
}
else
{
_SplitPane(*focusedTab, splitDirection, splitSize, newPane);
_SplitPaneOnTab(focusedTab, splitDirection, splitSize, newPane);
}
}
@@ -1818,10 +1984,10 @@ namespace winrt::TerminalApp::implementation
// - splitDirection: one value from the TerminalApp::SplitDirection enum, indicating how the
// new pane should be split from its parent.
// - splitSize: the size of the split
void TerminalPage::_SplitPane(TerminalTab& tab,
const SplitDirection splitDirection,
const float splitSize,
std::shared_ptr<Pane> newPane)
void TerminalPage::_SplitPaneOnTab(winrt::com_ptr<TerminalTab>& tab,
const SplitDirection splitDirection,
const float splitSize,
std::shared_ptr<Pane> newPane)
{
// If the caller is calling us with the return value of _MakePane
// directly, it's possible that nullptr was returned, if the connections
@@ -1829,20 +1995,21 @@ namespace winrt::TerminalApp::implementation
// nothing here. We don't have a pane with which to create the split.
if (!newPane)
{
// TODO! Is there an equivalent of this for OOP?
return;
}
const auto contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
const auto contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
const auto realSplitType = tab.PreCalculateCanSplit(splitDirection, splitSize, availableSpace);
const auto realSplitType = tab->PreCalculateCanSplit(splitDirection, splitSize, availableSpace);
if (!realSplitType)
{
return;
}
_UnZoomIfNeeded();
tab.SplitPane(*realSplitType, splitSize, newPane);
tab->SplitPane(*realSplitType, splitSize, newPane);
// After GH#6586, the control will no longer focus itself
// automatically when it's finished being laid out. Manually focus
@@ -1856,6 +2023,85 @@ namespace winrt::TerminalApp::implementation
}
}
winrt::fire_and_forget TerminalPage::_asyncSplitPaneActiveTab(const SplitDirection splitDirection,
const float splitSize,
PreparedContent preppedContent)
{
// _GetFocusedTabImpl requires us to be on the UI thread
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal);
auto focusedTab{ _GetFocusedTabImpl() };
// Clever hack for a crash in startup, with multiple sub-commands. Say
// you have the following commandline:
//
// wtd nt -p "elevated cmd" ; sp -p "elevated cmd" ; sp -p "Command Prompt"
//
// Where "elevated cmd" is an elevated profile.
//
// In that scenario, we won't dump off the commandline immediately to an
// elevated window, because it's got the final unelevated split in it.
// However, when we get to that command, there won't be a tab yet. So
// we'd crash right about here.
//
// Instead, let's just promote this first split to be a tab instead.
// Crash avoided, and we don't need to worry about inserting a new-tab
// command in at the start.
if (!focusedTab)
{
if (_tabs.Size() == 0)
{
_createNewTabFromContent(preppedContent);
}
else
{
// The focused tab isn't a terminal tab
co_return;
}
}
else
{
_asyncSplitPaneOnTab(focusedTab, splitDirection, splitSize, preppedContent);
}
}
winrt::fire_and_forget TerminalPage::_asyncSplitPaneOnTab(winrt::com_ptr<TerminalTab> tab,
const SplitDirection splitDirection,
const float splitSize,
PreparedContent preppedContent)
{
// calculate split type
const auto contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
const auto contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
const auto realSplitType = tab->PreCalculateCanSplit(splitDirection, splitSize, availableSpace);
if (!realSplitType)
{
co_return;
}
// unzoom
_UnZoomIfNeeded();
co_await winrt::resume_background();
auto content = co_await preppedContent.initContentProc;
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High);
auto pane = _makePaneFromContent(content, preppedContent.controlSettings, preppedContent.profile);
tab->SplitPane(*realSplitType, splitSize, pane);
// Manually focus the new pane, if we've already initialized
if (_startupState == StartupState::Initialized)
{
if (const auto control = _GetActiveControl())
{
control.Focus(FocusState::Programmatic);
}
}
}
// Method Description:
// - Switches the split orientation of the currently focused pane.
// Arguments:
@@ -2418,6 +2664,131 @@ namespace winrt::TerminalApp::implementation
}
}
static wil::unique_process_information _createHostClassProcess(const winrt::guid& g)
{
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(g) };
// Create an event that the content process will use to signal it is
// ready to go. We won't need the event after this function, so the
// unique_event will clean up our handle when we leave this scope. The
// ContentProcess is responsible for cleaning up its own handle.
wil::unique_event ev{ CreateEvent(nullptr, true, false, nullptr /*L"contentProcessStarted"*/) };
// Make sure to mark this handle as inheritable! Even with
// bInheritHandles=true, this is only inherited when it's explicitly
// allowed to be.
SetHandleInformation(ev.get(), HANDLE_FLAG_INHERIT, 1);
// god bless, fmt::format will format a HANDLE like `0xa80`
std::wstring commandline{
fmt::format(L"WindowsTerminal.exe --content {} --signal {}", guidStr, ev.get())
};
STARTUPINFO siOne{ 0 };
siOne.cb = sizeof(STARTUPINFOW);
wil::unique_process_information piOne;
auto succeeded = CreateProcessW(
nullptr,
commandline.data(),
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
true, // bInheritHandles
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // startingDirectory
&siOne, // lpStartupInfo
&piOne // lpProcessInformation
);
THROW_IF_WIN32_BOOL_FALSE(succeeded);
// Wait for the child process to signal that they're ready.
WaitForSingleObject(ev.get(), INFINITE);
return std::move(piOne);
}
PreparedContent TerminalPage::_prepareContentProc(const NewTerminalArgs& newTerminalArgs,
const bool duplicate)
{
PreparedContent preppedContent;
_evaluateSettings(newTerminalArgs, duplicate, preppedContent.controlSettings, preppedContent.profile);
// If the NewTerminalArgs had a content guid in there, then we don't
// have to start a new one. _AttachToContentProcess will give us an
// "Async" callback that will actually just immediately connect to the
// given content proc
preppedContent.initContentProc = (newTerminalArgs && newTerminalArgs.ContentGuid() != winrt::guid{}) ?
_AttachToContentProcess(newTerminalArgs.ContentGuid()) :
_CreateNewContentProcess(preppedContent.profile, preppedContent.controlSettings);
return preppedContent;
}
Windows::Foundation::IAsyncOperation<ContentProcess> TerminalPage::_CreateNewContentProcess(Profile profile,
TerminalSettingsCreateResult settings)
{
co_await winrt::resume_background();
winrt::guid contentGuid{ ::Microsoft::Console::Utils::CreateGuid() };
// Spawn a wt.exe, with the guid on the commandline
auto piContentProcess{ _createHostClassProcess(contentGuid) };
// DebugBreak();
// THIS MUST TAKE PLACE AFTER _createHostClassProcess.
// * If we're creating a new OOP control, _createHostClassProcess will
// spawn the process that will actually host the ContentProcess
// object.
// * If we're attaching, then that process already exists.
ContentProcess content{ nullptr };
try
{
content = create_instance<ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
}
catch (winrt::hresult_error hr)
{
co_return nullptr;
}
if (content == nullptr)
{
co_return nullptr;
}
TerminalConnection::ConnectionInformation connectInfo{ _CreateConnectionInfoFromSettings(profile, settings.DefaultSettings()) };
// Init the content proc with the focused/unfocused pair
if (!content.Initialize(settings.DefaultSettings(), settings.UnfocusedSettings(), connectInfo))
{
co_return nullptr;
}
co_return content;
}
Windows::Foundation::IAsyncOperation<ContentProcess> TerminalPage::_AttachToContentProcess(const winrt::guid contentGuid)
{
ContentProcess content{ nullptr };
try
{
content = create_instance<ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
}
catch (winrt::hresult_error hr)
{
}
co_return content;
}
// INVARIANT: Must be called on UI thread!
std::shared_ptr<Pane> TerminalPage::_makePaneFromContent(ContentProcess content,
TerminalSettingsCreateResult controlSettings,
Profile profile)
{
// Create the XAML control that will be attached to the content process.
// We're not passing in a connection, because the contentGuid will be used instead
const auto control = _InitControl(controlSettings, content.Guid());
_RegisterTerminalEvents(control);
return std::make_shared<Pane>(profile, control);
}
TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const ITerminalConnection& connection)
{
// Do any initialization that needs to apply to _every_ TermControl we
@@ -2438,6 +2809,26 @@ namespace winrt::TerminalApp::implementation
return term;
}
TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const winrt::guid& contentGuid)
{
// Do any initialization that needs to apply to _every_ TermControl we
// create here.
// TermControl will copy the settings out of the settings passed to it.
TermControl term{ contentGuid, settings.DefaultSettings(), settings.UnfocusedSettings(), nullptr };
// GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now.
if (_visible)
{
term.WindowVisibilityChanged(_visible);
}
if (_hostingHwnd.has_value())
{
term.OwningHwnd(reinterpret_cast<uint64_t>(*_hostingHwnd));
}
return term;
}
// Method Description:
// - Creates a pane and returns a shared_ptr to it
// - The caller should handle where the pane goes after creation,
@@ -2460,32 +2851,7 @@ namespace winrt::TerminalApp::implementation
{
TerminalSettingsCreateResult controlSettings{ nullptr };
Profile profile{ nullptr };
if (duplicate)
{
const auto focusedTab{ _GetFocusedTabImpl() };
if (focusedTab)
{
profile = focusedTab->GetFocusedProfile();
if (profile)
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
const auto workingDirectory = focusedTab->GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
}
}
}
}
if (!profile)
{
profile = _settings.GetProfileForArgs(newTerminalArgs);
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
}
_evaluateSettings(newTerminalArgs, duplicate, controlSettings, profile);
// Try to handle auto-elevation
if (_maybeElevate(newTerminalArgs, controlSettings, profile))
@@ -2540,6 +2906,38 @@ namespace winrt::TerminalApp::implementation
return resultPane;
}
void TerminalPage::_evaluateSettings(const NewTerminalArgs& newTerminalArgs,
const bool duplicate,
TerminalSettingsCreateResult& controlSettings,
Profile& profile)
{
if (duplicate)
{
const auto focusedTab{ _GetFocusedTabImpl() };
if (focusedTab)
{
profile = focusedTab->GetFocusedProfile();
if (profile)
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
const auto workingDirectory = focusedTab->GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
}
}
}
}
if (!profile)
{
profile = _settings.GetProfileForArgs(newTerminalArgs);
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
}
}
// Method Description:
// - Sets background image and applies its settings (stretch, opacity and alignment)
// - Checks path validity
@@ -4144,4 +4542,73 @@ namespace winrt::TerminalApp::implementation
_activated = activated;
_updateThemeColors();
}
void TerminalPage::_onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender,
winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e)
{
if (const auto terminalTab{ _GetFocusedTabImpl() })
{
auto startupActions = terminalTab->BuildStartupActions(true);
auto winRtActions{ winrt::single_threaded_vector<ActionAndArgs>(std::move(startupActions)) };
auto str = ActionAndArgs::Serialize(winRtActions);
e.Data().Properties().Insert(L"content", winrt::box_value(str));
e.Data().RequestedOperation(DataPackageOperation::Move);
e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak()](auto&&, auto&&) -> winrt::fire_and_forget {
auto tab = weakTab.get();
auto page = weakThis.get();
if (tab && page)
{
// TODO! this loop might be able to go outside the
// OperationCompleted. If we put if before the
// OperationCompleted, then we make sure we've got an extra
// reference to the content, if the operation _is_ completed
// before we hook up the Attached handlers. However,idk what
// happens then if the operation never happens.
//
// Collect all the content we're about to detach.
if (const auto rootPane = tab->GetRootPane())
{
rootPane->WalkTree([&](auto p) {
if (const auto& control{ p->GetTerminalControl() })
{
if (auto content{ control.ContentProc() })
{
content.Attached({ page->get_weak(), &TerminalPage::_finalizeDetach });
page->_recentlyDetachedContent.insert({ content.Guid(), content });
}
}
});
}
co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal);
page->_RemoveTab(*tab);
}
});
}
}
void TerminalPage::_onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::UI::Xaml::DragEventArgs e)
{
if (e.DataView().Properties().HasKey(L"content"))
{
e.AcceptedOperation(DataPackageOperation::Move);
}
}
winrt::fire_and_forget TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::UI::Xaml::DragEventArgs e)
{
auto contentObj{ e.DataView().Properties().TryLookup(L"content") };
if (contentObj == nullptr)
{
co_return;
}
auto contentString{ winrt::unbox_value<winrt::hstring>(contentObj) };
if (!contentString.empty())
{
AttachContent(contentString, 0);
}
}
}

View File

@@ -8,6 +8,7 @@
#include "AppKeyBindings.h"
#include "AppCommandlineArgs.h"
#include "RenameWindowRequestedArgs.g.h"
#include "RequestMoveContentArgs.g.h"
#include "Toast.h"
#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
@@ -50,6 +51,26 @@ namespace winrt::TerminalApp::implementation
_ProposedName{ name } {};
};
struct RequestMoveContentArgs : RequestMoveContentArgsT<RequestMoveContentArgs>
{
WINRT_PROPERTY(winrt::hstring, Window);
WINRT_PROPERTY(winrt::hstring, Content);
WINRT_PROPERTY(uint32_t, TabIndex);
public:
RequestMoveContentArgs(const winrt::hstring window, const winrt::hstring content, uint32_t tabIndex) :
_Window{ window },
_Content{ content },
_TabIndex{ tabIndex } {};
};
struct PreparedContent
{
Windows::Foundation::IAsyncOperation<winrt::Microsoft::Terminal::Control::ContentProcess> initContentProc{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult controlSettings{ nullptr };
winrt::Microsoft::Terminal::Settings::Model::Profile profile{ nullptr };
};
struct TerminalPage : TerminalPageT<TerminalPage>
{
public:
@@ -131,11 +152,14 @@ namespace winrt::TerminalApp::implementation
winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept;
bool IsQuakeWindow() const noexcept;
bool IsElevated() const noexcept;
void OpenSettingsUI();
void WindowActivated(const bool activated);
winrt::fire_and_forget AttachContent(winrt::hstring content, uint32_t tabIndex);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// -------------------------------- WinRT Events ---------------------------------
@@ -153,11 +177,14 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
TYPED_EVENT(CloseRequested, IInspectable, IInspectable);
TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable);
TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs)
TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs);
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr);
private:
@@ -227,6 +254,8 @@ namespace winrt::TerminalApp::implementation
int _renamerLayoutCount{ 0 };
bool _renamerPressedEnter{ false };
std::unordered_map<winrt::guid, winrt::Microsoft::Terminal::Control::ContentProcess> _recentlyDetachedContent{};
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
void _ShowAboutDialog();
@@ -238,13 +267,14 @@ namespace winrt::TerminalApp::implementation
void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs);
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile,
Microsoft::Terminal::Settings::Model::TerminalSettings settings);
winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
void _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
winrt::fire_and_forget _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
bool _displayingCloseDialog{ false };
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
@@ -272,7 +302,7 @@ namespace winrt::TerminalApp::implementation
void _DuplicateFocusedTab();
void _DuplicateTab(const TerminalTab& tab);
void _SplitTab(TerminalTab& tab);
void _SplitTab(winrt::com_ptr<TerminalTab>& tab);
winrt::fire_and_forget _ExportTab(const TerminalTab& tab, winrt::hstring filepath);
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
@@ -293,7 +323,8 @@ namespace winrt::TerminalApp::implementation
bool _SelectTab(uint32_t tabIndex);
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _MovePane(const uint32_t tabIdx);
bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args);
bool _MoveTab(const Microsoft::Terminal::Settings::Model::MoveTabArgs args);
template<typename F>
bool _ApplyToActiveControls(F f)
@@ -329,13 +360,13 @@ namespace winrt::TerminalApp::implementation
void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference<uint32_t>& rowsToScroll);
void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane);
void _SplitPane(TerminalTab& tab,
const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane);
void _SplitPaneActiveTab(const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane);
void _SplitPaneOnTab(winrt::com_ptr<TerminalTab>& tab,
const Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
std::shared_ptr<Pane> newPane);
void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
void _ToggleSplitOrientation();
@@ -450,6 +481,44 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
PreparedContent _prepareContentProc(const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs,
const bool duplicate);
Windows::Foundation::IAsyncOperation<winrt::Microsoft::Terminal::Control::ContentProcess> _CreateNewContentProcess(winrt::Microsoft::Terminal::Settings::Model::Profile profile,
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult settings);
Windows::Foundation::IAsyncOperation<winrt::Microsoft::Terminal::Control::ContentProcess> _AttachToContentProcess(const winrt::guid contentGuid);
winrt::fire_and_forget _createNewTabFromContent(PreparedContent preppedContent,
std::function<void(const winrt::com_ptr<TerminalTab>&)> postInitTab = nullptr);
winrt::fire_and_forget _asyncSplitPaneActiveTab(const Microsoft::Terminal::Settings::Model::SplitDirection splitDirection,
const float splitSize,
PreparedContent preppedContent);
winrt::fire_and_forget _asyncSplitPaneOnTab(winrt::com_ptr<TerminalTab> tab,
const Microsoft::Terminal::Settings::Model::SplitDirection splitDirection,
const float splitSize,
PreparedContent preppedContent);
std::shared_ptr<Pane> _makePaneFromContent(winrt::Microsoft::Terminal::Control::ContentProcess initContentProc,
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult controlSettings,
winrt::Microsoft::Terminal::Settings::Model::Profile profile);
winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
const winrt::guid& contentGuid);
void _evaluateSettings(const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs,
const bool duplicate,
winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& controlSettings,
winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
winrt::Microsoft::Terminal::TerminalConnection::ConnectionInformation _CreateConnectionInfoFromSettings(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
const winrt::Microsoft::Terminal::Settings::Model::TerminalSettings& settings);
void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender,
winrt::Windows::Foundation::IInspectable e);
void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e);
void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e);
winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e);
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);

View File

@@ -10,6 +10,12 @@ namespace TerminalApp
{
String ProposedName { get; };
};
[default_interface] runtimeclass RequestMoveContentArgs
{
String Window { get; };
String Content { get; };
UInt32 TabIndex { get; };
};
interface IDialogPresenter
{
@@ -44,6 +50,7 @@ namespace TerminalApp
String KeyboardServiceDisabledText { get; };
TaskbarState TaskbarState{ get; };
void AttachContent(String content, UInt32 tabIndex);
Windows.UI.Xaml.Media.Brush TitlebarBrush { get; };
void WindowActivated(Boolean activated);
@@ -60,8 +67,12 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, RequestMoveContentArgs> RequestMoveContent;
}
}

View File

@@ -27,6 +27,17 @@ namespace winrt::TerminalApp::implementation
{
TerminalTab::TerminalTab(std::shared_ptr<Pane> rootPane)
{
if (rootPane != nullptr)
{
AttachRootPane(rootPane);
}
_Setup();
}
void TerminalTab::AttachRootPane(std::shared_ptr<Pane> rootPane)
{
assert(_rootPane == nullptr);
_rootPane = rootPane;
_activePane = nullptr;
@@ -60,7 +71,13 @@ namespace winrt::TerminalApp::implementation
_mruPanes.insert(_mruPanes.begin(), id.value());
}
_Setup();
_rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
_ClosedHandlers(nullptr, nullptr);
});
Content(_rootPane->GetRootElement());
Initialize();
}
// Method Description:
@@ -71,12 +88,6 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::_Setup()
{
_rootClosedToken = _rootPane->Closed([=](auto&& /*s*/, auto&& /*e*/) {
_ClosedHandlers(nullptr, nullptr);
});
Content(_rootPane->GetRootElement());
_MakeTabViewItem();
_CreateContextMenu();
@@ -379,6 +390,10 @@ namespace winrt::TerminalApp::implementation
{
return _runtimeTabText;
}
if (!_activePane)
{
return L"";
}
if (!_activePane->_IsLeaf())
{
return RS_(L"MultiplePanes");
@@ -437,16 +452,16 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - A vector of commands
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions() const
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions(const bool asContent) const
{
// Give initial ids (0 for the child created with this tab,
// 1 for the child after the first split.
auto state = _rootPane->BuildStartupActions(0, 1);
auto state = _rootPane->BuildStartupActions(0, 1, asContent);
{
ActionAndArgs newTabAction{};
newTabAction.Action(ShortcutAction::NewTab);
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() };
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) };
newTabAction.Args(newTabArgs);
state.args.emplace(state.args.begin(), std::move(newTabAction));
@@ -742,6 +757,8 @@ namespace winrt::TerminalApp::implementation
bool TerminalTab::FocusPane(const uint32_t id)
{
if (_rootPane == nullptr)
return false;
_changingActivePane = true;
const auto res = _rootPane->FocusPane(id);
_changingActivePane = false;
@@ -864,7 +881,7 @@ namespace winrt::TerminalApp::implementation
}
});
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget {
co_await wil::resume_foreground(dispatcher);
// Check if Tab's lifetime has expired
if (auto tab{ weakThis.get() })
@@ -1069,7 +1086,7 @@ namespace winrt::TerminalApp::implementation
// Add a Closed event handler to the Pane. If the pane closes out from
// underneath us, and it's zoomed, we want to be able to make sure to
// update our state accordingly to un-zoom that pane. See GH#7252.
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) -> winrt::fire_and_forget {
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget {
if (auto tab{ weakThis.get() })
{
if (tab->_zoomedPane)

View File

@@ -36,6 +36,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> DetachRoot();
std::shared_ptr<Pane> DetachPane();
void AttachPane(std::shared_ptr<Pane> pane);
void AttachRootPane(std::shared_ptr<Pane> rootPane);
void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
@@ -82,7 +83,7 @@ namespace winrt::TerminalApp::implementation
void EnterZoom();
void ExitZoom();
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const override;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
int GetLeafPaneCount() const noexcept;

View File

@@ -36,21 +36,59 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
auto raw = reinterpret_cast<::IInspectable**>(pointer);
#pragma warning(pop)
// RoActivateInstance() will try to create an instance of the object,
// who's fully qualified name is the string in Name().
TerminalConnection::ITerminalConnection connection{ nullptr };
// A couple short-circuits, for connections that _we_ implement.
// Sometimes, RoActivateInstance is weird and fails with errors like the
// following
//
// The class has to be activatable. For the Terminal, this is easy
// enough - we're not hosting anything that's not already in our
// manifest, or living as a .dll & .winmd SxS.
//
// When we get to extensions (GH#4000), we may want to revisit.
if (LOG_IF_FAILED(RoActivateInstance(name, raw)))
/*
onecore\com\combase\inc\RegistryKey.hpp(527)\combase.dll!01234:
(caller: 01234) LogHr(2) tid(83a8) 800700A1 The specified
path is invalid.
Msg:[StaticNtOpen failed with
path:\REGISTRY\A\{A41685A4-AD85-4C4C-BA5D-A849ADBF3C40}\ActivatableClassId
\REGISTRY\MACHINE\Software\Classes\ActivatableClasses]
...\src\cascadia\TerminalConnection\ConnectionInformation.cpp(47)\TerminalConnection.dll!01234:
(caller: 01234) LogHr(1) tid(83a8) 800700A1 The specified
path is invalid.
[...TerminalConnection::implementation::ConnectionInformation::CreateConnection(RoActivateInstance(name,
raw))]
*/
//
// So to avoid those, we'll manually instantiate these
if (info.ClassName() == winrt::name_of<TerminalConnection::ConptyConnection>())
{
return nullptr;
connection = TerminalConnection::ConptyConnection();
}
else if (info.ClassName() == winrt::name_of<TerminalConnection::AzureConnection>())
{
connection = TerminalConnection::AzureConnection();
}
else if (info.ClassName() == winrt::name_of<TerminalConnection::EchoConnection>())
{
connection = TerminalConnection::EchoConnection();
}
else
{
// RoActivateInstance() will try to create an instance of the object,
// who's fully qualified name is the string in Name().
//
// The class has to be activatable. For the Terminal, this is easy
// enough - we're not hosting anything that's not already in our
// manifest, or living as a .dll & .winmd SxS.
//
// When we get to extensions (GH#4000), we may want to revisit.
if (LOG_IF_FAILED(RoActivateInstance(name, raw)))
{
return nullptr;
}
connection = inspectable.try_as<TerminalConnection::ITerminalConnection>();
}
// Now that thing we made, make sure it's actually a ITerminalConnection
if (const auto connection{ inspectable.try_as<TerminalConnection::ITerminalConnection>() })
if (connection)
{
// Initialize it, and return it.
connection.Initialize(info.Settings());

View File

@@ -0,0 +1,147 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ContentProcess.h"
#include "ContentProcess.g.cpp"
#include "ControlCore.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
ContentProcess::ContentProcess(winrt::guid g) :
_ourPID{ GetCurrentProcessId() },
_guid{ g } {}
bool ContentProcess::Initialize(Control::IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ConnectionInformation connectionInfo)
{
auto conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectionInfo) };
if (conn == nullptr)
{
return false;
}
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, conn);
return true;
}
ContentProcess::~ContentProcess()
{
}
// See https://docs.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/details-about-destructors#deferred-destruction
winrt::fire_and_forget ContentProcess::final_release(std::unique_ptr<ContentProcess> ptr) noexcept
{
winrt::com_ptr<ControlCore> coreImpl;
coreImpl.copy_from(winrt::get_self<ControlCore>(ptr->_interactivity.Core()));
if (coreImpl)
{
// Close() requires that it is called on the "main" thread. So we
// need to switch over to the DIspatcher thread, before calling
// Close.
co_await wil::resume_foreground(coreImpl->Dispatcher(), winrt::Windows::System::DispatcherQueuePriority::Normal);
// Typically, Close() runs async, closing the connection on a BG
// thread, so that the UI doesn't hang while waiting for the client
// process to exit. When we're running as a content process, that's
// not relevant. In that case, we need to close the connection NOW,
// because we're about to exit the whole process. If we close the
// process asynchronously (on a bg thread), then we might
// accidentally leak it as we exit() before the thread gets a time
// slice.
coreImpl->Close(false);
}
// DANGER - We're straight up going to EXIT THE ENTIRE PROCESS when we
// get destructed. This eliminates the need to do any sort of
// ref-counting weirdness. This entire process exists to host one
// singular ContentProcess instance. When we're destructed, it's because
// every other window process was done with us. We can die now, knowing
// that our job is complete.
std::exit(0);
}
Control::ControlInteractivity ContentProcess::GetInteractivity()
{
return _interactivity;
}
uint64_t ContentProcess::GetPID()
{
return _ourPID;
}
// Method Description:
// - Duplicate the swap chain handle to the provided process.
// - If the provided PID is our pid, then great - we don't need to do anything.
// Arguments:
// - callersPid: the PID of the process calling this method.
// Return Value:
// - The value of the swapchain handle in the callers process
// Notes:
// - This is BODGY! We're basically asking to marshal a HANDLE here. WinRT
// has no good mechanism for doing this, so we're doing it by casting the
// value to a uint64_t. In all reality, we _should_ be using a COM
// interface for this, because it can set up the security on these handles
// more appropriately. Fortunately, all we're dealing with is swapchains,
// so the security doesn't matter all that much.
uint64_t ContentProcess::RequestSwapChainHandle(const uint64_t callersPid)
{
auto ourPid = GetCurrentProcessId();
HANDLE ourHandle = reinterpret_cast<HANDLE>(_interactivity.Core().SwapChainHandle());
if (callersPid == ourPid)
{
return reinterpret_cast<uint64_t>(ourHandle);
}
wil::unique_handle hWindowProcess{ OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
static_cast<DWORD>(callersPid)) };
if (hWindowProcess.get() == nullptr)
{
TraceLoggingWrite(g_hTerminalControlProvider,
"ContentProcess::RequestSwapChainHandle_OpenOtherProcessFailed",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
LOG_LAST_ERROR();
return 0;
}
HANDLE theirHandle{ nullptr };
BOOL success = DuplicateHandle(GetCurrentProcess(),
ourHandle,
hWindowProcess.get(),
&theirHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (!success)
{
TraceLoggingWrite(g_hTerminalControlProvider,
"ContentProcess::RequestSwapChainHandle_DuplicateHandleFailed",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
LOG_LAST_ERROR();
return 0;
}
// At this point, the handle is now in their process space, with value
// theirHandle
return reinterpret_cast<uint64_t>(theirHandle);
}
winrt::guid ContentProcess::Guid()
{
return _guid;
}
void ContentProcess::Attach()
{
// TODO! This feels like a hack and I'm sure cppwinrt gives us some sort
// of hook for when we get an incremented refcount
_AttachedHandlers(*this, nullptr);
}
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "ContentProcess.g.h"
#include "ControlInteractivity.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct ContentProcess : ContentProcessT<ContentProcess>
{
ContentProcess(winrt::guid g);
~ContentProcess();
static winrt::fire_and_forget final_release(std::unique_ptr<ContentProcess> ptr) noexcept;
bool Initialize(Control::IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ConnectionInformation connectionInfo);
Control::ControlInteractivity GetInteractivity();
uint64_t GetPID();
uint64_t RequestSwapChainHandle(const uint64_t pid);
winrt::guid Guid();
void Attach();
TYPED_EVENT(Attached, IInspectable, IInspectable);
private:
Control::ControlInteractivity _interactivity{ nullptr };
uint64_t _ourPID;
winrt::guid _guid;
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(ContentProcess);
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ControlInteractivity.idl";
namespace Microsoft.Terminal.Control
{
runtimeclass ContentProcess {
ContentProcess(Guid g);
Boolean Initialize(IControlSettings settings,
IControlAppearance unfocusedAppearance,
Microsoft.Terminal.TerminalConnection.ConnectionInformation connectionInfo);
ControlInteractivity GetInteractivity();
UInt64 GetPID();
UInt64 RequestSwapChainHandle(UInt64 pid);
Guid Guid { get; };
void Attach();
event Windows.Foundation.TypedEventHandler<Object, Object> Attached;
};
}

View File

@@ -218,6 +218,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
UpdateSettings(settings, unfocusedAppearance);
}
winrt::Windows::System::DispatcherQueue ControlCore::Dispatcher()
{
return _dispatcher;
}
ControlCore::~ControlCore()
{
Close();
@@ -1343,6 +1348,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - duration - How long the note should be sustained (in microseconds).
void ControlCore::_terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration)
{
// TODO! GH#1256 This is intentionally here to conflict with https://github.com/microsoft/terminal/pull/13471#pullrequestreview-1039353718
// When we do tearout, we'll need to also recreate the midi thing
// We create the audio instance on demand, and lock it for the duration
// of the note output so it can't be destroyed while in use.
auto& midiAudio = _getMidiAudio();
@@ -1497,7 +1505,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlCore::Close()
void ControlCore::Close(const bool async)
{
if (!_IsClosing())
{
@@ -1507,12 +1515,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_connection.TerminalOutput(_connectionOutputEventToken);
_connectionStateChangedRevoker.revoke();
// GH#1996 - Close the connection asynchronously on a background
// thread.
// Since TermControl::Close is only ever triggered by the UI, we
// don't really care to wait for the connection to be completely
// closed. We can just do it whenever.
_asyncCloseConnection();
if (async)
{
// GH#1996 - Close the connection asynchronously on a background
// thread.
// Since TermControl::Close is only ever triggered by the UI, we
// don't really care to wait for the connection to be completely
// closed. We can just do it whenever.
_asyncCloseConnection();
}
else
{
// see notes in ContentProcess::final_release for why there's
// _also_ a synchronous version of this.
_connection.Close();
}
}
}
@@ -1975,6 +1992,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return Opacity() < 1.0f || UseAcrylic() || !_settings->BackgroundImage().empty() || _settings->UseBackgroundImageForWindow();
}
double ControlCore::DisplayScale() const
{
return _compositionScale;
}
uint64_t ControlCore::OwningHwnd()
{
return _owningHwnd;
@@ -1984,6 +2006,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (owner != _owningHwnd && _connection)
{
// TODO GH#1256 change the midi HWND too
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ReparentWindow(owner);

View File

@@ -65,6 +65,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SizeChanged(const double width, const double height);
void ScaleChanged(const double scale);
double DisplayScale() const;
uint64_t SwapChainHandle() const;
void AdjustFontSize(int fontSizeDelta);
@@ -104,7 +105,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::Console::Types::IUiaData* GetUiaData() const;
void Close();
void Close(const bool async = true);
#pragma region ICoreState
const size_t TaskbarState() const noexcept;
@@ -192,6 +193,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
uint64_t OwningHwnd();
void OwningHwnd(uint64_t owner);
winrt::Windows::System::DispatcherQueue Dispatcher();
RUNTIME_SETTING(double, Opacity, _settings->Opacity());
RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic());
@@ -317,6 +320,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
// This may not be strictly true if the core is running out of proc
// with XAML. This assert was hit frequently during initial x-proc
// development, but seemingly not anymore. Keep an eye on it.
if (_dispatcher)
{
// _closing isn't atomic and may only be accessed from the main thread.

View File

@@ -72,6 +72,7 @@ namespace Microsoft.Terminal.Control
Boolean HasUnfocusedAppearance();
UInt64 SwapChainHandle { get; };
Double DisplayScale { get; };
Windows.Foundation.Size FontSize { get; };
String FontFaceName { get; };

View File

@@ -43,7 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_controlPadding = til::rect{ til::math::rounding, padding };
}
void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider)
void InteractivityAutomationPeer::ParentProvider(const Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple& parentProvider)
{
_parentProvider = parentProvider;
}
@@ -171,7 +171,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double InteractivityAutomationPeer::GetScaleFactor() const noexcept
{
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
// If we were being used in a process that had a CoreWindow, we could just call
// DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
//
// We won't always be though, so instead, ask our model what the DPI is.
return _interactivity->Core().DisplayScale();
}
void InteractivityAutomationPeer::ChangeViewport(const til::inclusive_rect& NewWindow)
@@ -182,15 +186,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
XamlAutomation::ITextRangeProvider InteractivityAutomationPeer::_CreateXamlUiaTextRange(UIA::ITextRangeProvider* returnVal) const
{
// LOAD-BEARING: use _parentProvider->ProviderFromPeer(_parentProvider) instead of this->ProviderFromPeer(*this).
// Since we split the automation peer into TermControlAutomationPeer and InteractivityAutomationPeer,
// using "this" returns null. This can cause issues with some UIA Client scenarios like any navigation in Narrator.
const auto parent{ _parentProvider.get() };
if (!parent)
if (!_parentProvider)
{
return nullptr;
}
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parent.ProviderFromPeer(parent));
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, _parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
};

View File

@@ -43,7 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SetControlBounds(const Windows::Foundation::Rect bounds);
void SetControlPadding(const Core::Padding padding);
void ParentProvider(Windows::UI::Xaml::Automation::Peers::AutomationPeer parentProvider);
void ParentProvider(const Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple& parentProvider);
#pragma region IUiaEventDispatcher
void SignalSelectionChanged() override;
@@ -81,7 +81,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
weak_ref<Windows::UI::Xaml::Automation::Peers::AutomationPeer> _parentProvider;
winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider{ nullptr };
til::rect _controlBounds{};
til::rect _controlPadding{};

View File

@@ -3,13 +3,11 @@
namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass InteractivityAutomationPeer :
Windows.UI.Xaml.Automation.Peers.AutomationPeer,
Windows.UI.Xaml.Automation.Provider.ITextProvider
{
void SetControlBounds(Windows.Foundation.Rect bounds);
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
void ParentProvider(Windows.UI.Xaml.Automation.Peers.AutomationPeer parentProvider);
void ParentProvider(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple provider);
event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged;

View File

@@ -208,4 +208,10 @@ Please either install the missing font or choose another one.</value>
<value>No results found</value>
<comment>Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal.</comment>
</data>
<data name="TermControl_ContentDiedTextBlock.Text" xml:space="preserve">
<value>The content of this terminal was closed unexpectedly.</value>
</data>
<data name="TermControl_ContentDiedButton.Content" xml:space="preserve">
<value>Close</value>
</data>
</root>

View File

@@ -1,5 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// The functions for handling content processes in the TermControl are largely
// in TermControlContentManagement.cpp
#include "pch.h"
#include "TermControl.h"
@@ -31,8 +34,10 @@ using namespace winrt::Windows::ApplicationModel::DataTransfer;
// The updates are throttled to limit power usage.
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
// The minimum delay between updating the TSF input control.
// This is already throttled primarily in the ControlCore, with a timeout of 100ms. We're adding another smaller one here, as the (potentially x-proc) call will come in off the UI thread
// The minimum delay between updating the TSF input control. This is already
// throttled primarily in the ControlCore, with a timeout of 100ms. We're adding
// another smaller one here, as the (potentially x-proc) call will come in off
// the UI thread
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(8);
// The minimum delay between updating the locations of regex patterns
@@ -50,6 +55,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::TermControl(IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection) :
TermControl(winrt::guid{}, settings, unfocusedAppearance, connection) {}
TermControl::TermControl(winrt::guid contentGuid,
IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection) :
_initializedTerminal{ false },
_closing{ false },
_isInternalScrollBarUpdate{ false },
_autoScrollVelocity{ 0 },
_autoScrollingPointerPoint{ std::nullopt },
@@ -61,32 +74,52 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
InitializeComponent();
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection);
if (contentGuid != winrt::guid{})
{
_contentProc = create_instance<Control::ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
if (_contentProc != nullptr)
{
// TODO! think harder about ordering of Attach here.
_contentProc.Attach();
_interactivity = _contentProc.GetInteractivity();
_contentWaitInterrupt.create();
_createContentWaitThread();
}
}
if (_interactivity == nullptr)
{
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection);
}
_core = _interactivity.Core();
// These events might all be triggered by the connection, but that
// should be drained and closed before we complete destruction. So these
// are safe.
_core.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
_core.WarningBell({ this, &TermControl::_coreWarningBell });
_core.CursorPositionChanged({ this, &TermControl::_CursorPositionChanged });
_core.ScrollPositionChanged({ get_weak(), &TermControl::_ScrollPositionChanged });
_core.WarningBell({ get_weak(), &TermControl::_coreWarningBell });
_core.CursorPositionChanged({ get_weak(), &TermControl::_CursorPositionChanged });
// This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here.
_core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState });
_core.ConnectionStateChanged({ get_weak(), &TermControl::_coreConnectionStateChanged });
// These callbacks can only really be triggered by UI interactions. So
// they don't need weak refs - they can't be triggered unless we're
// alive.
_core.BackgroundColorChanged({ this, &TermControl::_coreBackgroundColorChanged });
_core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged });
_core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged });
_core.RaiseNotice({ this, &TermControl::_coreRaisedNotice });
_core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged });
_core.FoundMatch({ this, &TermControl::_coreFoundMatch });
_core.UpdateSelectionMarkers({ this, &TermControl::_updateSelectionMarkers });
_core.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
_interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
_interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
_core.BackgroundColorChanged({ get_weak(), &TermControl::_coreBackgroundColorChanged });
_core.FontSizeChanged({ get_weak(), &TermControl::_coreFontSizeChanged });
_core.TransparencyChanged({ get_weak(), &TermControl::_coreTransparencyChanged });
_core.RaiseNotice({ get_weak(), &TermControl::_coreRaisedNotice });
_core.HoveredHyperlinkChanged({ get_weak(), &TermControl::_hoveredHyperlinkChanged });
_core.FoundMatch({ get_weak(), &TermControl::_coreFoundMatch });
_core.UpdateSelectionMarkers({ get_weak(), &TermControl::_updateSelectionMarkers });
_core.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler });
_interactivity.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler });
_interactivity.ScrollPositionChanged({ get_weak(), &TermControl::_ScrollPositionChanged });
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
@@ -137,6 +170,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_ApplyUISettings();
}
Control::ContentProcess TermControl::ContentProc() const noexcept
{
return _contentProc;
}
void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
{
// Assumptions:
@@ -671,6 +709,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::~TermControl()
{
if (_contentIsOutOfProc())
{
_contentWaitInterrupt.SetEvent();
_contentWaitThread.join();
}
Close();
}
@@ -717,7 +760,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ConnectionState TermControl::ConnectionState() const
{
return _core.ConnectionState();
try
{
return _core.ConnectionState();
}
CATCH_LOG();
return TerminalConnection::ConnectionState::Failed;
}
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/)
@@ -731,8 +779,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (auto control{ weakThis.get() })
{
const auto chainHandle = reinterpret_cast<HANDLE>(control->_core.SwapChainHandle());
_AttachDxgiSwapChainToXaml(chainHandle);
control->_acquireAndAttachSwapChainHandle();
}
}
@@ -784,6 +831,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation
nativePanel->SetSwapChainHandle(swapChainHandle);
}
void TermControl::_acquireAndAttachSwapChainHandle()
{
const auto chainHandle = reinterpret_cast<HANDLE>(_contentIsOutOfProc() ?
_contentProc.RequestSwapChainHandle(GetCurrentProcessId()) :
_core.SwapChainHandle());
// If we're in-proc, then the render engine has it's own ownership
// handle to the swapchain. We don't need to also own it in the XAML
// layer. It doesn't really make sense for us to duplicate it to
// ourself again, so we're just gonna not.
//
// If we're out of proc though, the content proc has one handle in
// it's process, and now there's a HANDLE in our process with this
// value. Wrap that boy up in a unique_handle so that we can release
// our handle when needed.
if (_contentIsOutOfProc())
{
_contentSwapChain.reset(chainHandle);
}
_AttachDxgiSwapChainToXaml(chainHandle);
}
bool TermControl::_InitializeTerminal()
{
if (_initializedTerminal)
@@ -813,13 +882,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto coreInitialized = _core.Initialize(panelWidth,
panelHeight,
panelScaleX);
if (!coreInitialized)
// ControlCore::Initialize will return false if it was already
// initialized. In that case, don't init the rest of the interactivity,
// but do go on and hook us up to the core's swapchain and callbacks.
if (coreInitialized)
{
return false;
_interactivity.Initialize();
}
_interactivity.Initialize();
_AttachDxgiSwapChainToXaml(reinterpret_cast<HANDLE>(_core.SwapChainHandle()));
_acquireAndAttachSwapChainHandle();
// Tell the DX Engine to notify us when the swap chain changes. We do
// this after we initially set the swapchain so as to avoid unnecessary
@@ -1808,6 +1879,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (!_IsClosing())
{
// TODO:GH#5000 - move all the blinking inside of ControlCore.
// There's no reason that the timer should live in TermControl just
// to hop across the process boundary to toggle the content process.
// That can all live in the control core.
_core.BlinkCursor();
}
}
@@ -1822,6 +1897,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (!_IsClosing())
{
// TODO:GH#5000 - move all the blinking inside of ControlCore.
// There's no reason that the timer should live in TermControl just
// to hop across the process boundary to toggle the content process.
// That can all live in the control core.
_core.BlinkAttributeTick();
}
}
@@ -1964,8 +2043,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Disconnect the TSF input control so it doesn't receive EditContext events.
TSFInputControl().Close();
_autoScrollTimer.Stop();
_core.Close();
// THIS IS IMPORTANT!
//
// If we're out of proc, the control can be closed, but we DON'T
// want that to close our content. We'll want the content proc's
// teardown to be responsible for closing the core.
if (!_contentIsOutOfProc())
{
_core.Close();
}
}
}
@@ -3050,6 +3136,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.OwningHwnd();
}
void TermControl::_coreConnectionStateChanged(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
_ConnectionStateChangedHandlers(*this, nullptr);
}
void TermControl::AddMark(const Control::ScrollMark& mark)
{
_core.AddMark(mark);

View File

@@ -29,10 +29,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection);
TermControl(winrt::guid contentGuid,
IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance);
IControlSettings Settings() const;
winrt::guid ContentGuid() const;
hstring GetProfileName() const;
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
@@ -93,6 +100,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args);
void _RenderRetryButton_Click(const IInspectable& button, const IInspectable& args);
void _ContentDiedCloseButton_Click(const IInspectable& button, const IInspectable& args);
winrt::fire_and_forget _RendererWarning(IInspectable sender,
Control::RendererWarningArgs args);
@@ -130,6 +138,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void AdjustOpacity(const double opacity, const bool relative);
Control::ContentProcess ContentProc() const noexcept;
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// -------------------------------- WinRT Events ---------------------------------
@@ -140,9 +150,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged);
PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged);
PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged);
PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged);
PROJECTED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs, _core, ShowWindowChanged);
TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
@@ -171,6 +182,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::TermControlAutomationPeer _automationPeer{ nullptr };
Control::ControlInteractivity _interactivity{ nullptr };
Control::ControlCore _core{ nullptr };
Control::ContentProcess _contentProc{ nullptr };
winrt::com_ptr<SearchBoxControl> _searchBox;
@@ -209,6 +221,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
bool _showMarksInScrollbar{ false };
wil::unique_event _contentWaitInterrupt;
std::thread _contentWaitThread;
wil::unique_handle _contentSwapChain;
void _createContentWaitThread();
bool _contentIsOutOfProc() const;
void _acquireAndAttachSwapChainHandle();
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
@@ -300,8 +320,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
void _coreFoundMatch(const IInspectable& sender, const Control::FoundResultsArgs& args);
winrt::fire_and_forget _raiseContentDied();
void _coreConnectionStateChanged(const IInspectable& sender, const IInspectable& args);
til::point _toPosInDips(const Core::Point terminalCellPos);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
};

View File

@@ -7,6 +7,7 @@ import "IDirectKeyListener.idl";
import "EventArgs.idl";
import "ICoreState.idl";
import "ControlCore.idl";
import "ContentProcess.idl";
namespace Microsoft.Terminal.Control
{
@@ -17,6 +18,11 @@ namespace Microsoft.Terminal.Control
ICoreState,
Windows.UI.Xaml.Data.INotifyPropertyChanged
{
TermControl(Guid contentGuid,
IControlSettings settings,
IControlAppearance unfocusedAppearance,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
TermControl(IControlSettings settings,
IControlAppearance unfocusedAppearance,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
@@ -26,6 +32,9 @@ namespace Microsoft.Terminal.Control
void UpdateControlSettings(IControlSettings settings);
void UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance);
Guid ContentGuid{ get; };
ContentProcess ContentProc{ get; };
Microsoft.Terminal.Control.IControlSettings Settings { get; };
event FontSizeChangedEventArgs FontSizeChanged;

View File

@@ -1305,6 +1305,27 @@
</Border>
</Grid>
<Grid x:Name="ContentDiedNotice"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="False">
<Border Margin="8,8,8,8"
Padding="8,8,8,8"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
BorderBrush="{ThemeResource SystemAccentColor}"
BorderThickness="2,2,2,2"
CornerRadius="{ThemeResource OverlayCornerRadius}">
<StackPanel>
<TextBlock x:Uid="TermControl_ContentDiedTextBlock"
HorizontalAlignment="Center"
TextWrapping="WrapWholeWords" />
<Button x:Uid="TermControl_ContentDiedButton"
HorizontalAlignment="Right"
Click="_ContentDiedCloseButton_Click" />
</StackPanel>
</Border>
</Grid>
</Grid>
</UserControl>

View File

@@ -4,6 +4,8 @@
#include "pch.h"
#include <UIAutomationCore.h>
#include <LibraryResources.h>
#include <ScopedResourceLoader.h>
#include "TermControlAutomationPeer.h"
#include "TermControl.h"
#include "TermControlAutomationPeer.g.cpp"
@@ -83,7 +85,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); });
_contentAutomationPeer.CursorChanged([this](auto&&, auto&&) { SignalCursorChanged(); });
_contentAutomationPeer.NewOutput([this](auto&&, hstring newOutput) { NotifyNewOutput(newOutput); });
_contentAutomationPeer.ParentProvider(*this);
_contentAutomationPeer.ParentProvider(GetParentProvider());
};
// Method Description:
@@ -117,6 +119,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
XamlAutomation::IRawElementProviderSimple TermControlAutomationPeer::GetParentProvider()
{
const auto parentProvider = this->ProviderFromPeer(*this);
return parentProvider;
}
// Method Description:
// - Signals the ui automation client that the terminal's selection has changed and should be updated
// Arguments:
@@ -261,7 +269,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
{
return RS_(L"TerminalControl_ControlType");
// TerminalControl_ControlType is "terminal" in english. Usually, this
// will return the localized version of that string. For applications
// that use the TermControl that don't package up all the resources in a
// way that they can be accessed, this will fall back to just "terminal"
// (notably, the scratch solution doesn't package these resources)
//
// Stash this in a static variable in the off chance that this is called
// in a hot path by some a11y tool. There's absolutely no chance it's
// gonna change, so this is safe enough.
static auto resMap{ ScopedResourceLoader(L"Microsoft.Terminal.Control/Resources").GetResourceMap() };
return resMap ?
RS_(L"TerminalControl_ControlType") :
L"terminal";
}
Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const

View File

@@ -48,8 +48,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void UpdateControlBounds();
void SetControlPadding(const Core::Padding padding);
void RecordKeyEvent(const WORD vkey);
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple GetParentProvider();
#pragma region FrameworkElementAutomationPeer
hstring GetClassNameCore() const;
Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;

View File

@@ -12,5 +12,6 @@ namespace Microsoft.Terminal.Control
void UpdateControlBounds();
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple GetParentProvider();
}
}

View File

@@ -0,0 +1,160 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// The functions in this class are specific to the handling of out-of-proc
// content processes by the TermControl. Putting them all in one file keeps
// TermControl.cpp a little less cluttered.
#include "pch.h"
#include "TermControl.h"
using namespace ::Microsoft::Console::Types;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::System;
namespace winrt::Microsoft::Terminal::Control::implementation
{
winrt::guid TermControl::ContentGuid() const
{
return _contentIsOutOfProc() ? _contentProc.Guid() : winrt::guid{};
}
bool TermControl::_contentIsOutOfProc() const
{
return _contentProc != nullptr;
}
bool s_waitOnContentProcess(uint64_t contentPid, HANDLE contentWaitInterrupt)
{
// This is the array of HANDLEs that we're going to wait on in
// WaitForMultipleObjects below.
// * waits[0] will be the handle to the content process. It gets
// signalled when the process exits / dies.
// * waits[1] is the handle to our _contentWaitInterrupt event. Another
// thread can use that to manually break this loop. We'll do that when
// we're getting torn down.
HANDLE waits[2];
waits[1] = contentWaitInterrupt;
bool displayError = true;
// At any point in all this, the content process might die. If it does,
// we want to raise an error message, to inform that this control is now
// dead.
try
{
// This might fail to even ask the content for it's PID.
wil::unique_handle hContent{ OpenProcess(SYNCHRONIZE,
FALSE,
static_cast<DWORD>(contentPid)) };
// If we fail to open the content, then they don't exist
// anymore! We'll need to immediately raise the notification that the content has died.
if (hContent.get() == nullptr)
{
const auto gle = GetLastError();
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_FailedToOpenContent",
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
return displayError;
}
waits[0] = hContent.get();
switch (WaitForMultipleObjects(2, waits, FALSE, INFINITE))
{
case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the content process
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ContentDied",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
break;
case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ContentWaitInterrupted",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
displayError = false;
break;
case WAIT_TIMEOUT:
// This should be impossible.
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ContentWaitTimeout",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
break;
default:
{
// Returning any other value is invalid. Just die.
const auto gle = GetLastError();
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_WaitFailed",
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
break;
}
}
}
catch (...)
{
// Theoretically, if window[1] dies when we're trying to get
// its PID we'll get here. We can probably just exit here.
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ExceptionInWaitThread",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
return displayError;
}
void TermControl::_createContentWaitThread()
{
_contentWaitThread = std::thread([weakThis = get_weak(), contentPid = _contentProc.GetPID(), contentWaitInterrupt = _contentWaitInterrupt.get()] {
if (s_waitOnContentProcess(contentPid, contentWaitInterrupt))
{
// When s_waitOnContentProcess returns, if it returned true, we
// should display a dialog in our bounds to indicate that we
// were closed unexpectedly. If we closed in an expected way,
// then s_waitOnContentProcess will return false.
if (auto control{ weakThis.get() })
{
control->_raiseContentDied();
}
}
});
}
winrt::fire_and_forget TermControl::_raiseContentDied()
{
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
if (auto control{ weakThis.get() }; !control->_IsClosing())
{
if (auto loadedUiElement{ FindName(L"ContentDiedNotice") })
{
if (auto uiElement{ loadedUiElement.try_as<::winrt::Windows::UI::Xaml::UIElement>() })
{
uiElement.Visibility(Visibility::Visible);
}
}
}
}
// Method Description:
// - Handler for when the "Content Died" dialog's button is clicked.
void TermControl::_ContentDiedCloseButton_Click(IInspectable const& /*sender*/, IInspectable const& /*args*/)
{
// Alert whoever's hosting us that the connection was closed.
// When they come asking what the new connection state is, we'll reply with Closed
_ConnectionStateChangedHandlers(*this, nullptr);
}
}

View File

@@ -34,6 +34,9 @@
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="ContentProcess.h">
<DependentUpon>ContentProcess.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ControlCore.h">
<DependentUpon>ControlCore.idl</DependentUpon>
</ClInclude>
@@ -64,13 +67,18 @@
<ClInclude Include="TSFInputControl.h">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="XamlUiaTextRange.h" />
<ClInclude Include="XamlUiaTextRange.h" >
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="ContentProcess.cpp">
<DependentUpon>ContentProcess.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ControlCore.cpp">
<DependentUpon>ControlCore.idl</DependentUpon>
</ClCompile>
@@ -93,6 +101,9 @@
<ClCompile Include="TermControl.cpp">
<DependentUpon>TermControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TermControlContentManagement.cpp">
<DependentUpon>TermControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TSFInputControl.cpp">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</ClCompile>
@@ -103,10 +114,13 @@
<ClCompile Include="InteractivityAutomationPeer.cpp">
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="XamlUiaTextRange.cpp" />
<ClCompile Include="XamlUiaTextRange.cpp" >
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
</ClCompile>
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
<Midl Include="ContentProcess.idl" />
<Midl Include="ControlCore.idl" />
<Midl Include="ControlInteractivity.idl" />
<Midl Include="ICoreState.idl" />
@@ -129,6 +143,7 @@
<Midl Include="TSFInputControl.idl">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</Midl>
<Midl Include="XamlUiaTextRange.idl" />
</ItemGroup>
<!-- ========================= XAML Files ======================== -->
<ItemGroup>

View File

@@ -21,13 +21,13 @@ Author(s):
#pragma once
#include "TermControlAutomationPeer.h"
#include "XamlUiaTextRange.g.h"
#include <UIAutomationCore.h>
#include "../types/TermControlUiaTextRange.hpp"
namespace winrt::Microsoft::Terminal::Control::implementation
{
class XamlUiaTextRange :
public winrt::implements<XamlUiaTextRange, Windows::UI::Xaml::Automation::Provider::ITextRangeProvider>
class XamlUiaTextRange : public XamlUiaTextRangeT<XamlUiaTextRange>
{
public:
XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider) :

View File

@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass XamlUiaTextRange :
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider
{
}
}

View File

@@ -47,6 +47,7 @@
#include <winrt/Windows.ui.xaml.markup.h>
#include <winrt/Windows.ui.xaml.shapes.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.UI.Xaml.Shapes.h>

View File

@@ -417,4 +417,30 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
const auto found = GeneratedActionNames.find(_Action);
return found != GeneratedActionNames.end() ? found->second : L"";
}
winrt::hstring ActionAndArgs::Serialize(winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> args)
{
Json::Value json{ Json::objectValue };
JsonUtils::SetValueForKey(json, "actions", args);
Json::StreamWriterBuilder wbuilder;
auto str = Json::writeString(wbuilder, json);
return winrt::to_hstring(str);
}
winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> ActionAndArgs::Deserialize(winrt::hstring content)
{
auto data = winrt::to_string(content);
std::string errs;
std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
Json::Value root;
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
{
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
}
winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> result{ nullptr };
JsonUtils::GetValueForKey(root, "actions", result);
return result;
}
}

View File

@@ -16,6 +16,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
std::vector<SettingsLoadWarnings>& warnings);
static Json::Value ToJson(const Model::ActionAndArgs& val);
static winrt::hstring Serialize(winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> args);
static winrt::Windows::Foundation::Collections::IVector<Model::ActionAndArgs> Deserialize(winrt::hstring content);
ActionAndArgs() = default;
ActionAndArgs(ShortcutAction action);
ActionAndArgs(ShortcutAction action, IActionArgs args) :

View File

@@ -13,6 +13,7 @@
#include "ResizePaneArgs.g.cpp"
#include "MoveFocusArgs.g.cpp"
#include "MovePaneArgs.g.cpp"
#include "MoveTabArgs.g.cpp"
#include "SwapPaneArgs.g.cpp"
#include "AdjustFontSizeArgs.g.cpp"
#include "SendInputArgs.g.cpp"
@@ -28,7 +29,6 @@
#include "CloseOtherTabsArgs.g.cpp"
#include "CloseTabsAfterArgs.g.cpp"
#include "CloseTabArgs.g.cpp"
#include "MoveTabArgs.g.cpp"
#include "ScrollToMarkArgs.g.cpp"
#include "AddMarkArgs.g.cpp"
#include "FindMatchArgs.g.cpp"
@@ -661,6 +661,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
fmt::format(std::wstring_view(RS_(L"MoveTabCommandKey")),
directionString)
};
// TODO!
// return winrt::hstring{
// fmt::format(L"{}, window:{}", RS_(L"MovePaneCommandKey"), Window())
}
winrt::hstring ToggleCommandPaletteArgs::GenerateName() const

View File

@@ -171,8 +171,12 @@ private:
X(Windows::Foundation::IReference<uint32_t>, Index, "index", false, nullptr)
////////////////////////////////////////////////////////////////////////////////
#define MOVE_TAB_ARGS(X) \
X(MoveTabDirection, Direction, "direction", args->Direction() == MoveTabDirection::None, MoveTabDirection::None)
#define MOVE_TAB_ARGS(X) \
X(winrt::hstring, Window, "window", false, L"") \
X(MoveTabDirection, Direction, "direction", (args->Direction() == MoveTabDirection::None) && (args->Window().empty()), MoveTabDirection::None)
// Other ideas:
// X(uint32_t, TabIndex, "index", false, 0) \ // target? source?
////////////////////////////////////////////////////////////////////////////////
#define SCROLL_UP_ARGS(X) \
@@ -269,6 +273,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARG(Windows::Foundation::IReference<bool>, SuppressApplicationTitle, nullptr);
ACTION_ARG(winrt::hstring, ColorScheme);
ACTION_ARG(Windows::Foundation::IReference<bool>, Elevate, nullptr);
ACTION_ARG(winrt::guid, ContentGuid);
static constexpr std::string_view CommandlineKey{ "commandline" };
static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" };
@@ -279,6 +284,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" };
static constexpr std::string_view ColorSchemeKey{ "colorScheme" };
static constexpr std::string_view ElevateKey{ "elevate" };
static constexpr std::string_view ContentKey{ "__content" };
public:
hstring GenerateName() const;
@@ -297,7 +303,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
otherAsUs->_Profile == _Profile &&
otherAsUs->_SuppressApplicationTitle == _SuppressApplicationTitle &&
otherAsUs->_ColorScheme == _ColorScheme &&
otherAsUs->_Elevate == _Elevate;
otherAsUs->_Elevate == _Elevate &&
otherAsUs->_ContentGuid == _ContentGuid;
}
return false;
};
@@ -314,6 +321,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
JsonUtils::GetValueForKey(json, ElevateKey, args->_Elevate);
JsonUtils::GetValueForKey(json, ContentKey, args->_ContentGuid);
return *args;
}
static Json::Value ToJson(const Model::NewTerminalArgs& val)
@@ -333,6 +341,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
JsonUtils::SetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
JsonUtils::SetValueForKey(json, ElevateKey, args->_Elevate);
// TODO! We should probably have the ContentGuid be optional, or at
// least serialized smarter, so we definitely don't write it to the
// file, ever.
JsonUtils::SetValueForKey(json, ContentKey, args->_ContentGuid);
return json;
}
Model::NewTerminalArgs Copy() const
@@ -347,6 +360,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_SuppressApplicationTitle = _SuppressApplicationTitle;
copy->_ColorScheme = _ColorScheme;
copy->_Elevate = _Elevate;
copy->_ContentGuid = _ContentGuid;
return *copy;
}
size_t Hash() const
@@ -366,6 +380,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
h.write(SuppressApplicationTitle());
h.write(ColorScheme());
h.write(Elevate());
h.write(ContentGuid());
}
};
}

View File

@@ -132,6 +132,8 @@ namespace Microsoft.Terminal.Settings.Model
// not modify whatever the profile's value is (either true or false)
Windows.Foundation.IReference<Boolean> Elevate;
Guid ContentGuid{ get; set; };
Boolean Equals(NewTerminalArgs other);
String GenerateName();
String ToCommandline();
@@ -276,8 +278,9 @@ namespace Microsoft.Terminal.Settings.Model
[default_interface] runtimeclass MoveTabArgs : IActionArgs
{
MoveTabArgs(MoveTabDirection direction);
MoveTabArgs(String window, MoveTabDirection direction);
MoveTabDirection Direction { get; };
String Window { get; };
};
[default_interface] runtimeclass ScrollUpArgs : IActionArgs

View File

@@ -24,6 +24,9 @@ namespace Microsoft.Terminal.Settings.Model
ActionAndArgs();
ActionAndArgs(ShortcutAction action, IActionArgs args);
static String Serialize(IVector<ActionAndArgs> args);
static IVector<ActionAndArgs> Deserialize(String content);
IActionArgs Args;
ShortcutAction Action;
};

View File

@@ -329,6 +329,12 @@ void AppHost::_HandleCommandlineArgs()
}
_logic.SetNumberOfOpenWindows(numPeasants);
}
// TODO! add revoker
peasant.AttachRequested([this](auto&&, Remoting::AttachRequest args) {
_logic.AttachContent(args.Content(), args.TabIndex());
});
_logic.WindowName(peasant.WindowName());
_logic.WindowId(peasant.GetID());
}
@@ -423,6 +429,11 @@ void AppHost::Initialize()
_revokers.OpenSystemMenu = _logic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu });
_revokers.QuitRequested = _logic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll });
_revokers.ShowWindowChanged = _logic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged });
// TODO! revoker
// TODO! move to member method
_logic.RequestMoveContent([this](auto&&, winrt::TerminalApp::RequestMoveContentArgs args) {
_windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex());
});
// BODGY
// On certain builds of Windows, when Terminal is set as the default

View File

@@ -0,0 +1,150 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "AppHost.h"
#include "resource.h"
#include "../types/inc/User32Utils.hpp"
#include <WilErrorReporting.h>
using namespace winrt;
using namespace winrt::Windows::UI;
using namespace winrt::Windows::UI::Composition;
using namespace winrt::Windows::UI::Xaml::Hosting;
using namespace winrt::Windows::Foundation::Numerics;
// We keep a weak ref to our ContentProcess singleton here.
// Why?
//
// We need to always return the _same_ ContentProcess when someone comes to
// instantiate this class. So we want to track the single instance we make. We
// also want to track when the last outstanding reference to this object is
// removed. If we're keeping a strong ref, then the ref count will always be > 1
static winrt::weak_ref<winrt::Microsoft::Terminal::Control::ContentProcess> g_weak{ nullptr };
static wil::unique_event g_canExitThread;
struct ContentProcessFactory : implements<ContentProcessFactory, IClassFactory>
{
ContentProcessFactory(winrt::guid g) :
_guid{ g } {};
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
{
*result = nullptr;
if (outer)
{
return CLASS_E_NOAGGREGATION;
}
if (!g_weak)
{
// Instantiate the ContentProcess here
winrt::Microsoft::Terminal::Control::ContentProcess strong{ _guid };
// Now, create a weak ref to that ContentProcess object.
winrt::weak_ref<winrt::Microsoft::Terminal::Control::ContentProcess> weak{ strong };
// Stash away that weak ref for future callers.
g_weak = weak;
return strong.as(iid, result);
}
else
{
auto strong = g_weak.get();
// !! LOAD BEARING !! If you set this event in the _first_ branch
// here, when we first create the object, then there will be _no_
// references to the ContentProcess object for a small slice. We'll
// stash the ContentProcess in the weak_ptr, and return it, and at
// that moment, there will be 0 outstanding references, it'll dtor,
// and we'll ExitProcess.
//
// Instead, set the event here, once there's already a reference
// outside of just the weak one we keep. Experimentation showed this
// was always hit when creating the ContentProcess at least once.
g_canExitThread.SetEvent();
return strong.as(iid, result);
}
}
HRESULT __stdcall LockServer(BOOL) noexcept final
{
return S_OK;
}
private:
winrt::guid _guid;
};
static bool checkIfContentProcess(winrt::guid& contentProcessGuid, HANDLE& eventHandle)
{
std::vector<std::wstring> args;
if (auto commandline{ GetCommandLineW() })
{
int argc = 0;
// Get the argv, and turn them into a hstring array to pass to the app.
wil::unique_any<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(commandline, &argc) };
if (argv)
{
for (auto& elem : wil::make_range(argv.get(), argc))
{
args.emplace_back(elem);
}
}
}
if (args.size() == 5 &&
args.at(1) == L"--content" &&
args.at(3) == L"--signal")
{
auto& guidString{ args.at(2) };
auto canConvert = guidString.length() == 38 && guidString.front() == '{' && guidString.back() == '}';
if (canConvert)
{
GUID result{};
THROW_IF_FAILED(IIDFromString(guidString.c_str(), &result));
contentProcessGuid = result;
eventHandle = reinterpret_cast<HANDLE>(wcstoul(args.at(4).c_str(),
nullptr /*endptr*/,
16 /*base*/));
return true;
}
}
return false;
}
static void doContentProcessThing(const winrt::guid& contentProcessGuid, const HANDLE& eventHandle)
{
// !! LOAD BEARING !! - important to be a MTA for these COM calls.
winrt::init_apartment();
DWORD registrationHostClass{};
check_hresult(CoRegisterClassObject(contentProcessGuid,
make<ContentProcessFactory>(contentProcessGuid).get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&registrationHostClass));
// Signal the event handle that was passed to us that we're now set up and
// ready to go.
SetEvent(eventHandle);
CloseHandle(eventHandle);
}
void TryRunAsContentProcess()
{
winrt::guid contentProcessGuid{};
HANDLE eventHandle{ INVALID_HANDLE_VALUE };
if (checkIfContentProcess(contentProcessGuid, eventHandle))
{
g_canExitThread = wil::unique_event{ CreateEvent(nullptr, true, false, nullptr) };
doContentProcessThing(contentProcessGuid, eventHandle);
WaitForSingleObject(g_canExitThread.get(), INFINITE);
// This is the conhost thing - if we ExitThread the main thread, the
// other threads can keep running till one calls ExitProcess.
ExitThread(0);
}
}

View File

@@ -62,6 +62,7 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="ContentProcessMain.cpp" />
<ClCompile Include="AppHost.cpp" />
<ClCompile Include="IslandWindow.cpp" />
<ClCompile Include="NonClientIslandWindow.cpp" />

View File

@@ -31,6 +31,8 @@ TRACELOGGING_DEFINE_PROVIDER(
#include <LibraryResources.h>
UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"TerminalApp/Resources");
void TryRunAsContentProcess();
// Routine Description:
// - Takes an image architecture and locates a string resource that maps to that architecture.
// Arguments:
@@ -119,6 +121,21 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
// should choose and install the correct one from the bundle.
EnsureNativeArchitecture();
// Content processes are only enabled in dev builds, so that we can check
// the feature in to dev builds one piece at a time.
if constexpr (Feature_ContentProcess::IsEnabled())
{
// If we _are_ a content process, then this function will call ExitThread(),
// after spawning some COM threads to deal with inbound COM requests to the
// ContentProcess object.
TryRunAsContentProcess();
// If we are a content process, then TryRunAsContentProcess will
// ExitThread before it returns, so that nothing else will run here.
}
// If we weren't a content process, then we'll just move on, and do the
// normal WindowsTerminal thing.
// Make sure to call this so we get WM_POINTER messages.
EnableMouseInPointer(true);

View File

@@ -117,6 +117,16 @@
</alwaysDisabledBrandingTokens>
</feature>
<feature>
<name>Feature_ContentProcess</name>
<description>Enables the --content flag, for allowing TermControls to be created as OOP content for the window.</description>
<stage>AlwaysDisabled</stage>
<!-- This is VERY MUCH a WIP, so we're using velocity to start checking in code to dev branches without shipping it. -->
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_DECPSViaMidiPlayer</name>
<description>Enables playing sound via DECPS using the MIDI player.</description>