Compare commits

...

33 Commits

Author SHA1 Message Date
Dustin L. Howett
d48870c49b Helix: Decode HTML entities in the test comment field (#15141
I have observed the test comment coming back from Helix with `"`
and friends in it.

It ends badly as you might imagine.

This unescape will be a no-op if the data is already well-formed.

(cherry picked from commit 90bbd2927d)
Service-Card-Id:
Service-Version: 1.17
2023-04-14 15:11:05 -05:00
Dustin L. Howett
0eafd10e3d PGO: Update the Helix payload to rely on the unpackaged distribution (#15123)
The unpackaged distribution was made for this exact use, so let's *do
it*!

(cherry picked from commit c7498a4269)
Service-Card-Id: 88876486
Service-Version: 1.17
2023-04-14 15:11:04 -05:00
Dustin L. Howett
436c037021 Only try to hand off to ConhostV1 if building for Windows (#15131)
Some of our automated tooling detects this as being a private API that
we're accessing via LoadLibrary/GetProcAddress. It's not *wrong*, but
it's also not *right*.

It's easier for us to just not do it (and save all the code for it!) in
OpenConsole.

(cherry picked from commit 7fbd3be8c3)
Service-Card-Id: 88788306
Service-Version: 1.17
2023-04-14 15:08:11 -05:00
Dustin L. Howett
99ac5f632f wpf: Bump the history length to 9001 instead of 1000 (#15129)
This was an oversight in the original implementation.

(cherry picked from commit de09671d8a)
Service-Card-Id: 88787776
Service-Version: 1.17
2023-04-14 15:07:09 -05:00
Mike Griese
55f7bec9d1 Add "legacy" themes (#15108)
This is a minimal version of the requests for #14858. In that thread we
discussed FULL reverting the default themes to the old ones. In later
discussion, we decided _meh_, let's just ship the legacy themes too, so
it's easy to go back if you should choose. The default still remains the
sane `dark`, but the `legacy*` themes are all right there, and given the
same special treatment as the other inbox themes.

Closes #14858
Closes #14844

(cherry picked from commit fe66ba5f58)
Service-Card-Id: 88785748
Service-Version: 1.17
2023-04-14 15:07:08 -05:00
Dustin L. Howett
9c9e8ecc91 Stop the beef when you hover off a hyperlink (#15120)
Big surprise, apparently W.F.Uri can parse the empty string into
garbage!

(cherry picked from commit a98a0cf2c6)
Service-Card-Id: 88773730
Service-Version: 1.17
2023-04-14 15:07:06 -05:00
Leonard Hecker
65324d6049 Replace statics in headers with inline constants (#15100)
C++ is a very well balanced and reasonable language, which is why
`static` inside classes means "shared between all instances", whereas
`static` outside of classes means "not shared between all .cpp files".

32 years after this problem was written onto parchment it was fixed with
the introduction of inline variables in C++17, which tell the compiler
to deduplicate variables the same way it deduplicates functions.

(cherry picked from commit 47a17cf2d7)
Service-Card-Id: 88757825
Service-Version: 1.17
2023-04-04 16:13:58 -05:00
Dustin L. Howett
87333f79ed Format URLs for display when we show the tooltip (#15095)
This will reduce the incidence of confusables, RTL, and non-printables
messing with the display of the URL.

(cherry picked from commit 06174a9cb3)
Service-Card-Id: 88740699
Service-Version: 1.17
2023-04-04 16:13:26 -05:00
Dustin L. Howett
30419b99df Add "portable mode", where settings load from the install path (#15051)
This pull request implements portable mode for Windows Terminal, which
will make side by side deployment of different versions generally more
feasible.

Portable mode was specified in #15032.

There are three broad categories of changes in this PR:

1. Changes to settings loading.
2. A new indicator in the settings UI plus a link to the portable mode
   docs.
3. A new application display name, `Terminal (Portable)`, which users
   will hopefully include in their bug reports.

It's surprisingly small for how big a deal it is!

Related to #15034
Closes #1386

(cherry picked from commit e6a3fa8d4e)
Service-Card-Id: 88719279
Service-Version: 1.17
2023-03-31 17:47:57 -05:00
Dustin L. Howett
c16e5d99cc Add support for an unpackaged distribution of Terminal (#15034)
Since the removal of the Win10-specific variant of the Terminal MSIX in
#15031, there has been no officially-sanctioned (or even unofficially
tested!) way to get an unzippable double-click-runnable version of
Windows Terminal.

Due to a quirk in the resource loading system, an unpackaged
distribution of Terminal needs to ship all of XAML's resources and all
of is own resources in a single `resources.pri` file. The tooling to
support this is minimal, and we were previously just coasting by on
Visual Studio's generosity plus how the prerelease distribution of XAML
embedded itself into the consuming package.

This pull request introduces a build phase plus a supporting script (or
three) that produces a ZIP file distribution of Windows Terminal when
given a Terminal MSIX and an XAML AppX.

The three scripts are:

1. A script to merge any number of PRI files and/or PRI dump files (made
   with `makepri dump /dt detailed`)
2. A script that specifically merges XAML's resources with Terminal's.
   This is necessary because the XAML package emits a couple PRI
   resources into Terminal's resources _even when it is not
   co-packaged._ We need to remove the conflicting resources.
3. Finally, a script to take a WT and XAML distribution and combine them
   -- resources, files, everything -- and strip out the things that we
   don't need. This script is an all-in-one that calls the other two and
   produces a ZIP file at the end.

The final distribution is named after the PFN
(`Microsoft.WindowsTerminal`, or `...Preview` or `WindowsTerminalDev`),
the version number and the architecture. When expanded, it produces a
directory named `terminal-X.Y.Z.A` (version number.)

I've also added the build script to the release pipeline.

As a treat, this also produces an unpackaged distribution out of every
CI build... that way, contributors can download live deployable copies
of WT Unpackaged to test out their changes. Pretty cool.

Refs #1386

(cherry picked from commit dd63a0590b)
Service-Card-Id: 88602021
Service-Version: 1.17
2023-03-31 17:47:56 -05:00
Dustin L. Howett
9523d6ad20 Add flexible virtualization rules for HKCU\Console\Startup (#15050)
[Flexible Virtualization] is a little more restrictive than
`unvirtualizedResources`, but it's more descriptive and stands a chance
of working on Windows 10.

This makes `unvirtualizedResources` actually work for us - we can tell
the system exactly which registry keys we want to use. This is required
for our registry writes to work on Windows 10.

[Flexible Virtualization]:
https://learn.microsoft.com/en-us/windows/msix/desktop/flexible-virtualization

(cherry picked from commit c7816fdb05)
Service-Card-Id: 88719280
Service-Version: 1.17
2023-03-31 17:47:55 -05:00
Leonard Hecker
799d48ffea Fix font size of HTML clipboard contents (#15046)
This regression is caused by 0eff8c0. It previously said `.Y` here.
I went through the diff again and found no other width/height mistake.

Closes #14762
Closes #15043

(cherry picked from commit d9efdae982)
Service-Card-Id: 88652707
Service-Version: 1.17
2023-03-31 17:47:53 -05:00
Dustin L. Howett
acd32f252d Add a second way of detecting whether DefTerm is available (#15040)
This will become meaningful soon.

(cherry picked from commit c4d029829a)
Service-Card-Id: 88617801
Service-Version: 1.17
2023-03-31 17:47:52 -05:00
Dustin L. Howett
9b4fbff1a2 Consolidate our MSIX distribution back down to one package (#15031)
We ship a separate package to Windows 10, which contains a copy of XAML
embedded in it, because of a bug in activating classes from framework
packages while we're elevated.

We did this to avoid wasting disk space on Windows 11 installs (which is
critical given that we're preinstalled in the Windows image.)

The fix for this issue was released in a servicing update in April 2022.
Thanks to KB5011831, we no longer need this workaround!

And finally, this means that we no longer need to depend on a copy of
"pre-release" XAML. We only did that because it would copy all of its
assets into our package.

Introduced in #12560
Closes #14106
Closes (discussion) #14981
Reverts #14660

(cherry picked from commit f5e9e8ea77)
Service-Card-Id: 88600517
Service-Version: 1.17
2023-03-31 17:47:51 -05:00
Dustin L. Howett
d6d35610bc VsDevCmdGenerator: respect the user's startingDirectory (#15035)
The PowerShell equivalent was added in the initial pull request, #7774.

Closes #13721

(cherry picked from commit f06cd1759f)
Service-Card-Id: 88719282
Service-Version: 1.17
2023-03-31 17:47:34 -05:00
Dustin L. Howett
31ae78d1bc Remove useRegExe and add rescap:unvirtualizedResources (#15028)
Due to a limitation in the Windows App Installer UI, Terminal had to
shell out to `reg.exe` to write the Delegation registry keys. The team
in charge of AppInstaller lifted that (once by-policy) limitation.

Therefore, we can remove our BODGY workaround.

(cherry picked from commit e0046a4cca)
Service-Card-Id: 88581048
Service-Version: 1.17
2023-03-31 17:47:33 -05:00
Dustin L. Howett
d8c8e780b6 Enable the Hybrid CRT for all C++ projects (#15010)
The less we need the C++ runtime, the better.

I measured this as growing our package by a fair amount...
but less than the size of XamlHost and all the forwarders combined.

Reducing our dependency surface makes us easier to deploy and more
reliable.

_as of 1.17 (2022-10)_

| **File**                | **Before** | **After** |        **Delta** |
| ----------------------- | ----------:| ---------:| ----------------:|
| `OpenConsole`           |  1,273,344 | 1,359,360 |   +86,016 (84kb) |
| `TerminalApp`           |  2,037,248 | 2,120,704 |   +83,456 (82kb) |
| `TerminalControl`       |  1,412,608 | 1,502,720 |   +90,112 (88kb) |
| `TerminalSettingsModel` |  1,510,912 | 1,621,504 | +110,592 (108kb) |
| `wt`                    |     97,280 |   122,368 |   +25,088 (25kb) |
| `WindowsTerminal`       |    508,928 |   575,488 |   +66,560 (65kb) |
| **MSIX Overall**        |  6,488,301 | 6,799,017 | +310,716 (303kb) |

(cherry picked from commit b6bb3e0a80)
Service-Card-Id: 88536379
Service-Version: 1.17
2023-03-31 17:47:32 -05:00
Mike Griese
a33fdcfd75 Properly configure the project dependencies for TerminalAzBridge (#15008)
I don't think this is the resolution for #14581, but this can't hurt. These deps were using the wrong GUIDs

(cherry picked from commit 5c9f756891)
Service-Card-Id: 88719283
Service-Version: 1.17
2023-03-31 17:47:27 -05:00
Mike Griese
31aa23f717 Allow wsl$ in file URIs; generally allow all URI schemes (#14993)
Does two things related to URLs emitted via OSC8.

* Allows `wsl$` and `wsl.localhost` as the hostname in `file://` URIs
* Generally allows _all_ URIs that parse as a URI.

The relevant security comments: https://github.com/microsoft/terminal/pull/7526#issuecomment-764160208
> this doesn't let a would-be attacker specify command-line arguments (ala "cmd.exe /s /c do_a_bad_thing") (using somebody else's reputation to cause mayhem)
>
> `ShellExecute` de-elevates because it bounces a launch request off the shell
>
> "Works predictably for 15% of applications" (h/t to PhMajerus' AXSH, and other on-Windows requestors) is better in so many ways than "Works for 0% of applications", in my estimation. Incremental progress 😄 while we work on features that'll make it even more broadly applicable.

Closes #10188
Closes #7562

(cherry picked from commit 65640f6fe3)
Service-Card-Id: 88719284
Service-Version: 1.17
2023-03-31 17:47:26 -05:00
Mike Griese
9670230925 Rebuild the profile nav via MenuItemsSource; mitigate a crash (#14630)
Directly manipulating the `NavigationView::MenuItems` vector is bad. If
you do that, you're gonna get crashes, in WinUI code for `Measure`.
However, a WinUI PR (below) gave me an idea: Changing
`NavigationView::MenuItemsSource` will wholly invalidate the entirety of
the nav view items, and that will avoid the crash.

This code does that. It's a wee bit janky, but it works.

Closes #13673

_might_ affect #12333, need to try and repro.

See also:
* #9273
* #10390
* https://github.com/microsoft/microsoft-ui-xaml/issues/6302
* https://github.com/microsoft/microsoft-ui-xaml/pull/3138, which was
the fix for https://github.com/microsoft/microsoft-ui-xaml/issues/2818

(cherry picked from commit 8c17475a9f)
Service-Card-Id: 88428128
Service-Version: 1.17
2023-03-31 17:47:24 -05:00
Leonard Hecker
c9a4ab7624 Fix offset calculation in the outlines shader (#14971)
The `Sample` method has an offset parameter which we can use here.
The result is not identical to the old shader, as the older shader
used the height of the terminal for drawing horizontal edges and so
the result looked way fatter than it was seemingly originally intended.
On my 150% scale display I found an offset of +/- 2px to produce an
acceptable result, although in the future it might be worthwhile to
make the offset dependent on the UI scale.

Closes #14953

(cherry picked from commit 6c80390de7)
Service-Card-Id: 88719285
Service-Version: 1.17
2023-03-31 17:47:23 -05:00
Dustin L. Howett
986ced95c1 Switch to the new Helix queues (#14933)
The old ones are pushing up daisies.

(cherry picked from commit 3e7e8d59f2)
Service-Card-Id: 88338601
Service-Version: 1.17
2023-03-31 17:47:22 -05:00
Dustin L. Howett
0631bf5bd7 Add a proper reference from TermCore to MidiAudio (#14868)
It was just by luck that TerminalCore usually built after MidiAudio

(cherry picked from commit e1145c362f)
Service-Card-Id: 88719295
Service-Version: 1.17
2023-03-31 17:46:52 -05:00
Leonard Hecker
0e2b3e6079 Ignore CHAR_INFO trailers during WriteConsoleOutput (#14840)
#13626 contains a small "regression" compared to #13321:
It now began to store trailers in the buffer wherever possible to allow
a region
of the buffer to be backed up and restored via Read/WriteConsoleOutput.
But we're unfortunately still ill-equipped to handle anything but UCS-2
via
WriteConsoleOutput, so it's best to again ignore trailers just like in
#13321.

## Validation Steps Performed
* Added unit test 

(cherry picked from commit 9dcdcac0bb)
Service-Card-Id: 88719297
Service-Version: 1.17
2023-03-31 17:46:50 -05:00
Leonard Hecker
6edaa15d4c Update ControlsV2 scrollbar template (#14846)
This commit updates our scrollbar template to microsoft-ui-xaml at ceeab5f.
This incorporates the bug fix for MSFT-39442675.

(cherry picked from commit 6a8bba96b2)
Service-Card-Id: 88103610
Service-Version: 1.17
2023-03-31 17:46:49 -05:00
PankajBhojwani
dd9e0148c2 Pass the window root to the profile page views, instead of the view model (#14816)
## Summary of the Pull Request
Let the profile pages' views have access to the window root, rather than the `ProfileViewModel`. The window root is passed along when the page is navigated to.

## Validation Steps Performed
Clicking `Browse` no longer crashes.

## PR Checklist
- [x] Closes #14808

(cherry picked from commit e4bba3cd9a)
Service-Card-Id: 88031914
Service-Version: 1.17
2023-03-31 17:46:48 -05:00
Dustin L. Howett
f0b09421da AzureConnection: remove our dependency on cpprestsdk (#14776)
This pull request removes, in full, our dependency on cpprestsdk. This
allows us to shed 500KiB-1.2MiB from our package off the top and enables
the following future investments:

- Removal of the App CRT forwarders to save an additional ~500KiB
- Switching over to the HybridCRT and removing our dependency on _any
  CRT_.

cpprest was built on my dev box two or so years ago, and is in _utter_
violation of our compliance guidelines on SBOM et al.

In addition, this change allows us to use the proxy server configured
in Windows Settings.

I did this in four steps (represented roughly by the individual commits):

1. Switch from cpprest's http_client/json to Windows.Web.Http and
   Windows.Data.Json
2. Switch from websocketpp to winhttp.dll's WebSocket implementation¹
3. Remove all remaining utility classes
4. Purge all dependencies from all projects and scripts on cpprest.

I also took this opportunity to add a feature flag that allows Dev
builds to run AzureConnection in-process.

¹ Windows.Networking.Sockets' API is so unergonomic that it was simply
infeasible (and also _horrible_) to use it.

## Validation Steps

I've run the Azure Connection quite a bit inproc.

Closes #4575.
Might be related to #5977, #11714, and with the user agent thing maybe #14403.

(cherry picked from commit 4903cfd484)
Service-Card-Id: 88581854
Service-Version: 1.17
2023-03-31 17:46:47 -05:00
Dustin L. Howett
7083738146 Generalize OpenConsoleProxy's HybridCRT logic (#14733)
This pull request moves the Hybrid CRT logic out of the Host.Proxy
project and makes it available for all other projects in our solution.

(cherry picked from commit fc960e3327)
Service-Card-Id: 88719300
Service-Version: 1.17
2023-03-31 17:46:45 -05:00
Dustin Howett
80c529aced Revert the removal of LayoutUpdated_revoker from ColorSchemes in ab79a8538 2023-01-23 19:01:07 -06:00
Dustin Howett
ab79a85381 Revert "Manually set the automation name of the default color scheme for screen reader (#14704)"
This reverts commit 47f38e31a1.
2023-01-23 18:52:19 -06:00
Carlos Zamora
b9089d9d1d [UIA] Dispatch a TextChanged event on new output (#14723)
For some reason, Windows Terminal stops dispatching UIA TextChanged events sometimes. There isn't a reliable repro for this bug.

However, under NVDA's logger, it appears that when the bug does occur, we still dispatch UIA notifications (which may be ignored by NVDA in some configurations). A "quick fix" here is to dispatch a TextChanged event if we're going to dispatch a notification. Since we're just enabling a flag, we won't send two events at once.

Closes #10911

(cherry picked from commit a0e830cc1a)
Service-Card-Id: 87730855
Service-Version: 1.17
2023-01-23 16:57:05 -06:00
Carlos Zamora
272e7b0905 Ensure TermControl is not closing when firing UIA events (#14714)
The `SignalTextChanged` crash seems to be occurring due to the `TermControlAutomationPeer` being destructed by the time the UIA event is actually dispatched. Even though we're already checking if TCAP and TermControl still exist, it could be that the TermControl is being closed as text is being output.

The proposed fix here is to record when the closing process starts and exposing that information directly to the TCAP. If TCAP sees that we're in the process of closing, don't bother sending a UIA event.

Closes #13978

(cherry picked from commit 3dd40791c9)
Service-Card-Id: 87728853
Service-Version: 1.17
2023-01-23 12:44:39 -06:00
Mike Griese
7326eaaa6f Resize our ContentDialog's when the window resizes (#14722)
Major thanks to @dongle-the-gadget in https://github.com/microsoft/microsoft-ui-xaml/issues/3577#issuecomment-1399250405 for coming up with this workaround.

This PR will manually forward a `WM_SIZE` message to our `CoreWindow`, to trigger the `ContentDialog` to resize itself.

We always closed these issues as dupes of the upstream one, so this doesn't actually close anything.
HOWEVER, these are the following issues that reported this bug:
- #2380
- #4463
- #5252
- #5810
- #6181
- #7113
- #7225
- #8245
- #8496
- #8643
- #9336
- #9563
- #5808
- #10351
- #10634
- #10995
- #11770
- #13796

(cherry picked from commit a4cf4e2761)
Service-Card-Id: 87727806
Service-Version: 1.17
2023-01-23 12:43:04 -06:00
106 changed files with 1248 additions and 820 deletions

View File

@@ -55,6 +55,8 @@ GETMOUSEHOVERTIME
Hashtable
HIGHCONTRASTON
HIGHCONTRASTW
hinternet
HINTERNET
hotkeys
href
hrgn
@@ -214,6 +216,8 @@ Viewbox
virtualalloc
wcsstr
wcstoui
WDJ
winhttp
winmain
winsta
winstamin
@@ -221,6 +225,7 @@ wmemcmp
wpc
WSF
wsregex
WWH
wwinmain
xchg
XDocument

View File

@@ -33,6 +33,7 @@ libucrtd
LKG
LOCKFILE
Lxss
makepri
mfcribbon
microsoft
microsoftonline
@@ -50,10 +51,13 @@ pgo
pgosweep
powerrename
powershell
priconfig
PRIINFO
propkey
pscustomobject
QWORD
regedit
resfiles
robocopy
SACLs
segoe

View File

@@ -257,6 +257,7 @@ condrv
conechokey
conemu
configurability
confusables
conhost
conime
conimeinfo
@@ -311,7 +312,6 @@ CPLINFO
cplusplus
CPPCORECHECK
cppcorecheckrules
cpprest
cpprestsdk
cppwinrt
CProc
@@ -1437,7 +1437,6 @@ PPEB
ppf
ppguid
ppidl
pplx
PPROC
PPROCESS
ppropvar
@@ -1577,6 +1576,7 @@ replatformed
Replymessage
repositorypath
Requiresx
rerasterize
rescap
Resequence
RESETCONTENT
@@ -1777,6 +1777,7 @@ srv
srvinit
srvpipe
ssa
startdir
STARTF
STARTUPINFO
STARTUPINFOEX
@@ -1950,6 +1951,7 @@ trx
tsattrs
tsf
tsgr
tsm
TStr
TSTRFORMAT
TSub
@@ -2114,7 +2116,6 @@ WDDMCONSOLECONTEXT
wdm
webpage
websites
websockets
wekyb
wex
wextest

View File

@@ -35,7 +35,7 @@ ROY\sG\.\sBIV
# 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,})
(?:^|[\t ,"'`=(])-[X](?!aml)(?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
# hit-count: 41 file-count: 28
# version suffix <word>v#

View File

@@ -326,6 +326,9 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.Tests.Feature", "src\winconpty\ft_pty\winconpty.FeatureTests.vcxproj", "{024052DE-83FB-4653-AEA4-90790D29D5BD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAzBridge", "src\cascadia\TerminalAzBridge\TerminalAzBridge.vcxproj", "{067F0A06-FCB7-472C-96E9-B03B54E8E18D}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "src\dep\fmt\fmt.vcxproj", "{6BAE5851-50D5-4934-8D5E-30361A8A40F3}"
EndProject

View File

@@ -55,14 +55,7 @@ Copy-Item "build\helix\runtests.cmd" $payloadDir
Copy-Item "build\helix\InstallTestAppDependencies.ps1" "$payloadDir"
Copy-Item "build\Helix\EnsureMachineState.ps1" "$payloadDir"
# Copy the APPX package from the 'drop' artifact dir and Windows Kits
Copy-Item "$repoDirectory\Artifacts\$ArtifactName\appx\CascadiaPackage_0.0.1.0_$Platform.msix" $payloadDir\CascadiaPackage.zip
Copy-Item "C:\program files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs\Microsoft.VCLibs.Desktop\14.0\Appx\Retail\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" $payloadDir\VCLibs.zip
# Rename it to extension of ZIP because Expand-Archive is real sassy on the build machines
# and refuses to unzip it because of its file extension while on a desktop, it just
# does the job without complaining.
# Extract the APPX package
Expand-Archive -LiteralPath $payloadDir\CascadiaPackage.zip -DestinationPath $payloadDir\appx
Expand-Archive -LiteralPath $payloadDir\VCLibs.zip -DestinationPath $payloadDir\appx -Force
# Extract the unpackaged distribution of Windows Terminal to the payload directory,
# where it will create a subdirectory named terminal-0.0.1.0
# This is referenced in TerminalApp.cs later as part of the test harness.
& tar -x -v -f "$repoDirectory\Artifacts\$ArtifactName\unpackaged\WindowsTerminalDev_0.0.1.0_x64.zip" -C "$payloadDir"

View File

@@ -70,7 +70,7 @@ foreach ($testRun in $testRuns.value)
foreach ($testResult in $testResults.value)
{
$info = ConvertFrom-Json $testResult.comment
$info = ConvertFrom-Json ([System.Web.HttpUtility]::HtmlDecode($testResult.comment))
$helixJobId = $info.HelixJobId
$helixWorkItemName = $info.HelixWorkItemName

View File

@@ -67,51 +67,6 @@
}
]
}
},
{
// THIRD PARTY SOFTWARE
"MatchedPath": [
"cpprest*.dll"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-231522",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-231522",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
}
]
}

View File

@@ -3,8 +3,6 @@
<package id="MUXCustomBuildTasks" version="1.0.48" targetFramework="native" />
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
<!-- This cannot be included in another project that depends on XAML (as it would be a duplicate package ID) -->
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
<package id="Microsoft.Debugging.Tools.PdbStr" version="20220617.1556.0" targetFramework="native" />
<package id="Microsoft.Debugging.Tools.SrcTool" version="20220617.1556.0" targetFramework="native" />
</packages>

View File

@@ -56,15 +56,10 @@ parameters:
- x64
- x86
- arm64
- name: buildWindowsVersions
type: object
default:
- Win10
- Win11
variables:
MakeAppxPath: 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\MakeAppx.exe'
TerminalInternalPackageVersion: "0.0.7"
TerminalInternalPackageVersion: "0.0.8"
# If we are building a branch called "release-*", change the NuGet suffix
# to "preview". If we don't do that, XES will set the suffix to "release1"
# because it truncates the value after the first period.
@@ -87,13 +82,6 @@ variables:
NuGetPackBetaVersion: preview
${{ elseif eq(variables['Build.SourceBranchName'], 'main') }}:
NuGetPackBetaVersion: experimental
# The NuGet packages have to use *somebody's* DLLs. We used to force them to
# use the Win10 build outputs, but if there isn't a Win10 build we should use
# the Win11 one.
${{ if containsValue(parameters.buildWindowsVersions, 'Win10') }}:
TerminalBestVersionForNuGetPackages: Win10
${{ else }}:
TerminalBestVersionForNuGetPackages: Win11
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
resources:
@@ -107,11 +95,9 @@ jobs:
matrix:
${{ each config in parameters.buildConfigurations }}:
${{ each platform in parameters.buildPlatforms }}:
${{ each windowsVersion in parameters.buildWindowsVersions }}:
${{ config }}_${{ platform }}_${{ windowsVersion }}:
BuildConfiguration: ${{ config }}
BuildPlatform: ${{ platform }}
TerminalTargetWindowsVersion: ${{ windowsVersion }}
${{ config }}_${{ platform }}:
BuildConfiguration: ${{ config }}
BuildPlatform: ${{ platform }}
displayName: Build
timeoutInMinutes: 240
cancelTimeoutInMinutes: 1
@@ -185,10 +171,6 @@ jobs:
arguments: -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
pwsh: true
- ${{ if eq(parameters.buildTerminal, true) }}:
- pwsh: |-
./build/scripts/Patch-ManifestsToWindowsVersion.ps1 -NewWindowsVersion "10.0.22000.0"
displayName: Update manifest target version to Win11 (if necessary)
condition: and(succeeded(), eq(variables['TerminalTargetWindowsVersion'], 'Win11'))
- task: VSBuild@1
displayName: Build solution **\OpenConsole.sln
condition: true
@@ -205,7 +187,7 @@ jobs:
continueOnError: True
inputs:
PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog
ArtifactName: binlog-$(BuildPlatform)-$(TerminalTargetWindowsVersion)
ArtifactName: binlog-$(BuildPlatform)
- task: PowerShell@2
displayName: Check MSIX for common regressions
inputs:
@@ -254,16 +236,12 @@ jobs:
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
- ${{ if eq(parameters.buildTerminal, true) }}:
- task: CopyFiles@2
displayName: Copy *.appx/*.msix to Artifacts
displayName: Copy *.msix and symbols to Artifacts
inputs:
Contents: >-
**/*.appx
**/*.msix
**/*.appxsym
!**/Microsoft.VCLibs*.appx
TargetFolder: $(Build.ArtifactStagingDirectory)/appx
OverWrite: true
flattenFolders: true
@@ -297,7 +275,17 @@ jobs:
displayName: Publish Artifact (appx)
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)/appx
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion)
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)
- pwsh: |-
$XamlAppxPath = (Get-Item "src\cascadia\CascadiaPackage\AppPackages\*\Dependencies\$(BuildPlatform)\Microsoft.UI.Xaml*.appx").FullName
& .\build\scripts\New-UnpackagedTerminalDistribution.ps1 -TerminalAppX $(WindowsTerminalPackagePath) -XamlAppX $XamlAppxPath -Destination "$(Build.ArtifactStagingDirectory)/unpackaged"
displayName: Build Unpackaged Distribution
- publish: $(Build.ArtifactStagingDirectory)/unpackaged
artifact: unpackaged-$(BuildPlatform)-$(BuildConfiguration)
displayName: Publish Artifact (unpackaged)
- ${{ if eq(parameters.buildConPTY, true) }}:
- task: CopyFiles@2
displayName: Copy ConPTY to Artifacts
@@ -315,7 +303,7 @@ jobs:
displayName: Publish Artifact (ConPTY)
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)/conpty
ArtifactName: conpty-dll-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion)
ArtifactName: conpty-dll-$(BuildPlatform)-$(BuildConfiguration)
- ${{ if eq(parameters.buildWPF, true) }}:
- task: CopyFiles@2
displayName: Copy PublicTerminalCore.dll to Artifacts
@@ -329,7 +317,7 @@ jobs:
displayName: Publish Artifact (PublicTerminalCore)
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion)
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)
- task: PublishSymbols@2
displayName: Publish symbols path
@@ -347,11 +335,6 @@ jobs:
- ${{ if eq(parameters.buildTerminal, true) }}:
- job: BundleAndSign
strategy:
matrix:
${{ each windowsVersion in parameters.buildWindowsVersions }}:
${{ windowsVersion }}:
TerminalTargetWindowsVersion: ${{ windowsVersion }}
displayName: Create and sign AppX/MSIX bundles
variables:
${{ if eq(parameters.branding, 'Release') }}:
@@ -373,9 +356,9 @@ jobs:
disableOutputRedirect: true
- ${{ each platform in parameters.buildPlatforms }}:
- task: DownloadBuildArtifacts@0
displayName: Download Artifacts ${{ platform }} $(TerminalTargetWindowsVersion)
displayName: Download Artifacts ${{ platform }}
inputs:
artifactName: appx-${{ platform }}-Release-$(TerminalTargetWindowsVersion)
artifactName: appx-${{ platform }}-Release
# Add 3000 to the major version component, but only for the bundle.
# This is to ensure that it is newer than "2022.xx.yy.zz" or whatever the original bundle versions were before
# we switched to uniform naming.
@@ -385,7 +368,7 @@ jobs:
$Components[0] = ([int]$Components[0] + $VersionEpoch)
$BundleVersion = $Components -Join "."
New-Item -Type Directory "$(System.ArtifactsDirectory)\bundle"
.\build\scripts\Create-AppxBundle.ps1 -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\bundle\$(BundleStemName)_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
.\build\scripts\Create-AppxBundle.ps1 -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion $BundleVersion -OutputPath "$(System.ArtifactsDirectory)\bundle\$(BundleStemName)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
displayName: Create WindowsTerminal*.msixbundle
- task: EsrpCodeSigning@1
displayName: Submit *.msixbundle to ESRP for code signing
@@ -426,7 +409,7 @@ jobs:
displayName: 'Publish Artifact: appxbundle-signed'
inputs:
PathtoPublish: $(System.ArtifactsDirectory)\bundle
ArtifactName: appxbundle-signed-$(TerminalTargetWindowsVersion)
ArtifactName: appxbundle-signed
- ${{ if eq(parameters.buildConPTY, true) }}:
- job: PackageAndSignConPTY
@@ -451,7 +434,7 @@ jobs:
- task: DownloadBuildArtifacts@0
displayName: Download ${{ platform }} ConPTY binaries
inputs:
artifactName: conpty-dll-${{ platform }}-$(BuildConfiguration)-$(TerminalBestVersionForNuGetPackages)
artifactName: conpty-dll-${{ platform }}-$(BuildConfiguration)
downloadPath: bin\${{ platform }}\$(BuildConfiguration)\
extractTars: false
- task: PowerShell@2
@@ -542,7 +525,7 @@ jobs:
- task: DownloadBuildArtifacts@0
displayName: Download ${{ platform }} PublicTerminalCore
inputs:
artifactName: wpf-dll-${{ platform }}-$(BuildConfiguration)-$(TerminalBestVersionForNuGetPackages)
artifactName: wpf-dll-${{ platform }}-$(BuildConfiguration)
itemPattern: '**/*.dll'
downloadPath: bin\${{ platform }}\$(BuildConfiguration)\
extractTars: false
@@ -640,11 +623,10 @@ jobs:
# Download the appx-PLATFORM-CONFIG-VERSION artifact for every platform/version combo
- ${{ each platform in parameters.buildPlatforms }}:
- ${{ each windowsVersion in parameters.buildWindowsVersions }}:
- task: DownloadBuildArtifacts@0
displayName: Download Symbols ${{ platform }} ${{ windowsVersion }}
inputs:
artifactName: appx-${{ platform }}-Release-${{ windowsVersion }}
- task: DownloadBuildArtifacts@0
displayName: Download Symbols ${{ platform }}
inputs:
artifactName: appx-${{ platform }}-Release
# It seems easier to do this -- download every appxsym -- then enumerate all the PDBs in the build directory for the
# public symbol push. Otherwise, we would have to list all of the PDB files one by one.
@@ -704,7 +686,7 @@ jobs:
- task: DownloadBuildArtifacts@0
displayName: Download Build Artifacts
inputs:
artifactName: appxbundle-signed-Win11
artifactName: appxbundle-signed
extractTars: false
- task: PowerShell@2
displayName: Rename and stage packages for vpack
@@ -713,7 +695,7 @@ jobs:
script: >-
# Rename to known/fixed name for Windows build system
Get-ChildItem Microsoft.WindowsTerminal_Win11_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
# Create vpack directory and place item inside
@@ -721,13 +703,13 @@ jobs:
mkdir WindowsTerminal.app
mv Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle .\WindowsTerminal.app\
workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed-Win11
workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed
- task: PkgESVPack@12
displayName: 'Package ES - VPack'
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed-Win11\WindowsTerminal.app
sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app
description: VPack for the Windows Terminal Application
pushPkgName: WindowsTerminal.app
owner: conhost

View File

@@ -144,7 +144,7 @@ jobs:
inputs:
TargetPattern: guardianGlob
# See https://aka.ms/gdn-globs for how to do match patterns
AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll;-:file|**\cpprest*.dll
AnalyzeTargetGlob: $(Build.SourcesDirectory)\bin\**\*.dll;$(Build.SourcesDirectory)\bin\**\*.exe;-:file|**\Microsoft.UI.Xaml.dll;-:file|**\Microsoft.Toolkit.Win32.UI.XamlHost.dll;-:file|**\vcruntime*.dll;-:file|**\vcomp*.dll;-:file|**\vccorlib*.dll;-:file|**\vcamp*.dll;-:file|**\msvcp*.dll;-:file|**\concrt*.dll;-:file|**\TerminalThemeHelpers*.dll
continueOnError: true
# Set XES_SERIALPOSTBUILDREADY to run Security and Compliance task once per build

View File

@@ -64,17 +64,24 @@ steps:
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
- task: CopyFiles@2
displayName: 'Copy *.appx/*.msix to Artifacts (Non-PR builds only)'
displayName: 'Copy *.msix to Artifacts'
inputs:
Contents: |
**/*.appx
**/*.msix
**/*.appxsym
!**/Microsoft.VCLibs*.appx
TargetFolder: '$(Build.ArtifactStagingDirectory)/appx'
OverWrite: true
flattenFolders: true
condition: succeeded()
- pwsh: |-
$TerminalMsixPath = (Get-Item "$(Build.ArtifactStagingDirectory)\appx\Cascadia*.msix").FullName
$XamlAppxPath = (Get-Item "src\cascadia\CascadiaPackage\AppPackages\*\Dependencies\$(BuildPlatform)\Microsoft.UI.Xaml*.appx").FullName
& .\build\scripts\New-UnpackagedTerminalDistribution.ps1 -TerminalAppX $TerminalMsixPath -XamlAppX $XamlAppxPath -Destination "$(Build.ArtifactStagingDirectory)/unpackaged"
displayName: Build Unpackaged Distribution
- publish: $(Build.ArtifactStagingDirectory)/unpackaged
artifact: unpackaged-$(BuildPlatform)-$(BuildConfiguration)
displayName: Publish Artifact (unpackaged)
- task: CopyFiles@2
displayName: 'Copy outputs needed for test runs to Artifacts'

View File

@@ -14,8 +14,8 @@ parameters:
platform: ''
# if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline:
useBuildOutputFromPipeline: $(System.DefinitionId)
openHelixTargetQueues: 'windows.10.amd64.client21h1.open.xaml'
closedHelixTargetQueues: 'windows.10.amd64.client21h1.xaml'
openHelixTargetQueues: 'windows.11.amd64.client.open.reunion'
closedHelixTargetQueues: 'windows.11.amd64.client.reunion'
jobs:
- job: ${{ parameters.name }}

View File

@@ -0,0 +1,78 @@
Param(
[Parameter(Mandatory,
HelpMessage="List of PRI files or XML dumps (detailed only) to merge")]
[string[]]
$Path,
[Parameter(Mandatory,
HelpMessage="Output Path")]
[string]
$OutputPath,
[Parameter(HelpMessage="Name of index in output file; defaults to 'Application'")]
[string]
$IndexName = "Application",
[Parameter(HelpMessage="Path to makepri.exe")]
[ValidateScript({Test-Path $_ -Type Leaf})]
[string]
$MakePriPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\MakePri.exe"
)
$ErrorActionPreference = 'Stop'
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "tmp$([Convert]::ToString((Get-Random 65535),16).PadLeft(4,'0')).tmp"
New-Item -ItemType Directory -Path $tempDir | Out-Null
$priConfig = Join-Path $tempDir "priconfig.xml"
$priListFile = Join-Path $tempDir "pri.resfiles"
$dumpListFile = Join-Path $tempDir "dump.resfiles"
@"
<?xml version="1.0" encoding="utf-8"?>
<resources targetOsVersion="10.0.0" majorVersion="1">
<index root="\" startIndexAt="dump.resfiles">
<default>
<qualifier name="Language" value="en-US" />
<qualifier name="Contrast" value="standard" />
<qualifier name="Scale" value="200" />
<qualifier name="HomeRegion" value="001" />
<qualifier name="TargetSize" value="256" />
<qualifier name="LayoutDirection" value="LTR" />
<qualifier name="DXFeatureLevel" value="DX9" />
<qualifier name="Configuration" value="" />
<qualifier name="AlternateForm" value="" />
<qualifier name="Platform" value="UAP" />
</default>
<indexer-config type="PRIINFO" />
<indexer-config type="RESFILES" qualifierDelimiter="." />
</index>
<index root="\" startIndexAt="pri.resfiles">
<default>
<qualifier name="Language" value="en-US" />
<qualifier name="Contrast" value="standard" />
<qualifier name="Scale" value="200" />
<qualifier name="HomeRegion" value="001" />
<qualifier name="TargetSize" value="256" />
<qualifier name="LayoutDirection" value="LTR" />
<qualifier name="DXFeatureLevel" value="DX9" />
<qualifier name="Configuration" value="" />
<qualifier name="AlternateForm" value="" />
<qualifier name="Platform" value="UAP" />
</default>
<indexer-config type="PRI" />
<indexer-config type="RESFILES" qualifierDelimiter="." />
</index>
</resources>
"@ | Out-File -Encoding:utf8NoBOM $priConfig
$Path | Where { $_ -Like "*.pri" } | ForEach-Object {
Get-Item $_ | Select -Expand FullName
} | Out-File -Encoding:utf8NoBOM $priListFile
$Path | Where { $_ -Like "*.xml" } | ForEach-Object {
Get-Item $_ | Select -Expand FullName
} | Out-File -Encoding:utf8NoBOM $dumpListFile
& $MakePriPath new /pr $tempDir /cf $priConfig /o /in $IndexName /of $OutputPath
Remove-Item -Recurse -Force $tempDir

View File

@@ -0,0 +1,47 @@
Param(
[Parameter(Mandatory,
HelpMessage="Root directory of extracted Terminal AppX")]
[string[]]
$TerminalRoot,
[Parameter(Mandatory,
HelpMessage="Root directory of extracted Xaml AppX")]
[string[]]
$XamlRoot,
[Parameter(Mandatory,
HelpMessage="Output Path")]
[string]
$OutputPath,
[Parameter(HelpMessage="Path to makepri.exe")]
[ValidateScript({Test-Path $_ -Type Leaf})]
[string]
$MakePriPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\MakePri.exe"
)
$ErrorActionPreference = 'Stop'
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "tmp$([Convert]::ToString((Get-Random 65535),16).PadLeft(4,'0')).tmp"
New-Item -ItemType Directory -Path $tempDir | Out-Null
$terminalDump = Join-Path $tempDir "terminal.pri.xml"
& $MakePriPath dump /if (Join-Path $TerminalRoot "resources.pri") /of $terminalDump /dt detailed
Write-Verbose "Removing Microsoft.UI.Xaml node from Terminal to prevent a collision with XAML"
$terminalXMLDocument = [xml](Get-Content $terminalDump)
$resourceMap = $terminalXMLDocument.PriInfo.ResourceMap
$fileSubtree = $resourceMap.ResourceMapSubtree | Where-Object { $_.Name -eq "Files" }
$subtrees = $fileSubtree.ResourceMapSubtree
$xamlSubtreeChild = ($subtrees | Where-Object { $_.Name -eq "Microsoft.UI.Xaml" })
if ($Null -Ne $xamlSubtreeChild) {
$null = $fileSubtree.RemoveChild($xamlSubtreeChild)
$terminalXMLDocument.Save($terminalDump)
}
$indexName = $terminalXMLDocument.PriInfo.ResourceMap.name
& (Join-Path $PSScriptRoot "Merge-PriFiles.ps1") -Path $terminalDump, (Join-Path $XamlRoot "resources.pri") -IndexName $indexName -OutputPath $OutputPath -MakePriPath $MakePriPath
Remove-Item -Recurse -Force $tempDir

View File

@@ -0,0 +1,117 @@
Param(
[Parameter(Mandatory,
HelpMessage="Path to Terminal AppX")]
[ValidateScript({Test-Path $_ -Type Leaf})]
[string]
$TerminalAppX,
[Parameter(Mandatory,
HelpMessage="Path to Xaml AppX")]
[ValidateScript({Test-Path $_ -Type Leaf})]
[string]
$XamlAppX,
[Parameter(HelpMessage="Output Directory")]
[string]
$Destination = ".",
[Parameter(HelpMessage="Path to makeappx.exe")]
[ValidateScript({Test-Path $_ -Type Leaf})]
[string]
$MakeAppxPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\MakeAppx.exe"
)
$filesToRemove = @("*.xml", "*.winmd", "Appx*", "Images/*Tile*", "Images/*Logo*") # Remove from Terminal
$filesToKeep = @("Microsoft.Terminal.Remoting.winmd") # ... except for these
$filesToCopyFromXaml = @("Microsoft.UI.Xaml.dll", "Microsoft.UI.Xaml") # We don't need the .winmd
$ErrorActionPreference = 'Stop'
If ($null -Eq (Get-Item $MakeAppxPath -EA:SilentlyContinue)) {
Write-Error "Could not find MakeAppx.exe at `"$MakeAppxPath`".`nMake sure that -MakeAppxPath points to a valid SDK."
Exit 1
}
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "tmp$([Convert]::ToString((Get-Random 65535),16).PadLeft(4,'0')).tmp"
New-Item -ItemType Directory -Path $tempDir | Out-Null
$XamlAppX = Get-Item $XamlAppX | Select-Object -Expand FullName
$TerminalAppX = Get-Item $TerminalAppX | Select-Object -Expand FullName
########
# Reading the AppX Manifest for preliminary info
########
$appxManifestPath = Join-Path $tempDir AppxManifest.xml
& tar.exe -x -f "$TerminalAppX" -C $tempDir AppxManifest.xml
$manifest = [xml](Get-Content $appxManifestPath)
$pfn = $manifest.Package.Identity.Name
$version = $manifest.Package.Identity.Version
$architecture = $manifest.Package.Identity.ProcessorArchitecture
$distributionName = "{0}_{1}_{2}" -f ($pfn, $version, $architecture)
$terminalDir = "terminal-{0}" -f ($version)
########
# Unpacking Terminal and XAML
########
$terminalAppPath = Join-Path $tempdir $terminalDir
$xamlAppPath = Join-Path $tempdir "xaml"
New-Item -ItemType Directory -Path $terminalAppPath | Out-Null
New-Item -ItemType Directory -Path $xamlAppPath | Out-Null
& $MakeAppxPath unpack /p $TerminalAppX /d $terminalAppPath /o | Out-Null
If ($LASTEXITCODE -Ne 0) {
Throw "Unpacking $TerminalAppX failed"
}
& $MakeAppxPath unpack /p $XamlAppX /d $xamlAppPath /o | Out-Null
If ($LASTEXITCODE -Ne 0) {
Throw "Unpacking $XamlAppX failed"
}
########
# Some sanity checking
########
$xamlManifest = [xml](Get-Content (Join-Path $xamlAppPath "AppxManifest.xml"))
If ($xamlManifest.Package.Identity.Name -NotLike "Microsoft.UI.Xaml*") {
Throw "$XamlAppX is not a XAML package (instead, it looks like $($xamlManifest.Package.Identity.Name))"
}
If ($xamlManifest.Package.Identity.ProcessorArchitecture -Ne $architecture) {
Throw "$XamlAppX is not built for $architecture (instead, it is built for $($xamlManifest.Package.Identity.ProcessorArchitecture))"
}
########
# Preparation of source files
########
$itemsToRemove = $filesToRemove | ForEach-Object {
Get-Item (Join-Path $terminalAppPath $_) -EA:SilentlyContinue | Where-Object {
$filesToKeep -NotContains $_.Name
}
} | Sort-Object FullName -Unique
$itemsToRemove | Remove-Item -Recurse
$filesToCopyFromXaml | ForEach-Object {
Get-Item (Join-Path $xamlAppPath $_)
} | Copy-Item -Recurse -Destination $terminalAppPath
########
# Resource Management
########
$finalTerminalPriFile = Join-Path $terminalAppPath "resources.pri"
& (Join-Path $PSScriptRoot "Merge-TerminalAndXamlResources.ps1") `
-TerminalRoot $terminalAppPath `
-XamlRoot $xamlAppPath `
-OutputPath $finalTerminalPriFile `
-Verbose:$Verbose
########
# Packaging
########
New-Item -ItemType Directory -Path $Destination -ErrorAction:SilentlyContinue | Out-Null
$outputZip = (Join-Path $Destination ("{0}.zip" -f ($distributionName)))
& tar -c --format=zip -f $outputZip -C $tempDir $terminalDir
Get-Item $outputZip

View File

@@ -1,14 +0,0 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
Param(
[string]$NewWindowsVersion = "10.0.22000.0"
)
Get-ChildItem src/cascadia/CascadiaPackage -Recurse -Filter *.appxmanifest | ForEach-Object {
$xml = [xml](Get-Content $_.FullName)
$xml.Package.Dependencies.TargetDeviceFamily | Where-Object Name -Like "Windows*" | ForEach-Object {
$_.MinVersion = $NewWindowsVersion
}
$xml.Save($_.FullName)
}

View File

@@ -96,11 +96,6 @@ Try {
Throw "Failed to find App.xbf (TerminalApp project) in resources.pri"
}
If (($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10.dll" -EA:Ignore)) -And
($null -eq (Get-Item "$AppxPackageRootPath\cpprest142_2_10d.dll" -EA:Ignore))) {
Throw "Failed to find cpprest142_2_10.dll -- check the WAP packaging project"
}
If (($null -eq (Get-Item "$AppxPackageRootPath\wtd.exe" -EA:Ignore)) -And
($null -eq (Get-Item "$AppxPackageRootPath\wt.exe" -EA:Ignore))) {
Throw "Failed to find wt.exe/wtd.exe -- check the WAP packaging project"

View File

@@ -10,22 +10,4 @@
<OpenConsoleDir>$(MSBuildThisFileDirectory)</OpenConsoleDir>
</PropertyGroup>
<PropertyGroup>
<!--
For the Windows 10 build, we're targeting the prerelease version of Microsoft.UI.Xaml.
This version emits every XAML DLL directly into our package.
This is a workaround for us not having deliverable MSFT-21242953 on this version of Windows.
This version should be tracked in all project packages.config files for projects that depend on Xaml.
-->
<TerminalMUXVersion>2.7.3-prerelease.220816001</TerminalMUXVersion>
<!--
For the Windows 11-specific build, we're targeting the public version of Microsoft.UI.Xaml.
This version emits a package dependency instead of embedding the dependency in our own package.
This version should be tracked in build/packages.config.
-->
<TerminalMUXVersion Condition="'$(TerminalTargetWindowsVersion)'=='Win11'">2.7.3</TerminalMUXVersion>
</PropertyGroup>
</Project>

View File

@@ -2,18 +2,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This file is read by XES, which we use in our Release builds. -->
<PropertyGroup Label="Version">
<!--
The Windows 11 build is going to have the same package name, so it *must* have a different version.
The easiest way for us to do this is to add 1 to the revision field.
In short, for a given Terminal build 1.11, we will emit two different versions (assume this is build
4 on day 23 of the year):
- 1.11.234.0 for Windows 10
- 1.11.235.0 for Windows 11
This presents a potential for conflicts if we want to ship two builds produced back to back on the
same day... which is terribly unlikely.
-->
<VersionBuildRevision Condition="'$(TerminalTargetWindowsVersion)'=='Win11' and '$(VersionBuildRevision)'!=''">$([MSBuild]::Add($(VersionBuildRevision), 1))</VersionBuildRevision>
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2022</XesBaseYearForStoreVersion>
<VersionMajor>1</VersionMajor>

View File

@@ -5,11 +5,10 @@
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
<package id="vcpkg-cpprestsdk" version="2.10.14" targetFramework="native" />
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
<package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.6.220404001" targetFramework="native" />
<package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
<package id="Microsoft.UI.Xaml" version="2.7.3-prerelease.220816001" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.220201.1" targetFramework="native" developmentDependency="true" />
<!-- Managed packages -->

View File

@@ -6,14 +6,14 @@ SamplerState samplerState;
// Terminal settings such as the resolution of the texture
cbuffer PixelShaderSettings {
// The number of seconds since the pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
// The number of seconds since the pixel shader was enabled
float Time;
// UI Scale
float Scale;
// Resolution of the shaderTexture
float2 Resolution;
// Background color as rgba
float4 Background;
};
// A pixel shader is a program that given a texture coordinate (tex) produces a color.
@@ -29,38 +29,19 @@ float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
// effect, read the colors offset on the left, right, top, bottom of this
// fragment, as well as on the corners of this fragment.
//
// You could get away with fewer samples, but the resulting outlines will be
// blurrier.
//left, right, top, bottom:
float4 leftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 0.0)/Resolution.y);
float4 rightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 0.0)/Resolution.y);
float4 topColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, 1.0)/Resolution.y);
float4 bottomColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 0.0, -1.0)/Resolution.y);
// Corners
float4 topLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, 1.0)/Resolution.y);
float4 topRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, 1.0)/Resolution.y);
float4 bottomLeftColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2( 1.0, -1.0)/Resolution.y);
float4 bottomRightColor = shaderTexture.Sample(samplerState, tex+1.0*Scale*float2(-1.0, -1.0)/Resolution.y);
// Now, if any of those adjacent cells has text in it, then the *color vec4
// will have a non-zero .w (which is used for alpha). Use that alpha value
// to add some black to the current fragment.
//
// This will result in only coloring fragments adjacent to text, but leaving
// background images (for example) untouched.
float3 outlineColor = float3(0, 0, 0);
float4 result = color;
result = result + float4(outlineColor, leftColor.w);
result = result + float4(outlineColor, rightColor.w);
result = result + float4(outlineColor, topColor.w);
result = result + float4(outlineColor, bottomColor.w);
result = result + float4(outlineColor, topLeftColor.w);
result = result + float4(outlineColor, topRightColor.w);
result = result + float4(outlineColor, bottomLeftColor.w);
result = result + float4(outlineColor, bottomRightColor.w);
return result;
for (int dy = -2; dy <= 2; dy += 2) {
for (int dx = -2; dx <= 2; dx += 2) {
float4 neighbor = shaderTexture.Sample(samplerState, tex, int2(dx, dy));
color.a += neighbor.a;
}
}
return color;
}

View File

@@ -197,26 +197,6 @@
<!-- **END VC LIBS HACK** -->
<!-- **BEGIN TERMINAL CONNECTION HACK** -->
<!-- This is the same as the above VC libs hack, but for TerminalConnection.
TerminalConnection depends on cpprest*.dll, and if we don't include it in
the packaging output, we'll crash as soon as we try to load
TerminalConnection.dll.
The Sample sln needs to do this manually - the real exe has a
ProjectReference to TerminalConnection.vcxproj and can figure this out on
its own. -->
<ItemGroup>
<_TerminalConnectionDlls Include="$(OpenConsoleCommonOutDir)\TerminalConnection\*.dll" />
<PackagingOutputs Include="@(_TerminalConnectionDlls)">
<ProjectName>$(ProjectName)</ProjectName>
<OutputGroup>BuiltProjectOutputGroup</OutputGroup>
<TargetPath>%(Filename)%(Extension)</TargetPath>
</PackagingOutputs>
</ItemGroup>
<!-- **END TERMINAL CONNECTION HACK** -->
<!-- Same thing again here, with WindowsTerminal.exe -->
<ItemGroup>
<_WindowsTerminalExe Include="$(OpenConsoleCommonOutDir)\WindowsTerminal\*.exe" />

View File

@@ -198,6 +198,11 @@ OutputCellIterator::operator bool() const noexcept
CATCH_FAIL_FAST();
}
size_t OutputCellIterator::Position() const noexcept
{
return _pos;
}
// Routine Description:
// - Advances the iterator one position over the underlying data source.
// Return Value:

View File

@@ -48,6 +48,7 @@ public:
operator bool() const noexcept;
size_t Position() const noexcept;
til::CoordType GetCellDistance(OutputCellIterator other) const noexcept;
til::CoordType GetInputDistance(OutputCellIterator other) const noexcept;
friend til::CoordType operator-(OutputCellIterator one, OutputCellIterator two) = delete;

View File

@@ -311,16 +311,20 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const til::CoordType c
}
break;
case DbcsAttribute::Trailing:
// Handling the trailing half of wide chars ensures that we correctly restore
// wide characters when a user backs up and restores the viewport via CHAR_INFOs.
if (fillingFirstColumn)
{
// The wide char doesn't fit. Pad with whitespace.
// Ignore the character. There's no correct alternative way to handle this situation.
ClearCell(currentIndex);
}
else
else if (it.Position() == 0)
{
// A common way to back up and restore the buffer is via `ReadConsoleOutputW` and
// `WriteConsoleOutputW` respectively. But the area might bisect/intersect/clip wide characters and
// only backup either their leading or trailing half. In general, in the rest of conhost, we're
// throwing away the trailing half of all `CHAR_INFO`s (during text rendering, as well as during
// `ReadConsoleOutputW`), so to make this code behave the same and prevent surprises, we need to
// make sure to only look at the trailer if it's the first `CHAR_INFO` the user is trying to write.
ReplaceCharacters(currentIndex - 1, 2, chars);
}
++it;

View File

@@ -10,9 +10,11 @@
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
IgnorableNamespaces="uap mp rescap uap3">
IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization">
<Identity
Name="WindowsTerminalDev"
@@ -23,6 +25,14 @@
<DisplayName>ms-resource:AppStoreNameDev</DisplayName>
<PublisherDisplayName>A Lone Developer</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
<!-- Older versions of Windows 10 respect this -->
<desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>
<!-- Newer versions of Windows 10 plus all versions of Windows 11 respect this -->
<virtualization:RegistryWriteVirtualization>
<virtualization:ExcludedKeys>
<virtualization:ExcludedKey>HKEY_CURRENT_USER\Console\%%Startup</virtualization:ExcludedKey>
</virtualization:ExcludedKeys>
</virtualization:RegistryWriteVirtualization>
</Properties>
<Dependencies>
@@ -136,5 +146,6 @@
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="unvirtualizedResources" />
</Capabilities>
</Package>

View File

@@ -12,8 +12,10 @@
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap uap3">
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization">
<Identity
Name="Microsoft.WindowsTerminalPreview"
@@ -24,6 +26,14 @@
<DisplayName>ms-resource:AppStoreNamePre</DisplayName>
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
<!-- Older versions of Windows 10 respect this -->
<desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>
<!-- Newer versions of Windows 10 plus all versions of Windows 11 respect this -->
<virtualization:RegistryWriteVirtualization>
<virtualization:ExcludedKeys>
<virtualization:ExcludedKey>HKEY_CURRENT_USER\Console\%%Startup</virtualization:ExcludedKey>
</virtualization:ExcludedKeys>
</virtualization:RegistryWriteVirtualization>
</Properties>
<Dependencies>
@@ -225,6 +235,7 @@
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="unvirtualizedResources" />
</Capabilities>
<Extensions>

View File

@@ -12,8 +12,10 @@
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap uap3">
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
IgnorableNamespaces="uap mp rescap uap3 desktop6 virtualization">
<Identity
Name="Microsoft.WindowsTerminal"
@@ -24,6 +26,14 @@
<DisplayName>ms-resource:AppStoreName</DisplayName>
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
<!-- Older versions of Windows 10 respect this -->
<desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>
<!-- Newer versions of Windows 10 plus all versions of Windows 11 respect this -->
<virtualization:RegistryWriteVirtualization>
<virtualization:ExcludedKeys>
<virtualization:ExcludedKey>HKEY_CURRENT_USER\Console\%%Startup</virtualization:ExcludedKey>
</virtualization:ExcludedKeys>
</virtualization:RegistryWriteVirtualization>
</Properties>
<Dependencies>
@@ -225,6 +235,7 @@
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
<rescap:Capability Name="unvirtualizedResources" />
</Capabilities>
<Extensions>

View File

@@ -20,6 +20,7 @@
<ConfigurationType>DynamicLibrary</ConfigurationType>
<OpenConsoleCppWinRTProject>true</OpenConsoleCppWinRTProject>
<!-- TerminalCppWinrt is not set intentionally -->
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
@@ -99,15 +100,4 @@
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
<Import Project="$(OpenConsoleDir)src\common.build.tests.props" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<PropertyGroup>
<!-- From Microsoft.UI.Xaml.targets -->
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
</PropertyGroup>
<!-- We actually can just straight up reference MUX here, it's fine -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
</Project>

View File

@@ -23,6 +23,7 @@
<PropertyGroup Label="NuGet Dependencies">
<!-- TerminalCppWinrt is intentionally not set -->
<TerminalMUX>true</TerminalMUX>
</PropertyGroup>
<Import Project="$(SolutionDir)\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
@@ -85,15 +86,4 @@
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
<Import Project="$(OpenConsoleDir)src\common.build.tests.props" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<PropertyGroup>
<!-- From Microsoft.UI.Xaml.targets -->
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
</PropertyGroup>
<!-- We actually can just straight up reference MUX here, it's fine -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
</Project>

View File

@@ -14,6 +14,9 @@
<UseWmXml>true</UseWmXml>
<ConfigurationType>Application</ConfigurationType>
<OpenConsoleCppWinRTProject>true</OpenConsoleCppWinRTProject>
<EnableHybridCRT>false</EnableHybridCRT> <!-- C++/CLI projects can't deal -->
<TerminalMUX>true</TerminalMUX>
<!--
These two properties are very important!
@@ -125,8 +128,6 @@
</Reference>
</ItemGroup>
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)\src\common.build.post.props" />
<Import Project="$(OpenConsoleDir)\src\common.nugetversions.targets" />

View File

@@ -222,7 +222,7 @@ HRESULT HwndTerminal::Initialize()
_renderEngine = std::move(dxEngine);
_terminal->Create({ 80, 25 }, 1000, *_renderer);
_terminal->Create({ 80, 25 }, 9001, *_renderer);
_terminal->SetWriteInputCallback([=](std::wstring_view input) noexcept { _WriteTextToConnection(input); });
localPointerToThread->EnablePainting();

View File

@@ -60,4 +60,4 @@
<AdditionalDependencies>Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>
</Project>

View File

@@ -300,7 +300,6 @@
<PRIResource Include="Resources\en-US\Resources.resw" />
<PRIResource Include="Resources\en-US\ContextMenu.resw" />
<OCResourceDirectory Include="Resources" />
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>

View File

@@ -1247,10 +1247,17 @@ namespace winrt::TerminalApp::implementation
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");
connection = TerminalConnection::ConptyConnection();
if constexpr (Feature_AzureConnectionInProc::IsEnabled())
{
connection = TerminalConnection::AzureConnection{};
}
else
{
connection = TerminalConnection::ConptyConnection{};
}
auto valueSet = TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(),
L".",
L"Azure",
@@ -2466,14 +2473,27 @@ namespace winrt::TerminalApp::implementation
{
return true;
}
// GH#10188: WSL paths are okay. We'll let those through.
if (host == L"wsl$" || host == L"wsl.localhost")
{
return true;
}
// TODO: by the OSC 8 spec, if a hostname (other than localhost) is provided, we _should_ be
// comparing that value against what is returned by GetComputerNameExW and making sure they match.
// However, ShellExecute does not seem to be happy with file URIs of the form
// file://{hostname}/path/to/file.ext
// and so while we could do the hostname matching, we do not know how to actually open the URI
// if its given in that form. So for now we ignore all hostnames other than localhost
return false;
}
return false;
// In this case, the app manually output a URI other than file:// or
// http(s)://. We'll trust the user knows what they're doing when
// clicking on those sorts of links.
// See discussion in GH#7562 for more details.
return true;
}
// Important! Don't take this eventArgs by reference, we need to extend the

View File

@@ -12,9 +12,6 @@
#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
static constexpr uint32_t DefaultRowsToScroll{ 3 };
static constexpr std::wstring_view TabletInputServiceKey{ L"TabletInputService" };
namespace TerminalAppLocalTests
{
class TabTests;
@@ -28,6 +25,9 @@ namespace Microsoft::Terminal::Core
namespace winrt::TerminalApp::implementation
{
inline constexpr uint32_t DefaultRowsToScroll{ 3 };
inline constexpr std::wstring_view TabletInputServiceKey{ L"TabletInputService" };
enum StartupState : int
{
NotInitialized = 0,

View File

@@ -7,9 +7,6 @@
#include "TabBase.h"
#include "TerminalTab.g.h"
static constexpr double HeaderRenameBoxWidthDefault{ 165 };
static constexpr double HeaderRenameBoxWidthTitleLength{ std::numeric_limits<double>::infinity() };
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
@@ -107,6 +104,9 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
private:
static constexpr double HeaderRenameBoxWidthDefault{ 165 };
static constexpr double HeaderRenameBoxWidthTitleLength{ std::numeric_limits<double>::infinity() };
std::shared_ptr<Pane> _rootPane{ nullptr };
std::shared_ptr<Pane> _activePane{ nullptr };
std::shared_ptr<Pane> _zoomedPane{ nullptr };

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.UI.Xaml" version="2.7.3-prerelease.220816001" targetFramework="native" />
</packages>

View File

@@ -41,7 +41,7 @@
<!-- Dependencies -->
<ItemGroup>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
<Project>{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}</Project>
<Project>{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}</Project>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
</ItemGroup>

View File

@@ -3,8 +3,6 @@
#pragma once
#include "cpprest/json.h"
namespace Microsoft::Terminal::Azure
{
class AzureException : public std::runtime_error
@@ -12,14 +10,24 @@ namespace Microsoft::Terminal::Azure
std::wstring _code;
public:
static bool IsErrorPayload(const web::json::value& errorObject)
static bool IsErrorPayload(const winrt::Windows::Data::Json::JsonObject& errorObject)
{
return errorObject.has_string_field(L"error");
if (!errorObject.HasKey(L"error"))
{
return false;
}
if (errorObject.GetNamedValue(L"error").ValueType() != winrt::Windows::Data::Json::JsonValueType::String)
{
return false;
}
return true;
}
AzureException(const web::json::value& errorObject) :
runtime_error(til::u16u8(errorObject.at(L"error_description").as_string())), // surface the human-readable description as .what()
_code(errorObject.at(L"error").as_string())
AzureException(const winrt::Windows::Data::Json::JsonObject& errorObject) :
runtime_error(til::u16u8(errorObject.GetNamedString(L"error_description"))), // surface the human-readable description as .what()
_code(errorObject.GetNamedString(L"error"))
{
}

View File

@@ -3,4 +3,4 @@
#pragma once
static constexpr std::wstring_view AzureClientID = L"0";
inline constexpr std::wstring_view AzureClientID = L"0";

View File

@@ -15,22 +15,24 @@
#include "winrt/Windows.System.UserProfile.h"
#include "../../types/inc/Utils.hpp"
#include "winrt/Windows.Web.Http.Filters.h"
using namespace ::Microsoft::Console;
using namespace ::Microsoft::Terminal::Azure;
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace web::websockets::client;
using namespace winrt::Windows::Security::Credentials;
static constexpr int CurrentCredentialVersion = 1;
static constexpr auto PasswordVaultResourceName = L"Terminal";
static constexpr auto HttpUserAgent = L"Terminal/0.0";
static constexpr int CurrentCredentialVersion = 2;
static constexpr std::wstring_view PasswordVaultResourceName = L"Terminal";
static constexpr std::wstring_view HttpUserAgent = L"Mozilla/5.0 (Windows NT 10.0) Terminal/1.0";
static constexpr int USER_INPUT_COLOR = 93; // yellow - the color of something the user can type
static constexpr int USER_INFO_COLOR = 97; // white - the color of clarifying information
using namespace winrt::Windows::Foundation;
namespace WDJ = ::winrt::Windows::Data::Json;
namespace WSS = ::winrt::Windows::Storage::Streams;
namespace WWH = ::winrt::Windows::Web::Http;
static constexpr winrt::guid AzureConnectionType = { 0xd9fcfdfa, 0xa479, 0x412c, { 0x83, 0xb7, 0xc5, 0x64, 0xe, 0x61, 0xcd, 0x62 } };
static inline std::wstring _colorize(const unsigned int colorCode, const std::wstring_view text)
@@ -115,6 +117,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - creates the output thread (where we will do the authentication and actually connect to Azure)
void AzureConnection::Start()
{
_httpClient = winrt::Windows::Web::Http::HttpClient{};
_httpClient.DefaultRequestHeaders().UserAgent().TryParseAdd(HttpUserAgent);
// Create our own output handling thread
// Each connection needs to make sure to drain the output from its backing host.
_hOutputThread.reset(CreateThread(
@@ -126,7 +130,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
return pInstance->_OutputThread();
}
return gsl::narrow_cast<DWORD>(E_INVALIDARG);
return gsl::narrow<DWORD>(E_INVALIDARG);
},
this,
0,
@@ -183,12 +187,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_state == AzureState::TermConnected)
{
// If we're connected, we don't need to do any fun input shenanigans.
websocket_outgoing_message msg;
const auto str = winrt::to_string(data);
msg.set_utf8_message(str);
_cloudShellSocket.send(msg).get();
auto buff{ winrt::to_string(data) };
WinHttpWebSocketSend(_webSocket.get(), WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE, buff.data(), gsl::narrow<DWORD>(buff.size()));
return;
}
@@ -238,16 +238,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
else // We only transition to Connected when we've established the websocket.
{
// Initialize client
http_client terminalClient(_cloudShellUri);
auto uri{ fmt::format(L"{}terminals/{}/size?cols={}&rows={}&version=2019-01-01", _cloudShellUri, _terminalID, columns, rows) };
// Initialize the request
http_request terminalRequest(L"POST");
terminalRequest.set_request_uri(fmt::format(L"terminals/{}/size?cols={}&rows={}&version=2019-01-01", _terminalID, columns, rows));
terminalRequest.set_body(json::value::null());
WWH::HttpStringContent content{
L"",
WSS::UnicodeEncoding::Utf8,
// LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'"
L"application/json"
};
// Send the request (don't care about the response)
(void)_SendAuthenticatedRequestReturningJson(terminalClient, terminalRequest);
std::ignore = _SendRequestReturningJson(uri, content);
}
}
CATCH_LOG();
@@ -264,7 +265,10 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
if (_state == AzureState::TermConnected)
{
// Close the websocket connection
_cloudShellSocket.close();
std::ignore = WinHttpWebSocketClose(_webSocket.get(), WINHTTP_WEB_SOCKET_SUCCESS_CLOSE_STATUS, nullptr, 0); // throw away the error
_webSocket.reset();
_socketConnectionHandle.reset();
_socketSessionHandle.reset();
}
if (_hOutputThread)
@@ -287,44 +291,46 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - tenant - the unparsed tenant
// Return value:
// - a tuple containing the ID and display name of the tenant.
static Tenant _crackTenant(const json::value& jsonTenant)
static Tenant _crackTenant(const WDJ::IJsonValue& value)
{
auto jsonTenant{ value.GetObjectW() };
Tenant tenant{};
if (jsonTenant.has_string_field(L"tenantID"))
if (jsonTenant.HasKey(L"tenantID"))
{
// for compatibility with version 1 credentials
tenant.ID = jsonTenant.at(L"tenantID").as_string();
tenant.ID = jsonTenant.GetNamedString(L"tenantID");
}
else
{
// This one comes in off the wire
tenant.ID = jsonTenant.at(L"tenantId").as_string();
tenant.ID = jsonTenant.GetNamedString(L"tenantId");
}
if (jsonTenant.has_string_field(L"displayName"))
if (jsonTenant.HasKey(L"displayName"))
{
tenant.DisplayName = jsonTenant.at(L"displayName").as_string();
tenant.DisplayName = jsonTenant.GetNamedString(L"displayName");
}
if (jsonTenant.has_string_field(L"defaultDomain"))
if (jsonTenant.HasKey(L"defaultDomain"))
{
tenant.DefaultDomain = jsonTenant.at(L"defaultDomain").as_string();
tenant.DefaultDomain = jsonTenant.GetNamedString(L"defaultDomain");
}
return tenant;
}
static void _packTenant(json::value& jsonTenant, const Tenant& tenant)
static void _packTenant(const WDJ::JsonObject& jsonTenant, const Tenant& tenant)
{
jsonTenant[L"tenantId"] = json::value::string(tenant.ID);
jsonTenant.SetNamedValue(L"tenantId", WDJ::JsonValue::CreateStringValue(tenant.ID));
if (tenant.DisplayName.has_value())
{
jsonTenant[L"displayName"] = json::value::string(*tenant.DisplayName);
jsonTenant.SetNamedValue(L"displayName", WDJ::JsonValue::CreateStringValue(*tenant.DisplayName));
}
if (tenant.DefaultDomain.has_value())
{
jsonTenant[L"defaultDomain"] = json::value::string(*tenant.DefaultDomain);
jsonTenant.SetNamedValue(L"defaultDomain", WDJ::JsonValue::CreateStringValue(*tenant.DefaultDomain));
}
}
@@ -383,35 +389,42 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
case AzureState::TermConnected:
{
_transitionToState(ConnectionState::Connected);
while (true)
{
// Read from websocket
pplx::task<websocket_incoming_message> msgT;
try
WINHTTP_WEB_SOCKET_BUFFER_TYPE bufferType{};
DWORD read{};
THROW_IF_WIN32_ERROR(WinHttpWebSocketReceive(_webSocket.get(), _buffer.data(), gsl::narrow<DWORD>(_buffer.size()), &read, &bufferType));
switch (bufferType)
{
msgT = _cloudShellSocket.receive();
msgT.wait();
case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
{
const auto result{ til::u8u16(std::string_view{ _buffer.data(), read }, _u16Str, _u8State) };
if (FAILED(result))
{
// EXIT POINT
_transitionToState(ConnectionState::Failed);
return gsl::narrow<DWORD>(result);
}
if (_u16Str.empty())
{
continue;
}
// Pass the output to our registered event handlers
_TerminalOutputHandlers(_u16Str);
break;
}
catch (...)
{
// Websocket has been closed; consider it a graceful exit?
// This should result in our termination.
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
// EXIT POINT
if (_transitionToState(ConnectionState::Closed))
{
// End the output thread.
return S_FALSE;
}
}
auto msg = msgT.get();
auto msgStringTask = msg.extract_string();
auto msgString = msgStringTask.get();
// Convert to hstring
const auto hstr = winrt::to_hstring(msgString);
// Pass the output to our registered event handlers
_TerminalOutputHandlers(hstr);
}
return S_OK;
}
@@ -449,25 +462,29 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
_tenantList.clear();
for (const auto& entry : credList)
{
auto nameJson = json::value::parse(entry.UserName().c_str());
std::optional<int> credentialVersion;
if (nameJson.has_integer_field(U("ver")))
try
{
credentialVersion = nameJson.at(U("ver")).as_integer();
auto nameJson = WDJ::JsonObject::Parse(entry.UserName());
std::optional<int> credentialVersion;
if (nameJson.HasKey(L"ver"))
{
credentialVersion = static_cast<int>(nameJson.GetNamedNumber(L"ver"));
}
if (!credentialVersion.has_value() || credentialVersion.value() != CurrentCredentialVersion)
{
// ignore credentials that aren't from the latest credential revision
vault.Remove(entry);
oldVersionEncountered = true;
continue;
}
auto newTenant{ _tenantList.emplace_back(_crackTenant(nameJson)) };
_WriteStringWithNewline(_formatTenant(numTenants, newTenant));
numTenants++;
}
if (!credentialVersion.has_value() || credentialVersion.value() != CurrentCredentialVersion)
{
// ignore credentials that aren't from the latest credential revision
vault.Remove(entry);
oldVersionEncountered = true;
continue;
}
auto newTenant{ _tenantList.emplace_back(_crackTenant(nameJson)) };
_WriteStringWithNewline(_formatTenant(numTenants, newTenant));
numTenants++;
CATCH_LOG();
}
if (!numTenants)
@@ -533,11 +550,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// User wants to login with one of the saved connection settings
auto desiredCredential = credList.GetAt(selectedTenant);
desiredCredential.RetrievePassword();
auto passWordJson = json::value::parse(desiredCredential.Password().c_str());
auto passWordJson = WDJ::JsonObject::Parse(desiredCredential.Password());
_currentTenant = til::at(_tenantList, selectedTenant); // we already unpacked the name info, so we should just use it
_accessToken = passWordJson.at(L"accessToken").as_string();
_refreshToken = passWordJson.at(L"refreshToken").as_string();
_expiry = std::stoi(passWordJson.at(L"expiry").as_string());
_setAccessToken(passWordJson.GetNamedString(L"accessToken"));
_refreshToken = passWordJson.GetNamedString(L"refreshToken");
_expiry = std::stoi(winrt::to_string(passWordJson.GetNamedString(L"expiry")));
const auto t1 = std::chrono::system_clock::now();
const auto timeNow = std::chrono::duration_cast<std::chrono::seconds>(t1.time_since_epoch()).count();
@@ -577,17 +594,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
const auto deviceCodeResponse = _GetDeviceCode();
// Print the message and store the device code, polling interval and expiry
const auto message = winrt::to_hstring(deviceCodeResponse.at(L"message").as_string().c_str());
const auto message{ deviceCodeResponse.GetNamedString(L"message") };
_WriteStringWithNewline(message);
_WriteStringWithNewline(RS_(L"AzureCodeExpiry"));
const auto devCode = deviceCodeResponse.at(L"device_code").as_string();
const auto pollInterval = std::stoi(deviceCodeResponse.at(L"interval").as_string());
const auto expiresIn = std::stoi(deviceCodeResponse.at(L"expires_in").as_string());
const auto devCode = deviceCodeResponse.GetNamedString(L"device_code");
const auto pollInterval = std::stoi(winrt::to_string(deviceCodeResponse.GetNamedString(L"interval")));
const auto expiresIn = std::stoi(winrt::to_string(deviceCodeResponse.GetNamedString(L"expires_in")));
// Wait for user authentication and obtain the access/refresh tokens
auto authenticatedResponse = _WaitForUser(devCode, pollInterval, expiresIn);
_accessToken = authenticatedResponse.at(L"access_token").as_string();
_refreshToken = authenticatedResponse.at(L"refresh_token").as_string();
_setAccessToken(authenticatedResponse.GetNamedString(L"access_token"));
_refreshToken = authenticatedResponse.GetNamedString(L"refresh_token");
// Get the tenants and the required tenant id
_PopulateTenantList();
@@ -696,19 +713,18 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - Helper function to parse the preferred shell type from user settings returned by cloud console API.
// We need this function because the field might be missing in the settings
// created with old versions of cloud console API.
std::optional<utility::string_t> AzureConnection::_ParsePreferredShellType(const web::json::value& settingsResponse)
winrt::hstring AzureConnection::_ParsePreferredShellType(const WDJ::JsonObject& settingsResponse)
{
if (settingsResponse.has_object_field(L"properties"))
if (settingsResponse.HasKey(L"properties"))
{
const auto userSettings = settingsResponse.at(L"properties");
if (userSettings.has_string_field(L"preferredShellType"))
const auto userSettings = settingsResponse.GetNamedObject(L"properties");
if (userSettings.HasKey(L"preferredShellType"))
{
const auto preferredShellTypeValue = userSettings.at(L"preferredShellType");
return preferredShellTypeValue.as_string();
return userSettings.GetNamedString(L"preferredShellType");
}
}
return std::nullopt;
return L"pwsh";
}
// Method description:
@@ -717,7 +733,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
// Get user's cloud shell settings
const auto settingsResponse = _GetCloudShellUserSettings();
if (settingsResponse.has_field(L"error"))
if (settingsResponse.HasKey(L"error"))
{
_WriteStringWithNewline(RS_(L"AzureNoCloudAccount"));
_transitionToState(ConnectionState::Failed);
@@ -732,12 +748,36 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// Request for a terminal for said cloud shell
const auto shellType = _ParsePreferredShellType(settingsResponse);
_WriteStringWithNewline(RS_(L"AzureRequestingTerminal"));
const auto socketUri = _GetTerminal(shellType.value_or(L"pwsh"));
const auto socketUri = _GetTerminal(shellType);
_TerminalOutputHandlers(L"\r\n");
// Step 8: connecting to said terminal
const auto connReqTask = _cloudShellSocket.connect(socketUri);
connReqTask.wait();
//// Step 8: connecting to said terminal
{
wil::unique_winhttp_hinternet sessionHandle, connectionHandle, requestHandle, socketHandle;
Uri parsedUri{ socketUri };
sessionHandle.reset(WinHttpOpen(HttpUserAgent.data(), WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, nullptr, nullptr, 0));
THROW_LAST_ERROR_IF(!sessionHandle);
connectionHandle.reset(WinHttpConnect(sessionHandle.get(), parsedUri.Host().c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0));
THROW_LAST_ERROR_IF(!connectionHandle);
requestHandle.reset(WinHttpOpenRequest(connectionHandle.get(), L"GET", parsedUri.Path().c_str(), nullptr, nullptr, nullptr, WINHTTP_FLAG_SECURE));
THROW_LAST_ERROR_IF(!requestHandle);
THROW_IF_WIN32_BOOL_FALSE(WinHttpSetOption(requestHandle.get(), WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, nullptr, 0));
#pragma warning(suppress : 26477) // WINHTTP_NO_ADDITIONAL_HEADERS expands to NULL rather than nullptr (who would have thought?)
THROW_IF_WIN32_BOOL_FALSE(WinHttpSendRequest(requestHandle.get(), WINHTTP_NO_ADDITIONAL_HEADERS, 0, nullptr, 0, 0, 0));
THROW_IF_WIN32_BOOL_FALSE(WinHttpReceiveResponse(requestHandle.get(), nullptr));
socketHandle.reset(WinHttpWebSocketCompleteUpgrade(requestHandle.get(), 0));
THROW_LAST_ERROR_IF(!socketHandle);
requestHandle.reset(); // We no longer need the request once we've upgraded it.
// We have to keep the socket session and connection handles.
_socketSessionHandle = std::move(sessionHandle);
_socketConnectionHandle = std::move(connectionHandle);
_webSocket = std::move(socketHandle);
}
_state = AzureState::TermConnected;
@@ -752,58 +792,52 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// Method description:
// - helper function to send requests with default headers and extract responses as json values
// Arguments:
// - a http_client
// - a http_request for the client to send
// - the URI
// - optional body content
// - an optional HTTP method (defaults to POST if content is present, GET otherwise)
// Return value:
// - the response from the server as a json value
json::value AzureConnection::_SendRequestReturningJson(http_client& theClient, http_request theRequest)
WDJ::JsonObject AzureConnection::_SendRequestReturningJson(std::wstring_view uri, const WWH::IHttpContent& content, WWH::HttpMethod method)
{
auto& headers{ theRequest.headers() };
headers.add(L"User-Agent", HttpUserAgent);
headers.add(L"Accept", L"application/json");
if (!method)
{
method = content == nullptr ? WWH::HttpMethod::Get() : WWH::HttpMethod::Post();
}
json::value jsonResult;
const auto responseTask = theClient.request(theRequest);
responseTask.wait();
const auto response = responseTask.get();
const auto responseJsonTask = response.extract_json();
responseJsonTask.wait();
jsonResult = responseJsonTask.get();
WWH::HttpRequestMessage request{ method, Uri{ uri } };
request.Content(content);
auto headers{ request.Headers() };
headers.Accept().TryParseAdd(L"application/json");
const auto response{ _httpClient.SendRequestAsync(request).get() };
const auto string{ response.Content().ReadAsStringAsync().get() };
const auto jsonResult{ WDJ::JsonObject::Parse(string) };
THROW_IF_AZURE_ERROR(jsonResult);
return jsonResult;
}
// Method description:
// - helper function to send _authenticated_ requests with json bodies whose responses are expected
// to be json. builds on _SendRequestReturningJson.
// Arguments:
// - the http_request
json::value AzureConnection::_SendAuthenticatedRequestReturningJson(http_client& theClient, http_request theRequest)
void AzureConnection::_setAccessToken(std::wstring_view accessToken)
{
auto& headers{ theRequest.headers() };
headers.add(L"Authorization", L"Bearer " + _accessToken);
return _SendRequestReturningJson(theClient, std::move(theRequest));
_accessToken = accessToken;
_httpClient.DefaultRequestHeaders().Authorization(WWH::Headers::HttpCredentialsHeaderValue{ L"Bearer", _accessToken });
}
// Method description:
// - helper function to start the device code flow
// Return value:
// - the response to the device code flow initiation
json::value AzureConnection::_GetDeviceCode()
WDJ::JsonObject AzureConnection::_GetDeviceCode()
{
// Initialize the client
http_client loginClient(_loginUri);
// Initialize the request
http_request commonRequest(L"POST");
commonRequest.set_request_uri(L"common/oauth2/devicecode");
const auto body{ fmt::format(L"client_id={}&resource={}", AzureClientID, _wantedResource) };
commonRequest.set_body(body.c_str(), L"application/x-www-form-urlencoded");
// Send the request and receive the response as a json value
return _SendRequestReturningJson(loginClient, commonRequest);
auto uri{ fmt::format(L"{}common/oauth2/devicecode", _loginUri) };
WWH::HttpFormUrlEncodedContent content{
std::unordered_map<winrt::hstring, winrt::hstring>{
{ winrt::hstring{ L"client_id" }, winrt::hstring{ AzureClientID } },
{ winrt::hstring{ L"resource" }, winrt::hstring{ _wantedResource } },
}
};
return _SendRequestReturningJson(uri, content);
}
// Method description:
@@ -815,14 +849,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// Return value:
// - if authentication is done successfully, then return the response from the server
// - else, throw an exception
json::value AzureConnection::_WaitForUser(const utility::string_t deviceCode, int pollInterval, int expiresIn)
WDJ::JsonObject AzureConnection::_WaitForUser(const winrt::hstring& deviceCode, int pollInterval, int expiresIn)
{
// Initialize the client
http_client pollingClient(_loginUri);
// Continuously send a poll request until the user authenticates
const auto body{ fmt::format(L"grant_type=device_code&resource={}&client_id={}&code={}", _wantedResource, AzureClientID, deviceCode) };
const auto requestUri = L"common/oauth2/token";
auto uri{ fmt::format(L"{}common/oauth2/token", _loginUri) };
WWH::HttpFormUrlEncodedContent content{
std::unordered_map<winrt::hstring, winrt::hstring>{
{ winrt::hstring{ L"grant_type" }, winrt::hstring{ L"device_code" } },
{ winrt::hstring{ L"client_id" }, winrt::hstring{ AzureClientID } },
{ winrt::hstring{ L"resource" }, winrt::hstring{ _wantedResource } },
{ winrt::hstring{ L"code" }, deviceCode },
}
};
// use a steady clock here so it's not impacted by local time discontinuities
const auto tokenExpiry{ std::chrono::steady_clock::now() + std::chrono::seconds(expiresIn) };
@@ -837,14 +874,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
break;
}
http_request pollRequest(L"POST");
pollRequest.set_request_uri(requestUri);
pollRequest.set_body(body.c_str(), L"application/x-www-form-urlencoded");
try
{
auto response{ _SendRequestReturningJson(pollingClient, pollRequest) };
auto response = _SendRequestReturningJson(uri, content);
_WriteStringWithNewline(RS_(L"AzureSuccessfullyAuthenticated"));
// Got a valid response: we're done
return response;
}
@@ -859,7 +893,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
} // uncaught exceptions bubble up to the caller
}
return json::value::null();
return nullptr;
}
// Method description:
@@ -868,16 +902,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - the response which contains a list of the user's Azure tenants
void AzureConnection::_PopulateTenantList()
{
// Initialize the client
http_client tenantClient(_resourceUri);
// Initialize the request
http_request tenantRequest(L"GET");
tenantRequest.set_request_uri(L"tenants?api-version=2020-01-01");
auto uri{ fmt::format(L"{}tenants?api-version=2020-01-01", _resourceUri) };
// Send the request and return the response as a json value
auto tenantResponse{ _SendAuthenticatedRequestReturningJson(tenantClient, tenantRequest) };
auto tenantList{ tenantResponse.at(L"value").as_array() };
auto tenantResponse{ _SendRequestReturningJson(uri, nullptr) };
auto tenantList{ tenantResponse.GetNamedArray(L"value") };
_tenantList.clear();
std::transform(tenantList.begin(), tenantList.end(), std::back_inserter(_tenantList), _crackTenant);
@@ -889,82 +918,73 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - the response with the new tokens
void AzureConnection::_RefreshTokens()
{
// Initialize the client
http_client refreshClient(_loginUri);
// Initialize the request
http_request refreshRequest(L"POST");
refreshRequest.set_request_uri(_currentTenant->ID + L"/oauth2/token");
const auto body{ fmt::format(L"client_id={}&resource={}&grant_type=refresh_token&refresh_token={}", AzureClientID, _wantedResource, _refreshToken) };
refreshRequest.set_body(body.c_str(), L"application/x-www-form-urlencoded");
auto uri{ fmt::format(L"{}{}/oauth2/token", _loginUri, _currentTenant->ID) };
WWH::HttpFormUrlEncodedContent content{
std::unordered_map<winrt::hstring, winrt::hstring>{
{ winrt::hstring{ L"grant_type" }, winrt::hstring{ L"refresh_token" } },
{ winrt::hstring{ L"client_id" }, winrt::hstring{ AzureClientID } },
{ winrt::hstring{ L"resource" }, winrt::hstring{ _wantedResource } },
{ winrt::hstring{ L"refresh_token" }, winrt::hstring{ _refreshToken } },
}
};
// Send the request and return the response as a json value
auto refreshResponse{ _SendRequestReturningJson(refreshClient, refreshRequest) };
_accessToken = refreshResponse.at(L"access_token").as_string();
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
auto refreshResponse{ _SendRequestReturningJson(uri, content) };
_setAccessToken(refreshResponse.GetNamedString(L"access_token"));
_refreshToken = refreshResponse.GetNamedString(L"refresh_token");
_expiry = std::stoi(winrt::to_string(refreshResponse.GetNamedString(L"expires_on")));
}
// Method description:
// - helper function to get the user's cloud shell settings
// Return value:
// - the user's cloud shell settings
json::value AzureConnection::_GetCloudShellUserSettings()
WDJ::JsonObject AzureConnection::_GetCloudShellUserSettings()
{
// Initialize client
http_client settingsClient(_resourceUri);
// Initialize request
http_request settingsRequest(L"GET");
settingsRequest.set_request_uri(L"providers/Microsoft.Portal/userSettings/cloudconsole?api-version=2018-10-01");
return _SendAuthenticatedRequestReturningJson(settingsClient, settingsRequest);
auto uri{ fmt::format(L"{}providers/Microsoft.Portal/userSettings/cloudconsole?api-version=2020-04-01-preview", _resourceUri) };
return _SendRequestReturningJson(uri, nullptr);
}
// Method description:
// - helper function to request for a cloud shell
// Return value:
// - the uri for the cloud shell
utility::string_t AzureConnection::_GetCloudShell()
winrt::hstring AzureConnection::_GetCloudShell()
{
// Initialize client
http_client cloudShellClient(_resourceUri);
auto uri{ fmt::format(L"{}providers/Microsoft.Portal/consoles/default?api-version=2020-04-01-preview", _resourceUri) };
// Initialize request
http_request shellRequest(L"PUT");
shellRequest.set_request_uri(L"providers/Microsoft.Portal/consoles/default?api-version=2018-10-01");
// { "properties": { "osType": "linux" } }
auto body = json::value::object({ { U("properties"), json::value::object({ { U("osType"), json::value::string(U("linux")) } }) } });
shellRequest.set_body(body);
WWH::HttpStringContent content{
LR"-({"properties": {"osType": "linux"}})-",
WSS::UnicodeEncoding::Utf8,
L"application/json"
};
// Send the request and get the response as a json value
const auto cloudShell = _SendAuthenticatedRequestReturningJson(cloudShellClient, shellRequest);
const auto cloudShell = _SendRequestReturningJson(uri, content, WWH::HttpMethod::Put());
// Return the uri
return cloudShell.at(L"properties").at(L"uri").as_string() + L"/";
return winrt::hstring{ std::wstring{ cloudShell.GetNamedObject(L"properties").GetNamedString(L"uri") } + L"/" };
}
// Method description:
// - helper function to request for a terminal
// Return value:
// - the uri for the terminal
utility::string_t AzureConnection::_GetTerminal(utility::string_t shellType)
winrt::hstring AzureConnection::_GetTerminal(const winrt::hstring& shellType)
{
// Initialize client
http_client terminalClient(_cloudShellUri);
auto uri{ fmt::format(L"{}terminals?cols={}&rows={}&version=2019-01-01&shell={}", _cloudShellUri, _initialCols, _initialRows, shellType) };
// Initialize the request
http_request terminalRequest(L"POST");
terminalRequest.set_request_uri(fmt::format(L"terminals?cols={}&rows={}&version=2019-01-01&shell={}", _initialCols, _initialRows, shellType));
// LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'"
terminalRequest.set_body(json::value::null());
WWH::HttpStringContent content{
L"",
WSS::UnicodeEncoding::Utf8,
// LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'"
L"application/json"
};
// Send the request and get the response as a json value
const auto terminalResponse = _SendAuthenticatedRequestReturningJson(terminalClient, terminalRequest);
_terminalID = terminalResponse.at(L"id").as_string();
const auto terminalResponse = _SendRequestReturningJson(uri, content);
_terminalID = terminalResponse.GetNamedString(L"id");
// Return the uri
return terminalResponse.at(L"socketUri").as_string();
return terminalResponse.GetNamedString(L"socketUri");
}
// Method description:
@@ -972,16 +992,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - we store the display name, tenant ID, access/refresh tokens, and token expiry
void AzureConnection::_StoreCredential()
{
json::value userName;
userName[U("ver")] = CurrentCredentialVersion;
WDJ::JsonObject userName;
userName.SetNamedValue(L"ver", WDJ::JsonValue::CreateNumberValue(CurrentCredentialVersion));
_packTenant(userName, *_currentTenant);
json::value passWord;
passWord[U("accessToken")] = json::value::string(_accessToken);
passWord[U("refreshToken")] = json::value::string(_refreshToken);
passWord[U("expiry")] = json::value::string(std::to_wstring(_expiry));
WDJ::JsonObject passWord;
passWord.SetNamedValue(L"accessToken", WDJ::JsonValue::CreateStringValue(_accessToken));
passWord.SetNamedValue(L"refreshToken", WDJ::JsonValue::CreateStringValue(_refreshToken));
passWord.SetNamedValue(L"expiry", WDJ::JsonValue::CreateStringValue(std::to_wstring(_expiry)));
PasswordVault vault;
PasswordCredential newCredential{ PasswordVaultResourceName, userName.serialize(), passWord.serialize() };
PasswordCredential newCredential{ PasswordVaultResourceName, userName.Stringify(), passWord.Stringify() };
vault.Add(newCredential);
}

View File

@@ -5,9 +5,6 @@
#include "AzureConnection.g.h"
#include <cpprest/http_client.h>
#include <cpprest/http_listener.h>
#include <cpprest/ws_client.h>
#include <mutex>
#include <condition_variable>
@@ -56,30 +53,30 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void _RunStoreState();
void _RunConnectState();
const utility::string_t _loginUri{ U("https://login.microsoftonline.com/") };
const utility::string_t _resourceUri{ U("https://management.azure.com/") };
const utility::string_t _wantedResource{ U("https://management.core.windows.net/") };
static constexpr std::wstring_view _loginUri{ L"https://login.microsoftonline.com/" };
static constexpr std::wstring_view _resourceUri{ L"https://management.azure.com/" };
static constexpr std::wstring_view _wantedResource{ L"https://management.core.windows.net/" };
const int _expireLimit{ 2700 };
utility::string_t _accessToken;
utility::string_t _refreshToken;
winrt::hstring _accessToken;
winrt::hstring _refreshToken;
int _expiry{ 0 };
utility::string_t _cloudShellUri;
utility::string_t _terminalID;
winrt::hstring _cloudShellUri;
winrt::hstring _terminalID;
std::vector<::Microsoft::Terminal::Azure::Tenant> _tenantList;
std::optional<::Microsoft::Terminal::Azure::Tenant> _currentTenant;
void _WriteStringWithNewline(const std::wstring_view str);
void _WriteCaughtExceptionRecord();
web::json::value _SendRequestReturningJson(web::http::client::http_client& theClient, web::http::http_request theRequest);
web::json::value _SendAuthenticatedRequestReturningJson(web::http::client::http_client& theClient, web::http::http_request theRequest);
web::json::value _GetDeviceCode();
web::json::value _WaitForUser(utility::string_t deviceCode, int pollInterval, int expiresIn);
winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr);
void _setAccessToken(std::wstring_view accessToken);
winrt::Windows::Data::Json::JsonObject _GetDeviceCode();
winrt::Windows::Data::Json::JsonObject _WaitForUser(const winrt::hstring& deviceCode, int pollInterval, int expiresIn);
void _PopulateTenantList();
void _RefreshTokens();
web::json::value _GetCloudShellUserSettings();
utility::string_t _GetCloudShell();
utility::string_t _GetTerminal(utility::string_t shellType);
winrt::Windows::Data::Json::JsonObject _GetCloudShellUserSettings();
winrt::hstring _GetCloudShell();
winrt::hstring _GetTerminal(const winrt::hstring& shellType);
void _StoreCredential();
void _RemoveCredentials();
@@ -95,9 +92,16 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::optional<std::wstring> _ReadUserInput(InputMode mode);
web::websockets::client::websocket_client _cloudShellSocket;
winrt::Windows::Web::Http::HttpClient _httpClient{ nullptr };
wil::unique_winhttp_hinternet _socketSessionHandle;
wil::unique_winhttp_hinternet _socketConnectionHandle;
wil::unique_winhttp_hinternet _webSocket;
static std::optional<utility::string_t> _ParsePreferredShellType(const web::json::value& settingsResponse);
til::u8state _u8State{};
std::wstring _u16Str;
std::array<char, 4096> _buffer{};
static winrt::hstring _ParsePreferredShellType(const winrt::Windows::Data::Json::JsonObject& settingsResponse);
};
}

View File

@@ -11,7 +11,6 @@
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>
<TerminalCppRestSDK>true</TerminalCppRestSDK>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
@@ -99,39 +98,4 @@
<!-- 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" />
<!--
BODGY:
We depend on `cpprest142*.dll`, which comes from our vcpkg dependency. As a
part of the vcpkg dependency restore, msbuild will call the `deployBinary()`
function in
`packages\vcpkg-cpprestsdk.2.10.14\scripts\BuildSystems\msbuild\AppLocal.ps1`.
That function does the actual job of copying the file. It copies it outside of
MsBuild. MsBuild then, in the `_CopyFilesMarkedCopyLocal` target, determines
that it needs to copy `cpprest142*.dll`, because that dll is a member of
`@(ReferencesCopiedInThisBuild)`. However, the file's already been copied, so
MsBuild never copies it. But that also prevents MsBuild from setting
`WroteAtLeastOneFile`, which then means that MsBuild will never create the
.CopyComplete file for this project.
Because that file is missing, MsBuild will never think the project is up to
date, and the FastUpToDate check in VS will always force MsBuild to run a pass
on this project.
To mitigate this, we're adding this other target here, which runs after
_CopyFilesMarkedCopyLocal, and always creates the CopyUpToDateMarker. This
makes the FastUpToDate check succeed.
-->
<Target
Name="_Post_CopyFilesMarkedCopyLocal"
AfterTargets="_CopyFilesMarkedCopyLocal"
Condition="'@(ReferenceCopyLocalPaths)' != ''">
<Touch Files="@(CopyUpToDateMarker)"
AlwaysCreate="true" />
</Target>
</Project>

View File

@@ -24,8 +24,14 @@
#include "winrt/Windows.Security.Credentials.h"
#include "winrt/Windows.Foundation.Collections.h"
#include "winrt/Windows.Web.Http.h"
#include "winrt/Windows.Web.Http.Headers.h"
#include "winrt/Windows.Data.Json.h"
#include <Windows.h>
#include <winhttp.h>
#include <wil/resource.h>
#include <TraceLoggingProvider.h>
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalConnectionProvider);
#include <telemetry/ProjectTelemetry.h>

View File

@@ -1100,7 +1100,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// content, which is unexpected.
const auto htmlData = formats == nullptr || WI_IsFlagSet(formats.Value(), CopyFormat::HTML) ?
TextBuffer::GenHTML(bufferData,
_actualFont.GetUnscaledSize().width,
_actualFont.GetUnscaledSize().height,
_actualFont.GetFaceName(),
bgColor) :
"";

View File

@@ -1990,6 +1990,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (!_IsClosing())
{
_closing = true;
if (_automationPeer)
{
auto autoPeerImpl{ winrt::get_self<implementation::TermControlAutomationPeer>(_automationPeer) };
autoPeerImpl->Close();
}
_RestorePointerCursorHandlers(*this, nullptr);
@@ -2886,9 +2891,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto lastHoveredCell = _core.HoveredCell();
if (lastHoveredCell)
{
const auto uriText = _core.HoveredUriText();
if (!uriText.empty())
winrt::hstring uriText = _core.HoveredUriText();
if (uriText.empty())
{
co_return;
}
try
{
// DisplayUri will filter out non-printable characters and confusables.
Windows::Foundation::Uri parsedUri{ uriText };
if (!parsedUri)
{
co_return;
}
uriText = parsedUri.DisplayUri();
const auto panel = SwapChainPanel();
const auto scale = panel.CompositionScaleX();
const auto offset = panel.ActualOffset();
@@ -2910,6 +2928,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), locationInDIPs.x - offset.x);
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), locationInDIPs.y - offset.y);
}
CATCH_LOG();
}
}
}

View File

@@ -55,8 +55,6 @@
-->
<x:Double x:Key="ScrollBarSize">16</x:Double>
<CornerRadius x:Key="ScrollBarCornerRadius">0</CornerRadius>
<CornerRadius x:Key="ScrollBarThumbCornerRadius">3</CornerRadius>
<Style x:Key="ForkedScrollbarTemplate"
TargetType="ScrollBar">
@@ -120,16 +118,16 @@
Value="{ThemeResource ScrollBarButtonArrowForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
@@ -187,16 +185,16 @@
Value="{ThemeResource ScrollBarButtonArrowForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
@@ -253,16 +251,16 @@
Value="{ThemeResource ScrollBarButtonArrowForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
@@ -319,16 +317,16 @@
Value="{ThemeResource ScrollBarButtonArrowForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleX">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames RepeatBehavior="Forever"
Storyboard.TargetName="ScaleTransform"
Storyboard.TargetProperty="ScaleY">
Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<DiscreteDoubleKeyFrame KeyTime="0:0:0.016"
Value="{ThemeResource ScrollBarButtonArrowScalePressed}" />
<DiscreteDoubleKeyFrame KeyTime="0:0:30"
@@ -352,9 +350,11 @@
<ControlTemplate x:Key="VerticalThumbTemplate"
TargetType="Thumb">
<Rectangle x:Name="ThumbVisual"
contract7NotPresent:RadiusX="{Binding Source={ThemeResource ScrollBarCornerRadius}, Converter={StaticResource TopLeftCornerRadiusDoubleValueConverter}}"
contract7NotPresent:RadiusY="{Binding Source={ThemeResource ScrollBarCornerRadius}, Converter={StaticResource BottomRightCornerRadiusDoubleValueConverter}}"
contract7Present:RadiusX="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource TopLeftCornerRadiusDoubleValueConverter}}"
contract7Present:RadiusY="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BottomRightCornerRadiusDoubleValueConverter}}"
Fill="{TemplateBinding Background}"
RadiusX="{Binding Source={ThemeResource ScrollBarThumbCornerRadius}, Converter={StaticResource TopLeftCornerRadiusDoubleValueConverter}}"
RadiusY="{Binding Source={ThemeResource ScrollBarThumbCornerRadius}, Converter={StaticResource BottomRightCornerRadiusDoubleValueConverter}}"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="{ThemeResource ScrollBarThumbStrokeThickness}">
<VisualStateManager.VisualStateGroups>
@@ -409,6 +409,7 @@
</ControlTemplate>
</Grid.Resources>
<!-- Windows Terminal: This is another addition/customization made by us. -->
<VisualStateManager.CustomVisualStateManager>
<local:ScrollBarVisualStateManager />
</VisualStateManager.CustomVisualStateManager>
@@ -425,13 +426,13 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!--
Windows Terminal: Here we've removed the corner radius
to make our scrollbars be flush with the window frame.
-->
<Rectangle x:Name="HorizontalTrackRect"
Grid.ColumnSpan="5"
Margin="0"
contract7NotPresent:RadiusX="{Binding Source={ThemeResource ScrollBarCornerRadius}, Converter={StaticResource TopLeftCornerRadiusDoubleValueConverter2x}}"
contract7NotPresent:RadiusY="{Binding Source={ThemeResource ScrollBarCornerRadius}, Converter={StaticResource BottomRightCornerRadiusDoubleValueConverter2x}}"
contract7Present:RadiusX="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource TopLeftCornerRadiusDoubleValueConverter2x}}"
contract7Present:RadiusY="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BottomRightCornerRadiusDoubleValueConverter2x}}"
Fill="{ThemeResource ScrollBarTrackFill}"
Opacity="0"
Stroke="{ThemeResource ScrollBarTrackStroke}"
@@ -520,13 +521,13 @@
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!--
Windows Terminal: Here we've removed the corner radius
to make our scrollbars be flush with the window frame.
-->
<Rectangle x:Name="VerticalTrackRect"
Grid.RowSpan="5"
Margin="0"
contract7NotPresent:RadiusX="{Binding Source={ThemeResource ScrollBarCornerRadius}, Converter={StaticResource TopLeftCornerRadiusDoubleValueConverter2x}}"
contract7NotPresent:RadiusY="{Binding Source={ThemeResource ScrollBarCornerRadius}, Converter={StaticResource BottomRightCornerRadiusDoubleValueConverter2x}}"
contract7Present:RadiusX="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource TopLeftCornerRadiusDoubleValueConverter2x}}"
contract7Present:RadiusY="{Binding CornerRadius, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BottomRightCornerRadiusDoubleValueConverter2x}}"
Fill="{ThemeResource ScrollBarTrackFill}"
Opacity="0"
Stroke="{ThemeResource ScrollBarTrackStroke}"
@@ -929,6 +930,7 @@
To="1"
Duration="{StaticResource ScrollBarOpacityChangeDuration}" />
<!-- Because of the blurriness caused by SCALE animation performed on the object with rounded corners, we have to use dependent animation on width to rerasterize the mask on every tick of the animation. -->
<DoubleAnimationUsingKeyFrames BeginTime="{StaticResource ScrollBarExpandBeginTime}"
EnableDependentAnimation="True"
Storyboard.TargetName="VerticalThumb"

View File

@@ -117,6 +117,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void TermControlAutomationPeer::Close()
{
// GH#13978: If the TermControl has already been removed from the UI tree, XAML might run into weird bugs.
// This will prevent the `dispatcher.RunAsync` calls below from raising UIA events on the main thread.
_termControl = {};
}
// Method Description:
// - Signals the ui automation client that the terminal's selection has changed and should be updated
// Arguments:

View File

@@ -49,6 +49,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void UpdateControlBounds();
void SetControlPadding(const Core::Padding padding);
void RecordKeyEvent(const WORD vkey);
void Close();
#pragma region FrameworkElementAutomationPeer
hstring GetClassNameCore() const;

View File

@@ -17,8 +17,8 @@
#include <til/ticket_lock.h>
static constexpr std::wstring_view linkPattern{ LR"(\b(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|$!:,.;]*[A-Za-z0-9+&@#/%=~_|$])" };
static constexpr size_t TaskbarMinProgress{ 10 };
inline constexpr std::wstring_view linkPattern{ LR"(\b(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|$!:,.;]*[A-Za-z0-9+&@#/%=~_|$])" };
inline constexpr size_t TaskbarMinProgress{ 10 };
// You have to forward decl the ICoreSettings here, instead of including the header.
// If you include the header, there will be compilation errors with other

View File

@@ -46,6 +46,9 @@
<ProjectReference Include="$(OpenConsoleDir)src\renderer\dx\lib\dx.vcxproj">
<Project>{48d21369-3d7b-4431-9967-24e81292cf62}</Project>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\audio\midi\lib\midi.vcxproj">
<Project>{3c67784e-1453-49c2-9660-483e2cc7f7ad}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>

View File

@@ -338,7 +338,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
auto lifetime = get_strong();
const auto parentHwnd{ reinterpret_cast<HWND>(Appearance().WindowRoot().GetHostingWindow()) };
const auto parentHwnd{ reinterpret_cast<HWND>(WindowRoot().GetHostingWindow()) };
auto file = co_await OpenImagePicker(parentHwnd);
if (!file.empty())
{

View File

@@ -74,7 +74,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void CurrentColorScheme(const Editor::ColorSchemeViewModel& val);
WINRT_PROPERTY(bool, IsDefault, false);
WINRT_PROPERTY(IHostedInWindow, WindowRoot, nullptr);
// These settings are not defined in AppearanceConfig, so we grab them
// from the source profile itself. The reason we still want them in the
@@ -134,6 +133,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
DEPENDENCY_PROPERTY(Editor::AppearanceViewModel, Appearance);
WINRT_PROPERTY(Editor::ProfileViewModel, SourceProfile, nullptr);
WINRT_PROPERTY(IHostedInWindow, WindowRoot, nullptr);
GETSET_BINDABLE_ENUM_SETTING(BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch, Appearance().BackgroundImageStretchMode);

View File

@@ -35,7 +35,6 @@ namespace Microsoft.Terminal.Settings.Editor
void ClearColorScheme();
ColorSchemeViewModel CurrentColorScheme;
Windows.Foundation.Collections.IObservableVector<ColorSchemeViewModel> SchemesList;
IHostedInWindow WindowRoot; // necessary to send the right HWND into the file picker dialogs.
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(String, FontFace);
OBSERVABLE_PROJECTED_APPEARANCE_SETTING(Single, FontSize);
@@ -59,6 +58,7 @@ namespace Microsoft.Terminal.Settings.Editor
Appearances();
AppearanceViewModel Appearance;
ProfileViewModel SourceProfile;
IHostedInWindow WindowRoot;
static Windows.UI.Xaml.DependencyProperty AppearanceProperty { get; };
Boolean UsingMonospaceFont { get; };

View File

@@ -10,13 +10,13 @@
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
inline static constexpr uint8_t ColorTableDivider{ 8 };
inline static constexpr uint8_t ColorTableSize{ 16 };
inline constexpr uint8_t ColorTableDivider{ 8 };
inline constexpr uint8_t ColorTableSize{ 16 };
inline static constexpr std::wstring_view ForegroundColorTag{ L"Foreground" };
inline static constexpr std::wstring_view BackgroundColorTag{ L"Background" };
inline static constexpr std::wstring_view CursorColorTag{ L"CursorColor" };
inline static constexpr std::wstring_view SelectionBackgroundColorTag{ L"SelectionBackground" };
inline constexpr std::wstring_view ForegroundColorTag{ L"Foreground" };
inline constexpr std::wstring_view BackgroundColorTag{ L"Background" };
inline constexpr std::wstring_view CursorColorTag{ L"CursorColor" };
inline constexpr std::wstring_view SelectionBackgroundColorTag{ L"SelectionBackground" };
struct ColorSchemeViewModel : ColorSchemeViewModelT<ColorSchemeViewModel>, ViewModelHelper<ColorSchemeViewModel>
{

View File

@@ -42,16 +42,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// Only let this succeed once.
_layoutUpdatedRevoker.revoke();
for (const auto scheme : _ViewModel.AllColorSchemes())
{
if (scheme.IsDefaultScheme())
{
winrt::hstring newName{ fmt::format(L"{} ({})", scheme.Name(), RS_(L"ColorScheme_DefaultTag/Text")) };
Automation::AutomationProperties::SetName(ColorSchemeListView().ContainerFromItem(scheme), newName);
break;
}
}
ColorSchemeListView().Focus(FocusState::Programmatic);
});
}

View File

@@ -43,6 +43,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
constexpr std::wstring_view systemThemeName{ L"system" };
constexpr std::wstring_view darkThemeName{ L"dark" };
constexpr std::wstring_view lightThemeName{ L"light" };
constexpr std::wstring_view legacySystemThemeName{ L"legacySystem" };
constexpr std::wstring_view legacyDarkThemeName{ L"legacyDark" };
constexpr std::wstring_view legacyLightThemeName{ L"legacyLight" };
GlobalAppearanceViewModel::GlobalAppearanceViewModel(Model::GlobalAppSettings globalSettings) :
_GlobalSettings{ globalSettings },
@@ -248,6 +251,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
return RS_(L"Globals_ThemeSystem/Content");
}
else if (theme.Name() == legacyDarkThemeName)
{
return RS_(L"Globals_ThemeDarkLegacy/Content");
}
else if (theme.Name() == legacyLightThemeName)
{
return RS_(L"Globals_ThemeLightLegacy/Content");
}
else if (theme.Name() == legacySystemThemeName)
{
return RS_(L"Globals_ThemeSystemLegacy/Content");
}
return theme.Name();
}

View File

@@ -117,6 +117,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
lastBreadcrumb = _breadcrumbs.GetAt(size - 1);
}
// Collect all the values out of the old nav view item source
auto menuItems{ SettingsNav().MenuItems() };
// We'll remove a bunch of items and iterate over it twice.
@@ -156,7 +157,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}),
menuItemsSTL.end());
menuItems.ReplaceAll(menuItemsSTL);
// Now, we've got a list of just the static entries again. Lets take
// those and stick them back into a new winrt vector, and set that as
// the source again.
//
// By setting MenuItemsSource in its entirety, rather than manipulating
// MenuItems, we avoid a crash in WinUI.
auto newSource = winrt::single_threaded_vector<IInspectable>(std::move(menuItemsSTL));
SettingsNav().MenuItemsSource(newSource);
// Repopulate profile-related menu items
_InitializeProfilesList();
@@ -209,7 +217,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// Couldn't find the selected item, fallback to first menu item
// This happens when the selected item was a profile which doesn't exist in the new configuration
// We can use menuItemsSTL here because the only things they miss are profile entries.
const auto& firstItem{ menuItemsSTL.at(0).as<MUX::Controls::NavigationViewItem>() };
const auto& firstItem{ SettingsNav().MenuItems().GetAt(0).as<MUX::Controls::NavigationViewItem>() };
SettingsNav().SelectedItem(firstItem);
_Navigate(unbox_value<hstring>(firstItem.Tag()), BreadcrumbSubPage::None);
}
@@ -348,14 +356,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto currentPage = profile.CurrentPage();
if (currentPage == ProfileSubPage::Base)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), profile);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
_breadcrumbs.Clear();
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, breadcrumbText, BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
}
else if (currentPage == ProfileSubPage::Appearance)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Appearance>(), profile);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Appearance>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, RS_(L"Profile_Appearance/Header"), BreadcrumbSubPage::Profile_Appearance);
_breadcrumbs.Append(crumb);
}
@@ -400,12 +408,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
else if (clickedItemTag == globalProfileTag)
{
auto profileVM{ _viewModelForProfile(_settingsClone.ProfileDefaults(), _settingsClone) };
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes(), *this);
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
profileVM.IsBaseLayer(true);
_SetupProfileEventHandling(profileVM);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), profileVM);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<implementation::NavigateToProfileArgs>(profileVM, *this));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_ProfileDefaults/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
@@ -457,7 +465,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_SetupProfileEventHandling(profile);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), profile);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
const auto crumb = winrt::make<Breadcrumb>(box_value(profile), profile.Name(), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
@@ -527,7 +535,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void MainPage::_InitializeProfilesList()
{
const auto menuItems = SettingsNav().MenuItems();
const auto& itemSource{ SettingsNav().MenuItemsSource() };
if (!itemSource)
{
// There wasn't a MenuItemsSource set yet? The only way that's
// possible is if we haven't used
// _MoveXamlParsedNavItemsIntoItemSource to move the hardcoded menu
// entries from XAML into our runtime menu item source. Do that now.
_MoveXamlParsedNavItemsIntoItemSource();
}
const auto menuItems = SettingsNav().MenuItemsSource().try_as<IVector<IInspectable>>();
// Manually create a NavigationViewItem for each profile
// and keep a reference to them in a map so that we
@@ -538,7 +556,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (!profile.Deleted())
{
auto profileVM = _viewModelForProfile(profile, _settingsClone);
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes(), *this);
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
auto navItem = _CreateProfileNavViewItem(profileVM);
menuItems.Append(navItem);
}
@@ -557,11 +575,42 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
menuItems.Append(addProfileItem);
}
// BODGY
// Does the very wacky business of moving all our MenuItems that we
// hardcoded in XAML into a runtime MenuItemsSource. We'll then use _that_
// MenuItemsSource as the source for our nav view entries instead. This
// lets us hardcode the initial entries in precompiled XAML, but then adjust
// the items at runtime. Without using a MenuItemsSource, the NavView just
// crashes when items are removed (see GH#13673)
void MainPage::_MoveXamlParsedNavItemsIntoItemSource()
{
if (SettingsNav().MenuItemsSource())
{
// We've already copied over the original items to a source. We can
// just skip this now.
return;
}
auto menuItems{ SettingsNav().MenuItems() };
// Remove all the existing items, and move them to a separate vector
// that we'll use as a MenuItemsSource. By doing this, we avoid a WinUI
// bug (MUX#6302) where modifying the NavView.Items() directly causes a
// crash. By leaving these static entries in XAML, we maintain the
// benefit of instantiating them from the XBF, rather than at runtime.
//
// --> Copy it into an STL vector to simplify our code and reduce COM overhead.
std::vector<IInspectable> menuItemsSTL(menuItems.Size(), nullptr);
menuItems.GetMany(0, menuItemsSTL);
auto newSource = winrt::single_threaded_vector<IInspectable>(std::move(menuItemsSTL));
SettingsNav().MenuItemsSource(newSource);
}
void MainPage::_CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile)
{
const auto newProfile{ profile ? profile : _settingsClone.CreateNewProfile() };
const auto profileViewModel{ _viewModelForProfile(newProfile, _settingsClone) };
profileViewModel.SetupAppearances(_colorSchemesPageVM.AllColorSchemes(), *this);
profileViewModel.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
const auto navItem{ _CreateProfileNavViewItem(profileViewModel) };
SettingsNav().MenuItems().InsertAt(index, navItem);

View File

@@ -68,6 +68,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void _Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage);
void _UpdateBackgroundForMica();
void _MoveXamlParsedNavItemsIntoItemSource();
winrt::Microsoft::Terminal::Settings::Editor::ColorSchemesPageViewModel _colorSchemesPageVM{ nullptr };

View File

@@ -9,6 +9,7 @@
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:tsm="using:Microsoft.Terminal.Settings.Model"
mc:Ignorable="d">
<Page.Resources>
@@ -189,13 +190,29 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Uid="Settings_UnsavedSettingsWarning"
Margin="30,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="Goldenrod"
TextAlignment="Left"
Visibility="Collapsed" />
<StackPanel Margin="30,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Orientation="Vertical">
<TextBlock x:Uid="Settings_UnsavedSettingsWarning"
Foreground="Goldenrod"
TextAlignment="Left"
Visibility="Collapsed" />
<StackPanel VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="4"
Visibility="{x:Bind tsm:CascadiaSettings.IsPortableMode, Mode=OneTime}">
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}"
Foreground="SlateBlue"
Glyph="&#xE946;" />
<TextBlock Foreground="SlateBlue">
<Run x:Uid="Settings_PortableModeNote" />
<Hyperlink x:Uid="Settings_PortableModeInfoLink">
<Run x:Uid="Settings_PortableModeInfoLinkTextRun" />
</Hyperlink>
</TextBlock>
</StackPanel>
</StackPanel>
<StackPanel Margin="0,0,30,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"

View File

@@ -353,7 +353,6 @@
</PRIResource>
<OCResourceDirectory Include="Resources" />
<None Include="$(ProjectName).def" />
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>

View File

@@ -248,7 +248,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_unfocusedAppearanceViewModel = winrt::make<implementation::AppearanceViewModel>(_profile.UnfocusedAppearance().try_as<AppearanceConfig>());
_unfocusedAppearanceViewModel.SchemesList(DefaultAppearance().SchemesList());
_unfocusedAppearanceViewModel.WindowRoot(DefaultAppearance().WindowRoot());
_NotifyChanges(L"UnfocusedAppearance", L"HasUnfocusedAppearance", L"ShowUnfocusedAppearance");
}
@@ -350,14 +349,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_DeleteProfileHandlers(*this, *deleteProfileArgs);
}
void ProfileViewModel::SetupAppearances(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel> schemesList, Editor::IHostedInWindow windowRoot)
void ProfileViewModel::SetupAppearances(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel> schemesList)
{
DefaultAppearance().SchemesList(schemesList);
DefaultAppearance().WindowRoot(windowRoot);
if (UnfocusedAppearance())
{
UnfocusedAppearance().SchemesList(schemesList);
UnfocusedAppearance().WindowRoot(windowRoot);
}
}
}

View File

@@ -4,6 +4,7 @@
#pragma once
#include "DeleteProfileEventArgs.g.h"
#include "NavigateToProfileArgs.g.h"
#include "ProfileViewModel.g.h"
#include "Utils.h"
#include "ViewModelHelpers.h"
@@ -11,6 +12,21 @@
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct NavigateToProfileArgs : NavigateToProfileArgsT<NavigateToProfileArgs>
{
public:
NavigateToProfileArgs(ProfileViewModel profile, Editor::IHostedInWindow windowRoot) :
_Profile(profile),
_WindowRoot(windowRoot) {}
Editor::IHostedInWindow WindowRoot() const noexcept { return _WindowRoot; }
Editor::ProfileViewModel Profile() const noexcept { return _Profile; }
private:
Editor::IHostedInWindow _WindowRoot;
Editor::ProfileViewModel _Profile{ nullptr };
};
struct ProfileViewModel : ProfileViewModelT<ProfileViewModel>, ViewModelHelper<ProfileViewModel>
{
public:
@@ -23,7 +39,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Model::TerminalSettings TermSettings() const;
void DeleteProfile();
void SetupAppearances(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel> schemesList, Editor::IHostedInWindow windowRoot);
void SetupAppearances(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel> schemesList);
// bell style bits
bool IsBellStyleFlagSet(const uint32_t flag);
@@ -91,7 +107,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(bool, IsBaseLayer, false);
WINRT_PROPERTY(bool, FocusDeleteButton, false);
WINRT_PROPERTY(IHostedInWindow, WindowRoot, nullptr);
GETSET_BINDABLE_ENUM_SETTING(AntiAliasingMode, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode);
GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit);
GETSET_BINDABLE_ENUM_SETTING(ScrollState, Microsoft::Terminal::Control::ScrollbarState, ScrollState);

View File

@@ -14,6 +14,12 @@ import "ColorSchemesPageViewModel.idl";
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass NavigateToProfileArgs
{
ProfileViewModel Profile { get; };
IHostedInWindow WindowRoot { get; };
}
runtimeclass DeleteProfileEventArgs
{
Guid ProfileGuid { get; };
@@ -35,7 +41,7 @@ namespace Microsoft.Terminal.Settings.Editor
event Windows.Foundation.TypedEventHandler<ProfileViewModel, DeleteProfileEventArgs> DeleteProfile;
void SetupAppearances(Windows.Foundation.Collections.IObservableVector<ColorSchemeViewModel> schemesList, IHostedInWindow windowRoot);
void SetupAppearances(Windows.Foundation.Collections.IObservableVector<ColorSchemeViewModel> schemesList);
void SetAcrylicOpacityPercentageValue(Double value);
void SetPadding(Double value);

View File

@@ -28,7 +28,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Profiles_Appearance::OnNavigatedTo(const NavigationEventArgs& e)
{
_Profile = e.Parameter().as<Editor::ProfileViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
_Profile = args.Profile();
_windowRoot = args.WindowRoot();
// generate the font list, if we don't have one
if (_Profile.CompleteFontList() || !_Profile.MonospaceFontList())

View File

@@ -19,6 +19,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void CreateUnfocusedAppearance_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void DeleteUnfocusedAppearance_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
Editor::IHostedInWindow WindowRoot() { return _windowRoot; };
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr);
@@ -26,6 +28,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Microsoft::Terminal::Control::TermControl _previewControl;
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _AppearanceViewModelChangedRevoker;
Editor::IHostedInWindow _windowRoot;
};
};

View File

@@ -9,6 +9,7 @@ namespace Microsoft.Terminal.Settings.Editor
{
Profiles_Appearance();
ProfileViewModel Profile { get; };
IHostedInWindow WindowRoot { get; };
Windows.UI.Xaml.Controls.Slider OpacitySlider { get; };
}

View File

@@ -56,7 +56,8 @@
</Border>
<local:Appearances Appearance="{x:Bind Profile.DefaultAppearance, Mode=OneWay}"
SourceProfile="{x:Bind Profile, Mode=OneWay}" />
SourceProfile="{x:Bind Profile, Mode=OneWay}"
WindowRoot="{x:Bind WindowRoot, Mode=OneTime}" />
<!-- Grouping: Transparency -->
<StackPanel Style="{StaticResource PivotStackStyle}">
@@ -174,7 +175,8 @@
</StackPanel>
<local:Appearances Appearance="{x:Bind Profile.UnfocusedAppearance, Mode=OneWay}"
SourceProfile="{x:Bind Profile, Mode=OneWay}"
Visibility="{x:Bind Profile.ShowUnfocusedAppearance, Mode=OneWay}" />
Visibility="{x:Bind Profile.ShowUnfocusedAppearance, Mode=OneWay}"
WindowRoot="{x:Bind WindowRoot, Mode=OneTime}" />
</StackPanel>
</StackPanel>
</ScrollViewer>

View File

@@ -29,7 +29,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e)
{
_Profile = e.Parameter().as<Editor::ProfileViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
_Profile = args.Profile();
_windowRoot = args.WindowRoot();
// Check the use parent directory box if the starting directory is empty
if (_Profile.StartingDirectory().empty())
@@ -85,7 +87,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
};
static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };
const auto parentHwnd{ reinterpret_cast<HWND>(winrt::get_self<ProfileViewModel>(_Profile)->WindowRoot().GetHostingWindow()) };
const auto parentHwnd{ reinterpret_cast<HWND>(_windowRoot.GetHostingWindow()) };
auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExecutables));
try
@@ -109,7 +111,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
auto lifetime = get_strong();
const auto parentHwnd{ reinterpret_cast<HWND>(winrt::get_self<ProfileViewModel>(_Profile)->WindowRoot().GetHostingWindow()) };
const auto parentHwnd{ reinterpret_cast<HWND>(_windowRoot.GetHostingWindow()) };
auto file = co_await OpenImagePicker(parentHwnd);
if (!file.empty())
{
@@ -120,7 +122,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
fire_and_forget Profiles_Base::StartingDirectory_Click(const IInspectable&, const RoutedEventArgs&)
{
auto lifetime = get_strong();
const auto parentHwnd{ reinterpret_cast<HWND>(winrt::get_self<ProfileViewModel>(_Profile)->WindowRoot().GetHostingWindow()) };
const auto parentHwnd{ reinterpret_cast<HWND>(_windowRoot.GetHostingWindow()) };
auto folder = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {
static constexpr winrt::guid clientGuidFolderPicker{ 0xAADAA433, 0xB04D, 0x4BAE, { 0xB1, 0xEA, 0x1E, 0x6C, 0xD1, 0xCD, 0xA6, 0x8B } };
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidFolderPicker));

View File

@@ -30,6 +30,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
private:
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
Editor::IHostedInWindow _windowRoot;
};
};

View File

@@ -536,15 +536,27 @@
</data>
<data name="Globals_ThemeDark.Content" xml:space="preserve">
<value>Dark</value>
<comment>An option to choose from for the "tab width mode" setting. When selected, the app is in dark theme and darker colors are used throughout the app.</comment>
<comment>An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app.</comment>
</data>
<data name="Globals_ThemeSystem.Content" xml:space="preserve">
<value>Use Windows theme</value>
<comment>An option to choose from for the "tab width mode" setting. When selected, the app uses the theme selected in the Windows operating system.</comment>
<comment>An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system.</comment>
</data>
<data name="Globals_ThemeLight.Content" xml:space="preserve">
<value>Light</value>
<comment>An option to choose from for the "tab width mode" setting. When selected, the app is in light theme and lighter colors are used throughout the app.</comment>
<comment>An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app.</comment>
</data>
<data name="Globals_ThemeDarkLegacy.Content" xml:space="preserve">
<value>Dark (Legacy)</value>
<comment>An option to choose from for the "theme" setting. When selected, the app is in dark theme and darker colors are used throughout the app. This is an older version of the "dark" theme</comment>
</data>
<data name="Globals_ThemeSystemLegacy.Content" xml:space="preserve">
<value>Use Windows theme (Legacy)</value>
<comment>An option to choose from for the "theme" setting. When selected, the app uses the theme selected in the Windows operating system. This is an older version of the "Use Windows theme" theme</comment>
</data>
<data name="Globals_ThemeLightLegacy.Content" xml:space="preserve">
<value>Light (Legacy)</value>
<comment>An option to choose from for the "theme" setting. When selected, the app is in light theme and lighter colors are used throughout the app. This is an older version of the "light" theme</comment>
</data>
<data name="Globals_WordDelimiters.Header" xml:space="preserve">
<value>Word delimiters</value>
@@ -1569,4 +1581,16 @@
<value>Warn when closing more than one tab</value>
<comment>Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open.</comment>
</data>
</root>
<data name="Settings_PortableModeNote.Text" xml:space="preserve">
<value>Windows Terminal is running in portable mode.</value>
<comment>A disclaimer that indicates that Terminal is running in a mode that saves settings to a different folder.</comment>
</data>
<data name="Settings_PortableModeInfoLink.NavigateUri" xml:space="preserve">
<value>https://go.microsoft.com/fwlink/?linkid=2229086</value>
<comment>{Locked}</comment>
</data>
<data name="Settings_PortableModeInfoLinkTextRun.Text" xml:space="preserve">
<value>Learn more.</value>
<comment>A hyperlink displayed near Settings_PortableModeNote.Text that the user can follow for more information.</comment>
</data>
</root>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
</packages>

View File

@@ -1006,7 +1006,7 @@ winrt::hstring CascadiaSettings::ApplicationDisplayName()
}
CATCH_LOG();
return RS_(L"ApplicationDisplayNameUnpackaged");
return IsPortableMode() ? RS_(L"ApplicationDisplayNamePortable") : RS_(L"ApplicationDisplayNameUnpackaged");
}
winrt::hstring CascadiaSettings::ApplicationVersion()
@@ -1015,12 +1015,7 @@ winrt::hstring CascadiaSettings::ApplicationVersion()
{
const auto package{ winrt::Windows::ApplicationModel::Package::Current() };
const auto version{ package.Id().Version() };
// As of about 2022, the ones digit of the Build of our version is a
// placeholder value to differentiate the Windows 10 build from the
// Windows 11 build. Let's trim that out. For additional clarity,
// let's omit the Revision, which _must_ be .0, and doesn't provide any
// value to report.
winrt::hstring formatted{ wil::str_printf<std::wstring>(L"%u.%u.%u", version.Major, version.Minor, version.Build / 10) };
winrt::hstring formatted{ wil::str_printf<std::wstring>(L"%u.%u.%u.%u", version.Major, version.Minor, version.Build, version.Revision) };
return formatted;
}
CATCH_LOG();
@@ -1087,7 +1082,21 @@ bool CascadiaSettings::IsDefaultTerminalAvailable() noexcept
DWORDLONG dwlConditionMask = 0;
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
return VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE;
if (VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE)
{
return true;
}
static bool isOtherwiseAvailable = [] {
wil::unique_hkey key;
const auto lResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\SystemSettings\\SettingId\\SystemSettings_Developer_Mode_Setting_DefaultTerminalApp",
0,
KEY_READ,
&key);
return static_cast<bool>(key) && ERROR_SUCCESS == lResult;
}();
return isOtherwiseAvailable;
}
bool CascadiaSettings::IsDefaultTerminalSet() noexcept

View File

@@ -105,6 +105,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::hstring DefaultSettingsPath();
static winrt::hstring ApplicationDisplayName();
static winrt::hstring ApplicationVersion();
static bool IsPortableMode();
static void ExportFile(winrt::hstring path, winrt::hstring content);
CascadiaSettings() noexcept = default;

View File

@@ -15,6 +15,7 @@ namespace Microsoft.Terminal.Settings.Model
static String SettingsPath { get; };
static String DefaultSettingsPath { get; };
static Boolean IsPortableMode { get; };
static String ApplicationDisplayName { get; };
static String ApplicationVersion { get; };

View File

@@ -52,6 +52,9 @@ static constexpr std::string_view ThemesKey{ "themes" };
constexpr std::wstring_view systemThemeName{ L"system" };
constexpr std::wstring_view darkThemeName{ L"dark" };
constexpr std::wstring_view lightThemeName{ L"light" };
constexpr std::wstring_view legacySystemThemeName{ L"legacySystem" };
constexpr std::wstring_view legacyDarkThemeName{ L"legacyDark" };
constexpr std::wstring_view legacyLightThemeName{ L"legacyLight" };
static constexpr std::wstring_view jsonExtension{ L".json" };
static constexpr std::wstring_view FragmentsSubDirectory{ L"\\Fragments" };
@@ -562,8 +565,9 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source
{
if (const auto theme = Theme::FromJson(themeJson))
{
const auto& name{ theme->Name() };
if (origin != OriginTag::InBox &&
(theme->Name() == systemThemeName || theme->Name() == lightThemeName || theme->Name() == darkThemeName))
(name == systemThemeName || name == lightThemeName || name == darkThemeName || name == legacySystemThemeName || name == legacyDarkThemeName || name == legacyLightThemeName))
{
// If the theme didn't come from the in-box themes, and its
// name was one of the reserved names, then just ignore it.
@@ -822,7 +826,7 @@ try
// read settings.json from the Release stable file path if it exists.
// Otherwise use default settings file provided from original settings file
bool releaseSettingExists = false;
if (firstTimeSetup)
if (firstTimeSetup && !IsPortableMode())
{
#if defined(WT_BRANDING_PREVIEW)
{
@@ -953,10 +957,16 @@ void CascadiaSettings::_researchOnLoad()
// light: 1
// dark: 2
// a custom theme: 3
const auto themeChoice = themeInUse == L"system" ? 0 :
themeInUse == L"light" ? 1 :
themeInUse == L"dark" ? 2 :
3;
// system (legacy): 4
// light (legacy): 5
// dark (legacy): 6
const auto themeChoice = themeInUse == L"system" ? 0 :
themeInUse == L"light" ? 1 :
themeInUse == L"dark" ? 2 :
themeInUse == L"legacyDark" ? 4 :
themeInUse == L"legacyLight" ? 5 :
themeInUse == L"legacySystem" ? 6 :
3;
TraceLoggingWrite(
g_hSettingsModelProvider,
@@ -1160,6 +1170,11 @@ winrt::hstring CascadiaSettings::SettingsPath()
return winrt::hstring{ _settingsPath().native() };
}
bool CascadiaSettings::IsPortableMode()
{
return Model::IsPortableMode();
}
winrt::hstring CascadiaSettings::DefaultSettingsPath()
{
// Both of these posts suggest getting the path to the exe, then removing

View File

@@ -99,7 +99,7 @@ bool DefaultTerminal::HasCurrent()
void DefaultTerminal::Current(const Model::DefaultTerminal& term)
{
THROW_IF_FAILED(DelegationConfig::s_SetDefaultByPackage(winrt::get_self<DefaultTerminal>(term)->_pkg, true));
THROW_IF_FAILED(DelegationConfig::s_SetDefaultByPackage(winrt::get_self<DefaultTerminal>(term)->_pkg));
TraceLoggingWrite(g_hSettingsModelProvider,
"DefaultTerminalChanged",

View File

@@ -21,6 +21,6 @@ Author(s):
// will become disconnected from user settings.
// {2bde4a90-d05f-401c-9492-e40884ead1d8}
// uuidv5 properties: name format is UTF-16LE bytes
static constexpr GUID TERMINAL_PROFILE_NAMESPACE_GUID = { 0x2bde4a90, 0xd05f, 0x401c, { 0x94, 0x92, 0xe4, 0x8, 0x84, 0xea, 0xd1, 0xd8 } };
inline constexpr GUID TERMINAL_PROFILE_NAMESPACE_GUID = { 0x2bde4a90, 0xd05f, 0x401c, { 0x94, 0x92, 0xe4, 0x8, 0x84, 0xea, 0xd1, 0xd8 } };
winrt::com_ptr<winrt::Microsoft::Terminal::Settings::Model::implementation::Profile> CreateDynamicProfile(const std::wstring_view& name);

View File

@@ -15,14 +15,34 @@
static constexpr std::string_view Utf8Bom{ "\xEF\xBB\xBF", 3 };
static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" };
static constexpr std::wstring_view ReleaseSettingsFolder{ L"Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\" };
static constexpr std::wstring_view PortableModeMarkerFile{ L".portable" };
static constexpr std::wstring_view PortableModeSettingsFolder{ L"settings" };
namespace winrt::Microsoft::Terminal::Settings::Model
{
// Returns a path like C:\Users\<username>\AppData\Local\Packages\<packagename>\LocalState
// You can put your settings.json or state.json in this directory.
bool IsPortableMode()
{
static auto portableMode = []() {
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
modulePath.replace_filename(PortableModeMarkerFile);
return std::filesystem::exists(modulePath);
}();
return portableMode;
}
std::filesystem::path GetBaseSettingsPath()
{
static auto baseSettingsPath = []() {
if (!IsPackaged() && IsPortableMode())
{
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
modulePath.replace_filename(PortableModeSettingsFolder);
std::filesystem::create_directories(modulePath);
return modulePath;
}
wil::unique_cotaskmem_string localAppDataFolder;
// KF_FLAG_FORCE_APP_DATA_REDIRECTION, when engaged, causes SHGet... to return
// the new AppModel paths (Packages/xxx/RoamingState, etc.) for standard path requests.

View File

@@ -3,6 +3,7 @@
namespace winrt::Microsoft::Terminal::Settings::Model
{
bool IsPortableMode();
std::filesystem::path GetBaseSettingsPath();
std::filesystem::path GetReleaseSettingsPath();
std::string ReadUTF8File(const std::filesystem::path& path, const bool elevatedOnly = false, FILETIME* lastWriteTime = nullptr);

View File

@@ -16,6 +16,6 @@ Author(s):
#pragma once
static constexpr std::wstring_view WslGeneratorNamespace{ L"Windows.Terminal.Wsl" };
static constexpr std::wstring_view AzureGeneratorNamespace{ L"Windows.Terminal.Azure" };
static constexpr std::wstring_view PowershellCoreGeneratorNamespace{ L"Windows.Terminal.PowershellCore" };
inline constexpr std::wstring_view WslGeneratorNamespace{ L"Windows.Terminal.Wsl" };
inline constexpr std::wstring_view AzureGeneratorNamespace{ L"Windows.Terminal.Azure" };
inline constexpr std::wstring_view PowershellCoreGeneratorNamespace{ L"Windows.Terminal.PowershellCore" };

View File

@@ -126,6 +126,10 @@
<data name="SplitPaneParentCommandName" xml:space="preserve">
<value>Split Pane...</value>
</data>
<data name="ApplicationDisplayNamePortable" xml:space="preserve">
<value>Terminal (Portable)</value>
<comment>This display name is used when the Terminal application is running in a "portable" mode, where settings are not stored in a shared location.</comment>
</data>
<data name="ApplicationDisplayNameUnpackaged" xml:space="preserve">
<value>Terminal (Unpackaged)</value>
<comment>This display name is used when the application's name cannot be determined</comment>

View File

@@ -42,6 +42,9 @@ std::wstring VsDevCmdGenerator::GetProfileCommandLine(const VsSetupConfiguration
commandLine.reserve(256);
commandLine.append(LR"(cmd.exe /k ")");
commandLine.append(GetDevCmdScriptPath(instance));
// The "-startdir" parameter will prevent "vsdevcmd" from automatically
// setting the shell path so the path in the profile will be used instead.
commandLine.append(LR"(" -startdir=none)");
#if defined(_M_ARM64)
commandLine.append(LR"(" -arch=arm64 -host_arch=x64)");
#elif defined(_M_AMD64)

View File

@@ -316,6 +316,36 @@
"background": "terminalBackground",
"unfocusedBackground": "#00000000"
}
},
{
"name": "legacyDark",
"tab": {
"background": null,
"unfocusedBackground": null
},
"window": {
"applicationTheme": "dark"
}
},
{
"name": "legacyLight",
"tab": {
"background": null,
"unfocusedBackground": null
},
"window": {
"applicationTheme": "light"
}
},
{
"name": "legacySystem",
"tab": {
"background": null,
"unfocusedBackground": null
},
"window": {
"applicationTheme": "system"
},
}
],
"actions":

View File

@@ -9,6 +9,7 @@
#include "NotificationIcon.h"
#include <dwmapi.h>
#include <TerminalThemeHelpers.h>
#include <CoreWindow.h>
extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -499,6 +500,24 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
return 0;
}
}
// BODGY This is a fix for the upstream:
//
// https://github.com/microsoft/microsoft-ui-xaml/issues/3577
//
// ContentDialogs don't resize themselves when the XAML island resizes.
// However, if we manually resize our CoreWindow, that'll actually
// trigger a resize of the ContentDialog.
if (const auto& coreWindow{ winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread() })
{
if (const auto& interop{ coreWindow.as<ICoreWindowInterop>() })
{
HWND coreWindowInterop;
interop->get_WindowHandle(&coreWindowInterop);
PostMessage(coreWindowInterop, message, wparam, lparam);
}
}
break;
}
case WM_MOVING:

View File

@@ -75,9 +75,6 @@
<ItemGroup>
<Manifest Include="WindowsTerminal.manifest" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<!-- Dependencies -->
<ItemGroup>
<!-- Even though we do have proper recursive dependencies, we want to keep some of these here

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
</packages>

View File

@@ -50,8 +50,8 @@ namespace WindowsTerminal.UIA.Tests.Elements
this.context = context;
// If running locally, set WTPath to where we can find a loose deployment of Windows Terminal
// On the build machines, the scripts lay it out at the appx\ subfolder of the test deployment directory
string path = Path.GetFullPath(Path.Combine(context.TestDeploymentDir, @"appx\WindowsTerminal.exe"));
// On the build machines, the scripts lay it out at the terminal-0.0.1.0\ subfolder of the test deployment directory
string path = Path.GetFullPath(Path.Combine(context.TestDeploymentDir, @"terminal-0.0.1.0\WindowsTerminal.exe"));
if (context.Properties.Contains("WTPath"))
{
path = (string)context.Properties["WTPath"];

View File

@@ -4,10 +4,10 @@ Licensed under the MIT license.
--*/
#pragma once
constexpr int32_t WindowingBehaviorUseCurrent{ 0 };
constexpr int32_t WindowingBehaviorUseNew{ -1 };
constexpr int32_t WindowingBehaviorUseExisting{ -2 };
constexpr int32_t WindowingBehaviorUseAnyExisting{ -3 };
constexpr int32_t WindowingBehaviorUseName{ -4 };
inline constexpr int32_t WindowingBehaviorUseCurrent{ 0 };
inline constexpr int32_t WindowingBehaviorUseNew{ -1 };
inline constexpr int32_t WindowingBehaviorUseExisting{ -2 };
inline constexpr int32_t WindowingBehaviorUseAnyExisting{ -3 };
inline constexpr int32_t WindowingBehaviorUseName{ -4 };
static constexpr std::wstring_view QuakeWindowName{ L"_quake" };
inline constexpr std::wstring_view QuakeWindowName{ L"_quake" };

View File

@@ -83,13 +83,6 @@
<Import Project="$(OpenConsoleDir)src\common.build.tests.props" />
<Import Project="$(SolutionDir)src\common.nugetversions.targets" />
<PropertyGroup>
<!-- From Microsoft.UI.Xaml.targets -->
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
</PropertyGroup>
<ItemDefinitionGroup>
<PreBuildEvent>
<!-- Manually copy the Windows Terminal manifest to our project directory,

View File

@@ -4,6 +4,42 @@
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<!--
The Hybrid CRT model statically links the runtime and STL and dynamically
links the UCRT instead of the VC++ CRT. The UCRT ships with Windows.
WinAppSDK asserts that this is "supported according to the CRT maintainer."
This must come before Microsoft.Cpp.targets because it manipulates ClCompile.RuntimeLibrary.
-->
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and '$(Configuration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(EnableHybridCRT)'=='true' and ('$(Configuration)'=='Release' or '$(Configuration)'=='AuditMode')">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ItemDefinitionGroup>

View File

@@ -81,12 +81,26 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
<!--
We don't need to use HybridCRT for Fuzzing, so just skip it entirely.
Fuzzing requires the address sanitizer runtime library plus the fuzzing
harness. They're probably not compatible.
Audit, however, *does* require linking components built in Release mode...
-->
<ConfigurationSupportsHybridCRT Condition="'$(Configuration)'=='Fuzzing'">false</ConfigurationSupportsHybridCRT>
<ConfigurationSupportsHybridCRT Condition="'$(ConfigurationSupportsHybridCRT)'==''">true</ConfigurationSupportsHybridCRT>
</PropertyGroup>
<!-- For ALL build types-->
<PropertyGroup Label="Configuration">
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<LinkIncremental>false</LinkIncremental>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
<EnableHybridCRT Condition="'$(EnableHybridCRT)'=='' and '$(ConfigurationSupportsHybridCRT)'=='true'">true</EnableHybridCRT>
<UseCrtSDKReferenceStaticWarning Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReferenceStaticWarning>
</PropertyGroup>
<ItemDefinitionGroup>

View File

@@ -43,9 +43,6 @@
<!-- TAEF -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets" Condition="'$(TerminalTAEF)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets')" />
<!-- CppRestSDK -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets" Condition="'$(TerminalCppRestSDK)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets')" />
<!-- VCRTForwarders -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets" Condition="'$(TerminalVCRTForwarders)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" />
@@ -56,7 +53,7 @@
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="'$(TerminalVisualStudioSetup)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />
<!-- WinUI (which depends on WebView2 as of 2.8.0) -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="'$(TerminalMUX)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets" Condition="'$(TerminalMUX)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" />
<!-- WIL (so widely used that this one does not have a TerminalWIL opt-in property; it is automatic) -->
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
@@ -79,9 +76,6 @@
<!-- TAEF -->
<Error Condition="'$(TerminalTAEF)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002\build\Microsoft.Taef.targets'))" />
<!-- CppRestSDK -->
<Error Condition="'$(TerminalCppRestSDK)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\vcpkg-cpprestsdk.2.10.14\build\native\vcpkg-cpprestsdk.targets'))" />
<!-- VCRTForwarders -->
<Error Condition="'$(TerminalVCRTForwarders)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets'))" />
@@ -92,7 +86,7 @@
<Error Condition="'$(TerminalVisualStudioSetup)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets'))" />
<!-- WinUI (which depends on WebView2 as of 2.8.0) -->
<Error Condition="'$(TerminalMUX)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="'$(TerminalMUX)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.2.7.3\build\native\Microsoft.UI.Xaml.targets'))" />
<!-- WIL (so widely used that this one does not have a TerminalWIL opt-in property; it is automatic) -->
<Error Condition="!Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />

View File

@@ -28,6 +28,7 @@
<AppContainerApplication>true</AppContainerApplication>
<WindowsStoreApp>true</WindowsStoreApp>
<ApplicationType>Windows Store</ApplicationType>
<UseCrtSDKReference Condition="'$(EnableHybridCRT)'=='true'">false</UseCrtSDKReference> <!-- The SDK reference breaks the Hybrid CRT -->
</PropertyGroup>
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'!='true'">
<!-- Some of our projects include the cppwinrt build options to

View File

@@ -67,6 +67,15 @@
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_LegacyConhost</name>
<description>conhost should support ForceV2=false, and try to load conhostv1.dll</description>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>WindowsInbox</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_AtlasEngine</name>
<description>If enabled, AtlasEngine is used by default</description>
@@ -145,4 +154,14 @@
<stage>AlwaysDisabled</stage>
</feature>
<feature>
<name>Feature_AzureConnectionInProc</name>
<description>Host the AzureConnection inside Terminal rather than via TerminalAzBridge</description>
<id>4661</id>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
</featureStaging>

View File

@@ -52,6 +52,27 @@ class DefaultOutOfProcModuleWithRegistrationFlag : public OutOfProcModuleWithReg
// Holds the wwinmain open until COM tells us there are no more server connections
wil::unique_event _comServerExitEvent;
[[nodiscard]] static HRESULT ValidateServerHandle(const HANDLE handle)
{
// Make sure this is a console file.
FILE_FS_DEVICE_INFORMATION DeviceInformation;
IO_STATUS_BLOCK IoStatusBlock;
const auto Status = NtQueryVolumeInformationFile(handle, &IoStatusBlock, &DeviceInformation, sizeof(DeviceInformation), FileFsDeviceInformation);
if (!NT_SUCCESS(Status))
{
RETURN_NTSTATUS(Status);
}
else if (DeviceInformation.DeviceType != FILE_DEVICE_CONSOLE)
{
return E_INVALIDARG;
}
else
{
return S_OK;
}
}
#if TIL_FEATURE_LEGACYCONHOST_ENABLED
static bool useV2 = true;
static bool ConhostV2ForcedInRegistry()
{
@@ -101,26 +122,6 @@ static bool ConhostV2ForcedInRegistry()
return fShouldUseConhostV2;
}
[[nodiscard]] static HRESULT ValidateServerHandle(const HANDLE handle)
{
// Make sure this is a console file.
FILE_FS_DEVICE_INFORMATION DeviceInformation;
IO_STATUS_BLOCK IoStatusBlock;
const auto Status = NtQueryVolumeInformationFile(handle, &IoStatusBlock, &DeviceInformation, sizeof(DeviceInformation), FileFsDeviceInformation);
if (!NT_SUCCESS(Status))
{
RETURN_NTSTATUS(Status);
}
else if (DeviceInformation.DeviceType != FILE_DEVICE_CONSOLE)
{
return E_INVALIDARG;
}
else
{
return S_OK;
}
}
static bool ShouldUseLegacyConhost(const ConsoleArguments& args)
{
if (args.InConptyMode())
@@ -185,6 +186,7 @@ static bool ShouldUseLegacyConhost(const ConsoleArguments& args)
return hr;
}
#endif // TIL_FEATURE_LEGACYCONHOST_ENABLED
// Routine Description:
// - Called back when COM says there is nothing left for our server to do and we can tear down.
@@ -303,6 +305,7 @@ int CALLBACK wWinMain(
else
#endif
{
#if TIL_FEATURE_LEGACYCONHOST_ENABLED
if (ShouldUseLegacyConhost(args))
{
useV2 = false;
@@ -321,6 +324,7 @@ int CALLBACK wWinMain(
}
}
if (useV2)
#endif // TIL_FEATURE_LEGACYCONHOST_ENABLED
{
if (args.ShouldCreateServerHandle())
{

View File

@@ -80,8 +80,7 @@ namespace DbcsWriteRead
const bool fReadUnicode,
CharInfoPattern& rgChars);
void Verify(const CharInfoPattern& rgExpected,
const CharInfoPattern& rgActual);
void Verify(std::span<CHAR_INFO> rgExpected, std::span<CHAR_INFO> rgActual);
void PrepExpected(
const WORD wAttrWritten,
@@ -158,6 +157,10 @@ class DbcsTests
BEGIN_TEST_METHOD(TestDbcsBackupRestore)
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
END_TEST_METHOD()
BEGIN_TEST_METHOD(TestInvalidTrailer)
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
END_TEST_METHOD()
};
bool DbcsTests::DbcsTestSetup()
@@ -422,7 +425,7 @@ namespace PrepPattern
static constexpr WORD leading = COMMON_LVB_LEADING_BYTE;
static constexpr WORD trailing = COMMON_LVB_TRAILING_BYTE;
constexpr void replaceColorPlaceholders(CharInfoPattern& pattern, WORD attr)
constexpr void replaceColorPlaceholders(std::span<CHAR_INFO> pattern, WORD attr)
{
for (auto& info : pattern)
{
@@ -1317,9 +1320,9 @@ void DbcsWriteRead::RetrieveOutput(const HANDLE hOut,
}
}
void DbcsWriteRead::Verify(const CharInfoPattern& rgExpected,
const CharInfoPattern& rgActual)
void DbcsWriteRead::Verify(std::span<CHAR_INFO> rgExpected, std::span<CHAR_INFO> rgActual)
{
VERIFY_ARE_EQUAL(rgExpected.size(), rgActual.size());
// We will walk through for the number of CHAR_INFOs expected.
for (size_t i = 0; i < rgExpected.size(); i++)
{
@@ -2049,39 +2052,50 @@ void DbcsTests::TestDbcsStdCoutScenario()
// In other words, writing a trailing CHAR_INFO will also automatically write a leading CHAR_INFO in the preceding cell.
void DbcsTests::TestDbcsBackupRestore()
{
static_assert(PrepPattern::DoubledW.size() == 16);
const auto out = GetStdHandle(STD_OUTPUT_HANDLE);
std::array<CHAR_INFO, 16> expected = PrepPattern::DoubledW;
// We backup/restore 2 lines at once to ensure that it works even then. After all, an incorrect implementation
// might ignore all but the absolutely first CHAR_INFO instead of handling the first CHAR_INFO *on each row*.
std::array<CHAR_INFO, 32> expected;
std::ranges::copy(PrepPattern::DoubledW, expected.begin() + 0);
std::ranges::copy(PrepPattern::DoubledW, expected.begin() + 16);
PrepPattern::replaceColorPlaceholders(expected, FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_GREEN);
// DoubledW will show up like this in the top/left corner of the terminal:
// +----------------
// |QいかなZYXWVUTに
// |QいかなZYXWVUTに
//
// Since those 4 Japanese characters probably aren't going to be monospace for you in your editor
// (as they most likely aren't exactly 2 ASCII characters wide), I'll continue referring to them like this:
// +----------------
// |QaabbccZYXWVUTdd
// |QaabbccZYXWVUTdd
{
SMALL_RECT region{ .Left = 0, .Right = 15 };
VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleOutputW(out, expected.data(), { 16, 1 }, {}, &region));
SMALL_RECT region{ 0, 0, 15, 1 };
VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleOutputW(out, expected.data(), { 16, 2 }, {}, &region));
}
// Make a "backup" of the viewport. The twist is that our backup region only
// copies the trailing/leading half of the first/last glyph respectively like so:
// +----------------
// | abbccZYXWVUTd
std::array<CHAR_INFO, 13> backup{};
constexpr COORD backupSize{ 13, 1 };
SMALL_RECT backupRegion{ .Left = 2, .Right = 14 };
std::array<CHAR_INFO, 26> backup{};
constexpr COORD backupSize{ 13, 2 };
SMALL_RECT backupRegion{ 2, 0, 14, 1 };
VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleOutputW(out, backup.data(), backupSize, {}, &backupRegion));
// Destroy the text with some narrow ASCII characters, resulting in:
// +----------------
// |Qxxxxxxxxxxxxxxx
// |Qxxxxxxxxxxxxxxx
{
DWORD ignored;
VERIFY_WIN32_BOOL_SUCCEEDED(FillConsoleOutputCharacterW(out, L'x', 15, { 1, 0 }, &ignored));
VERIFY_WIN32_BOOL_SUCCEEDED(FillConsoleOutputCharacterW(out, L'x', 15, { 1, 1 }, &ignored));
}
// Restore our "backup". The trailing half of the first wide glyph (indicated as "a" above)
@@ -2089,20 +2103,52 @@ void DbcsTests::TestDbcsBackupRestore()
// matching leading/trailing half respectively. In other words, this:
// +----------------
// | abbccZYXWVUTd
// | abbccZYXWVUTd
//
// turns into this:
// +----------------
// | aabbccZYXWVUTdd
// | aabbccZYXWVUTdd
//
// and so we restore this, overwriting all the "x" characters in the process:
// +----------------
// |QいかなZYXWVUTに
// |QいかなZYXWVUTに
VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleOutputW(out, backup.data(), backupSize, {}, &backupRegion));
std::array<CHAR_INFO, 16> infos{};
std::array<CHAR_INFO, 32> infos{};
{
SMALL_RECT region{ .Left = 0, .Right = 15 };
VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleOutputW(out, infos.data(), { 16, 1 }, {}, &region));
SMALL_RECT region{ 0, 0, 15, 1 };
VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleOutputW(out, infos.data(), { 16, 2 }, {}, &region));
}
DbcsWriteRead::Verify(expected, infos);
}
// As tested by TestDbcsBackupRestore(), we do want to allow users to write trailers into the buffer, to allow
// for an area of the buffer to be backed up and restored via Read/WriteConsoleOutput. But apart from that use
// case, we'd generally do best to avoid trailers whenever possible, as conhost basically ignored them in the
// past and only rendered leaders. Applications might now be relying on us effectively ignoring trailers.
void DbcsTests::TestInvalidTrailer()
{
auto expected = PrepPattern::DoubledW;
auto input = expected;
decltype(input) output{};
for (auto& v : input)
{
if (WI_IsFlagSet(v.Attributes, COMMON_LVB_TRAILING_BYTE))
{
v.Char.UnicodeChar = 0xfffd;
}
}
{
static constexpr COORD bufferSize{ 16, 1 };
SMALL_RECT region{ 0, 0, 15, 0 };
const auto out = GetStdHandle(STD_OUTPUT_HANDLE);
VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleOutputW(out, input.data(), bufferSize, {}, &region));
VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleOutputW(out, output.data(), bufferSize, {}, &region));
}
DbcsWriteRead::Verify(expected, output);
}

View File

@@ -17,6 +17,13 @@
regular builds, while preventing the build failure during fuzzing builds.
-->
<ConfigurationType Condition="'$(Configuration)'=='Fuzzing'">StaticLibrary</ConfigurationType>
<!--
OpenConsoleProxy gets copied out of our app package and into a shared system store. As such, it can't take a
dependency on any libraries inside our package **or** inside any of our dependency packages. It has to stand
on its own.
-->
<EnableHybridCRT>true</EnableHybridCRT>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
@@ -77,43 +84,6 @@
</Link>
</ItemDefinitionGroup>
<!--
OpenConsoleProxy gets copied out of our app package and into a shared system store. As such, it can't take a
dependency on any libraries inside our package **or** inside any of our dependency packages. It has to stand
on its own.
Therefore, we're going to use the Hybrid CRT model from WinAppSDK for only OpenConsoleProxy. It statically
links the runtime and STL and dynamically links the UCRT instead of the VC++ CRT. The UCRT ships with Windows.
WinAppSDK asserts that this is "supported according to the CRT maintainer."
-->
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />
</Project>

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