mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 14:19:45 +00:00
Compare commits
60 Commits
dev/lhecke
...
v1.17.1139
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ce63b923b | ||
|
|
4b71d7b470 | ||
|
|
73244461cc | ||
|
|
d056f22219 | ||
|
|
e9074fe138 | ||
|
|
8b9f807391 | ||
|
|
e967ad772d | ||
|
|
4977fc9883 | ||
|
|
3ec5fe4eee | ||
|
|
752df35cf5 | ||
|
|
526704b725 | ||
|
|
8108f22262 | ||
|
|
9e3cc9d379 | ||
|
|
0db21e8904 | ||
|
|
b4fcf28b92 | ||
|
|
8a4def793f | ||
|
|
95636b976d | ||
|
|
5ca7991ded | ||
|
|
31dc6d80c5 | ||
|
|
9ba0636590 | ||
|
|
57264e0b2a | ||
|
|
f4da9c0e98 | ||
|
|
4d4111b9ed | ||
|
|
5d1ecce97c | ||
|
|
dd02ef0666 | ||
|
|
cd28ed7a4a | ||
|
|
a1882ce852 | ||
|
|
d48870c49b | ||
|
|
0eafd10e3d | ||
|
|
436c037021 | ||
|
|
99ac5f632f | ||
|
|
55f7bec9d1 | ||
|
|
9c9e8ecc91 | ||
|
|
65324d6049 | ||
|
|
87333f79ed | ||
|
|
30419b99df | ||
|
|
c16e5d99cc | ||
|
|
9523d6ad20 | ||
|
|
799d48ffea | ||
|
|
acd32f252d | ||
|
|
9b4fbff1a2 | ||
|
|
d6d35610bc | ||
|
|
31ae78d1bc | ||
|
|
d8c8e780b6 | ||
|
|
a33fdcfd75 | ||
|
|
31aa23f717 | ||
|
|
9670230925 | ||
|
|
c9a4ab7624 | ||
|
|
986ced95c1 | ||
|
|
0631bf5bd7 | ||
|
|
0e2b3e6079 | ||
|
|
6edaa15d4c | ||
|
|
dd9e0148c2 | ||
|
|
f0b09421da | ||
|
|
7083738146 | ||
|
|
80c529aced | ||
|
|
ab79a85381 | ||
|
|
b9089d9d1d | ||
|
|
272e7b0905 | ||
|
|
7326eaaa6f |
5
.github/actions/spelling/allow/apis.txt
vendored
5
.github/actions/spelling/allow/apis.txt
vendored
@@ -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
|
||||
|
||||
4
.github/actions/spelling/allow/microsoft.txt
vendored
4
.github/actions/spelling/allow/microsoft.txt
vendored
@@ -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
|
||||
|
||||
7
.github/actions/spelling/allow/names.txt
vendored
7
.github/actions/spelling/allow/names.txt
vendored
@@ -1,6 +1,6 @@
|
||||
Anup
|
||||
austdi
|
||||
arkthur
|
||||
austdi
|
||||
Ballmer
|
||||
bhoj
|
||||
Bhojwani
|
||||
@@ -31,8 +31,8 @@ jerrysh
|
||||
Kaiyu
|
||||
kimwalisch
|
||||
KMehrain
|
||||
KODELIFE
|
||||
Kodelife
|
||||
KODELIFE
|
||||
Kourosh
|
||||
kowalczyk
|
||||
leonardder
|
||||
@@ -61,6 +61,7 @@ oising
|
||||
oldnewthing
|
||||
opengl
|
||||
osgwiki
|
||||
Ottosson
|
||||
pabhojwa
|
||||
panos
|
||||
paulcam
|
||||
@@ -88,8 +89,8 @@ Wirt
|
||||
Wojciech
|
||||
zadjii
|
||||
Zamor
|
||||
Zamora
|
||||
zamora
|
||||
Zamora
|
||||
zljubisic
|
||||
Zoey
|
||||
zorio
|
||||
|
||||
1
.github/actions/spelling/excludes.txt
vendored
1
.github/actions/spelling/excludes.txt
vendored
@@ -111,6 +111,7 @@
|
||||
^src/tools/pixels/pixels\.bat$
|
||||
^src/tools/texttests/fira\.txt$
|
||||
^src/tools/U8U16Test/(?:fr|ru|zh)\.txt$
|
||||
^src/types/ColorFix.cpp
|
||||
^src/types/ut_types/UtilsTests.cpp$
|
||||
^tools/ReleaseEngineering/ServicingPipeline.ps1$
|
||||
ignore$
|
||||
|
||||
15
.github/actions/spelling/expect/expect.txt
vendored
15
.github/actions/spelling/expect/expect.txt
vendored
@@ -41,6 +41,7 @@ antialiasing
|
||||
ANull
|
||||
anycpu
|
||||
APARTMENTTHREADED
|
||||
APCA
|
||||
APCs
|
||||
APIENTRY
|
||||
apiset
|
||||
@@ -194,6 +195,8 @@ chk
|
||||
CHT
|
||||
Cic
|
||||
CLA
|
||||
cielab
|
||||
Cielab
|
||||
Clcompile
|
||||
CLE
|
||||
cleartype
|
||||
@@ -257,6 +260,7 @@ condrv
|
||||
conechokey
|
||||
conemu
|
||||
configurability
|
||||
confusables
|
||||
conhost
|
||||
conime
|
||||
conimeinfo
|
||||
@@ -311,7 +315,6 @@ CPLINFO
|
||||
cplusplus
|
||||
CPPCORECHECK
|
||||
cppcorecheckrules
|
||||
cpprest
|
||||
cpprestsdk
|
||||
cppwinrt
|
||||
CProc
|
||||
@@ -581,6 +584,7 @@ ENU
|
||||
ENUMLOGFONT
|
||||
ENUMLOGFONTEX
|
||||
enumranges
|
||||
EOK
|
||||
eplace
|
||||
EPres
|
||||
EQU
|
||||
@@ -1284,6 +1288,8 @@ OEMFONT
|
||||
OEMFORMAT
|
||||
OEMs
|
||||
offboarded
|
||||
oklab
|
||||
Oklab
|
||||
OLEAUT
|
||||
OLECHAR
|
||||
onecore
|
||||
@@ -1437,7 +1443,6 @@ PPEB
|
||||
ppf
|
||||
ppguid
|
||||
ppidl
|
||||
pplx
|
||||
PPROC
|
||||
PPROCESS
|
||||
ppropvar
|
||||
@@ -1577,6 +1582,7 @@ replatformed
|
||||
Replymessage
|
||||
repositorypath
|
||||
Requiresx
|
||||
rerasterize
|
||||
rescap
|
||||
Resequence
|
||||
RESETCONTENT
|
||||
@@ -1773,10 +1779,13 @@ srcsrv
|
||||
SRCSRVTRG
|
||||
srctool
|
||||
srect
|
||||
srgb
|
||||
Srgb
|
||||
srv
|
||||
srvinit
|
||||
srvpipe
|
||||
ssa
|
||||
startdir
|
||||
STARTF
|
||||
STARTUPINFO
|
||||
STARTUPINFOEX
|
||||
@@ -1950,6 +1959,7 @@ trx
|
||||
tsattrs
|
||||
tsf
|
||||
tsgr
|
||||
tsm
|
||||
TStr
|
||||
TSTRFORMAT
|
||||
TSub
|
||||
@@ -2114,7 +2124,6 @@ WDDMCONSOLECONTEXT
|
||||
wdm
|
||||
webpage
|
||||
websites
|
||||
websockets
|
||||
wekyb
|
||||
wex
|
||||
wextest
|
||||
|
||||
@@ -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#
|
||||
|
||||
49
NOTICE.md
49
NOTICE.md
@@ -276,41 +276,28 @@ OTHER DEALINGS IN THE SOFTWARE.
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
```
|
||||
|
||||
## ConEmu
|
||||
**Source**: [https://github.com/Maximus5/ConEmu](https://github.com/Maximus5/ConEmu)
|
||||
## Oklab
|
||||
**Source**: [https://bottosson.github.io/posts/oklab/](https://bottosson.github.io/posts/oklab/)
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2009-2017, Maximus5 <ConEmu.Maximus5@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
Copyright (c) 2020 Björn Ottosson
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
# Microsoft Open Source
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -55,14 +55,9 @@ 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.
|
||||
## 1.17 HAX: Release Build
|
||||
& tar -x -v -f "$repoDirectory\Artifacts\$ArtifactName\unpackaged\Microsoft.WindowsTerminal_1.0.0.0_x64.zip" -C "$payloadDir"
|
||||
Move-Item "$payloadDir\terminal-*" "$payloadDir\terminal-0.0.1.0"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Optional, defaults to main. Name of the branch which will be used for calculating branch point. -->
|
||||
<PGOBranch>main</PGOBranch>
|
||||
<PGOBranch>release-1.17</PGOBranch>
|
||||
|
||||
<!-- Mandatory. Name of the NuGet package which will contain PGO databases for consumption by build system. -->
|
||||
<PGOPackageName>Microsoft.Internal.Windows.Terminal.PGODatabase</PGOPackageName>
|
||||
|
||||
@@ -18,6 +18,7 @@ stages:
|
||||
- template: ./templates/build-console-pgo.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
additionalBuildArguments: "/p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=Release"
|
||||
- stage: Publish_PGO_Databases
|
||||
displayName: Publish PGO databases
|
||||
dependsOn: ['Build_x64']
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
78
build/scripts/Merge-PriFiles.ps1
Normal file
78
build/scripts/Merge-PriFiles.ps1
Normal 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
|
||||
47
build/scripts/Merge-TerminalAndXamlResources.ps1
Normal file
47
build/scripts/Merge-TerminalAndXamlResources.ps1
Normal 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
|
||||
117
build/scripts/New-UnpackagedTerminalDistribution.ps1
Normal file
117
build/scripts/New-UnpackagedTerminalDistribution.ps1
Normal 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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
12
custom.props
12
custom.props
@@ -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>
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
235
doc/color_nudging.html
Normal file
235
doc/color_nudging.html
Normal file
@@ -0,0 +1,235 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Perceptual Color Nudging</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<style>
|
||||
html {
|
||||
background-color: #0c0c0c;
|
||||
color: #cccccc;
|
||||
font-family: "Cascadia Code", "Cascadia Mono", monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
form,
|
||||
h2 {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
p,
|
||||
pre {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="flex: 2; align-items: flex-start; background-color: #0c0c0c">
|
||||
<form>
|
||||
<input id="background-color" name="background-color" type="color" value="#0c0c0c" />
|
||||
<label for="background-color">background color</label>
|
||||
</form>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Input</td>
|
||||
<td>WCAG21:<br>APCA:</td>
|
||||
<td id="stats-input"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ΔE2000<br>(ConEmu)</td>
|
||||
<td>WCAG21:<br>APCA:</td>
|
||||
<td id="stats-cielab"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ΔEOK</td>
|
||||
<td>WCAG21:<br>APCA:</td>
|
||||
<td id="stats-oklab"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="input" style="flex: 1">
|
||||
<h2>Input</h2>
|
||||
<pre style="font-size: 8pt">猫</pre>
|
||||
<pre style="font-size: 10pt">猫</pre>
|
||||
<pre style="font-size: 12pt">猫</pre>
|
||||
<pre style="font-size: 14pt">猫</pre>
|
||||
<pre style="font-size: 16pt">猫</pre>
|
||||
<pre style="font-size: 32pt">猫</pre>
|
||||
<pre style="font-size: 64pt">猫</pre>
|
||||
</div>
|
||||
<div id="cielab" style="flex: 1">
|
||||
<h2>ΔE2000 (ConEmu)</h2>
|
||||
<pre style="font-size: 8pt">猫</pre>
|
||||
<pre style="font-size: 10pt">猫</pre>
|
||||
<pre style="font-size: 12pt">猫</pre>
|
||||
<pre style="font-size: 14pt">猫</pre>
|
||||
<pre style="font-size: 16pt">猫</pre>
|
||||
<pre style="font-size: 32pt">猫</pre>
|
||||
<pre style="font-size: 64pt">猫</pre>
|
||||
</div>
|
||||
<div id="oklab" style="flex: 1">
|
||||
<h2>ΔEOK</h2>
|
||||
<pre style="font-size: 8pt">猫</pre>
|
||||
<pre style="font-size: 10pt">猫</pre>
|
||||
<pre style="font-size: 12pt">猫</pre>
|
||||
<pre style="font-size: 14pt">猫</pre>
|
||||
<pre style="font-size: 16pt">猫</pre>
|
||||
<pre style="font-size: 32pt">猫</pre>
|
||||
<pre style="font-size: 64pt">猫</pre>
|
||||
</div>
|
||||
<script type="module">
|
||||
import Color from "https://cdn.jsdelivr.net/npm/colorjs.io@0.4.3/+esm";
|
||||
|
||||
window.Color = Color;
|
||||
|
||||
const input = document.getElementById("input");
|
||||
const cielab = document.getElementById("cielab");
|
||||
const oklab = document.getElementById("oklab");
|
||||
|
||||
const statsInput = document.getElementById("stats-input");
|
||||
const statsCielab = document.getElementById("stats-cielab");
|
||||
const statsOklab = document.getElementById("stats-oklab");
|
||||
|
||||
let backgroundColor = new Color("#0c0c0c");
|
||||
let foregroundColor = new Color("#0c0c0c");
|
||||
let foregroundColorRange = null;
|
||||
let previousSecsIntegral = -1;
|
||||
|
||||
function saturate(val) {
|
||||
return val < 0 ? 0 : val > 1 ? 1 : val;
|
||||
}
|
||||
|
||||
function clipToSrgb(color) {
|
||||
return color.to("srgb").toGamut({ method: "clip" });
|
||||
}
|
||||
|
||||
function nudgeCielab(backgroundColor, foregroundColor) {
|
||||
const backgroundCielab = backgroundColor.to("lab-d65");
|
||||
const foregroundCielab = foregroundColor.to("lab-d65");
|
||||
|
||||
const de1 = Color.deltaE(foregroundColor, backgroundCielab, "2000");
|
||||
if (de1 >= 12.0) {
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
for (let i = 0; i <= 1; i++) {
|
||||
const step = (i == 0) ? 5.0 : -5.0;
|
||||
foregroundCielab.l += step;
|
||||
|
||||
while (((i == 0) && foregroundCielab.l <= 100) || (i == 1 && foregroundCielab.l >= 0)) {
|
||||
const de2 = Color.deltaE(foregroundCielab, backgroundCielab, "2000");
|
||||
if (de2 >= 20.0) {
|
||||
return clipToSrgb(foregroundCielab);
|
||||
}
|
||||
foregroundCielab.l += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function nudgeOklab(backgroundColor, foregroundColor) {
|
||||
const backgroundOklab = backgroundColor.to("oklab");
|
||||
const foregroundOklab = foregroundColor.to("oklab");
|
||||
const deltaSquared = {
|
||||
l: (backgroundOklab.l - foregroundOklab.l) ** 2,
|
||||
a: (backgroundOklab.a - foregroundOklab.a) ** 2,
|
||||
b: (backgroundOklab.b - foregroundOklab.b) ** 2,
|
||||
};
|
||||
const distance = deltaSquared.l + deltaSquared.a + deltaSquared.b;
|
||||
|
||||
if (distance >= 0.25) {
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
let deltaL = Math.sqrt(0.25 - deltaSquared.a - deltaSquared.b);
|
||||
if (foregroundOklab.l < backgroundOklab.l)
|
||||
{
|
||||
deltaL = -deltaL;
|
||||
}
|
||||
|
||||
foregroundOklab.l = backgroundOklab.l + deltaL;
|
||||
if (foregroundOklab.l < 0 || foregroundOklab.l > 1)
|
||||
{
|
||||
foregroundOklab.l = backgroundOklab.l - deltaL;
|
||||
}
|
||||
|
||||
return clipToSrgb(foregroundOklab);
|
||||
}
|
||||
|
||||
function contrastStringLevels(num, level0, level1) {
|
||||
const str = num.toFixed(1);
|
||||
if (num < level0) {
|
||||
return `<span style="color:crimson">${str}</span>`;
|
||||
}
|
||||
if (num < level1) {
|
||||
return `<span style="color:coral">${str}</span>`;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function contrastString(foregroundColor) {
|
||||
const contrastWCAG21 = contrastStringLevels(foregroundColor.contrast(backgroundColor, "WCAG21"), 3, 4.5);
|
||||
const contrastAPCA = contrastStringLevels(Math.abs(foregroundColor.contrast(backgroundColor, "APCA")), 45, 60);
|
||||
return `${contrastWCAG21}<br/>${contrastAPCA}`;
|
||||
}
|
||||
|
||||
function animate(time) {
|
||||
const timeScale = time / 1000;
|
||||
const secsIntegral = Math.trunc(timeScale);
|
||||
const secsFractional = timeScale % 1;
|
||||
|
||||
if (previousSecsIntegral != secsIntegral) {
|
||||
const foregroundColorTarget = new Color("srgb", backgroundColor.coords.map(c => saturate(c + Math.random() - 0.5)));
|
||||
foregroundColorRange = foregroundColor.range(foregroundColorTarget, { space: "srgb" });
|
||||
previousSecsIntegral = secsIntegral;
|
||||
}
|
||||
|
||||
foregroundColor = foregroundColorRange(secsFractional);
|
||||
input.style.color = foregroundColor.toString({ inGamut: false });
|
||||
|
||||
const foregroundCielabNudged = nudgeCielab(backgroundColor, foregroundColor);
|
||||
const foregroundOklabNudged = nudgeOklab(backgroundColor, foregroundColor);
|
||||
|
||||
cielab.style.color = foregroundCielabNudged;
|
||||
oklab.style.color = foregroundOklabNudged;
|
||||
|
||||
statsInput.innerHTML = contrastString(foregroundColor);
|
||||
statsCielab.innerHTML = contrastString(foregroundCielabNudged);
|
||||
statsOklab.innerHTML = contrastString(foregroundOklabNudged);
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
document.getElementById("background-color").addEventListener("input", event => {
|
||||
backgroundColor = new Color(event.target.value);
|
||||
document.documentElement.style.backgroundColor = backgroundColor;
|
||||
}, false);
|
||||
|
||||
document.documentElement.style.backgroundColor = backgroundColor;
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -64,7 +64,7 @@ bool TextColor::CanBeBrightened() const noexcept
|
||||
|
||||
bool TextColor::IsLegacy() const noexcept
|
||||
{
|
||||
return IsIndex16() || (IsIndex256() && _index < 16);
|
||||
return (IsIndex16() || IsIndex256()) && _index < 16;
|
||||
}
|
||||
|
||||
bool TextColor::IsIndex16() const noexcept
|
||||
@@ -82,6 +82,11 @@ bool TextColor::IsDefault() const noexcept
|
||||
return _meta == ColorType::IsDefault;
|
||||
}
|
||||
|
||||
bool TextColor::IsDefaultOrLegacy() const noexcept
|
||||
{
|
||||
return _meta != ColorType::IsRgb && _index < 16;
|
||||
}
|
||||
|
||||
bool TextColor::IsRgb() const noexcept
|
||||
{
|
||||
return _meta == ColorType::IsRgb;
|
||||
|
||||
@@ -37,12 +37,14 @@ Revision History:
|
||||
#include "WexTestClass.h"
|
||||
#endif
|
||||
|
||||
// The enum values being in this particular order allows the compiler to do some useful optimizations,
|
||||
// like simplifying `IsIndex16() || IsIndex256()` into a simple range check without branching.
|
||||
enum class ColorType : BYTE
|
||||
{
|
||||
IsIndex256 = 0x0,
|
||||
IsIndex16 = 0x1,
|
||||
IsDefault = 0x2,
|
||||
IsRgb = 0x3
|
||||
IsDefault,
|
||||
IsIndex16,
|
||||
IsIndex256,
|
||||
IsRgb
|
||||
};
|
||||
|
||||
enum class ColorAlias : size_t
|
||||
@@ -121,6 +123,7 @@ public:
|
||||
bool IsIndex16() const noexcept;
|
||||
bool IsIndex256() const noexcept;
|
||||
bool IsDefault() const noexcept;
|
||||
bool IsDefaultOrLegacy() const noexcept;
|
||||
bool IsRgb() const noexcept;
|
||||
|
||||
void SetColor(const COLORREF rgbColor) noexcept;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\"</_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>
|
||||
|
||||
@@ -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>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\"</_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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -60,4 +60,4 @@
|
||||
<AdditionalDependencies>Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -12,11 +12,42 @@
|
||||
#include "WindowManager.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
#include <WtExeUtils.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
namespace
|
||||
{
|
||||
const GUID& MonarchCLSID()
|
||||
{
|
||||
if (!IsPackaged()) [[unlikely]]
|
||||
{
|
||||
// Unpackaged installations don't have the luxury of magic package isolation
|
||||
// to stop them from accidentally touching each other's monarchs.
|
||||
// We need to enforce that ourselves by making their monarch CLSIDs unique
|
||||
// per install.
|
||||
// This applies in both portable mode and normal unpackaged mode.
|
||||
// We'll use a v5 UUID based on the install folder to unique them.
|
||||
static GUID processRootHashedGuid = []() {
|
||||
// {5456C4DB-557D-4A22-B043-B1577418E4AF}
|
||||
static constexpr GUID processRootHashedGuidBase = { 0x5456c4db, 0x557d, 0x4a22, { 0xb0, 0x43, 0xb1, 0x57, 0x74, 0x18, 0xe4, 0xaf } };
|
||||
|
||||
// Make a temporary monarch CLSID based on the unpackaged install root
|
||||
std::filesystem::path modulePath{ wil::GetModuleFileNameW<std::wstring>(wil::GetModuleInstanceHandle()) };
|
||||
modulePath.remove_filename();
|
||||
std::wstring pathRootAsString{ modulePath.wstring() };
|
||||
|
||||
return Utils::CreateV5Uuid(processRootHashedGuidBase, gsl::as_bytes(gsl::make_span(pathRootAsString)));
|
||||
}();
|
||||
return processRootHashedGuid;
|
||||
}
|
||||
return Monarch_clsid;
|
||||
}
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
WindowManager::WindowManager()
|
||||
@@ -325,7 +356,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
void WindowManager::_registerAsMonarch()
|
||||
{
|
||||
winrt::check_hresult(CoRegisterClassObject(Monarch_clsid,
|
||||
winrt::check_hresult(CoRegisterClassObject(MonarchCLSID(),
|
||||
winrt::make<::MonarchFactory>().get(),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
REGCLS_MULTIPLEUSE,
|
||||
@@ -342,7 +373,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
//
|
||||
// * If we're running unpackaged: the .winmd must be a sibling of the .exe
|
||||
// * If we're running packaged: the .winmd must be in the package root
|
||||
_monarch = create_instance<Remoting::IMonarch>(Monarch_clsid,
|
||||
_monarch = create_instance<Remoting::IMonarch>(MonarchCLSID(),
|
||||
CLSCTX_LOCAL_SERVER);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ HRESULT OpenTerminalHere::GetState(IShellItemArray* psiItemArray,
|
||||
|
||||
SFGAOF attributes;
|
||||
const bool isFileSystemItem = psi && (psi->GetAttributes(SFGAO_FILESYSTEM, &attributes) == S_OK);
|
||||
*pCmdState = isFileSystemItem ? ECS_ENABLED : ECS_HIDDEN;
|
||||
const bool isCompressed = psi && (psi->GetAttributes(SFGAO_FOLDER | SFGAO_STREAM, &attributes) == S_OK);
|
||||
*pCmdState = isFileSystemItem && !isCompressed ? ECS_ENABLED : ECS_HIDDEN;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_isUwp = true;
|
||||
FAIL_FAST_MSG("Terminal is not intended to run as a Universal Windows Application");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,22 +77,6 @@ namespace winrt::TerminalApp::implementation
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
void App::OnLaunched(const LaunchActivatedEventArgs& /*e*/)
|
||||
{
|
||||
// if this is a UWP... it means its our problem to hook up the content to the window here.
|
||||
if (_isUwp)
|
||||
{
|
||||
auto content = Window::Current().Content();
|
||||
if (content == nullptr)
|
||||
{
|
||||
auto logic = Logic();
|
||||
logic.RunAsUwp(); // Must set UWP status first, settings might change based on it.
|
||||
logic.ReloadSettings();
|
||||
logic.Create();
|
||||
|
||||
auto page = logic.GetRoot().as<TerminalPage>();
|
||||
|
||||
Window::Current().Content(page);
|
||||
Window::Current().Activate();
|
||||
}
|
||||
}
|
||||
// We used to support a pure UWP version of Terminal. We no longer do so.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
|
||||
private:
|
||||
bool _isUwp = false;
|
||||
winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager _windowsXamlManager = nullptr;
|
||||
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::UI::Xaml::Markup::IXamlMetadataProvider> _providers = winrt::single_threaded_vector<Windows::UI::Xaml::Markup::IXamlMetadataProvider>();
|
||||
bool _bIsClosed = false;
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<Application x:Class="TerminalApp.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:TA="using:TerminalApp"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:primitives="using:Microsoft.UI.Xaml.Controls.Primitives"
|
||||
mc:Ignorable="d">
|
||||
<!--
|
||||
If you want to prove this works, then add `RequestedTheme="Light"` to
|
||||
@@ -48,6 +48,23 @@
|
||||
<Thickness x:Key="TabViewHeaderPadding">9,0,5,0</Thickness>
|
||||
<Thickness x:Key="TabViewItemBorderThickness">1,1,1,0</Thickness>
|
||||
|
||||
<!--
|
||||
Disable the EntranceThemeTransition for our muxc:TabView, which would slowly slide in the tabs
|
||||
while the window opens. The difference is especially noticeable if window fade-in transitions are
|
||||
disabled system-wide. On my system this shaves off about 10% of the startup cost and looks better.
|
||||
-->
|
||||
<Style TargetType="primitives:TabViewListView">
|
||||
<Setter Property="ItemContainerTransitions">
|
||||
<Setter.Value>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition />
|
||||
<ContentThemeTransition />
|
||||
<ReorderThemeTransition />
|
||||
</TransitionCollection>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Shadow that can be used by any control. -->
|
||||
<ThemeShadow x:Name="SharedShadow" />
|
||||
|
||||
|
||||
@@ -285,6 +285,28 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleEnablePaneReadOnly(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
activeTab->SetPaneReadOnly(true);
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleDisablePaneReadOnly(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (const auto activeTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
activeTab->SetPaneReadOnly(false);
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
|
||||
@@ -186,7 +186,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// The TerminalPage has to be constructed during our construction, to
|
||||
// make sure that there's a terminal page for callers of
|
||||
// SetTitleBarContent
|
||||
_isElevated = ::Microsoft::Console::Utils::IsElevated();
|
||||
_isElevated = ::Microsoft::Console::Utils::IsRunningElevated();
|
||||
_canDragDrop = ::Microsoft::Console::Utils::CanUwpDragDrop();
|
||||
_root = winrt::make_self<TerminalPage>();
|
||||
|
||||
_reloadSettings = std::make_shared<ThrottledFuncTrailing<>>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() {
|
||||
@@ -208,38 +209,19 @@ namespace winrt::TerminalApp::implementation
|
||||
return _root->Initialize(hwnd);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called around the codebase to discover if this is a UWP where we need to turn off specific settings.
|
||||
// Arguments:
|
||||
// - <none> - reports internal state
|
||||
// Return Value:
|
||||
// - True if UWP, false otherwise.
|
||||
bool AppLogic::IsUwp() const noexcept
|
||||
{
|
||||
return _isUwp;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called around the codebase to discover if Terminal is running elevated
|
||||
// Arguments:
|
||||
// - <none> - reports internal state
|
||||
// Return Value:
|
||||
// - True if elevated, false otherwise.
|
||||
bool AppLogic::IsElevated() const noexcept
|
||||
bool AppLogic::IsRunningElevated() const noexcept
|
||||
{
|
||||
return _isElevated;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called by UWP context invoker to let us know that we may have to change some of our behaviors
|
||||
// for being a UWP
|
||||
// Arguments:
|
||||
// - <none> (sets to UWP = true, one way change)
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppLogic::RunAsUwp()
|
||||
bool AppLogic::CanDragDrop() const noexcept
|
||||
{
|
||||
_isUwp = true;
|
||||
return _canDragDrop;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -259,13 +241,6 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
_root->DialogPresenter(*this);
|
||||
|
||||
// In UWP mode, we cannot handle taking over the title bar for tabs,
|
||||
// so this setting is overridden to false no matter what the preference is.
|
||||
if (_isUwp)
|
||||
{
|
||||
_settings.GlobalSettings().ShowTabsInTitlebar(false);
|
||||
}
|
||||
|
||||
// Pay attention, that even if some command line arguments were parsed (like launch mode),
|
||||
// we will not use the startup actions from settings.
|
||||
// While this simplifies the logic, we might want to reconsider this behavior in the future.
|
||||
@@ -563,11 +538,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// return true in that case, to be less noisy (though, that is unexpected)
|
||||
bool AppLogic::_IsKeyboardServiceEnabled()
|
||||
{
|
||||
if (IsUwp())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If at any point we fail to open the service manager, the service,
|
||||
// etc, then just quick return true to disable the dialog. We'd rather
|
||||
// not be noisy with this dialog if we failed for some reason.
|
||||
@@ -811,7 +781,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
try
|
||||
{
|
||||
auto newSettings = _isUwp ? CascadiaSettings::LoadUniversal() : CascadiaSettings::LoadAll();
|
||||
auto newSettings = CascadiaSettings::LoadAll();
|
||||
|
||||
if (newSettings.GetLoadingError())
|
||||
{
|
||||
@@ -1184,6 +1154,21 @@ namespace winrt::TerminalApp::implementation
|
||||
if (!focusedObject)
|
||||
{
|
||||
focusedObject = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(focusedElement);
|
||||
|
||||
// We were unable to find a focused object. Give the
|
||||
// TerminalPage one last chance to let the alt+space
|
||||
// menu still work.
|
||||
//
|
||||
// We return always, because the TerminalPage handler
|
||||
// will return false for just a bare `alt` press, and
|
||||
// don't want to go around the loop again.
|
||||
if (!focusedObject)
|
||||
{
|
||||
if (auto keyListener{ _root.try_as<IDirectKeyListener>() })
|
||||
{
|
||||
return keyListener.OnDirectKeyEvent(vkey, scanCode, down);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -59,9 +59,8 @@ namespace winrt::TerminalApp::implementation
|
||||
STDMETHODIMP Initialize(HWND hwnd);
|
||||
|
||||
void Create();
|
||||
bool IsUwp() const noexcept;
|
||||
void RunAsUwp();
|
||||
bool IsElevated() const noexcept;
|
||||
bool IsRunningElevated() const noexcept;
|
||||
bool CanDragDrop() const noexcept;
|
||||
void ReloadSettings();
|
||||
|
||||
[[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept;
|
||||
@@ -148,8 +147,8 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs);
|
||||
|
||||
private:
|
||||
bool _isUwp{ false };
|
||||
bool _isElevated{ false };
|
||||
bool _canDragDrop{ false };
|
||||
|
||||
// If you add controls here, but forget to null them either here or in
|
||||
// the ctor, you're going to have a bad time. It'll mysteriously fail to
|
||||
|
||||
@@ -46,9 +46,8 @@ namespace TerminalApp
|
||||
// registered?" when it definitely is.
|
||||
void Create();
|
||||
|
||||
Boolean IsUwp();
|
||||
void RunAsUwp();
|
||||
Boolean IsElevated();
|
||||
Boolean IsRunningElevated();
|
||||
Boolean CanDragDrop();
|
||||
|
||||
Boolean HasCommandlineArguments();
|
||||
Boolean HasSettingsStartupActions();
|
||||
|
||||
@@ -107,8 +107,7 @@
|
||||
x:Uid="TabColorClearButton"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="ClearColorButton_Click"
|
||||
Content="Reset" />
|
||||
Click="ClearColorButton_Click" />
|
||||
<ToggleButton x:Name="CustomColorButton"
|
||||
x:Uid="TabColorCustomButton"
|
||||
Grid.Column="1"
|
||||
|
||||
@@ -795,4 +795,8 @@
|
||||
<data name="NewTabMenuFolderEmpty" xml:space="preserve">
|
||||
<value>Empty...</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="TabColorClearButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.FullDescription" xml:space="preserve">
|
||||
<value>Reset tab color</value>
|
||||
<comment>Text used to identify the reset button</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace winrt
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
}
|
||||
|
||||
#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
SettingsTab::SettingsTab(MainPage settingsUI,
|
||||
@@ -36,6 +38,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void SettingsTab::UpdateSettings(CascadiaSettings settings)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
auto settingsUI{ Content().as<MainPage>() };
|
||||
settingsUI.UpdateSettings(settings);
|
||||
|
||||
@@ -52,6 +56,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - The list of actions.
|
||||
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions() const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
ActionAndArgs action;
|
||||
action.Action(ShortcutAction::OpenSettings);
|
||||
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
|
||||
@@ -68,6 +74,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void SettingsTab::Focus(WUX::FocusState focusState)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_focusState = focusState;
|
||||
|
||||
if (_focusState != FocusState::Unfocused)
|
||||
@@ -96,20 +104,14 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget SettingsTab::_CreateIcon()
|
||||
void SettingsTab::_CreateIcon()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
// This is the Setting icon (looks like a gear)
|
||||
static constexpr std::wstring_view glyph{ L"\xE713" };
|
||||
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
auto glyph = L"\xE713"; // This is the Setting icon (looks like a gear)
|
||||
|
||||
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
|
||||
Icon(glyph);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(glyph));
|
||||
}
|
||||
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
|
||||
Icon(winrt::hstring{ glyph });
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(glyph));
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush()
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
|
||||
|
||||
void _MakeTabViewItem() override;
|
||||
winrt::fire_and_forget _CreateIcon();
|
||||
void _CreateIcon();
|
||||
|
||||
virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
|
||||
};
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace winrt
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
}
|
||||
|
||||
#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
WUX::FocusState TabBase::FocusState() const noexcept
|
||||
@@ -32,6 +34,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Prepares this tab for being removed from the UI hierarchy
|
||||
void TabBase::Shutdown()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
Content(nullptr);
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
}
|
||||
@@ -159,6 +163,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void TabBase::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
TabViewIndex(idx);
|
||||
TabViewNumTabs(numTabs);
|
||||
_EnableCloseMenuItems();
|
||||
@@ -167,11 +173,15 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void TabBase::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_dispatch = dispatch;
|
||||
}
|
||||
|
||||
void TabBase::SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_actionMap = actionMap;
|
||||
_UpdateSwitchToTabKeyChord();
|
||||
}
|
||||
@@ -183,26 +193,18 @@ namespace winrt::TerminalApp::implementation
|
||||
// - keyChord - string representation of the key chord that switches to the current tab
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TabBase::_UpdateSwitchToTabKeyChord()
|
||||
void TabBase::_UpdateSwitchToTabKeyChord()
|
||||
{
|
||||
const auto keyChord = _actionMap ? _actionMap.GetKeyBindingForAction(ShortcutAction::SwitchToTab, SwitchToTabArgs{ _TabViewIndex }) : nullptr;
|
||||
const auto keyChordText = keyChord ? KeyChordSerialization::ToString(keyChord) : L"";
|
||||
|
||||
if (_keyChord == keyChordText)
|
||||
{
|
||||
co_return;
|
||||
return;
|
||||
}
|
||||
|
||||
_keyChord = keyChordText;
|
||||
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
_UpdateToolTip();
|
||||
}
|
||||
_UpdateToolTip();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -268,6 +270,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
std::optional<winrt::Windows::UI::Color> TabBase::GetTabColor()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -275,6 +279,8 @@ namespace winrt::TerminalApp::implementation
|
||||
const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& unfocused,
|
||||
const til::color& tabRowColor)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_themeColor = focused;
|
||||
_unfocusedThemeColor = unfocused;
|
||||
_tabRowColor = tabRowColor;
|
||||
@@ -292,49 +298,37 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TabBase::_RecalculateAndApplyTabColor()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
// GetTabColor will return the color set by the color picker, or the
|
||||
// color specified in the profile. If neither of those were set,
|
||||
// then look to _themeColor to see if there's a value there.
|
||||
// Otherwise, clear our color, falling back to the TabView defaults.
|
||||
const auto currentColor = GetTabColor();
|
||||
if (currentColor.has_value())
|
||||
{
|
||||
_ApplyTabColorOnUIThread(currentColor.value());
|
||||
}
|
||||
else if (_themeColor != nullptr)
|
||||
{
|
||||
// Safely get the active control's brush.
|
||||
const Media::Brush terminalBrush{ _BackgroundBrush() };
|
||||
|
||||
TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
|
||||
auto ptrTab = weakThis.get();
|
||||
if (!ptrTab)
|
||||
if (const auto themeBrush{ _themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto tab{ ptrTab };
|
||||
|
||||
// GetTabColor will return the color set by the color picker, or the
|
||||
// color specified in the profile. If neither of those were set,
|
||||
// then look to _themeColor to see if there's a value there.
|
||||
// Otherwise, clear our color, falling back to the TabView defaults.
|
||||
const auto currentColor = tab->GetTabColor();
|
||||
if (currentColor.has_value())
|
||||
{
|
||||
tab->_ApplyTabColorOnUIThread(currentColor.value());
|
||||
}
|
||||
else if (tab->_themeColor != nullptr)
|
||||
{
|
||||
// Safely get the active control's brush.
|
||||
const Media::Brush terminalBrush{ tab->_BackgroundBrush() };
|
||||
|
||||
if (const auto themeBrush{ tab->_themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
|
||||
{
|
||||
// ThemeColor.Evaluate will get us a Brush (because the
|
||||
// TermControl could have an acrylic BG, for example). Take
|
||||
// that brush, and get the color out of it. We don't really
|
||||
// want to have the tab items themselves be acrylic.
|
||||
tab->_ApplyTabColorOnUIThread(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_ClearTabBackgroundColor();
|
||||
}
|
||||
// ThemeColor.Evaluate will get us a Brush (because the
|
||||
// TermControl could have an acrylic BG, for example). Take
|
||||
// that brush, and get the color out of it. We don't really
|
||||
// want to have the tab items themselves be acrylic.
|
||||
_ApplyTabColorOnUIThread(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_ClearTabBackgroundColor();
|
||||
_ClearTabBackgroundColor();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_ClearTabBackgroundColor();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _EnableCloseMenuItems();
|
||||
void _CloseTabsAfter();
|
||||
void _CloseOtherTabs();
|
||||
winrt::fire_and_forget _UpdateSwitchToTabKeyChord();
|
||||
void _UpdateSwitchToTabKeyChord();
|
||||
void _UpdateToolTip();
|
||||
|
||||
void _RecalculateAndApplyTabColor();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -149,27 +149,26 @@ namespace winrt::TerminalApp::implementation
|
||||
_systemRowsToScroll = _ReadSystemRowsToScroll();
|
||||
}
|
||||
|
||||
bool TerminalPage::IsElevated() const noexcept
|
||||
bool TerminalPage::IsRunningElevated() const noexcept
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
// This won't change over the lifetime of the application
|
||||
|
||||
static const auto isElevated = []() {
|
||||
// *** THIS IS A SINGLETON ***
|
||||
auto result = false;
|
||||
|
||||
// GH#2455 - Make sure to try/catch calls to Application::Current,
|
||||
// because that _won't_ be an instance of TerminalApp::App in the
|
||||
// LocalTests
|
||||
try
|
||||
{
|
||||
result = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsElevated();
|
||||
}
|
||||
CATCH_LOG();
|
||||
return result;
|
||||
}();
|
||||
|
||||
return isElevated;
|
||||
// GH#2455 - Make sure to try/catch calls to Application::Current,
|
||||
// because that _won't_ be an instance of TerminalApp::App in the
|
||||
// LocalTests
|
||||
try
|
||||
{
|
||||
return Application::Current().as<TerminalApp::App>().Logic().IsRunningElevated();
|
||||
}
|
||||
CATCH_LOG();
|
||||
return false;
|
||||
}
|
||||
bool TerminalPage::CanDragDrop() const noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
return Application::Current().as<TerminalApp::App>().Logic().CanDragDrop();
|
||||
}
|
||||
CATCH_LOG();
|
||||
return true;
|
||||
}
|
||||
|
||||
void TerminalPage::Create()
|
||||
@@ -182,11 +181,11 @@ namespace winrt::TerminalApp::implementation
|
||||
_tabView = _tabRow.TabView();
|
||||
_rearranging = false;
|
||||
|
||||
const auto isElevated = IsElevated();
|
||||
const auto canDragDrop = CanDragDrop();
|
||||
|
||||
_tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler });
|
||||
_tabView.CanReorderTabs(!isElevated);
|
||||
_tabView.CanDragTabs(!isElevated);
|
||||
_tabView.CanReorderTabs(canDragDrop);
|
||||
_tabView.CanDragTabs(canDragDrop);
|
||||
_tabView.TabDragStarting({ get_weak(), &TerminalPage::_TabDragStarted });
|
||||
_tabView.TabDragCompleted({ get_weak(), &TerminalPage::_TabDragCompleted });
|
||||
|
||||
@@ -308,7 +307,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Setup mouse vanish attributes
|
||||
SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &_shouldMouseVanish, false);
|
||||
|
||||
_tabRow.ShowElevationShield(IsElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
_tabRow.ShowElevationShield(IsRunningElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
|
||||
// Store cursor, so we can restore it, e.g., after mouse vanishing
|
||||
// (we'll need to adapt this logic once we make cursor context aware)
|
||||
@@ -347,7 +346,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// GH#12267: Don't forget about defterm handoff here. If we're being
|
||||
// created for embedding, then _yea_, we don't need to handoff to an
|
||||
// elevated window.
|
||||
if (!_startupActions || IsElevated() || _shouldStartInboundListener)
|
||||
if (!_startupActions || IsRunningElevated() || _shouldStartInboundListener)
|
||||
{
|
||||
// there aren't startup actions, or we're elevated. In that case, go for it.
|
||||
return false;
|
||||
@@ -840,61 +839,48 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// add static items
|
||||
{
|
||||
// GH#2455 - Make sure to try/catch calls to Application::Current,
|
||||
// because that _won't_ be an instance of TerminalApp::App in the
|
||||
// LocalTests
|
||||
auto isUwp = false;
|
||||
try
|
||||
// Create the settings button.
|
||||
auto settingsItem = WUX::Controls::MenuFlyoutItem{};
|
||||
settingsItem.Text(RS_(L"SettingsMenuItem"));
|
||||
const auto settingsToolTip = RS_(L"SettingsToolTip");
|
||||
|
||||
WUX::Controls::ToolTipService::SetToolTip(settingsItem, box_value(settingsToolTip));
|
||||
Automation::AutomationProperties::SetHelpText(settingsItem, settingsToolTip);
|
||||
|
||||
WUX::Controls::SymbolIcon ico{};
|
||||
ico.Symbol(WUX::Controls::Symbol::Setting);
|
||||
settingsItem.Icon(ico);
|
||||
|
||||
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
|
||||
newTabFlyout.Items().Append(settingsItem);
|
||||
|
||||
auto actionMap = _settings.ActionMap();
|
||||
const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
|
||||
if (settingsKeyChord)
|
||||
{
|
||||
isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
|
||||
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (!isUwp)
|
||||
// Create the command palette button.
|
||||
auto commandPaletteFlyout = WUX::Controls::MenuFlyoutItem{};
|
||||
commandPaletteFlyout.Text(RS_(L"CommandPaletteMenuItem"));
|
||||
const auto commandPaletteToolTip = RS_(L"CommandPaletteToolTip");
|
||||
|
||||
WUX::Controls::ToolTipService::SetToolTip(commandPaletteFlyout, box_value(commandPaletteToolTip));
|
||||
Automation::AutomationProperties::SetHelpText(commandPaletteFlyout, commandPaletteToolTip);
|
||||
|
||||
WUX::Controls::FontIcon commandPaletteIcon{};
|
||||
commandPaletteIcon.Glyph(L"\xE945");
|
||||
commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
|
||||
commandPaletteFlyout.Icon(commandPaletteIcon);
|
||||
|
||||
commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick });
|
||||
newTabFlyout.Items().Append(commandPaletteFlyout);
|
||||
|
||||
const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
|
||||
if (commandPaletteKeyChord)
|
||||
{
|
||||
// Create the settings button.
|
||||
auto settingsItem = WUX::Controls::MenuFlyoutItem{};
|
||||
settingsItem.Text(RS_(L"SettingsMenuItem"));
|
||||
const auto settingsToolTip = RS_(L"SettingsToolTip");
|
||||
|
||||
WUX::Controls::ToolTipService::SetToolTip(settingsItem, box_value(settingsToolTip));
|
||||
Automation::AutomationProperties::SetHelpText(settingsItem, settingsToolTip);
|
||||
|
||||
WUX::Controls::SymbolIcon ico{};
|
||||
ico.Symbol(WUX::Controls::Symbol::Setting);
|
||||
settingsItem.Icon(ico);
|
||||
|
||||
settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick });
|
||||
newTabFlyout.Items().Append(settingsItem);
|
||||
|
||||
auto actionMap = _settings.ActionMap();
|
||||
const auto settingsKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::OpenSettings, OpenSettingsArgs{ SettingsTarget::SettingsUI }) };
|
||||
if (settingsKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(settingsItem, settingsKeyChord);
|
||||
}
|
||||
|
||||
// Create the command palette button.
|
||||
auto commandPaletteFlyout = WUX::Controls::MenuFlyoutItem{};
|
||||
commandPaletteFlyout.Text(RS_(L"CommandPaletteMenuItem"));
|
||||
const auto commandPaletteToolTip = RS_(L"CommandPaletteToolTip");
|
||||
|
||||
WUX::Controls::ToolTipService::SetToolTip(commandPaletteFlyout, box_value(commandPaletteToolTip));
|
||||
Automation::AutomationProperties::SetHelpText(commandPaletteFlyout, commandPaletteToolTip);
|
||||
|
||||
WUX::Controls::FontIcon commandPaletteIcon{};
|
||||
commandPaletteIcon.Glyph(L"\xE945");
|
||||
commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
|
||||
commandPaletteFlyout.Icon(commandPaletteIcon);
|
||||
|
||||
commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick });
|
||||
newTabFlyout.Items().Append(commandPaletteFlyout);
|
||||
|
||||
const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) };
|
||||
if (commandPaletteKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord);
|
||||
}
|
||||
_SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord);
|
||||
}
|
||||
|
||||
// Create the about button.
|
||||
@@ -924,6 +910,10 @@ namespace winrt::TerminalApp::implementation
|
||||
newTabFlyout.Opening([this](auto&&, auto&&) {
|
||||
_FocusCurrentTab(true);
|
||||
});
|
||||
// Necessary for fly-out sub items to get focus on a tab before collapsing. Related to #15049
|
||||
newTabFlyout.Closing([this](auto&&, auto&&) {
|
||||
_FocusCurrentTab(true);
|
||||
});
|
||||
_newTabButton.Flyout(newTabFlyout);
|
||||
}
|
||||
|
||||
@@ -1172,7 +1162,7 @@ namespace winrt::TerminalApp::implementation
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
const auto dispatchToElevatedWindow = ctrlPressed && !IsElevated();
|
||||
const auto dispatchToElevatedWindow = ctrlPressed && !IsRunningElevated();
|
||||
|
||||
if ((shiftPressed || dispatchToElevatedWindow) && !debugTap)
|
||||
{
|
||||
@@ -1247,10 +1237,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 +2463,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
|
||||
@@ -2913,7 +2923,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// want to create an animation.
|
||||
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_settings.GlobalSettings().DisableAnimations());
|
||||
|
||||
_tabRow.ShowElevationShield(IsElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
_tabRow.ShowElevationShield(IsRunningElevated() && _settings.GlobalSettings().ShowAdminShield());
|
||||
|
||||
Media::SolidColorBrush transparent{ Windows::UI::Colors::Transparent() };
|
||||
_tabView.Background(transparent);
|
||||
@@ -3694,18 +3704,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// - The OS-localized name for the TabletInputService
|
||||
winrt::hstring _getTabletServiceName()
|
||||
{
|
||||
auto isUwp = false;
|
||||
try
|
||||
{
|
||||
isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
if (isUwp)
|
||||
{
|
||||
return winrt::hstring{ TabletInputServiceKey };
|
||||
}
|
||||
|
||||
wil::unique_schandle hManager{ OpenSCManagerW(nullptr, nullptr, 0) };
|
||||
|
||||
if (LOG_LAST_ERROR_IF(!hManager.is_valid()))
|
||||
@@ -4157,7 +4155,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Try to handle auto-elevation
|
||||
const auto requestedElevation = controlSettings.DefaultSettings().Elevate();
|
||||
const auto currentlyElevated = IsElevated();
|
||||
const auto currentlyElevated = IsRunningElevated();
|
||||
|
||||
// We aren't elevated, but we want to be.
|
||||
if (requestedElevation && !currentlyElevated)
|
||||
|
||||
@@ -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,
|
||||
@@ -131,7 +131,8 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::hstring WindowIdForDisplay() const noexcept;
|
||||
winrt::hstring WindowNameForDisplay() const noexcept;
|
||||
bool IsQuakeWindow() const noexcept;
|
||||
bool IsElevated() const noexcept;
|
||||
bool CanDragDrop() const noexcept;
|
||||
bool IsRunningElevated() const noexcept;
|
||||
|
||||
void OpenSettingsUI();
|
||||
void WindowActivated(const bool activated);
|
||||
|
||||
@@ -104,7 +104,8 @@
|
||||
PrimaryButtonClick="_SendFeedbackOnClick">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock IsTextSelectionEnabled="True">
|
||||
<Run Text="{x:Bind ApplicationDisplayName}" /> <LineBreak />
|
||||
<Run AutomationProperties.HeadingLevel="1"
|
||||
Text="{x:Bind ApplicationDisplayName}" /> <LineBreak />
|
||||
<Run x:Uid="AboutDialog_VersionLabel" />
|
||||
<Run Text="{x:Bind ApplicationVersion}" />
|
||||
</TextBlock>
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace winrt
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
}
|
||||
|
||||
#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalTab::TerminalTab(std::shared_ptr<Pane> rootPane)
|
||||
@@ -143,30 +145,23 @@ namespace winrt::TerminalApp::implementation
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalTab::_UpdateHeaderControlMaxWidth()
|
||||
void TerminalTab::_UpdateHeaderControlMaxWidth()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
try
|
||||
{
|
||||
try
|
||||
// Make sure to try/catch this, because the LocalTests won't be
|
||||
// able to use this helper.
|
||||
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
|
||||
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
|
||||
{
|
||||
// Make sure to try/catch this, because the LocalTests won't be
|
||||
// able to use this helper.
|
||||
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
|
||||
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
|
||||
{
|
||||
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
|
||||
}
|
||||
_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -182,6 +177,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// that was last focused.
|
||||
TermControl TerminalTab::GetActiveTerminalControl() const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
if (_activePane)
|
||||
{
|
||||
return _activePane->GetLastFocusedTerminalControl();
|
||||
@@ -198,6 +195,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::Initialize()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
|
||||
// Attach event handlers to each new pane
|
||||
_AttachEventHandlersToPane(pane);
|
||||
@@ -217,6 +216,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::Focus(WUX::FocusState focusState)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_focusState = focusState;
|
||||
|
||||
if (_focusState != FocusState::Unfocused)
|
||||
@@ -249,6 +250,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// focused, else the GUID of the profile of the last control to be focused
|
||||
Profile TerminalTab::GetFocusedProfile() const noexcept
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return _activePane->GetFocusedProfile();
|
||||
}
|
||||
|
||||
@@ -260,6 +263,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::UpdateSettings()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// The tabWidthMode may have changed, update the header control accordingly
|
||||
_UpdateHeaderControlMaxWidth();
|
||||
}
|
||||
@@ -270,12 +275,14 @@ namespace winrt::TerminalApp::implementation
|
||||
// - iconPath: The new path string to use as the IconPath for our TabViewItem
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TerminalTab::UpdateIcon(const winrt::hstring iconPath)
|
||||
void TerminalTab::UpdateIcon(const winrt::hstring iconPath)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// Don't reload our icon if it hasn't changed.
|
||||
if (iconPath == _lastIconPath)
|
||||
{
|
||||
co_return;
|
||||
return;
|
||||
}
|
||||
|
||||
_lastIconPath = iconPath;
|
||||
@@ -284,19 +291,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// for when we show the icon again)
|
||||
if (_iconHidden)
|
||||
{
|
||||
co_return;
|
||||
return;
|
||||
}
|
||||
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
}
|
||||
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -304,28 +304,23 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Used when we want to show the progress ring, which should replace the icon
|
||||
// Arguments:
|
||||
// - hide: if true, we hide the icon; if false, we show the icon
|
||||
winrt::fire_and_forget TerminalTab::HideIcon(const bool hide)
|
||||
void TerminalTab::HideIcon(const bool hide)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
if (_iconHidden != hide)
|
||||
{
|
||||
if (tab->_iconHidden != hide)
|
||||
if (hide)
|
||||
{
|
||||
if (hide)
|
||||
{
|
||||
Icon({});
|
||||
TabViewItem().IconSource(IconSource{ nullptr });
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
}
|
||||
tab->_iconHidden = hide;
|
||||
Icon({});
|
||||
TabViewItem().IconSource(IconSource{ nullptr });
|
||||
}
|
||||
else
|
||||
{
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
}
|
||||
_iconHidden = hide;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,37 +328,27 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Hide or show the bell indicator in the tab header
|
||||
// Arguments:
|
||||
// - show: if true, we show the indicator; if false, we hide the indicator
|
||||
winrt::fire_and_forget TerminalTab::ShowBellIndicator(const bool show)
|
||||
void TerminalTab::ShowBellIndicator(const bool show)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
_tabStatus.BellIndicator(show);
|
||||
}
|
||||
_tabStatus.BellIndicator(show);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Activates the timer for the bell indicator in the tab
|
||||
// - Called if a bell raised when the tab already has focus
|
||||
winrt::fire_and_forget TerminalTab::ActivateBellIndicatorTimer()
|
||||
void TerminalTab::ActivateBellIndicatorTimer()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
if (!_bellIndicatorTimer.has_value())
|
||||
{
|
||||
if (!tab->_bellIndicatorTimer.has_value())
|
||||
{
|
||||
DispatcherTimer bellIndicatorTimer;
|
||||
bellIndicatorTimer.Interval(std::chrono::milliseconds(2000));
|
||||
bellIndicatorTimer.Tick({ get_weak(), &TerminalTab::_BellIndicatorTimerTick });
|
||||
bellIndicatorTimer.Start();
|
||||
tab->_bellIndicatorTimer.emplace(std::move(bellIndicatorTimer));
|
||||
}
|
||||
DispatcherTimer bellIndicatorTimer;
|
||||
bellIndicatorTimer.Interval(std::chrono::milliseconds(2000));
|
||||
bellIndicatorTimer.Tick({ get_weak(), &TerminalTab::_BellIndicatorTimerTick });
|
||||
bellIndicatorTimer.Start();
|
||||
_bellIndicatorTimer.emplace(std::move(bellIndicatorTimer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,21 +381,18 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TerminalTab::UpdateTitle()
|
||||
void TerminalTab::UpdateTitle()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await wil::resume_foreground(TabViewItem().Dispatcher());
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
const auto activeTitle = _GetActiveTitle();
|
||||
// Bubble our current tab text to anyone who's listening for changes.
|
||||
Title(activeTitle);
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// Update the control to reflect the changed title
|
||||
_headerControl.Title(activeTitle);
|
||||
Automation::AutomationProperties::SetName(tab->TabViewItem(), activeTitle);
|
||||
_UpdateToolTip();
|
||||
}
|
||||
const auto activeTitle = _GetActiveTitle();
|
||||
// Bubble our current tab text to anyone who's listening for changes.
|
||||
Title(activeTitle);
|
||||
|
||||
// Update the control to reflect the changed title
|
||||
_headerControl.Title(activeTitle);
|
||||
Automation::AutomationProperties::SetName(TabViewItem(), activeTitle);
|
||||
_UpdateToolTip();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -421,12 +403,11 @@ namespace winrt::TerminalApp::implementation
|
||||
// - delta: a number of lines to move the viewport relative to the current viewport.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TerminalTab::Scroll(const int delta)
|
||||
void TerminalTab::Scroll(const int delta)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
auto control = GetActiveTerminalControl();
|
||||
|
||||
co_await wil::resume_foreground(control.Dispatcher());
|
||||
|
||||
const auto currentOffset = control.ScrollOffset();
|
||||
control.ScrollViewport(::base::ClampAdd(currentOffset, delta));
|
||||
}
|
||||
@@ -440,6 +421,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - A vector of commands
|
||||
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions() const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// Give initial ids (0 for the child created with this tab,
|
||||
// 1 for the child after the first split.
|
||||
auto state = _rootPane->BuildStartupActions(0, 1);
|
||||
@@ -513,6 +496,8 @@ namespace winrt::TerminalApp::implementation
|
||||
const float splitSize,
|
||||
std::shared_ptr<Pane> pane)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// Add the new event handlers to the new pane(s)
|
||||
// and update their ids.
|
||||
pane->WalkTree([&](auto p) {
|
||||
@@ -560,6 +545,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - The removed pane, if the remove succeeded.
|
||||
std::shared_ptr<Pane> TerminalTab::DetachPane()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// if we only have one pane, or the focused pane is the root, remove it
|
||||
// entirely and close this tab
|
||||
if (_rootPane == _activePane)
|
||||
@@ -587,6 +574,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - The root pane.
|
||||
std::shared_ptr<Pane> TerminalTab::DetachRoot()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// remove the closed event handler since we are closing the tab
|
||||
// manually.
|
||||
_rootPane->Closed(_rootClosedToken);
|
||||
@@ -613,6 +602,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::AttachPane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// Add the new event handlers to the new pane(s)
|
||||
// and update their ids.
|
||||
pane->WalkTree([&](auto p) {
|
||||
@@ -658,6 +649,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::AttachColorPicker(TerminalApp::ColorPickupFlyout& colorPicker)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
_tabColorPickup = colorPicker;
|
||||
@@ -696,6 +689,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::ToggleSplitOrientation()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_rootPane->ToggleSplitOrientation();
|
||||
}
|
||||
|
||||
@@ -703,6 +698,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - See Pane::CalcSnappedDimension
|
||||
float TerminalTab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return _rootPane->CalcSnappedDimension(widthOrHeight, dimension);
|
||||
}
|
||||
|
||||
@@ -715,6 +712,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::ResizePane(const ResizeDirection& direction)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
_rootPane->ResizePane(direction);
|
||||
@@ -730,6 +729,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// to the terminal when no other panes are present (GH#6219)
|
||||
bool TerminalTab::NavigateFocus(const FocusDirection& direction)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (const auto newFocus = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
|
||||
@@ -760,6 +761,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - true if two panes were swapped.
|
||||
bool TerminalTab::SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// You cannot swap panes with the parent/child pane because of the
|
||||
// circular reference.
|
||||
if (direction == FocusDirection::Parent || direction == FocusDirection::Child)
|
||||
@@ -783,6 +786,13 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
bool TerminalTab::FocusPane(const uint32_t id)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
if (_rootPane == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_changingActivePane = true;
|
||||
const auto res = _rootPane->FocusPane(id);
|
||||
_changingActivePane = false;
|
||||
@@ -793,6 +803,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
|
||||
void TerminalTab::Shutdown()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
if (_rootPane)
|
||||
{
|
||||
_rootPane->Shutdown();
|
||||
@@ -809,22 +821,30 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::ClosePane()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_activePane->Close();
|
||||
}
|
||||
|
||||
void TerminalTab::SetTabText(winrt::hstring title)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_runtimeTabText = title;
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
winrt::hstring TerminalTab::GetTabText() const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return _runtimeTabText;
|
||||
}
|
||||
|
||||
void TerminalTab::ResetTabText()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_runtimeTabText = L"";
|
||||
UpdateTitle();
|
||||
}
|
||||
@@ -838,6 +858,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::ActivateTabRenamer()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_headerControl.BeginRename();
|
||||
}
|
||||
|
||||
@@ -885,7 +907,8 @@ namespace winrt::TerminalApp::implementation
|
||||
auto dispatcher = TabViewItem().Dispatcher();
|
||||
ControlEventTokens events{};
|
||||
|
||||
events.titleToken = control.TitleChanged([weakThis](auto&&, auto&&) {
|
||||
events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
@@ -895,7 +918,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
events.colorToken = control.TabColorChanged([weakThis](auto&&, auto&&) {
|
||||
events.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The control's tabColor changed, but it is not necessarily the
|
||||
@@ -914,14 +938,16 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
events.readOnlyToken = control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
|
||||
events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_RecalculateAndApplyReadOnly();
|
||||
}
|
||||
});
|
||||
|
||||
events.focusToken = control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
|
||||
events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto&& sender, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (const auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_focusState != FocusState::Unfocused)
|
||||
@@ -951,6 +977,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// progress percentage of all our panes.
|
||||
winrt::TerminalApp::TaskbarState TerminalTab::GetCombinedTaskbarState() const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
std::vector<winrt::TerminalApp::TaskbarState> states;
|
||||
if (_rootPane)
|
||||
{
|
||||
@@ -1110,13 +1138,11 @@ namespace winrt::TerminalApp::implementation
|
||||
// Add a Closed event handler to the Pane. If the pane closes out from
|
||||
// underneath us, and it's zoomed, we want to be able to make sure to
|
||||
// update our state accordingly to un-zoom that pane. See GH#7252.
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) -> winrt::fire_and_forget {
|
||||
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_zoomedPane)
|
||||
{
|
||||
co_await wil::resume_foreground(tab->Content().Dispatcher());
|
||||
|
||||
tab->Content(tab->_rootPane->GetRootElement());
|
||||
tab->ExitZoom();
|
||||
}
|
||||
@@ -1129,7 +1155,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// did not actually change. Triggering
|
||||
if (pane != tab->_activePane && !tab->_activePane->_IsLeaf())
|
||||
{
|
||||
co_await wil::resume_foreground(tab->Content().Dispatcher());
|
||||
tab->_UpdateActivePane(tab->_activePane);
|
||||
}
|
||||
|
||||
@@ -1383,6 +1408,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - The tab's color, if any
|
||||
std::optional<winrt::Windows::UI::Color> TerminalTab::GetTabColor()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
std::optional<winrt::Windows::UI::Color> controlTabColor;
|
||||
if (const auto& control = GetActiveTerminalControl())
|
||||
{
|
||||
@@ -1421,6 +1448,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_runtimeTabColor.emplace(color);
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
@@ -1435,6 +1464,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::ResetRuntimeTabColor()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_runtimeTabColor.reset();
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
@@ -1458,6 +1489,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::RequestColorPicker()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
_ColorPickerRequestedHandlers();
|
||||
}
|
||||
|
||||
@@ -1469,6 +1502,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - The total number of leaf panes hosted by this tab.
|
||||
int TerminalTab::GetLeafPaneCount() const noexcept
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return _rootPane->GetLeafPaneCount();
|
||||
}
|
||||
|
||||
@@ -1486,6 +1521,8 @@ namespace winrt::TerminalApp::implementation
|
||||
const float splitSize,
|
||||
winrt::Windows::Foundation::Size availableSpace) const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return _rootPane->PreCalculateCanSplit(_activePane, splitType, splitSize, availableSpace).value_or(std::nullopt);
|
||||
}
|
||||
|
||||
@@ -1497,6 +1534,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::UpdateZoom(std::shared_ptr<Pane> newFocus)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// clear the existing content so the old zoomed pane can be added back to the root tree
|
||||
Content(nullptr);
|
||||
_rootPane->Restore(_zoomedPane);
|
||||
@@ -1517,6 +1556,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalTab::ToggleZoom()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
if (_zoomedPane)
|
||||
{
|
||||
ExitZoom();
|
||||
@@ -1529,6 +1570,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void TerminalTab::EnterZoom()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// Clear the content first, because with parent focusing it is possible
|
||||
// to zoom the root pane, but setting the content will not trigger the
|
||||
// property changed event since it is the same and you would end up with
|
||||
@@ -1542,6 +1585,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
void TerminalTab::ExitZoom()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
Content(nullptr);
|
||||
_rootPane->Restore(_zoomedPane);
|
||||
_zoomedPane = nullptr;
|
||||
@@ -1552,6 +1597,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
bool TerminalTab::IsZoomed()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return _zoomedPane != nullptr;
|
||||
}
|
||||
|
||||
@@ -1561,16 +1608,18 @@ namespace winrt::TerminalApp::implementation
|
||||
// the same read-only status.
|
||||
void TerminalTab::TogglePaneReadOnly()
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
auto hasReadOnly = false;
|
||||
auto allReadOnly = true;
|
||||
_activePane->WalkTree([&](auto p) {
|
||||
_activePane->WalkTree([&](const auto& p) {
|
||||
if (const auto& control{ p->GetTerminalControl() })
|
||||
{
|
||||
hasReadOnly |= control.ReadOnly();
|
||||
allReadOnly &= control.ReadOnly();
|
||||
}
|
||||
});
|
||||
_activePane->WalkTree([&](auto p) {
|
||||
_activePane->WalkTree([&](const auto& p) {
|
||||
if (const auto& control{ p->GetTerminalControl() })
|
||||
{
|
||||
// If all controls have the same read only state then just toggle
|
||||
@@ -1587,6 +1636,38 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Set read-only mode on the active pane
|
||||
// - If a parent pane is selected, this will ensure that all children have
|
||||
// the same read-only status.
|
||||
void TerminalTab::SetPaneReadOnly(const bool readOnlyState)
|
||||
{
|
||||
auto hasReadOnly = false;
|
||||
auto allReadOnly = true;
|
||||
_activePane->WalkTree([&](const auto& p) {
|
||||
if (const auto& control{ p->GetTerminalControl() })
|
||||
{
|
||||
hasReadOnly |= control.ReadOnly();
|
||||
allReadOnly &= control.ReadOnly();
|
||||
}
|
||||
});
|
||||
_activePane->WalkTree([&](const auto& p) {
|
||||
if (const auto& control{ p->GetTerminalControl() })
|
||||
{
|
||||
// If all controls have the same read only state then just disable
|
||||
if (allReadOnly || !hasReadOnly)
|
||||
{
|
||||
control.SetReadOnly(readOnlyState);
|
||||
}
|
||||
// otherwise set to all read only.
|
||||
else if (!control.ReadOnly())
|
||||
{
|
||||
control.SetReadOnly(readOnlyState);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Calculates if the tab is read-only.
|
||||
// The tab is considered read-only if one of the panes is read-only.
|
||||
@@ -1606,6 +1687,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
std::shared_ptr<Pane> TerminalTab::GetActivePane() const
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
return _activePane;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -31,7 +28,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
|
||||
|
||||
winrt::fire_and_forget Scroll(const int delta);
|
||||
void Scroll(const int delta);
|
||||
|
||||
std::shared_ptr<Pane> DetachRoot();
|
||||
std::shared_ptr<Pane> DetachPane();
|
||||
@@ -44,11 +41,11 @@ namespace winrt::TerminalApp::implementation
|
||||
std::shared_ptr<Pane> newPane);
|
||||
|
||||
void ToggleSplitOrientation();
|
||||
winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath);
|
||||
winrt::fire_and_forget HideIcon(const bool hide);
|
||||
void UpdateIcon(const winrt::hstring iconPath);
|
||||
void HideIcon(const bool hide);
|
||||
|
||||
winrt::fire_and_forget ShowBellIndicator(const bool show);
|
||||
winrt::fire_and_forget ActivateBellIndicatorTimer();
|
||||
void ShowBellIndicator(const bool show);
|
||||
void ActivateBellIndicatorTimer();
|
||||
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
@@ -61,7 +58,7 @@ namespace winrt::TerminalApp::implementation
|
||||
bool FocusPane(const uint32_t id);
|
||||
|
||||
void UpdateSettings();
|
||||
winrt::fire_and_forget UpdateTitle();
|
||||
void UpdateTitle();
|
||||
|
||||
void Shutdown() override;
|
||||
void ClosePane();
|
||||
@@ -87,6 +84,8 @@ namespace winrt::TerminalApp::implementation
|
||||
int GetLeafPaneCount() const noexcept;
|
||||
|
||||
void TogglePaneReadOnly();
|
||||
void SetPaneReadOnly(const bool readOnlyState);
|
||||
|
||||
std::shared_ptr<Pane> GetActivePane() const;
|
||||
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;
|
||||
|
||||
@@ -107,6 +106,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 };
|
||||
@@ -153,7 +155,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _MakeTabViewItem() override;
|
||||
|
||||
winrt::fire_and_forget _UpdateHeaderControlMaxWidth();
|
||||
void _UpdateHeaderControlMaxWidth();
|
||||
|
||||
void _CreateContextMenu() override;
|
||||
virtual winrt::hstring _CreateToolTipTitle() override;
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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"))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
static constexpr std::wstring_view AzureClientID = L"0";
|
||||
inline constexpr std::wstring_view AzureClientID = L"0";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) :
|
||||
"";
|
||||
@@ -1667,6 +1667,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_isReadOnly = !_isReadOnly;
|
||||
}
|
||||
|
||||
void ControlCore::SetReadOnlyMode(const bool readOnlyState)
|
||||
{
|
||||
_isReadOnly = readOnlyState;
|
||||
}
|
||||
|
||||
void ControlCore::_raiseReadOnlyWarning()
|
||||
{
|
||||
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Info, RS_(L"TermControlReadOnly"));
|
||||
|
||||
@@ -193,6 +193,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
bool IsInReadOnlyMode() const;
|
||||
void ToggleReadOnlyMode();
|
||||
void SetReadOnlyMode(const bool readOnlyState);
|
||||
|
||||
hstring ReadEntireBuffer() const;
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
void ToggleShaderEffects();
|
||||
void ToggleReadOnlyMode();
|
||||
void SetReadOnlyMode(Boolean readOnlyState);
|
||||
|
||||
Microsoft.Terminal.Core.Point CursorPosition { get; };
|
||||
void ResumeRendering();
|
||||
|
||||
@@ -848,11 +848,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
auto bufferHeight = _core.BufferHeight();
|
||||
|
||||
ScrollBar().Maximum(bufferHeight - bufferHeight);
|
||||
ScrollBar().Maximum(0);
|
||||
ScrollBar().Minimum(0);
|
||||
ScrollBar().Value(0);
|
||||
ScrollBar().ViewportSize(bufferHeight);
|
||||
ScrollBar().LargeChange(std::max(bufferHeight - 1, 0)); // scroll one "screenful" at a time when the scroll bar is clicked
|
||||
ScrollBar().LargeChange(bufferHeight); // scroll one "screenful" at a time when the scroll bar is clicked
|
||||
|
||||
// Set up blinking cursor
|
||||
int blinkTime = GetCaretBlinkTime();
|
||||
@@ -874,7 +874,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// _cursorTimer doesn't exist, and it would never turn on the
|
||||
// cursor. To mitigate, we'll initialize the cursor's 'on' state
|
||||
// with `_focused` here.
|
||||
_core.CursorOn(_focused);
|
||||
_core.CursorOn(_focused || _DisplayCursorWhileBlurred);
|
||||
if (_DisplayCursorWhileBlurred)
|
||||
{
|
||||
_cursorTimer->Start();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1749,7 +1753,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TSFInputControl().NotifyFocusLeave();
|
||||
}
|
||||
|
||||
if (_cursorTimer)
|
||||
if (_cursorTimer && !_DisplayCursorWhileBlurred)
|
||||
{
|
||||
_cursorTimer->Stop();
|
||||
_core.CursorOn(false);
|
||||
@@ -1990,6 +1994,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);
|
||||
|
||||
@@ -2864,6 +2873,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_ReadOnlyChangedHandlers(*this, winrt::box_value(_core.IsInReadOnlyMode()));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the read-only flag, raises event describing the value change
|
||||
void TermControl::SetReadOnly(const bool readOnlyState)
|
||||
{
|
||||
_core.SetReadOnlyMode(readOnlyState);
|
||||
_ReadOnlyChangedHandlers(*this, winrt::box_value(_core.IsInReadOnlyMode()));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handle a mouse exited event, specifically clearing last hovered cell
|
||||
// and removing selection from hyper link if exists
|
||||
@@ -2886,9 +2903,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 +2940,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), locationInDIPs.x - offset.x);
|
||||
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), locationInDIPs.y - offset.y);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
bool ReadOnly() const noexcept;
|
||||
void ToggleReadOnly();
|
||||
void SetReadOnly(const bool readOnlyState);
|
||||
|
||||
static Control::MouseButtonState GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point);
|
||||
static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point);
|
||||
@@ -163,6 +164,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// clang-format on
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);
|
||||
WINRT_PROPERTY(bool, DisplayCursorWhileBlurred, false);
|
||||
|
||||
private:
|
||||
friend struct TermControlT<TermControl>; // friend our parent so it can bind private event handlers
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace Microsoft.Terminal.Control
|
||||
|
||||
Boolean ReadOnly { get; };
|
||||
void ToggleReadOnly();
|
||||
void SetReadOnly(Boolean readOnlyState);
|
||||
|
||||
String ReadEntireBuffer();
|
||||
|
||||
@@ -97,6 +98,8 @@ namespace Microsoft.Terminal.Control
|
||||
// opacity set by the settings should call this instead.
|
||||
Double BackgroundOpacity { get; };
|
||||
|
||||
Boolean DisplayCursorWhileBlurred;
|
||||
|
||||
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
|
||||
|
||||
void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
|
||||
|
||||
@@ -47,16 +47,21 @@
|
||||
latest ControlsV2 version of the template can be found at:
|
||||
https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/CommonStyles/ScrollBar_themeresources.xaml#L218
|
||||
|
||||
We're also removing the corner radius, cause that should be flush
|
||||
Additionally we have:
|
||||
* removed the corner radius, because that should be flush
|
||||
with the top of the window above the TermControl.
|
||||
* set ScrollBarExpandBeginTime to 0 so that the scrollbar, fades in instantly when it's expanded.
|
||||
This makes it feel much better with cursors compared to the questionable standard 400ms delay in
|
||||
the Win11-style WinUI ScrollView. If you also have the "Always show scrollbars" setting enabled in
|
||||
the settings app (do it if you haven't already), it avoids any and all animations during startup which
|
||||
makes the app start feel noticeably better and also shaves off another ~167ms of our "busy time".
|
||||
|
||||
We're also planning on making this adjustable in the future
|
||||
(GH#9218), where we might need this anyways.
|
||||
-->
|
||||
|
||||
<x:Double x:Key="ScrollBarSize">16</x:Double>
|
||||
<CornerRadius x:Key="ScrollBarCornerRadius">0</CornerRadius>
|
||||
<CornerRadius x:Key="ScrollBarThumbCornerRadius">3</CornerRadius>
|
||||
<x:String x:Key="ScrollBarExpandBeginTime">0</x:String>
|
||||
|
||||
<Style x:Key="ForkedScrollbarTemplate"
|
||||
TargetType="ScrollBar">
|
||||
@@ -120,16 +125,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 +192,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 +258,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 +324,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 +357,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 +416,7 @@
|
||||
</ControlTemplate>
|
||||
</Grid.Resources>
|
||||
|
||||
<!-- Windows Terminal: This is another addition/customization made by us. -->
|
||||
<VisualStateManager.CustomVisualStateManager>
|
||||
<local:ScrollBarVisualStateManager />
|
||||
</VisualStateManager.CustomVisualStateManager>
|
||||
@@ -425,13 +433,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 +528,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 +937,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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -180,7 +180,6 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance)
|
||||
{
|
||||
_renderSettings.SetColorTableEntry(i, til::color{ appearance.GetColorTableEntry(i) });
|
||||
}
|
||||
_renderSettings.MakeAdjustedColorArray();
|
||||
|
||||
auto cursorShape = CursorType::VerticalBar;
|
||||
switch (appearance.CursorShape())
|
||||
@@ -1464,8 +1463,6 @@ void Terminal::ApplyScheme(const Scheme& colorScheme)
|
||||
|
||||
_renderSettings.SetColorTableEntry(TextColor::CURSOR_COLOR, til::color{ colorScheme.CursorColor });
|
||||
|
||||
_renderSettings.MakeAdjustedColorArray();
|
||||
|
||||
// Tell the control that the scrollbar has somehow changed. Used as a
|
||||
// workaround to force the control to redraw any scrollbar marks whose color
|
||||
// may have changed.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -114,7 +114,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
DependencyProperty Appearances::_AppearanceProperty{ nullptr };
|
||||
|
||||
Appearances::Appearances() :
|
||||
_ShowAllFonts{ false }
|
||||
_ShowAllFonts{ false },
|
||||
_ShowProportionalFontWarning{ false }
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@@ -239,6 +240,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
const auto selectedItem{ e.AddedItems().GetAt(0) };
|
||||
const auto newFontFace{ unbox_value<Editor::Font>(selectedItem) };
|
||||
Appearance().FontFace(newFontFace.LocalizedName());
|
||||
if (!UsingMonospaceFont())
|
||||
{
|
||||
ShowProportionalFontWarning(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowProportionalFontWarning(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Appearances::_ViewModelChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*args*/)
|
||||
@@ -300,6 +309,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentAdjustIndistinguishableColors" });
|
||||
}
|
||||
else if (settingName == L"ShowProportionalFontWarning")
|
||||
{
|
||||
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowProportionalFontWarning" });
|
||||
}
|
||||
// YOU THERE ADDING A NEW APPEARANCE SETTING
|
||||
// Make sure you add a block like
|
||||
//
|
||||
@@ -331,6 +344,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"UsingMonospaceFont" });
|
||||
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentIntenseTextStyle" });
|
||||
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentAdjustIndistinguishableColors" });
|
||||
_PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"ShowProportionalFontWarning" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +352,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())
|
||||
{
|
||||
@@ -411,4 +425,5 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// whereas SelectedItem identifies which one was selected by the user.
|
||||
return FontWeightComboBox().SelectedItem() == _CustomFontWeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,10 +133,11 @@ 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);
|
||||
|
||||
GETSET_BINDABLE_ENUM_SETTING(IntenseTextStyle, Microsoft::Terminal::Settings::Model::IntenseStyle, Appearance().IntenseTextStyle);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, ShowProportionalFontWarning, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
bool _ShowAllFonts;
|
||||
|
||||
@@ -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,10 +58,12 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
Appearances();
|
||||
AppearanceViewModel Appearance;
|
||||
ProfileViewModel SourceProfile;
|
||||
IHostedInWindow WindowRoot;
|
||||
static Windows.UI.Xaml.DependencyProperty AppearanceProperty { get; };
|
||||
|
||||
Boolean UsingMonospaceFont { get; };
|
||||
Boolean ShowAllFonts;
|
||||
Boolean ShowProportionalFontWarning;
|
||||
|
||||
IInspectable CurrentCursorShape;
|
||||
Boolean IsVintageCursor { get; };
|
||||
|
||||
@@ -179,11 +179,13 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Font Face -->
|
||||
|
||||
<local:SettingContainer x:Uid="Profile_FontFace"
|
||||
ClearSettingValue="{x:Bind Appearance.ClearFontFace}"
|
||||
HasSettingValue="{x:Bind Appearance.HasFontFace, Mode=OneWay}"
|
||||
SettingOverrideSource="{x:Bind Appearance.FontFaceOverrideSource, Mode=OneWay}"
|
||||
Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}">
|
||||
|
||||
<StackPanel Margin="0,8,0,0">
|
||||
<!--
|
||||
Binding the ItemsSource to a separate variable that switches between the
|
||||
@@ -210,6 +212,10 @@
|
||||
IsEnabled="{x:Bind UsingMonospaceFont, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</local:SettingContainer>
|
||||
<muxc:InfoBar x:Uid="Profile_FontFace_ProportionalFontWarning"
|
||||
IsOpen="{x:Bind ShowProportionalFontWarning, Mode=OneWay}"
|
||||
Severity="Warning" />
|
||||
|
||||
|
||||
<!-- Font Size -->
|
||||
<local:SettingContainer x:Uid="Profile_FontSize"
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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="" />
|
||||
<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"
|
||||
|
||||
@@ -353,7 +353,6 @@
|
||||
</PRIResource>
|
||||
<OCResourceDirectory Include="Resources" />
|
||||
<None Include="$(ProjectName).def" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,7 +8,17 @@
|
||||
using namespace ::winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace ::winrt::Windows::Foundation;
|
||||
|
||||
static constexpr std::wstring_view PreviewText{ L"Windows Terminal\r\nCopyright (c) Microsoft Corporation\r\n\nC:\\Windows\\Terminal> " };
|
||||
// clang-format off
|
||||
static constexpr std::wstring_view PreviewText{
|
||||
L"Windows Terminal\r\n"
|
||||
L"C:\\> \x1b[93m" L"git\x1b[m diff \x1b[90m-w\x1b[m\r\n"
|
||||
L"\x1b[1m" L"diff --git a/win b/win\x1b[m\r\n"
|
||||
L"\x1b[36m@@ -1 +1 @@\x1b[m\r\n"
|
||||
L"\x1b[31m- Windows Console\x1b[m\r\n"
|
||||
L"\x1b[32m+ Windows Terminal!\x1b[m\r\n"
|
||||
L"C:\\> \x1b[93mWrite-Host \x1b[36m\"\xd83c\xdf2f!\"\x1b[1D\x1b[m"
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
@@ -16,8 +26,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void PreviewConnection::Start() noexcept
|
||||
{
|
||||
// First send a sequence to disable cursor blinking
|
||||
_TerminalOutputHandlers(L"\x1b[?12l");
|
||||
// Send the preview text
|
||||
_TerminalOutputHandlers(PreviewText);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -16,19 +16,16 @@ using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
Profiles_Appearance::Profiles_Appearance() :
|
||||
_previewControl{ Control::TermControl(Model::TerminalSettings{}, nullptr, make<PreviewConnection>()) }
|
||||
Profiles_Appearance::Profiles_Appearance()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_previewControl.IsEnabled(false);
|
||||
_previewControl.AllowFocusWhenDisabled(false);
|
||||
ControlPreview().Child(_previewControl);
|
||||
}
|
||||
|
||||
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())
|
||||
@@ -36,25 +33,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
ProfileViewModel::UpdateFontList();
|
||||
}
|
||||
|
||||
if (!_previewControl)
|
||||
{
|
||||
const auto settings = _Profile.TermSettings();
|
||||
_previewControl = Control::TermControl(settings, settings, make<PreviewConnection>());
|
||||
_previewControl.IsEnabled(false);
|
||||
_previewControl.AllowFocusWhenDisabled(false);
|
||||
_previewControl.DisplayCursorWhileBlurred(true);
|
||||
ControlPreview().Child(_previewControl);
|
||||
}
|
||||
|
||||
// Subscribe to some changes in the view model
|
||||
// These changes should force us to update our own set of "Current<Setting>" members,
|
||||
// and propagate those changes to the UI
|
||||
_ViewModelChangedRevoker = _Profile.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) {
|
||||
_previewControl.UpdateControlSettings(_Profile.TermSettings(), _Profile.TermSettings());
|
||||
});
|
||||
|
||||
_ViewModelChangedRevoker = _Profile.PropertyChanged(winrt::auto_revoke, { this, &Profiles_Appearance::_onProfilePropertyChanged });
|
||||
// The Appearances object handles updating the values in the settings UI, but
|
||||
// we still need to listen to the changes here just to update the preview control
|
||||
_AppearanceViewModelChangedRevoker = _Profile.DefaultAppearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) {
|
||||
_previewControl.UpdateControlSettings(_Profile.TermSettings(), _Profile.TermSettings());
|
||||
});
|
||||
|
||||
// There is a possibility that the control has not fully initialized yet,
|
||||
// so wait for it to initialize before updating the settings (so we know
|
||||
// that the renderer is set up)
|
||||
_previewControl.Initialized([&](auto&& /*s*/, auto&& /*e*/) {
|
||||
_previewControl.UpdateControlSettings(_Profile.TermSettings(), _Profile.TermSettings());
|
||||
});
|
||||
_AppearanceViewModelChangedRevoker = _Profile.DefaultAppearance().PropertyChanged(winrt::auto_revoke, { this, &Profiles_Appearance::_onProfilePropertyChanged });
|
||||
}
|
||||
|
||||
void Profiles_Appearance::OnNavigatedFrom(const NavigationEventArgs& /*e*/)
|
||||
@@ -72,4 +67,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
_Profile.DeleteUnfocusedAppearance();
|
||||
}
|
||||
|
||||
void Profiles_Appearance::_onProfilePropertyChanged(const IInspectable&, const PropertyChangedEventArgs&) const
|
||||
{
|
||||
const auto settings = _Profile.TermSettings();
|
||||
_previewControl.UpdateControlSettings(settings, settings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,18 @@ 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);
|
||||
|
||||
private:
|
||||
Microsoft::Terminal::Control::TermControl _previewControl;
|
||||
void _onProfilePropertyChanged(const IInspectable&, const PropertyChangedEventArgs&) const;
|
||||
|
||||
Microsoft::Terminal::Control::TermControl _previewControl{ nullptr };
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _AppearanceViewModelChangedRevoker;
|
||||
Editor::IHostedInWindow _windowRoot;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user