diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.yml b/.github/ISSUE_TEMPLATE/Bug_Report.yml index ee0ead7f1ec..7c0ad9502ce 100644 --- a/.github/ISSUE_TEMPLATE/Bug_Report.yml +++ b/.github/ISSUE_TEMPLATE/Bug_Report.yml @@ -1,5 +1,6 @@ name: "Bug report 🐛" description: Report errors or unexpected behavior +labels: [Issue-Bug] body: - type: markdown attributes: diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index bdbcfc923bd..50d9b75bc70 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -2,7 +2,10 @@ admins apc Apc bsd +breadcrumb +breadcrumbs calt +CMMI ccmp changelog clickable @@ -37,9 +40,11 @@ img inlined It'd kje +libfuzzer liga lje Llast +llvm Lmid locl lorem @@ -48,11 +53,13 @@ maxed mkmk mnt mru +noreply nje noreply ogonek ok'd overlined +pipeline postmodern ptys qof diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 062bba9f622..13d32233b58 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -10,16 +10,16 @@ bitfields BUILDBRANCH BUILDMSG BUILDNUMBER -BYPOSITION BYCOMMAND +BYPOSITION charconv CLASSNOTAVAILABLE cmdletbinding COLORPROPERTY colspan COMDLG -comparand commandlinetoargv +comparand cstdint CXICON CYICON @@ -30,6 +30,7 @@ DERR dlldata DONTADDTORECENT DWORDLONG +endfor enumset environstrings EXPCMDFLAGS @@ -51,6 +52,8 @@ hotkeys href hrgn HTCLOSE +hwinsta +HWINSTA IActivation IApp IAppearance @@ -73,8 +76,8 @@ IObject iosfwd IPackage IPeasant -isspace ISetup +isspace IStorage istream IStringable @@ -88,15 +91,17 @@ llabs llu localtime lround +Lsa +lsass LSHIFT +memicmp MENUCOMMAND MENUDATA MENUINFO MENUITEMINFOW -memicmp -mptt MOUSELEAVE mov +mptt msappx MULTIPLEUSE NCHITTEST @@ -120,6 +125,8 @@ oaidl ocidl ODR offsetof +ofstream +onefuzz osver OSVERSIONINFOEXW otms @@ -170,6 +177,7 @@ THEMECHANGED tlg TME tmp +tmpdir tolower toupper TRACKMOUSEEVENT @@ -178,13 +186,18 @@ TVal UChar UFIELD ULARGE +UOI UPDATEINIFILE userenv +USEROBJECTFLAGS wcsstr wcstoui winmain +winsta +winstamin wmemcmp wpc +WSF wsregex wwinmain xchg diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index a961314685e..1f4a28664f2 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -19,6 +19,7 @@ CPRs cryptbase DACL DACLs +defaultlib diffs disposables dotnetfeed @@ -27,6 +28,8 @@ DWINRT enablewttlogging Intelli IVisual +libucrt +libucrtd LKG LOCKFILE Lxss @@ -36,8 +39,10 @@ microsoftonline MSAA msixbundle MSVC +MSVCP muxc netcore +Onefuzz osgvsowi PFILETIME pgc @@ -62,6 +67,8 @@ systemroot taskkill tasklist tdbuildteamid +ucrt +ucrtd unvirtualized VCRT vcruntime diff --git a/.github/actions/spelling/allow/names.txt b/.github/actions/spelling/allow/names.txt index 2a13d67badd..4965a52939b 100644 --- a/.github/actions/spelling/allow/names.txt +++ b/.github/actions/spelling/allow/names.txt @@ -8,6 +8,7 @@ dhowett Diviness dsafa duhowett +DXP ekg eryksun ethanschoonover @@ -69,6 +70,7 @@ sonpham stakx thereses Walisch +WDX Wellons Wirt Wojciech diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 84fcd8e14b5..6706e4ef049 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -237,6 +237,7 @@ charlespetzold charset CHARSETINFO chcp +Checkin checkbox checkboxes chh @@ -318,6 +319,7 @@ concat concfg conclnt conddkrefs +condev condrv conechokey conemu @@ -471,6 +473,7 @@ cygwin CYHSCROLL CYMIN CYPADDEDBORDER +CYRL CYSIZE CYSIZEFRAME CYSMICON @@ -671,10 +674,10 @@ dwriteglyphrundescriptionclustermap dxgi dxgidwm dxinterop +dxp dxsm dxttbmp Dyreen -eachother eaf EASTEUROPE ECH @@ -825,7 +828,6 @@ FORCEOFFFEEDBACK FORCEONFEEDBACK FORCEV foreach -forground fprintf framebuffer FRAMECHANGED @@ -1151,6 +1153,7 @@ ioctl iomanip iostream iot +ipa ipch ipconfig IPersist @@ -1238,12 +1241,14 @@ KLF KLMNO KLMNOPQRST KLMNOPQRSTQQQQQ +KOK KPRIORITY KVM langid LANGUAGELIST lasterror lastexitcode +LATN LAYOUTRTL LBN LBound @@ -1460,6 +1465,7 @@ msctls msdata msdn msft +MSDL MSGCMDLINEF MSGF MSGFILTER @@ -1471,6 +1477,7 @@ MSGSELECTMODE msiexec MSIL msix +mspartners msrc msvcrt MSVCRTD @@ -1689,7 +1696,6 @@ outfile Outof OUTOFCONTEXT OUTOFMEMORY -outout Outptr outstr OVERLAPPEDWINDOW @@ -1792,6 +1798,9 @@ placeholders platforming playsound plist +PLOC +PLOCA +PLOCM PLOGICAL plugin PMv @@ -1838,7 +1847,6 @@ pragma prc prealigned prebuilt -precendence precomp prect prefast @@ -1925,6 +1933,7 @@ qsort queryable QUESTIONMARK quickedit +QUZ QWER qzmp RAII @@ -2437,6 +2446,7 @@ TREX triaged triaging TRIANGLESTRIP +Tribool TRIMZEROHEADINGS truetype trx @@ -2508,6 +2518,7 @@ UNICRT uninit uninitialize uninstall +unintense Uniscribe unittest unittesting @@ -2658,6 +2669,7 @@ wddmcon wddmconrenderer WDDMCONSOLECONTEXT wdm +wdx webclient webpage website @@ -2692,6 +2704,7 @@ wincontypes WINCORE windbg WINDEF +windev WINDIR windll WINDOWALPHA diff --git a/.github/actions/spelling/expect/web.txt b/.github/actions/spelling/expect/web.txt index 4b95ef2e627..60ae118cb48 100644 --- a/.github/actions/spelling/expect/web.txt +++ b/.github/actions/spelling/expect/web.txt @@ -17,6 +17,7 @@ mdtauk cppreference gfycat Guake +azurewebsites askubuntu dostips viewtopic diff --git a/.vsconfig b/.vsconfig index 79c5279a774..46aed406c83 100644 --- a/.vsconfig +++ b/.vsconfig @@ -25,6 +25,7 @@ "Microsoft.VisualStudio.Component.VC.Redist.14.Latest", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "Microsoft.VisualStudio.Component.VC.Tools.ARM64", + "Microsoft.VisualStudio.Component.VC.ASAN", "Microsoft.VisualStudio.Component.VC.v142.x86.x64", "Microsoft.VisualStudio.Component.VC.v142.ARM64", "Microsoft.VisualStudio.ComponentGroup.UWP.VC", diff --git a/OpenConsole.sln b/OpenConsole.sln index 510b7a1a023..45613781537 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -1200,11 +1200,11 @@ Global {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|x86.Build.0 = Debug|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|ARM.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32 + {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 + {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|DotNet_x64Test.ActiveCfg = Debug|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|DotNet_x86Test.ActiveCfg = Debug|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|x64.ActiveCfg = Debug|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Fuzzing|x86.ActiveCfg = Debug|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|Any CPU.ActiveCfg = Release|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|ARM.ActiveCfg = Release|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|ARM64.ActiveCfg = Release|ARM64 @@ -1271,7 +1271,6 @@ Global {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32 {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32 {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|x64.Build.0 = Fuzzing|x64 {919544AC-D39B-463F-8414-3C3C67CF727C}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|Any CPU.ActiveCfg = Release|Win32 {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|ARM.ActiveCfg = Release|Win32 @@ -3241,13 +3240,10 @@ Global {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|x64.Build.0 = Fuzzing|x64 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Fuzzing|x86.Build.0 = Fuzzing|Win32 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|Any CPU.ActiveCfg = Release|Win32 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|ARM.ActiveCfg = Release|Win32 {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B}.Release|ARM64.ActiveCfg = Release|ARM64 @@ -3321,7 +3317,6 @@ Global {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32 {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32 {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x64.Build.0 = Fuzzing|x64 {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x86.Build.0 = Fuzzing|Win32 {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|Any CPU.ActiveCfg = Release|Win32 diff --git a/README.md b/README.md index 80679c49564..fef18a43983 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Related repositories include: ## Installing and running Windows Terminal -> 🔴 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later +> 🔴 Note: Windows Terminal requires Windows 10 2004 (build 19041) or later ### Microsoft Store [Recommended] @@ -274,7 +274,7 @@ If you would like to ask a question that you feel doesn't warrant an issue * Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett) * Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa) -* Mike Griese, Developer: [@zadjii](https://twitter.com/zadjii) +* Mike Griese, Senior Developer: [@zadjii](https://twitter.com/zadjii) * Carlos Zamora, Developer: [@cazamor_msft](https://twitter.com/cazamor_msft) * Leon Liang, Developer: [@leonmsft](https://twitter.com/leonmsft) * Pankaj Bhojwani, Developer @@ -284,7 +284,7 @@ If you would like to ask a question that you feel doesn't warrant an issue ## Prerequisites -* You must be running Windows 1903 (build >= 10.0.18362.0) or later to run +* You must be running Windows 10 2004 (build >= 10.0.19041.0) or later to run Windows Terminal * You must [enable Developer Mode in the Windows Settings app](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) diff --git a/build/Fuzz/notifications-ado.json b/build/Fuzz/notifications-ado.json new file mode 100644 index 00000000000..dc1a7218cc7 --- /dev/null +++ b/build/Fuzz/notifications-ado.json @@ -0,0 +1,34 @@ +{ + "config": { + "base_url": "https://dev.azure.com/microsoft/os", + "auth_token": "INSERT_PAT_HERE", + "project": "OpenConsole", + "type": "Bug", + "unique_fields": [ + "Microsoft.VSTS.Common.CustomString03" + ], + "comment": "This input caused the fuzz target {{ report.executable }} to crash. The faulting input SHA256 hash is {{ report.input_sha256 }}
", + "ado_fields": { + "System.AssignedTo": "INSERT_ASSIGNED_HERE", + "System.Tags": "OneFuzz", + "System.AreaPath": "OS\\WDX\\DXP\\WinDev\\Terminal", + "OSG.Watson.Telemetry14DaysInMarketHits": "1", + "System.IterationPath": "OS\\Future", + "Microsoft.VSTS.Common.CustomString01": "{{ job.project }}", + "Microsoft.VSTS.Common.CustomString02": "{{ job.name }}", + "Microsoft.VSTS.Common.CustomString03": "{{ report.minimized_stack_function_lines_sha256}}", + "System.Title": "[Fuzzing] - {{ report.crash_site }}", + "Microsoft.VSTS.CMMI.HowFound": "Security: Fuzzing", + "OSG.SecurityImpact": "Security Triage Requested", + "OSG.SDLSeverity": "Moderate", + "Microsoft.VSTS.TCM.ReproSteps": "The fuzzing target ({{ job.project }} {{ job.name }} {{ job.build }}) reported a crash.
{%if report.asan_log %} AddressSanitizer reported the following details:
 {{ report.asan_log }} 
{% else %} Faulting call stack:
{% endif %} You can reproduce the issue remotely in OneFuzz by running the following command:
 {{ repro_cmd }} 
" + }, + "on_duplicate": { + "set_state": {"Resolved": "Active", "Closed": "Active"}, + "ado_fields": { + "System.IterationPath": "OS\\Future" + }, + "increment": ["OSG.Watson.Telemetry14DaysInMarketHits"] + } + } +} diff --git a/build/Helix/OutputTestResults.ps1 b/build/Helix/OutputTestResults.ps1 index eee4c5baece..a23b456afe8 100644 --- a/build/Helix/OutputTestResults.ps1 +++ b/build/Helix/OutputTestResults.ps1 @@ -31,7 +31,7 @@ $totalTestsExecutedCount = 0 # We assume that we only have one testRun with a given name that we care about # We only process the last testRun with a given name (based on completedDate) -# The name of a testRun is set to the Helix queue that it was run on (e.g. windows.10.amd64.client19h1.xaml) +# The name of a testRun is set to the Helix queue that it was run on (e.g. windows.10.amd64.client21h1.xaml) # If we have multiple test runs on the same queue that we care about, we will need to re-visit this logic foreach ($testRun in ($testRuns.value | Sort-Object -Property "completedDate" -Descending)) { diff --git a/build/config/GitCheckin.json b/build/config/GitCheckin.json new file mode 100644 index 00000000000..8bfcb203023 --- /dev/null +++ b/build/config/GitCheckin.json @@ -0,0 +1,24 @@ +{ + "Branch": [ + { + "collection": "microsoft", + "project": "OS", + "repo": "os.2020", + "name": "official/rs_wdx_dxp_windev", + "workitem": "38106206", + "CheckinFiles": [ + { + "source": "WindowsTerminal.app.man", + "path": "/redist/mspartners/ipa/WindowsTerminal", + "type": "File" + } + ] + } + ], + "Email": [ + { + "sendTo": "condev", + "sendOnErrorOnly": "False" + } + ] + } \ No newline at end of file diff --git a/build/packages.config b/build/packages.config index 34e0e10c1c9..7b2f271d521 100644 --- a/build/packages.config +++ b/build/packages.config @@ -2,5 +2,5 @@ - + diff --git a/build/pgo/Terminal.PGO.props b/build/pgo/Terminal.PGO.props index 1e82c2305d4..55a5e39f4d8 100644 --- a/build/pgo/Terminal.PGO.props +++ b/build/pgo/Terminal.PGO.props @@ -1,7 +1,7 @@ $(MSBuildThisFileDirectory)..\..\packages - $(NuGetPackageDirectory)\Microsoft.PGO-Helpers.Cpp.0.2.22 + $(NuGetPackageDirectory)\Microsoft.Internal.PGO-Helpers.Cpp.0.2.34 @@ -20,6 +20,12 @@ $(VersionMinor) + + 0 + + + + $(MSBuildThisFileDirectory)..\..\nuget.config diff --git a/build/pipelines/ci.yml b/build/pipelines/ci.yml index a13b7cbb58d..1c23df92487 100644 --- a/build/pipelines/ci.yml +++ b/build/pipelines/ci.yml @@ -38,6 +38,7 @@ stages: - template: ./templates/build-console-audit-job.yml parameters: platform: x64 + - stage: Build_x64 displayName: Build x64 dependsOn: [] @@ -61,6 +62,7 @@ stages: - template: ./templates/build-console-ci.yml parameters: platform: ARM64 + - stage: Test_x64 displayName: Test x64 dependsOn: [Build_x64] @@ -76,6 +78,16 @@ stages: - template: ./templates/test-console-ci.yml parameters: platform: x86 + + - stage: Helix_x64 + displayName: Helix x64 + dependsOn: [Build_x64] + condition: and(succeeded(), not(eq(variables['Build.Reason'], 'PullRequest'))) + jobs: + - template: ./templates/console-ci-helix-job.yml + parameters: + platform: x64 + - stage: Scripts displayName: Code Health Scripts dependsOn: [] diff --git a/build/pipelines/feature-flag-ci.yml b/build/pipelines/feature-flag-ci.yml new file mode 100644 index 00000000000..0ca4400463d --- /dev/null +++ b/build/pipelines/feature-flag-ci.yml @@ -0,0 +1,29 @@ +trigger: none + +pr: + branches: + include: + - main + paths: + include: + - src/features.xml + +variables: + - name: runCodesignValidationInjectionBG + value: false + +parameters: + - name: buildBrandings + type: object + default: + - Release + - Preview + # Dev is built automatically + # WindowsInbox does not typically build with VS. + +jobs: + - ${{ each branding in parameters.buildBrandings }}: + - template: ./templates/build-console-ci.yml + parameters: + platform: x64 + branding: ${{ branding }} diff --git a/build/pipelines/fuzz.yml b/build/pipelines/fuzz.yml new file mode 100644 index 00000000000..b5604b5f227 --- /dev/null +++ b/build/pipelines/fuzz.yml @@ -0,0 +1,59 @@ +trigger: + batch: true + branches: + include: + - main + paths: + exclude: + - docs/* + - samples/* + - tools/* + +pr: none + +# 0.0.yyMM.dd## +# 0.0.1904.0900 +name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr) + +stages: + - stage: Build_Fuzz_Config + displayName: Build Fuzzers + dependsOn: [] + condition: succeeded() + jobs: + - template: ./templates/build-console-fuzzing.yml + parameters: + platform: x64 + - stage: OneFuzz + displayName: Submit OneFuzz Job + dependsOn: ['Build_Fuzz_Config'] + condition: succeeded() + pool: + vmImage: 'ubuntu-latest' + variables: + artifactName: fuzzingBuildOutput + jobs: + - job: + steps: + - task: DownloadBuildArtifacts@0 + inputs: + artifactName: $(artifactName) + downloadPath: $(Build.ArtifactStagingDirectory) + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.x' + addToPath: true + architecture: 'x64' + - bash: | + set -ex + pip -q install onefuzz + onefuzz config --endpoint $(endpoint) --client_id $(client_id) --authority $(authority) --tenant_domain $(tenant_domain) --client_secret $(client_secret) + sed -i s/INSERT_PAT_HERE/$(ado_pat)/ build/Fuzz/notifications-ado.json + sed -i s/INSERT_ASSIGNED_HERE/$(ado_assigned_to)/ build/Fuzz/notifications-ado.json + displayName: Configure OneFuzz + - bash: | + onefuzz template libfuzzer basic --colocate_all_tasks --vm_count 1 --target_exe $target_exe_path --notification_config @./build/Fuzz/notifications-ado.json OpenConsole $test_name $(Build.SourceVersion) default + displayName: Submit OneFuzz Job + env: + target_exe_path: $(Build.ArtifactStagingDirectory)/$(artifactName)/Fuzzing/x64/test/OpenConsoleFuzzer.exe + test_name: WriteCharsLegacy diff --git a/build/pipelines/release.yml b/build/pipelines/release.yml index ea92fcab764..abe79847680 100644 --- a/build/pipelines/release.yml +++ b/build/pipelines/release.yml @@ -22,6 +22,10 @@ parameters: displayName: "Run Compliance and Security Build" type: boolean default: true + - name: publishSymbolsToPublic + displayName: "Publish Symbols to MSDL" + type: boolean + default: true - name: buildTerminalVPack displayName: "Build Windows Terminal VPack" type: boolean @@ -48,6 +52,11 @@ parameters: - x64 - x86 - arm64 + - name: buildWindowsVersions + type: object + default: + - Win10 + - Win11 variables: TerminalInternalPackageVersion: "0.0.7" @@ -64,9 +73,11 @@ jobs: matrix: ${{ each config in parameters.buildConfigurations }}: ${{ each platform in parameters.buildPlatforms }}: - ${{ config }}_${{ platform }}: - BuildConfiguration: ${{ config }} - BuildPlatform: ${{ platform }} + ${{ each windowsVersion in parameters.buildWindowsVersions }}: + ${{ config }}_${{ platform }}_${{ windowsVersion }}: + BuildConfiguration: ${{ config }} + BuildPlatform: ${{ platform }} + TerminalTargetWindowsVersion: ${{ windowsVersion }} displayName: Build timeoutInMinutes: 240 cancelTimeoutInMinutes: 1 @@ -151,6 +162,11 @@ jobs: $Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore } pwsh: true + - task: PowerShell@2 + displayName: Copy the Context Menu Loc Resources to CascadiaPackage + inputs: + filePath: ./build/scripts/Copy-ContextMenuResourcesToCascadiaPackage.ps1 + pwsh: true - task: PowerShell@2 displayName: Generate NOTICE.html from NOTICE.md inputs: @@ -158,13 +174,17 @@ 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 inputs: solution: '**\OpenConsole.sln' vsVersion: 16.0 - msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /t:Terminal\CascadiaPackage;Terminal\WindowsTerminalUniversal /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog + msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /t:Terminal\CascadiaPackage /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog platform: $(BuildPlatform) configuration: $(BuildConfiguration) clean: true @@ -175,7 +195,7 @@ jobs: continueOnError: True inputs: PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog - ArtifactName: binlog-$(BuildPlatform) + ArtifactName: binlog-$(BuildPlatform)-$(TerminalTargetWindowsVersion) - task: PowerShell@2 displayName: Check MSIX for common regressions inputs: @@ -238,7 +258,7 @@ jobs: displayName: Publish Artifact (appx) inputs: PathtoPublish: $(Build.ArtifactStagingDirectory)/appx - ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration) + ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion) - ${{ if eq(parameters.buildWPF, true) }}: - task: CopyFiles@2 displayName: Copy PublicTerminalCore.dll to Artifacts @@ -256,7 +276,7 @@ jobs: condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64')) inputs: PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf - ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration) + ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion) - task: PublishSymbols@2 displayName: Publish symbols path @@ -274,6 +294,11 @@ 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 dependsOn: Build steps: @@ -286,20 +311,16 @@ jobs: displayName: Package ES - Setup Build inputs: disableOutputRedirect: true - - task: DownloadBuildArtifacts@0 - displayName: Download Artifacts (*.appx, *.msix) - inputs: - downloadType: specific - itemPattern: >- - **/*.msix - - **/*.appx - extractTars: false + - ${{ each platform in parameters.buildPlatforms }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Artifacts ${{ platform }} $(TerminalTargetWindowsVersion) + inputs: + artifactName: appx-${{ platform }}-Release-$(TerminalTargetWindowsVersion) - task: PowerShell@2 displayName: Create WindowsTerminal*.msixbundle inputs: filePath: build\scripts\Create-AppxBundle.ps1 - arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle" + arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle" - task: EsrpCodeSigning@1 displayName: Submit *.msixbundle to ESRP for code signing inputs: @@ -334,11 +355,12 @@ jobs: "ToolVersion": "1.0" } ] + - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: appxbundle-signed' inputs: PathtoPublish: $(System.ArtifactsDirectory) - ArtifactName: appxbundle-signed + ArtifactName: appxbundle-signed-$(TerminalTargetWindowsVersion) - ${{ if eq(parameters.buildWPF, true) }}: - job: PackageAndSignWPF @@ -362,14 +384,14 @@ jobs: - task: DownloadBuildArtifacts@0 displayName: Download x86 PublicTerminalCore inputs: - artifactName: wpf-dll-x86-$(BuildConfiguration) + artifactName: wpf-dll-x86-$(BuildConfiguration)-Win10 itemPattern: '**/*.dll' downloadPath: bin\Win32\$(BuildConfiguration)\ extractTars: false - task: DownloadBuildArtifacts@0 displayName: Download x64 PublicTerminalCore inputs: - artifactName: wpf-dll-x64-$(BuildConfiguration) + artifactName: wpf-dll-x64-$(BuildConfiguration)-Win10 itemPattern: '**/*.dll' downloadPath: bin\x64\$(BuildConfiguration)\ extractTars: false @@ -451,6 +473,71 @@ jobs: PathtoPublish: $(Build.ArtifactStagingDirectory)\nupkg ArtifactName: wpf-nupkg-$(BuildConfiguration) +- ${{ if eq(parameters.publishSymbolsToPublic, true) }}: + - job: PublishSymbols + displayName: Publish Symbols + dependsOn: BundleAndSign + steps: + - checkout: self + clean: true + fetchDepth: 1 + submodules: true + - task: PkgESSetupBuild@12 + displayName: Package ES - Setup Build + + # 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 }} + + # 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. + - pwsh: |- + mkdir $(Build.SourcesDirectory)/appxsym-temp + Get-ChildItem "$(System.ArtifactsDirectory)" -Filter *.appxsym -Recurse | % { + $src = $_.FullName + $dest = Join-Path "$(Build.SourcesDirectory)/appxsym-temp/" $_.Name + + mkdir $dest + Write-Host "Extracting $src to $dest..." + tar -x -v -f $src -C $dest + } + displayName: Extract symbols for public consumption + + # Pull the Windows SDK for the developer tools like the debuggers so we can index sources later + - template: .\templates\install-winsdk-steps.yml + - task: PowerShell@2 + displayName: Source Index PDBs (the public ones) + inputs: + filePath: build\scripts\Index-Pdbs.ps1 + arguments: -SearchDir '$(Build.SourcesDirectory)/appxsym-temp' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion) + + # Publish the app symbols to the public MSDL symbol server + # accessible via https://msdl.microsoft.com/download/symbols + - task: PublishSymbols@2 + displayName: 'Publish app symbols to MSDL' + inputs: + symbolsFolder: '$(Build.SourcesDirectory)/appxsym-temp' + searchPattern: '**/*.pdb' + SymbolsMaximumWaitTime: 30 + SymbolServerType: 'TeamServices' + SymbolsProduct: 'Windows Terminal Application Binaries' + SymbolsVersion: '$(XES_APPXMANIFESTVERSION)' + # The ADO task does not support indexing of GitHub sources. + indexSources: false + detailedLog: true + # There is a bug which causes this task to fail if LIB includes an inaccessible path (even though it does not depend on it). + # To work around this issue, we just force LIB to be any dir that we know exists. + # Copied from https://github.com/microsoft/icu/blob/f869c214adc87415dfe751d81f42f1bca55dcf5f/build/azure-nuget.yml#L564-L583 + env: + LIB: $(Build.SourcesDirectory) + ArtifactServices_Symbol_AccountName: microsoftpublicsymbols + ArtifactServices_Symbol_PAT: $(ADO_microsoftpublicsymbols_PAT) + + - ${{ if eq(parameters.buildTerminalVPack, true) }}: - job: VPack displayName: Create Windows vPack @@ -465,7 +552,7 @@ jobs: - task: DownloadBuildArtifacts@0 displayName: Download Build Artifacts inputs: - artifactName: appxbundle-signed + artifactName: appxbundle-signed-Win11 extractTars: false - task: PowerShell@2 displayName: Rename and stage packages for vpack @@ -474,7 +561,7 @@ jobs: script: >- # Rename to known/fixed name for Windows build system - Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' } + Get-ChildItem Microsoft.WindowsTerminal_Win11_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' } # Create vpack directory and place item inside @@ -489,7 +576,18 @@ jobs: SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app - description: Windows Terminal pre-install application + description: VPack for the Windows Terminal Application pushPkgName: WindowsTerminal.app - owner: condev + owner: conhost + - task: PublishPipelineArtifact@1 + displayName: 'Copy VPack Manifest to Drop' + inputs: + targetPath: $(XES_VPACKMANIFESTDIRECTORY) + artifactName: VPackManifest + - task: PkgESFCIBGit@12 + displayName: 'Submit VPack Manifest to Windows' + inputs: + configPath: '$(Build.SourcesDirectory)\build\config\GitCheckin.json' + artifactsDirectory: $(XES_VPACKMANIFESTDIRECTORY) + prTimeOut: 5 ... diff --git a/build/pipelines/templates/build-console-ci.yml b/build/pipelines/templates/build-console-ci.yml index 0ff8b6b5406..45d6bd4267e 100644 --- a/build/pipelines/templates/build-console-ci.yml +++ b/build/pipelines/templates/build-console-ci.yml @@ -1,16 +1,16 @@ parameters: configuration: 'Release' + branding: 'Dev' platform: '' additionalBuildArguments: '' - minimumExpectedTestsExecutedCount: 10 # Sanity check for minimum expected tests to be reported - rerunPassesRequiredToAvoidFailure: 5 jobs: -- job: Build${{ parameters.platform }}${{ parameters.configuration }} - displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }} +- job: Build${{ parameters.platform }}${{ parameters.configuration }}${{ parameters.branding }} + displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }} ${{ parameters.branding }} variables: BuildConfiguration: ${{ parameters.configuration }} BuildPlatform: ${{ parameters.platform }} + WindowsTerminalBranding: ${{ parameters.branding }} pool: ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}: name: WinDevPoolOSS-L @@ -28,21 +28,3 @@ jobs: - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 displayName: 'Component Detection' condition: and(succeededOrFailed(), not(eq(variables['Build.Reason'], 'PullRequest'))) - -- template: helix-runtests-job.yml - parameters: - name: 'RunTestsInHelix' - dependsOn: Build${{ parameters.platform }}${{ parameters.configuration }} - condition: and(succeeded(), and(eq('${{ parameters.platform }}', 'x64'), not(eq(variables['Build.Reason'], 'PullRequest')))) - testSuite: 'DevTestSuite' - platform: ${{ parameters.platform }} - configuration: ${{ parameters.configuration }} - rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }} - -- template: helix-processtestresults-job.yml - parameters: - dependsOn: - - RunTestsInHelix - condition: and(succeededOrFailed(), and(eq('${{ parameters.platform }}', 'x64'), not(eq(variables['Build.Reason'], 'PullRequest')))) - rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }} - minimumExpectedTestsExecutedCount: ${{ parameters.minimumExpectedTestsExecutedCount }} \ No newline at end of file diff --git a/build/pipelines/templates/build-console-fuzzing.yml b/build/pipelines/templates/build-console-fuzzing.yml new file mode 100644 index 00000000000..1c8dee82cc2 --- /dev/null +++ b/build/pipelines/templates/build-console-fuzzing.yml @@ -0,0 +1,114 @@ +parameters: + configuration: 'Fuzzing' + platform: '' + additionalBuildArguments: '' + +jobs: +- job: Build${{ parameters.platform }}${{ parameters.configuration }} + displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }} + variables: + BuildConfiguration: ${{ parameters.configuration }} + BuildPlatform: ${{ parameters.platform }} + pool: + ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}: + name: WinDevPoolOSS-L + ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}: + name: WinDevPool-L + demands: ImageOverride -equals WinDevVS16-latest + + steps: + - checkout: self + submodules: true + clean: true + + - task: NuGetToolInstaller@0 + displayName: 'Use NuGet 5.2.0' + inputs: + versionSpec: 5.2.0 + + # In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous. + # This should be `task: NuGetCommand@2` + - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 + displayName: Restore NuGet packages for solution + inputs: + command: restore + feedsToUse: config + configPath: NuGet.config + restoreSolution: OpenConsole.sln + restoreDirectory: '$(Build.SourcesDirectory)\packages' + + - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 + displayName: Restore NuGet packages for extraneous build actions + inputs: + command: restore + feedsToUse: config + configPath: NuGet.config + restoreSolution: build/packages.config + restoreDirectory: '$(Build.SourcesDirectory)\packages' + + # The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves. + - script: | + "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt + set /p _VSINSTALLDIR15=<%TEMP%\vsinstalldir.txt + del %TEMP%\vsinstalldir.txt + call "%_VSINSTALLDIR15%\Common7\Tools\VsDevCmd.bat" + echo VCToolsInstallDir = %VCToolsInstallDir% + echo ##vso[task.setvariable variable=VCToolsInstallDir]%VCToolsInstallDir% + displayName: 'Retrieve VC tools directory' + + - task: VSBuild@1 + displayName: 'Build solution **\OpenConsole.sln' + inputs: + solution: '**\OpenConsole.sln' + vsVersion: 16.0 + platform: '$(BuildPlatform)' + configuration: '$(BuildConfiguration)' + msbuildArgs: "${{ parameters.additionalBuildArguments }}" + clean: true + maximumCpuCount: true + + - task: PowerShell@2 + displayName: 'Rationalize build platform' + inputs: + targetType: inline + script: | + $Arch = "$(BuildPlatform)" + If ($Arch -Eq "x86") { $Arch = "Win32" } + Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}" + + - task: CopyFiles@2 + displayName: 'Copy result logs to Artifacts' + inputs: + Contents: | + **/*.wtl + **/*onBuildMachineResults.xml + ${{ parameters.testLogPath }} + TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test' + OverWrite: true + flattenFolders: true + + - task: CopyFiles@2 + displayName: 'Copy outputs needed for test runs to Artifacts' + inputs: + Contents: | + $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.exe + $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.dll + $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.xml + **/Microsoft.VCLibs.*.appx + **/TestHostApp/*.exe + **/TestHostApp/*.dll + **/TestHostApp/*.xml + !**/*.pdb + !**/*.ipdb + !**/*.obj + !**/*.pch + TargetFolder: '$(Build.ArtifactStagingDirectory)/$(BuildConfiguration)/$(BuildPlatform)/test' + OverWrite: true + flattenFolders: true + condition: succeeded() + + - task: PublishBuildArtifacts@1 + displayName: 'Publish All Build Artifacts' + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: 'fuzzingBuildOutput' \ No newline at end of file diff --git a/build/pipelines/templates/build-console-steps.yml b/build/pipelines/templates/build-console-steps.yml index 03141ac5f81..89e091ff736 100644 --- a/build/pipelines/templates/build-console-steps.yml +++ b/build/pipelines/templates/build-console-steps.yml @@ -126,7 +126,7 @@ steps: displayName: 'Publish All Build Artifacts' inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)' - ArtifactName: 'drop' + ArtifactName: 'drop' - task: CopyFiles@2 displayName: 'Copy PGO databases needed for PGO instrumentation run' @@ -147,7 +147,7 @@ steps: - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: binlog' - condition: failed() + condition: always() continueOnError: True inputs: PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog diff --git a/build/pipelines/templates/console-ci-helix-job.yml b/build/pipelines/templates/console-ci-helix-job.yml new file mode 100644 index 00000000000..07476712a02 --- /dev/null +++ b/build/pipelines/templates/console-ci-helix-job.yml @@ -0,0 +1,25 @@ +parameters: + configuration: 'Release' + platform: '' + minimumExpectedTestsExecutedCount: 10 # Sanity check for minimum expected tests to be reported + rerunPassesRequiredToAvoidFailure: 5 + +jobs: +- template: helix-runtests-job.yml + parameters: + name: 'RunTestsInHelix' + # We're not setting dependsOn as we want to rely on the "stage" dependency above us + testSuite: 'DevTestSuite' + platform: ${{ parameters.platform }} + configuration: ${{ parameters.configuration }} + rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }} + +- template: helix-processtestresults-job.yml + parameters: + dependsOn: + - RunTestsInHelix + # the default condition is succeededOrFailed(), and the "stage" condition ensures we only run as needed + platform: ${{ parameters.platform }} + configuration: ${{ parameters.configuration }} + rerunPassesRequiredToAvoidFailure: ${{ parameters.rerunPassesRequiredToAvoidFailure }} + minimumExpectedTestsExecutedCount: ${{ parameters.minimumExpectedTestsExecutedCount }} diff --git a/build/pipelines/templates/helix-processtestresults-job.yml b/build/pipelines/templates/helix-processtestresults-job.yml index 1e5ff3c73e5..bd8e967de92 100644 --- a/build/pipelines/templates/helix-processtestresults-job.yml +++ b/build/pipelines/templates/helix-processtestresults-job.yml @@ -8,6 +8,7 @@ parameters: jobs: - job: ProcessTestResults + displayName: Process Helix Results ${{ parameters.platform }} ${{ parameters.configuration }} condition: ${{ parameters.condition }} dependsOn: ${{ parameters.dependsOn }} pool: diff --git a/build/pipelines/templates/helix-runtests-job.yml b/build/pipelines/templates/helix-runtests-job.yml index 042c54c12ee..853125d709a 100644 --- a/build/pipelines/templates/helix-runtests-job.yml +++ b/build/pipelines/templates/helix-runtests-job.yml @@ -14,11 +14,12 @@ parameters: platform: '' # if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline: useBuildOutputFromPipeline: $(System.DefinitionId) - openHelixTargetQueues: 'windows.10.amd64.client19h1.open.xaml' - closedHelixTargetQueues: 'windows.10.amd64.client19h1.xaml' + openHelixTargetQueues: 'windows.10.amd64.client21h1.open.xaml' + closedHelixTargetQueues: 'windows.10.amd64.client21h1.xaml' jobs: - job: ${{ parameters.name }} + displayName: Submit Helix ${{ parameters.platform }} ${{ parameters.configuration }} dependsOn: ${{ parameters.dependsOn }} condition: ${{ parameters.condition }} pool: diff --git a/build/pipelines/templates/pgo-merge-pgd-job.yml b/build/pipelines/templates/pgo-merge-pgd-job.yml index 930472f5822..8a1c2423113 100644 --- a/build/pipelines/templates/pgo-merge-pgd-job.yml +++ b/build/pipelines/templates/pgo-merge-pgd-job.yml @@ -55,7 +55,7 @@ jobs: solution: $(Build.SourcesDirectory)\OpenConsole.sln platform: $(buildPlatform) configuration: $(buildConfiguration) - msbuildArguments: '/t:MergePGOCounts /p:PGDPath=$(pgoArtifactsPath)\$(buildPlatform) /p:PGCRootPath=$(pgoArtifactsPath)\$(buildPlatform)' + msbuildArguments: '/t:MergePGOCounts /p:PGOBuildMode=Instrument /p:PGDPath=$(pgoArtifactsPath)\$(buildPlatform) /p:PGCRootPath=$(pgoArtifactsPath)\$(buildPlatform)' - task: CopyFiles@2 displayName: 'Copy merged pgd to artifact staging' @@ -67,4 +67,4 @@ jobs: - task: PublishBuildArtifacts@1 inputs: pathToPublish: $(Build.ArtifactStagingDirectory) - artifactName: ${{ parameters.pgoArtifact }} \ No newline at end of file + artifactName: ${{ parameters.pgoArtifact }} diff --git a/build/pipelines/templates/test-console-ci.yml b/build/pipelines/templates/test-console-ci.yml index 25c28ac5235..542edef2916 100644 --- a/build/pipelines/templates/test-console-ci.yml +++ b/build/pipelines/templates/test-console-ci.yml @@ -2,8 +2,6 @@ parameters: configuration: 'Release' platform: '' additionalBuildArguments: '' - minimumExpectedTestsExecutedCount: 10 # Sanity check for minimum expected tests to be reported - rerunPassesRequiredToAvoidFailure: 5 artifactName: 'drop' testLogPath: '$(Build.BinariesDirectory)\$(BuildPlatform)\$(BuildConfiguration)\testsOnBuildMachine.wtl' diff --git a/build/scripts/Copy-ContextMenuResourcesToCascadiaPackage.ps1 b/build/scripts/Copy-ContextMenuResourcesToCascadiaPackage.ps1 new file mode 100644 index 00000000000..8111b3fc66e --- /dev/null +++ b/build/scripts/Copy-ContextMenuResourcesToCascadiaPackage.ps1 @@ -0,0 +1,34 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +$LocalizationsFromContextMenu = Get-ChildItem ./src/cascadia/TerminalApp/Resources -Recurse -Filter ContextMenu.resw +$Languages = [System.Collections.HashTable]::New() +$LocalizationsFromContextMenu | ForEach-Object { + $Languages[$_.Directory.Name] = $_ +} + +ForEach ($pair in $Languages.GetEnumerator()) { + $LanguageDir = "./src/cascadia/CascadiaPackage/Resources/$($pair.Key)" + $ResPath = "$LanguageDir/Resources.resw" + $PreexistingResw = Get-Item $ResPath -EA:Ignore + If ($null -eq $PreexistingResw) { + Write-Host "Copying $($pair.Value.FullName) to $ResPath" + New-Item -type Directory $LanguageDir -EA:Ignore + Copy-Item $pair.Value.FullName $ResPath + } Else { + # Merge Them! + Write-Host "Merging $($pair.Value.FullName) into $ResPath" + $existingXml = [xml](Get-Content $PreexistingResw.FullName) + $newXml = [xml](Get-Content $pair.Value.FullName) + $newDataKeys = $newXml.root.data.name + $existingXml.root.data | % { + If ($_.name -in $newDataKeys) { + $null = $existingXml.root.RemoveChild($_) + } + } + $newXml.root.data | % { + $null = $existingXml.root.AppendChild($existingXml.ImportNode($_, $true)) + } + $existingXml.Save($PreexistingResw.FullName) + } +} diff --git a/build/scripts/Patch-ManifestsToWindowsVersion.ps1 b/build/scripts/Patch-ManifestsToWindowsVersion.ps1 new file mode 100644 index 00000000000..fe86f24fd8c --- /dev/null +++ b/build/scripts/Patch-ManifestsToWindowsVersion.ps1 @@ -0,0 +1,14 @@ +# 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) +} diff --git a/common.openconsole.props b/common.openconsole.props index e521ad61996..400555dbdd5 100644 --- a/common.openconsole.props +++ b/common.openconsole.props @@ -10,4 +10,18 @@ $(MSBuildThisFileDirectory) + + + 2.7.0-prerelease.210913003 + + 2.7.0 + + diff --git a/custom.props b/custom.props index 23f7ff5aa82..aab0b65870b 100644 --- a/custom.props +++ b/custom.props @@ -2,10 +2,22 @@ + + $([MSBuild]::Add($(VersionBuildRevision), 1)) + true - 2021 + 2022 1 - 13 + 14 Windows Terminal diff --git a/dep/Console/conapi.h b/dep/Console/conapi.h index 84535ec6800..e3667083032 100644 --- a/dep/Console/conapi.h +++ b/dep/Console/conapi.h @@ -1,5 +1,6 @@ /*++ -Copyright (c) Microsoft Corporation +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. Module Name: - conapi.h diff --git a/dep/Console/condrv.h b/dep/Console/condrv.h index b5654d98bbd..d2d435ca5c5 100644 --- a/dep/Console/condrv.h +++ b/dep/Console/condrv.h @@ -1,6 +1,7 @@ /*++ Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT license. Module Name: diff --git a/dep/Console/conmsgl1.h b/dep/Console/conmsgl1.h index 41ef9c74108..ce83e966415 100644 --- a/dep/Console/conmsgl1.h +++ b/dep/Console/conmsgl1.h @@ -1,6 +1,7 @@ /*++ Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT license. Module Name: diff --git a/dep/Console/conmsgl2.h b/dep/Console/conmsgl2.h index d1abb749ab5..2ab5d65bce2 100644 --- a/dep/Console/conmsgl2.h +++ b/dep/Console/conmsgl2.h @@ -1,6 +1,7 @@ /*++ Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT license. Module Name: diff --git a/dep/Console/conmsgl3.h b/dep/Console/conmsgl3.h index 9e3d8e65453..9533c83d792 100644 --- a/dep/Console/conmsgl3.h +++ b/dep/Console/conmsgl3.h @@ -1,6 +1,7 @@ /*++ -Copyright (c) 1985 - 1999, Microsoft Corporation +Copyright (c) 1985 - 1999, Microsoft Corporation. +Licensed under the MIT license. Module Name: diff --git a/dep/Console/ntcon.h b/dep/Console/ntcon.h index 9b196672883..201ff69a05e 100644 --- a/dep/Console/ntcon.h +++ b/dep/Console/ntcon.h @@ -1,5 +1,6 @@ // // Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. // #ifndef _NTCON_ #define _NTCON_ diff --git a/dep/Console/winconp.h b/dep/Console/winconp.h index eb05f329307..23fba76c144 100644 --- a/dep/Console/winconp.h +++ b/dep/Console/winconp.h @@ -1,3 +1,7 @@ +/*++ +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +--*/ #ifndef _WINCONP_ #define _WINCONP_ diff --git a/dep/NT/ntioapi_x.h b/dep/NT/ntioapi_x.h index d89e0fc888a..abbf5ce6d30 100644 --- a/dep/NT/ntioapi_x.h +++ b/dep/NT/ntioapi_x.h @@ -1,3 +1,8 @@ +/*++ +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +--*/ + #pragma once #define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 diff --git a/dep/Win32K/winuserp.h b/dep/Win32K/winuserp.h index faea9854f33..4174fce5399 100644 --- a/dep/Win32K/winuserp.h +++ b/dep/Win32K/winuserp.h @@ -1,4 +1,7 @@ /* + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT license. + * * Reserved console space. * * This was moved from the console code so that we can localize it diff --git a/doc/Niksa.md b/doc/Niksa.md index c4c7e479885..c3f8a1fa1af 100644 --- a/doc/Niksa.md +++ b/doc/Niksa.md @@ -163,7 +163,7 @@ Given that we're using Xaml islands to host a modern UI and stitching a DirectX Now, the obvious followup question is _"why can't you have one elevated connection in a tab next to a non-elevated connection?"_ This is where @sba923 should pick up reading (:smile:). I'm probably going to cover some things that you (@robomac) know already. -[2] When you have two windows on the same desktop in the same window station, they can communicate with eachother. I can use `SendKeys` easily through `WScript.Shell` to send keyboard input to any window that the shell can see. +[2] When you have two windows on the same desktop in the same window station, they can communicate with each other. I can use `SendKeys` easily through `WScript.Shell` to send keyboard input to any window that the shell can see. Running a process elevated _severs_ that connection. The shell can't see the elevated window. No other program at the same integrity level as the shell can see the elevated window. Even if it has its window handle, it can't really interact with it. This is also why you can't drag/drop from explorer into notepad if notepad is running elevated. Only another elevated process can interact with another elevated window. diff --git a/doc/cascadia/Unittesting-CppWinRT-Xaml.md b/doc/cascadia/Unittesting-CppWinRT-Xaml.md index 4160e035e2e..9ddcae046a0 100644 --- a/doc/cascadia/Unittesting-CppWinRT-Xaml.md +++ b/doc/cascadia/Unittesting-CppWinRT-Xaml.md @@ -268,7 +268,7 @@ this: ``` -Again, verify the correct paths to your dependant C++/WinRT dlls, as they may be +Again, verify the correct paths to your dependent C++/WinRT dlls, as they may be different than the above #### Activating the manifest from TAEF diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 437cc09c861..b7abd93b661 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -1709,7 +1709,7 @@ }, "startOnUserLogin": { "default": false, - "description": "When set to true, this enables the launch of Windows Terminal at startup. Setting this to false will disable the startup task entry. If the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.", + "description": "When set to true, this enables the launch of Terminal at startup. Setting this to false will disable the startup task entry. If the Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.", "type": "boolean" }, "firstWindowPreference": { @@ -1761,7 +1761,7 @@ }, "useAcrylicInTabRow": { "default": false, - "description": "When set to true, the tab row will have an acrylic background with 50% opacity.", + "description": "When set to true, the tab row will have an acrylic material background with 50% opacity.", "type": "boolean" }, "actions": { @@ -2059,6 +2059,11 @@ "description": "Use to set a path to a pixel shader to use with the Terminal. Overrides `experimental.retroTerminalEffect`. This is an experimental feature, and its continued existence is not guaranteed.", "type": "string" }, + "experimental.useAtlasEngine": { + "description": "Enable using the experimental new rendering engine for this profile. This is an experimental feature, and its continued existence is not guaranteed.", + "type": "boolean", + "default": false + }, "fontFace": { "default": "Cascadia Mono", "description": "[deprecated] Define 'face' within the 'font' object instead.", @@ -2219,7 +2224,7 @@ }, "useAcrylic": { "default": false, - "description": "When set to true, the window will have an acrylic background. When set to false, the window will have a plain, untextured background.", + "description": "When set to true, the window will have an acrylic material background. When set to false, the window will have a plain, untextured background.", "type": "boolean" } }, diff --git a/doc/fuzzing.md b/doc/fuzzing.md new file mode 100644 index 00000000000..5d855f5fd17 --- /dev/null +++ b/doc/fuzzing.md @@ -0,0 +1,60 @@ +# Fuzzing + +## Setting up a fuzzer locally + +OpenConsole can be built with a `Fuzzing` configuration. To set up a fuzzer, you'll need an `LLVMFuzzerTestOneInput` function. This serves as a way for the fuzzer to attach itself and inject tests into your fuzz target. + +To build the fuzzer locally, build the OpenConsole solution in the `Fuzzing` configuration. This should output an executable that runs the fuzzer on the provided test case. In the case of PR #9604, the desired executable is located at `bin\x64\Fuzzing\OpenConsoleFuzzer.exe`. + +### Resources +- [LibFuzzer Docs](https://www.llvm.org/docs/LibFuzzer.html) +- [#9604](https://github.com/microsoft/terminal/pull/9604) + +## Setting up OneFuzz + +OneFuzz allows us to run our fuzzers in CI and be alerted of new bugs found in this endeavor. + +### Installing OneFuzz + +You can download the latest OneFuzz CLI on their [releases page](https://github.com/microsoft/onefuzz/releases). + +### Configuring OneFuzz + +To run OneFuzz locally, you'll need to configure its endpoint, client ID, and client secret. Windows has a preset configuration available; this can be found at [this tutorial](https://www.osgwiki.com/wiki/Fuzzing_Service_-_Azure_Edge_and_Platform#Configure_OneFuzz_CLI) on osgwiki. + + + +`onefuzz config --endpoint $(endpoint) --client_id $(client_id) --authority $(authority) --tenant_domain $(tenant_domain)` + +**NOTE**: Our pipeline is already set up with these variables, so you don't need to worry about this when running this on Azure DevOps. + +### Running a job on OneFuzz + +You should now be able to run a job using the following command: + +`onefuzz template libfuzzer basic --target_exe ` + +- `project`: the name of the project +- `name`: the name of the test +- `build`: the identifier for the build (i.e. commit SHA1) +- `pool`: the VM pool to run this on +- `exe_path`: the fuzzer executable output from building your project + +This should also output more information (i.e. job ID) about the newly created job in a JSON format. + +### Enabling notifications + +**NOTE**: Our pipeline is already set up with this functionality. However, here is a quick guide on how to get it set up and modify it to our liking. + +OneFuzz supports multiple notification systems at once including MS Teams and Azure DevOps. See the resources below to learn more about setting these up. + +Our pipeline has been set up to create Azure DevOps work items. + +### Resources +- [OneFuzz GitHub](https://github.com/microsoft/onefuzz) + - [Getting started using OneFuzz](https://github.com/microsoft/onefuzz/blob/main/docs/getting-started.md) + - [Releases Page](https://github.com/microsoft/onefuzz/releases) +- [Notifications](https://github.com/microsoft/onefuzz/blob/main/docs/notifications.md) + - [MS Teams](https://github.com/microsoft/onefuzz/blob/main/docs/notifications/teams.md) + - [Azure DevOps](https://github.com/microsoft/onefuzz/blob/main/docs/notifications/ado.md) +- [OSG Wiki - OneFuzz](https://www.osgwiki.com/wiki/Fuzzing_Service_-_Azure_Edge_and_Platform) \ No newline at end of file diff --git a/doc/specs/#1790 - Font features and axes-spec.md b/doc/specs/#1790 - Font features and axes-spec.md index 63b56230361..fd0f59bb7e9 100644 --- a/doc/specs/#1790 - Font features and axes-spec.md +++ b/doc/specs/#1790 - Font features and axes-spec.md @@ -74,7 +74,7 @@ Should not affect security. ### Reliability -Aside from additional parsing required for the settings file (which inherently offers more locations for parsing to fail), we need to be careful about badly formed/non-existant feature tags or axes specified in the user-defined dictionaries. We must make sure to ignore such declarations (perhaps alongside emitting a warning to the user) and only apply those that are correctly formed and exist. +Aside from additional parsing required for the settings file (which inherently offers more locations for parsing to fail), we need to be careful about badly formed/non-existent feature tags or axes specified in the user-defined dictionaries. We must make sure to ignore such declarations (perhaps alongside emitting a warning to the user) and only apply those that are correctly formed and exist. ### Compatibility diff --git a/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md b/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md index 64fcf5d782b..e4b5ec6a4f1 100644 --- a/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md +++ b/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md @@ -241,7 +241,7 @@ So `focusPane(target=1, direction=up)` will attempt to focus the pane above pane > 👉 **NOTE**: At this point, the author considered "Do we even want a separate > action to engage the tab switcher with panes expanded?" Perhaps panes being -> visible in the tab switcher is just part fo the tab switcher's behavior. Maybe +> visible in the tab switcher is just part of the tab switcher's behavior. Maybe > there shouldn't be a separate "open the tab switcher with the panes expanded > to the pane I'm currently on, and the panes listed in MRU order" action. diff --git a/doc/specs/#492 - Default Terminal/spec.md b/doc/specs/#492 - Default Terminal/spec.md new file mode 100644 index 00000000000..e54b4d71b9d --- /dev/null +++ b/doc/specs/#492 - Default Terminal/spec.md @@ -0,0 +1,270 @@ +--- +author: Michael Niksa @miniksa +created on: 2020-08-14 +last updated: 2022-01-13 +issue id: #492 +--- + +# Default Terminal Choice in Windows OS + +## Abstract + +Since the beginning, Windows has offered a single choice in default terminal hosting behavior. Specifically, the default terminal is defined as the one that the operating system will start on your behalf when a command-line application is started without a terminal attached. This specification intends to detail how we will offer customers the ultimate in choice among first and third party replacements for their default terminal experience. + +## Inspiration + +We've had a lot of success in the past several years on our terminal team journey. We updated the old console host user interface with long-desired features. We updated the console environment to bring Windows closer to Linux and Mac by implementing the client (receiving) end of Virtual Terminal sequences to unlock WSL, Docker, and other cross-platform command-line application compatibility. We then created the ConPTY to expose the server end of the console environment to first and third party applications to enable the hosting of any of those command-line clients within their own user interfaces by implementing the server (sending) end of Virtual Terminal sequences. And then we built Windows Terminal as our flagship implementation of the development environment on this model. + +Through all of this, the entrypoint for alternatives to the console host UX continued to be "Start your alternative terminal implementation first, then start the command-line application inside." For those familiar with Linux and Mac or for those using the broad ecosystem of alternative Windows Terminals like ConEmu, Cmder, Console2, and the like... that was natural. But Windows did it differently a long time ago allowing the starting of a command-line application directly from the shell or kernel without a terminal specified. On noticing the missing terminal, the system would just-in-time start and attach the one terminal it could count on as always present, `conhost.exe`. + +And so the inspiration of this is simple: We want to allow our customers to choose whichever terminal they want as the just-in-time terminal attached to an application without one present/specified on launch. This final move completes our journey to allow the ultimate in choice AND decouple the terminal experience from the operating system release schedule. + +## Solution Design + +There are three components to the proposed design: + +1. **Inbox console**: This is the `conhost.exe` that is resident inside every Windows installation. +1. **Updated console**: This is the `openconsole.exe` that we ship with the Windows Terminal to provide a more up-to-date console server experience. +1. **Terminal UX**: This is `WindowsTerminal.exe`, the new Terminal user interface that runs on VT sequences. + +And there are a few scenarios here to consider: + +1. Replacement console API server and replacement terminal UX. + 1. This is the Windows Terminal scenario today. `OpenConsole.exe` is packed in the package to be the console API server and ConPTY environment for `WindowsTerminal.exe`. +1. Replacement console API server and legacy terminal UX. + 1. We don't explicitly distribute this today, but it's technically possible to just run `OpenConsole.exe` to accomplish this. +1. Inbox console API server and replacement terminal UX. + 1. The WSL environment does this when doing Windows interop and I believe VS Code does this too when told to use the ConPTY environment. (And since VS Code does it, anything using node-pty also does it, covering some 3rd party terminals as well). +1. Inbox console API server and inbox terminal UX. + 1. This is what we have today in `conhost.exe` running as the default application. + +The goal is to offer the ultimate in choice here where any of the components can be replaced as necessary for a 1st or 3rd party scenario. + +### Overview + +#### Inbox console + +The inbox console will be updated to support delegation of the incoming console client application connection to another console API server if one is registered and available. + +We leave the inbox console in-place and always available on the operating system for these reasons: + +1. A last chance fall-back should any of the delegation operations fail +1. An ongoing host for applications that aren't going to need a window at all +1. Continued support of our legacy `conhostv1.dll` environment, if chosen + +The general operation is as follows: + +- A command-line client application is started (from the start menu, run box, or any other `CreateProcess` or `ShellExecute` route) without an existing console server attached +- The inbox console is launched from `C:\windows\system32\conhost.exe` as always by the initialization routines inside `kernelbase.dll`. +- The inbox console accepts the incoming initial connection and looks for the `ShowWindow` information on the connection packet, as received from the kernel's process creation routines based on the parameters given to the `CreateProcess` call. (See [CREATE_NO_WINDOW](https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags) flag for details.) +- If the session is about to create a window, check for registration of a delegated/updated console and hand-off to it if it exists. +- Otherwise, start normally. + +This workflow affords us several benefits: + +- The only inbox component we have to change is `conhost.exe`, the one we already regularly update from open source on a regular basis. There is no change to the `kernelbase.dll` console initialization routines, `conclnt.lib` communication protocol, nor the `condrv.sys` driver. +- We should be able to make this change quickly, relatively easily, and the code delta should be relatively small + - This makes it easy to squeeze in early in the development of the solution and get it into the Windows OS product as soon as possible for self-hosting, validation, and potentially shipping in the OS before the remainder of the solution has shaken out + - This also makes it potentially possible to backport this portion of the code change to popular in-market versions of Windows 10. For instance, WSL2 has just backported to 1903 and 1909. The less churn and risk, the easier it is to sell a backport. + +*Potential future:* +- ~~If no updated console exists, potentially check for registration of a terminal UX that is willing to use the inbox ConPTY bits, start it, and transition to being a PTY instead.~~ +- **CUT FROM v1**: To simplify the story for end-users, we're offering this as a package deal in the first revision. Explaining the difference between consoles and terminals to end users is very difficult. + +The registration would operate as follows: +- A registry key in `HKCU\Console\%%Startup` (format `REG_SZ`, name `DelegationConsole`) would specify ~~the path to ~~the replacement console that would be used to service the remainder of the connection process. + - Alternatively or additionally, this same `REG_SZ` could list a COM server ID that could be looked up in the classes root and invoked. **V1 NOTE:** This was what was done. + - Packaged applications and classic applications can easily register a COM server + - WinRT libraries should be able to be easily registered as the COM server as well (given WinRT is COM underneath) + - WinRT cannot be exposed outside of the package context itself, so the `conhost.exe` that is in the OS and is naturally outside the package cannot find it. + - **V1 NOTE:** The subkey `%%Startup` was chosen to separate these keys (this one and the `DelegationTerminal` one below) in case we needed to ACL them or protect them in some way. We want a per-user choice of which Terminal/Console are used, but we might need to take action to prevent these keys from being slammed at some point in the future. Why `%%`? The subkeys are traditionally used to resolve paths to client binaries that have their own console preferences set. The `%%` should never be resolvable as it won't lead to a valid path or expanded path variable. + +The delegation process would operate as follows: +- A method contract is established between the existing inbox console and any updated console (an interface). + - `HRESULT ConsoleEstablishHandoff(HANDLE server, HANDLE driverInputEvent, const PortableConnectMessage* const msg, HANDLE signalPipe, HANDLE inboxProcess, HANDLE* process)` + - `HANDLE server`: This is the server side handle to the console driver, used with `DeviceIoControl` to receive/send messages with the client command-line application + - `HANDLE driverInputEvent`: The input event is created and assigned to the driver immediately on first connection, before any messages are read from the driver, to ensure that it can track a blocking state should first message be an input request that we do not yet have data to fill. As such, the inbox console will have created this and assigned it to the driver before pulling off the connection packet and determining that it wants to delegate. Therefore, we will transfer ownership of this event to the updated console. + - ~~`const PortableArguments* const args`: This contains the startup argument information that was passed in when the process was started including the original command line and the in/out handles.~~ + - ~~The `ConsoleArguments` structure could technically change between versions, so we will make a version agnostic portable structure that just carries the communication from the old one to the new one.~~ + - **CUT FROM V1**: The only arguments coming in from a default light-up are the server handle. Pretty much all the other arguments are related to the operation of the PTY. Since this feature is about "default application" launches where no arguments are specified, this was cut from the initial revision. + - `const PortableConnectMessage* const msg`: + - The `CONSOLE_API_MSG` structure contains both the actual packet data from the driver as well as some overhead/administration data related to the packet state, ordering, completion, errors, and buffers. It's a broad scope structure for every type of message we process and it can change over time as we improve the way the `conserver.lib` handles packets. + - This represents a version agnostic variant for ONLY the connect message that can pass along the initial connect information structure, the packet sequencing information, and other relevant payload only to that one message type. It will purposefully discard references to things like a specific set of API servicing routines because the point of handing off is to get updated routines, if necessary. + - **V1 NOTE:** This was named `CONSOLE_PORTABLE_ATTACH_MSG` + - `HANDLE signalPipe`: During authoring, it was identified that Ctrl+C and other similar signals need to make it back to the original `conhost.exe` application as the Operating System grants it special privilege over the originally attached client application. This privilege cannot be transferred to the delegated console. So this channel remains for the delegated one to send its signals back through the original one for commanding the underlying client. (This also implies the original `conhost.exe` inbox cannot close and must remain a part of the process tree for the life of the session to maintain this control.) + - `HANDLE inboxProcess`: Since we have to keep the inbox `conhost.exe` running for signal/ownership reasons, we also need to track its lifetime. If it disappears for whatever reason, we need to tear down the entire chain as part of our operation has been compromised. + - `HANDLE* process`: On the contrary to `inboxProcess`, we need to give our process handle back so it can also be tracked. After the inbox console delegates, it remains in a very limited capacity. If the delegation one disappears, the session will no longer function and needs to be torn down (and the client closed). + - *Return* `HRESULT`: This is one of the older style methods in the initialization. We moved them from mostly `NTSTATUS` to mostly `HRESULT` a while ago to take advantage of `wil`. This one will continue to follow the pattern and not move to exceptions. A return of `S_OK` will symbolize that the handoff worked and the inbox console can clean itself up and stop handling the session. +- When the connection packet is parsed for visibility information (see `srvinit.cpp`), we will attempt to resolve the registered handoff and call it. + - ~~In the initial revision here, I have this as a `LoadLibrary`/`GetProcAddress` to the above exported contract method from the updated console. This maintains the server session in the same process space and avoids:~~ + 1. ~~The issue of passing the server, event, and other handles into another process space. We're not entirely sure if the console driver will happily accept these things moving to a different process. It probably should, but unconfirmed.~~ + 1. ~~Some command-line client applications rely on spelunking the process tree to figure out who is their servicing application. Maintaining the delegated/updated console inside the same process space maintains some level of continuity for these sorts of applications.~~ + - **Alternative:** We may make this just be a COM server/client contract. ~~An in-proc COM server should operate in much the same fashion here (loading the DLL into the process and running particular method) while being significantly more formal and customizable (version revisions, moving to out-of-proc, not really needing to know the binary path because the catalog knows).~~ + - **V1 NOTE:** We landed on an out-of-proc COM server/client here. This maintains the isolation of the newly running code from the old code. Since we're maintaining the original `conhost.exe` for signaling purposes, we're no longer worried about the spelunking the process tree and not having the relationship for clients to find. + - **Not considering:** ~~WinRT. `conhost.exe` has no WinRT. Adding WinRT to it would significantly increase the complexity of compilation in the inbox and out of box code base. It would also significantly increase the compilation time, binary size, library link list, etc... unless we use just the ABI to access it. But I don't see an advantage to that over just using classic COM at that point. This is only one handoff method and a rather simplistic one at that. Every benefit WinRT provides is outweighed by the extra effort that would be required over just a classic COM server in this case.~~ +- After delegation is complete, the inbox console will have to clean up any threads, handles, and state related to the session. We do a fairly good job with this normally, but some portions of the `conhost.exe` codebase are reliant on the process exiting for final cleanup. There may be a bit of extra effort to do some explicit cleanup here. + - **V1 NOTE:** The inbox one cleans up everything it can and sits in a state waiting for the child/delegated process handle to exit. It also maintains a thread listening for the signals to come through in case it needs to send a command to the client application using the privilege granted to it by the driver. + +#### Updated console + +The updated replacement console will have the same console API server capabilities as the inbox console, but will be a later, updated, or customized-to-the-scenario version of the API server generally revolving around improving ConPTY support for a Terminal application. + +On receiving the handoff from the method signature listed above, the updated console will: +- Establish its own set of IO threading, device communication infrastructure, and API messaging routines while storing the handles given +- ~~Re-parse the command line arguments, if necessary, and store them for guiding the remainder of launch~~ +- Dispatch the attach message as if it were received normally +- Continue execution from there + +There will then either be a registration for a Terminal UX to take over the session by using ConPTY, ~~or the updated console will choose to launch its potentially updated version of the `conhost` UX~~. + +For registration, we repeat the dance above with another key: +- A registry key in `HKCU\Console\%%Startup` (format `REG_SZ`, name `DelegationTerminal`). + +The delegation repeats the same dance as above as well: +- A contract (interface) is established between the updated console and the terminal + - `HRESULT EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client)` + - `HANDLE in`: The handle to read client application output from the ConPTY and display on the Terminal + - `HANDLE out`: The handle to write user input from the Terminal to the ConPTY + - `HANDLE signal`: The signal handle for the ConPTY for out-of-band communication between PTY server and Terminal application + - ~~`COORD size`: The initial window size from the starting application, as it can be a preference in the connection structure. (A resize message may get sent back downward almost immediately from the Terminal as its dimensions could be different.)~~ **V1 NOTE:** This proved unnecessary as the resize operations sorted themselves out naturally. + - `HANDLE ref`: This is a "client reference handle" to the console driver and session. We hold onto a copy of this in the Terminal so the session will stay alive until we let go. (The other console hosts in the chain also hold one of these, as should the client.) + - `HANDLE server`: This is a process handle to the PTY we're attached to. We monitor this to know when the PTY is still alive from the Terminal side. + - `HANDLE client`: This is a process handle to the underlying client application. The terminal tracks this for exit handling. + - **Alternative:** This should likely just be a COM server/client contract as well. This would be consistent with the above and wouldn't require argument parsing or wink/nudge understanding of standard handle passing. It also conveys the same COM flexibility as described in the inbox console section. **V1 NOTE:** We used this alternative. We used COM, not a well-known exported function from the prototype. +- The contract is called and on success, responsibility of the UX is given over to the Terminal. The console sits in PTY mode. + - On failure, the console launches interactive. + +#### Terminal UX + +The terminal will be its own complete presentation and input solution on top of a ConPTY connection, separating the concerns between API servicing and the user experience. + +Today the Terminal knows how to start and then launches a ConPTY under it. The Terminal will need to be updated to accept a pre-existing ConPTY connection on launch (or when the multi-process model arrives, as an inbound connection), and connect that to a new tab/pane instead of using the `winconpty.lib` libraries to make its own. + +For now, I'm considering only the fresh-start scenario. +- The Terminal will have to detect the inbound connection through ~~its argument parsing (or through~~ a new entrypoint in the COM alternative ~~)~~ and store the PTY in/out/signal handles for that connection in the startup arguments information +- When the control is instantiated on a new tab, that initial creation where normally the "default profile" is launched will instead have to place the PTY in/out/signal handles already received into the `ConPtyConnection` object and use that as if it was already created. +- The Terminal can then let things run normally and the connection will come through and be hosted inside the session. + +There are several issues/concerns: +- Which profile/settings get loaded? We don't really know anything about the client that is coming in already-established. That makes it difficult to know what user preferences to apply to the inbound tab. We could: + - Use only the defaults for the incoming connection. Do not apply any profile-specific settings. + - Use the profile information from the default profile to some degree. This could cause some weird scenarios/mismatches if that profile has a particular icon or a color scheme that makes it recognizable to the user. + - Create some sort of "inbound profile" profile that is used for incoming connections + - Add a heuristic that attempts to match the name/path of the connecting client binary to a profile that exists and use those settings, falling back if one is not found. + - **Proposal:** Do the first one immediately for bootstrapping, then investigate the others as a revision going forward. +- The handles that are coming in are "raw" and "unpacked", not in the nice opaque `HPCON` structure that is usually provided to the `ConPtyConnection` object by the `winconpty.lib`. + - Add methods to `winconpty.lib` that allow for the packing of incoming raw handles into the `HPCON` structure so the rest of the lifetime can be treated the same + - Put the entrypoint for the COM server (or delegate the entrypoint for an argument) directly into this library so it can pack them up right away and hand of a ready-made `HPCON`. + +## UI/UX Design + +The user experience for this feature overall should be: + +1. The user launches a command-line client application through the Start Menu, Win+X menu, the Windows Explorer, the Run Dialog box (WinKey+R), or through another existing Windows application. +1. Using the established settings, the console system transparently starts, delegates itself to the updated console, switches itself into ConPTY mode, and a copy of Windows Terminal launches with the first tab open to host the command-line client application. + - **NOTE:** I'm not precluding 3rd party registrations of either the delegation updated console nor the delegation terminal. It is our intention to allow either or both of these pieces to be replaced to the user's desires. The example is for brevity of our golden path and motivation for this scenario. +1. The user is able to interact with the command-line client application as they would with the original console host. + - The user receives the additional benefit that short-running executions of a command-line application may not "blink in and disappear" as they do today when a user runs something like `ipconfig` from the run dialog. The Terminal's default states tend to leave the tab open and say that the client has exited. This would allow a Run Dialog `ipconfig` user an improved experience over the default console host state of disappearing quickly. +1. If any portion of the delegation fails, we will progressively degrade back to a `conhost` style Win32+GDI UX and nothing will be different from before. + +The settings experience includes: +- Configuration of the delegation operations: + - Locations: + - With the registry + - This is what's going to be available first and will remain available. We will progress to some or all of the below after. + - We will need to potentially add specifications to this to both the default profile (for new installations of Windows) or to upgrade/migration profiles (for users coming from previous editions of Windows) to enable the delegation process, especially if we put a copy of Windows Terminal directly into the box. + - **V1 NOTE:** we didn't add additional migration logic here as `HKCU\Console*` and subkeys were already in the migration logic, so adding another should just carry along. + - Inside Windows Terminal + - Inside the new Settings UI, we will likely need a page that configures the delegation keys in `HKCU\Console\%%Startup` ~~or a link out to the Windows Settings panel, should we manage to get the settings configurable there~~. + - Inside the console property sheet + - Same as for Terminal but with `comctl` controls over XAML +/- a link to the Windows Settings panel + - Inside the Settings panel for Windows (probably on the developer settings page) + - The ultimate location for this is likely a panel directly inside Windows. This is the hardest one to accomplish because of the timelines of the Windows product. We may not get this in an initial revision, but it should likely be our ultimate goal. **V1 NOTE:** We did it! + - Operation: + - Specify paths/server IDs - This is the initial revision + - Offer a list of registered servers or discovered manifests from the app catalog - This is the ideal scenario where we search the installed app catalog +/- the COM catalog and offer a list of apps that conform to the contract in a drop-down. + - The final process was to use [App Extensions](https://docs.microsoft.com/windows/uwp/launch-resume/how-to-create-an-extension) inside the Terminal APPX package to declare the COM GUIDs that were available for the `DelegationConsole` and `DelegationTerminal` fields respectively. A configuration class `DelegationConfig` was added to `propslib.lib` that enables the lookup of these from the application state catalog and presents a list of them to choose from. It also manages reading and writing the registry keys. + - **V1 NOTE:** Our configuration options currently allow pairings of replacement consoles and terminals to be adjusted in lock-step from the UI. That's not to say further combinations are not possible or even necessarily inhibited by the code. We just went for minimal confusion in our first round. + +- Configuration of the legacy console state: + - ~~Since we could end up in an experience where the default launch experience gets you directly into Windows Terminal, we believe that the Terminal will likely need an additional setting or settings in the new Settings UI that will allow the toggling of some of the `HKCU\Console` values to do things like set/remove the legacy console state.~~ **V1 NOTE:** Cut as low priority. Switch back to console and configure it that way or use the existing property sheet or tamper with registry keys. + - We have left the per-launch debugging and advanced access hole of calling something like `conhost.exe cmd.exe` which will use the inbox conhost to launch `cmd.exe` even if there is a default specified. + +Concerns: +- State separation policy for Windows. I believe `HKCU\Console` is already specified as a part of the "user state" that should be mutable and carried forward on OS Swap, especially as we have been improving the OS swap experience. +- Ability for installers/elevated scripts to stomp the Delegation keys + - This was a long time problem for default app registrations and was limited in our OS. Are we about to run down the same path? + - What is the alternative here? To use a protocol handler? To store this configuration state data with other protected state in a registry area that is mutable, but only ACL'd to the `SYSTEM` user like some other things in the Settings control panel? + - **V1 NOTE:** We set ourselves up for some future ACL thing with the subkey, but we otherwise haven't enforced anything at this time. + +## Capabilities + +### Accessibility + +Accessibility applications are the most likely to resort to a method of spelunking the process tree or window handles to attempt to find content to read out. Presuming they have hardcoded rules for console-type applications, these algorithms could be surprised by the substitution of another terminal environment. + +The major players here that I am considering are NVDA, JAWS, and Narrator. As far as I am aware, all of these applications attempt to drive their interactivity through UI Automation where possible. And we have worked with all of these applications in the past in improving their support for both `conhost.exe` and the Windows Terminal product. I have relatively high confidence that we will be able to work with them again to help update these assistive products to understand the new UI delegation, if necessary. + +### Security + +Let's hit the elephant in the room. "You plan on pulling a completely different binary inside the `conhost.exe` process and just... delegating all activity to it?" Yes. + +(**V1 NOTE:** Well, it's out of proc now. But it is at the same privilege level as the original one thanks to the mechanics of COM.) + +As far as I'm concerned, the `conhost.exe` that is started to host the command-line client application is running at the same integrity level as the client binary that is partially started and waiting for its server to be ready. This is the long-standing existing protection that we have from the Windows operating system. Anything running in the same integrity level is already expected to be able to tamper with anything else at the same integrity level. The delegated binary that we would be loading into our process space will also be at the same integrity level. Nothing really stops a malicious actor from launching that binary in any other way in the same integrity level as a part of the command-line client application's startup. + +The mitigation here, if necessary, would be to use `WinVerifyTrust` to validate the certification path of the `OpenConsole.exe` binary to ensure that only one that is signed by Microsoft can be the substitute server host for the application. This doesn't stop third parties from redistributing our `OpenConsole.exe` off of GitHub if necessary with their products, but it would stop someone from introducing any random binary that met the signature interface of the delegation methods into `conhost.exe`. The only value I see this providing is stopping someone from being "tricked" into delegating their `conhost.exe` to another binary through the configuration methods we provide. It doesn't really stop someone (or an attacker) from taking ownership of the `conhost.exe` in System32 and replacing it directly. So this point might be moot. (It is expected that replacement of the System32 one is already protected, to some degree, by being owned by the SYSTEM account and requiring some measure of authority to replace.) + +### Reliability + +The change on its own may honestly improve reliability of the hosting system. The existing just-in-time startup of the console host application only had a single chance at initializing a user experience before it would give up and return that the command-line application could not be started. + +However, there are now several phases in the startup process that will have the opportunity to make multiple attempts at multiple versions or applications to find a suitable host for the starting application before giving up. + +One layer of this is where the `conhost.exe` baked into the operating system will be on the lookout for an `OpenConsole.exe` that will replace its server activities. The delegation binary loses a bit of reliability, theoretically, by the fact that loading another process during launch could have versioning/resolution/path/dependency issues, but it simultaneously offers us the opportunity for improved reliability by being able to service that binary quickly outside the Windows OS release cycle. Fixes can arrive in days instead of months to years. + +Another layer of this is where either `conhost.exe` or the delegated `OpenConsole.exe` server will search for a terminal user experience host, like `WindowsTerminal.exe` or another registered first or third party host, and split the responsibility of hosting the session with that binary. Again, there's a theoretical reliability loss with the additional process launch/load, but there's much to be gained by reducing the scope of what each binary must accomplish. Removing the need to handle user interaction from `conhost.exe` or `OpenConsole.exe` and delegating those activities means there is less surface area running and less chance for a UX interaction to interfere with API call servicing and vice versa. And again, having the delegation to external components means that they can be fixed on a timeline of days instead of months or years as when baked into the operating system. + +### Compatibility + +One particular scenario that this could break is an application that makes use of spelunking the process tree when a command-line application starts to identify the hosting terminal application window by HWND to inject input, extract output, or otherwise hook and bind to hosting services. As the default application UI that will launch may not have the `conhost.exe` name (for spelunking via searching processes) and the HWND located may either be the ConPTY fake HWND or an HWND belonging to a completely different UI, these applications might not work. + +Two considerations here: + +1. At a minimum, we must offer an opt-out of the delegation to another terminal for the default application. +1. We may also want to offer a process-name, policy, manifest, or other per client application opt-out mechanism. + +**V1 NOTE:** There is no per-client specific way of doing this. The toggle is per-user and can be adjusted in 3 different places. + +### Performance, Power, and Efficiency + +I expect to take some degree of performance, power, and efficiency hit by implementing this replacement default app scenario just by it's nature. We will be loading multiple processes, performing tests and branches during startup, and we will likely need to load COM/WinRT and packaging data that was not loaded prior to resolve the final state of default application load. I would expect this to accrue to some failures in the performance and power gates inside the Windows product. Additionally, the efficiency of running pretty much everything through the ConPTY is lower than just rendering it directly to `conhost.exe`'s embedded GDI-powered UI itself thanks to the multiple levels of translation and parsing that occur in this scenario. + +The mitigations to these losses are as follows: + +1. We will delay load any of the interface load and packaging data lookup libraries to only be pulled into process space should we determine that the application is non-interactive. + 1. That should save us some of the commit and power costs for the sorts of non-interactive scripts and applications that typically run early in OS startup (and leverage `conhost.exe` as their host environment). + 1. We will still likely get hit with the on-disk commit cost for the additional export libraries linked as well as additional code. That would be a by-design change. + +1. We plan to begin Profile Guided Optimization across our `OpenConsole.exe` and `WindowsTerminal.exe` binaries. This should allow us to optimize the startup paths for this scenario and bias the `OpenConsole.exe` binary that we redistribute to focus its efforts and efficiency on the ConPTY role specifically, ignoring all of the interactive Win32/GDI portions that aren't typically used. + 1. We may need to add a PGO scenario inside Windows to tune the optimization of `conhost.exe` especially if we're going to go full on Windows Terminal in the box default application. The existing PGO that occurs in the optimization branches is running on several `conhost.exe` interactive scenarios, none of which will be relevant here. We would probably want to update it to focus on the default app delegation routine AND on the non-interactive scenario for hosted applications (where delegation will not occur but Win32/GDI will still not be involved). + +## Potential Issues + +### Passing Handles with COM +COM doesn't inherently expose a way for us to pass handles directly between processes with the existing contracts. We know this is possible because Windows does it all the time, but it doesn't appear to be public. We believe the mission forward is to expose this functionality to the public as if it's good enough for us internally and it is a requirement to build complex functionality like this... then it should be good enough for the public. + +**V1 NOTE:** We gained approval to open this up and documented it. [`system_handle` attribute](https://docs.microsoft.com/windows/win32/midl/system-handle). It didn't require any code changes because the public IDL compiler already recognized the existence of this attribute and did the correct thing. It just wasn't documented for use. + +## Future considerations + +* We additionally would like to leave the door open to distributing updated `OpenConsole.exe`s in their own app package as a dependency that others could rely on. + * This was one of the original management requests when we were opening the source of the console product as well as the Terminal back in spring of 2019. For the sake of ongoing servicing and maintainability, it was requested that we reach a point where our dependencies could be serviced potentially independently of the product as a whole static unit. We didn't achieve that goal initially, but this design would enable us to do something like this. + * One negative to this scenario is that dependency resolution and the installation of dependent packages through APPX is currently lacking in several ways. It's difficult/impossible to do in environments where the store or the internet is unavailable. And it's a problem often enough that the Windows Terminal package embeds the VC runtimes inside itself instead of relying on the dependency resolution of the app platform. + +## Resources + +- [Windows Terminal Process Model 2.0 spec](https://github.com/microsoft/terminal/pull/7240) +- [Windows Terminal 2.0 Process Model Improvements](https://github.com/microsoft/terminal/issues/5000) +- [Console allocation policy specifications](https://github.com/microsoft/terminal/pull/7337) +- [Fine-grained console allocation policy feature](https://github.com/microsoft/terminal/issues/7335) diff --git a/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md b/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md index 1757352e9ca..a1137a77ac9 100644 --- a/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md +++ b/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md @@ -552,7 +552,7 @@ following behaviors: * `false`: If the current window is elevated, try to create a new unelevated window to host this connection. -We could always re-introduce this setting, to supercede `elevate`. +We could always re-introduce this setting, to supersede `elevate`. ### Change profile appearance for elevated windows diff --git a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md index c1a3dee38d9..e32af6b72ea 100644 --- a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md +++ b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md @@ -1123,7 +1123,7 @@ elevated windows, when they trust the extension. We could have an additional set of settings the user could use to enable certain extensions in elevated windows. However, this setting cannot live in the normal `settings.json` or even `state.json` (see [#7972], since those files are writable by any medium-IL -process. Instead, this setting would ned to live in a separate file that's +process. Instead, this setting would need to live in a separate file that's protected to only be writable by elevated processes. This would ensure that an attacker could not just add their extension to the list of white-listed extensions. When the settings UI wants to modify that setting, it'll need to diff --git a/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md b/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md index c3d2871cb36..50faf9d1efb 100644 --- a/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md +++ b/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md @@ -26,7 +26,7 @@ This spec was largely inspired by the following diagram from @DHowett: ![figure 1](data-mockup.png) The goal is to introduce an `id` parameter by which actions could be uniquely -refered to. If we'd ever like to use an action outside the list of `actions`, we +referred to. If we'd ever like to use an action outside the list of `actions`, we can simply refer to the action's ID, allowing the user to only define the action _once_. diff --git a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md index c7dbefeab0b..ad361218403 100644 --- a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md +++ b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md @@ -230,7 +230,7 @@ def cloneGraph(oldSource, newSource, visited): for old in oldSource.adj: # Below check is for backtracking, so new - # nodes don't get initialized everytime + # nodes don't get initialized every time if clone is None or(clone is not None and clone.key != old.key): clone = Node(old.key, []) newSource.adj.append(clone) diff --git a/doc/specs/TerminalSettings-spec.md b/doc/specs/TerminalSettings-spec.md index 1bf098274a8..16d3cfbd938 100644 --- a/doc/specs/TerminalSettings-spec.md +++ b/doc/specs/TerminalSettings-spec.md @@ -58,7 +58,7 @@ VS needs to be able to persist settings just as a simple set of global settings. When the application needs to retrieve these settings, they need to use them as a tripartite structure: frontend-component-terminal settings. Each frontend will have its own set of settings. -Each component implementation will also ned to have some settings that control it. +Each component implementation will also need to have some settings that control it. The terminal also will have some settings specific to the terminal. ### Globals and Profiles diff --git a/doc/specs/drafts/#1256 - Tab tearoff.md b/doc/specs/drafts/#1256 - Tab tearoff.md index d57a695067c..fa1b2e9a0d5 100644 --- a/doc/specs/drafts/#1256 - Tab tearoff.md +++ b/doc/specs/drafts/#1256 - Tab tearoff.md @@ -124,7 +124,7 @@ There's a few areas to study here. #### Communicating the launch For the parameters passing, I see a few options: -1. `conhost.exe` can look up the package registration for `wt.exe` and call an entrypoint with arguments. This could be adapted to instead look up which package is registered as the default one instead of `wt.exe` for third party hosts. We would have to build provisions into the OS to select this, or use some sort of publically documented registry key mechanism. Somewhat gross. +1. `conhost.exe` can look up the package registration for `wt.exe` and call an entrypoint with arguments. This could be adapted to instead look up which package is registered as the default one instead of `wt.exe` for third party hosts. We would have to build provisions into the OS to select this, or use some sort of publicly documented registry key mechanism. Somewhat gross. 1. `conhost.exe` can call the execution alias with parameters. WSL distro launchers use this. 1. We can define a protocol handler for these sorts of connections and let `wt.exe` register for it. Protocol handlers are already well supported and understood both by classic applications and by packaged/modern applications on Windows. They must have provisions to communicate at least some semblance of argument data as well. This is the route I'd probably prefer. `ms-term://incoming/` or something like that. The receiving `wt.exe` can contact the manager process (or set one up if it is the first) and negotiate receiving the session that was specified into a new tab. diff --git a/doc/specs/drafts/#997 Non-Terminal-Panes.md b/doc/specs/drafts/#997 Non-Terminal-Panes.md index 512165580bd..6fb539cf33c 100644 --- a/doc/specs/drafts/#997 Non-Terminal-Panes.md +++ b/doc/specs/drafts/#997 Non-Terminal-Panes.md @@ -77,7 +77,7 @@ is a bigger discussion than the feature at hand, however. ### Performance, Power, and Efficiency decide to host a WebView in a pane, then it surely could impact these measures. -I don't believe this will have a noticable impact _on its own_. Should the user +I don't believe this will have a noticeable impact _on its own_. Should the user However, I leave that discussion to the implementation of the actual alternative pane content itself. diff --git a/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs b/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs index 84d3d3d44bf..50cb6064b64 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs @@ -26,7 +26,7 @@ internal static PseudoConsole Create(SafeFileHandle inputReadSide, SafeFileHandl 0, out IntPtr hPC); if(createResult != 0) { - throw new InvalidOperationException("Could not create psuedo console. Error Code " + createResult); + throw new InvalidOperationException("Could not create pseudo console. Error Code " + createResult); } return new PseudoConsole(hPC); } diff --git a/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs b/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs index 2b0c8088a21..cc3c9158a1e 100644 --- a/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs +++ b/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs @@ -136,7 +136,7 @@ public override int Read(byte[] buffer, int offset, int count) if (record.EventType != Kernel32.EVENT_TYPE.FOCUS_EVENT) { // I assume success adding records - this is not so critical - // if it is critical to you, loop on this with a miniscule delay + // if it is critical to you, loop on this with a minuscule delay _nonKeyEvents.TryAdd(record); } } @@ -195,4 +195,4 @@ private void ValidateRead(byte[] buffer, int offset, int count) if (!CanRead) throw new NotSupportedException("Get read not supported."); } } -} \ No newline at end of file +} diff --git a/scratch/ScratchIslandApp/Package/Package.wapproj b/scratch/ScratchIslandApp/Package/Package.wapproj index 8f526698b1c..4de7a05a3cf 100644 --- a/scratch/ScratchIslandApp/Package/Package.wapproj +++ b/scratch/ScratchIslandApp/Package/Package.wapproj @@ -140,12 +140,12 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj index 04b9b082496..5d84890f014 100644 --- a/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj +++ b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj @@ -147,13 +147,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj index 59d500ccc6e..91592d6f771 100644 --- a/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj +++ b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj @@ -80,13 +80,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/scratch/ScratchIslandApp/SampleApp/packages.config b/scratch/ScratchIslandApp/SampleApp/packages.config index ec5cd9a0fbd..f167ce9570a 100644 --- a/scratch/ScratchIslandApp/SampleApp/packages.config +++ b/scratch/ScratchIslandApp/SampleApp/packages.config @@ -1,6 +1,6 @@ - + diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj index 18a5a05fe33..f83b237149c 100644 --- a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj +++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj @@ -137,14 +137,14 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/scratch/ScratchIslandApp/WindowExe/packages.config b/scratch/ScratchIslandApp/WindowExe/packages.config index 319e836803c..cbcb02cb6dc 100644 --- a/scratch/ScratchIslandApp/WindowExe/packages.config +++ b/scratch/ScratchIslandApp/WindowExe/packages.config @@ -2,6 +2,6 @@ - + diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index e626db89f3b..35046da07e8 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -91,7 +91,7 @@ TextAttribute TextAttribute::StripErroneousVT16VersionsOfLegacyDefaults(const Te const auto bg{ attribute.GetBackground() }; auto copy{ attribute }; if (fg.IsIndex16() && - attribute.IsBold() == WI_IsFlagSet(s_ansiDefaultForeground, FOREGROUND_INTENSITY) && + attribute.IsIntense() == WI_IsFlagSet(s_ansiDefaultForeground, FOREGROUND_INTENSITY) && fg.GetIndex() == (s_ansiDefaultForeground & ~FOREGROUND_INTENSITY)) { // We don't want to turn 1;37m into 39m (or even 1;39m), as this was meant to mimic a legacy color. @@ -115,7 +115,7 @@ WORD TextAttribute::GetLegacyAttributes() const noexcept const BYTE fgIndex = _foreground.GetLegacyIndex(s_legacyDefaultForeground); const BYTE bgIndex = _background.GetLegacyIndex(s_legacyDefaultBackground); const WORD metaAttrs = _wAttrLegacy & META_ATTRS; - const bool brighten = IsBold() && _foreground.CanBeBrightened(); + const bool brighten = IsIntense() && _foreground.CanBeBrightened(); return fgIndex | (bgIndex << 4) | metaAttrs | (brighten ? FOREGROUND_INTENSITY : 0); } @@ -255,9 +255,9 @@ void TextAttribute::SetRightVerticalDisplayed(const bool isDisplayed) noexcept WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_RVERTICAL, isDisplayed); } -bool TextAttribute::IsBold() const noexcept +bool TextAttribute::IsIntense() const noexcept { - return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Bold); + return WI_IsFlagSet(_extendedAttrs, ExtendedAttributes::Intense); } bool TextAttribute::IsFaint() const noexcept @@ -305,9 +305,9 @@ bool TextAttribute::IsReverseVideo() const noexcept return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO); } -void TextAttribute::SetBold(bool isBold) noexcept +void TextAttribute::SetIntense(bool isIntense) noexcept { - WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Bold, isBold); + WI_UpdateFlag(_extendedAttrs, ExtendedAttributes::Intense, isIntense); } void TextAttribute::SetFaint(bool isFaint) noexcept diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index 70b15073642..bc8e42dae47 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -84,7 +84,7 @@ class TextAttribute final friend constexpr bool operator!=(const WORD& legacyAttr, const TextAttribute& attr) noexcept; bool IsLegacy() const noexcept; - bool IsBold() const noexcept; + bool IsIntense() const noexcept; bool IsFaint() const noexcept; bool IsItalic() const noexcept; bool IsBlinking() const noexcept; @@ -95,7 +95,7 @@ class TextAttribute final bool IsOverlined() const noexcept; bool IsReverseVideo() const noexcept; - void SetBold(bool isBold) noexcept; + void SetIntense(bool isIntense) noexcept; void SetFaint(bool isFaint) noexcept; void SetItalic(bool isItalic) noexcept; void SetBlinking(bool isBlinking) noexcept; @@ -214,10 +214,10 @@ namespace WEX static WEX::Common::NoThrowString ToString(const TextAttribute& attr) { return WEX::Common::NoThrowString().Format( - L"{FG:%s,BG:%s,bold:%d,wLegacy:(0x%04x),ext:(0x%02x)}", + L"{FG:%s,BG:%s,intense:%d,wLegacy:(0x%04x),ext:(0x%02x)}", VerifyOutputTraits::ToString(attr._foreground).GetBuffer(), VerifyOutputTraits::ToString(attr._background).GetBuffer(), - attr.IsBold(), + attr.IsIntense(), attr._wAttrLegacy, static_cast(attr._extendedAttrs)); } diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp index e20195ceaac..a1913189249 100644 --- a/src/buffer/out/TextColor.cpp +++ b/src/buffer/out/TextColor.cpp @@ -133,8 +133,8 @@ void TextColor::SetDefault() noexcept // - If brighten is true, and we've got a 16 color index in the "dark" // portion of the color table (indices [0,7]), then we'll look up the // bright version of this color (from indices [8,15]). This should be -// true for TextAttributes that are "Bold" and we're treating bold as -// bright (which is the default behavior of most terminals.) +// true for TextAttributes that are "intense" and we're treating intense +// as bright (which is the default behavior of most terminals.) // * If we're a default color, we'll return the default color provided. // Arguments: // - colorTable: The table of colors we should use to look up the value of diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 36266710a86..a7df90e8c76 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1751,7 +1751,7 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF, if (copyTextColor) { - // cant see CR/LF so just use black FG & BK + // can't see CR/LF so just use black FG & BK COLORREF const Blackness = RGB(0x00, 0x00, 0x00); selectionFgAttr.push_back(Blackness); selectionFgAttr.push_back(Blackness); @@ -2034,20 +2034,8 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi const auto writeAccumulatedChars = [&](bool includeCurrent) { if (col >= startOffset) { - const auto unescapedText = ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row)).substr(startOffset, col - startOffset + includeCurrent)); - for (const auto c : unescapedText) - { - switch (c) - { - case '\\': - case '{': - case '}': - contentBuilder << "\\" << c; - break; - default: - contentBuilder << c; - } - } + const auto text = std::wstring_view{ rows.text.at(row) }.substr(startOffset, col - startOffset + includeCurrent); + _AppendRTFText(contentBuilder, text); startOffset = col; } @@ -2146,6 +2134,31 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi } } +void TextBuffer::_AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text) +{ + for (const auto codeUnit : text) + { + if (codeUnit <= 127) + { + switch (codeUnit) + { + case L'\\': + case L'{': + case L'}': + contentBuilder << "\\" << gsl::narrow(codeUnit); + break; + default: + contentBuilder << gsl::narrow(codeUnit); + } + } + else + { + // Windows uses unsigned wchar_t - RTF uses signed ones. + contentBuilder << "\\u" << std::to_string(til::bit_cast(codeUnit)) << "?"; + } + } +} + // Function Description: // - Reflow the contents from the old buffer into the new buffer. The new buffer // can have different dimensions than the old buffer. If it does, then this diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index a2b34ed5b88..fe87ee8822d 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -247,6 +247,8 @@ class TextBuffer final void _PruneHyperlinks(); + static void _AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text); + std::unordered_map _idsAndPatterns; size_t _currentPatternId; diff --git a/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp b/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp index ae39257a9f1..b334b214c02 100644 --- a/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp +++ b/src/buffer/out/ut_textbuffer/TextAttributeTests.cpp @@ -24,7 +24,7 @@ class TextAttributeTests TEST_METHOD(TestTextAttributeColorGetters); TEST_METHOD(TestReverseDefaultColors); TEST_METHOD(TestRoundtripDefaultColors); - TEST_METHOD(TestBoldAsBright); + TEST_METHOD(TestIntenseAsBright); RenderSettings _renderSettings; const COLORREF _defaultFg = RGB(1, 2, 3); @@ -257,7 +257,7 @@ void TextAttributeTests::TestRoundtripDefaultColors() TextAttribute::SetLegacyDefaultAttributes(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } -void TextAttributeTests::TestBoldAsBright() +void TextAttributeTests::TestIntenseAsBright() { const auto& colorTable = _renderSettings.GetColorTable(); const COLORREF darkBlack = til::at(colorTable, 0); @@ -267,8 +267,8 @@ void TextAttributeTests::TestBoldAsBright() TextAttribute attr{}; // verify that calculated foreground/background are the same as the direct - // values when not bold - VERIFY_IS_FALSE(attr.IsBold()); + // values when not intense + VERIFY_IS_FALSE(attr.IsIntense()); VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(colorTable, _defaultFgIndex)); VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(colorTable, _defaultBgIndex)); @@ -277,46 +277,46 @@ void TextAttributeTests::TestBoldAsBright() _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, false); VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), _renderSettings.GetAttributeColors(attr)); - // with bold set, calculated foreground/background values shouldn't change for the default colors. - attr.SetBold(true); - VERIFY_IS_TRUE(attr.IsBold()); + // with intense set, calculated foreground/background values shouldn't change for the default colors. + attr.SetIntense(true); + VERIFY_IS_TRUE(attr.IsIntense()); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, true); VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), _renderSettings.GetAttributeColors(attr)); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, false); VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), _renderSettings.GetAttributeColors(attr)); attr.SetIndexedForeground(TextColor::DARK_BLACK); - VERIFY_IS_TRUE(attr.IsBold()); + VERIFY_IS_TRUE(attr.IsIntense()); - Log::Comment(L"Foreground should be bright black when bold is bright is enabled"); + Log::Comment(L"Foreground should be bright black when intense is bright is enabled"); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, true); VERIFY_ARE_EQUAL(std::make_pair(brightBlack, _defaultBg), _renderSettings.GetAttributeColors(attr)); - Log::Comment(L"Foreground should be dark black when bold is bright is disabled"); + Log::Comment(L"Foreground should be dark black when intense is bright is disabled"); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, false); VERIFY_ARE_EQUAL(std::make_pair(darkBlack, _defaultBg), _renderSettings.GetAttributeColors(attr)); attr.SetIndexedBackground(TextColor::DARK_GREEN); - VERIFY_IS_TRUE(attr.IsBold()); + VERIFY_IS_TRUE(attr.IsIntense()); - Log::Comment(L"background should be unaffected by 'bold is bright'"); + Log::Comment(L"background should be unaffected by 'intense is bright'"); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, true); VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), _renderSettings.GetAttributeColors(attr)); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, false); VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), _renderSettings.GetAttributeColors(attr)); - attr.SetBold(false); - VERIFY_IS_FALSE(attr.IsBold()); - Log::Comment(L"when not bold, 'bold is bright' changes nothing"); + attr.SetIntense(false); + VERIFY_IS_FALSE(attr.IsIntense()); + Log::Comment(L"when not intense, 'intense is bright' changes nothing"); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, true); VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), _renderSettings.GetAttributeColors(attr)); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, false); VERIFY_ARE_EQUAL(std::make_pair(darkBlack, darkGreen), _renderSettings.GetAttributeColors(attr)); - Log::Comment(L"When set to a bright color, and bold, 'bold is bright' changes nothing"); - attr.SetBold(true); + Log::Comment(L"When set to a bright color, and intense, 'intense is bright' changes nothing"); + attr.SetIntense(true); attr.SetIndexedForeground(TextColor::BRIGHT_BLACK); - VERIFY_IS_TRUE(attr.IsBold()); + VERIFY_IS_TRUE(attr.IsIntense()); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, true); VERIFY_ARE_EQUAL(std::make_pair(brightBlack, darkGreen), _renderSettings.GetAttributeColors(attr)); _renderSettings.SetRenderMode(RenderSettings::Mode::IntenseIsBright, false); diff --git a/src/buffer/out/ut_textbuffer/sources b/src/buffer/out/ut_textbuffer/sources index 6e025707077..570467fce63 100644 --- a/src/buffer/out/ut_textbuffer/sources +++ b/src/buffer/out/ut_textbuffer/sources @@ -21,6 +21,7 @@ SOURCES = \ TARGETLIBS = \ $(CONSOLE_OBJ_PATH)\buffer\out\lib\$(O)\ConBufferOut.lib \ + $(CONSOLE_OBJ_PATH)\renderer\base\lib\$(O)\ConRenderBase.lib \ $(CONSOLE_OBJ_PATH)\types\lib\$(O)\ConTypes.lib \ $(TARGETLIBS) \ diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 48d8caffdf9..9aaffac83ee 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -11,7 +11,7 @@ --> false false - wtd + wtd wt @@ -38,7 +38,7 @@ Designer - + Designer @@ -52,7 +52,7 @@ - + -Dev @@ -138,36 +138,34 @@ - - - + + + - - + + + - - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest index 20c75fd5bba..0627906f7c0 100644 --- a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest @@ -20,7 +20,7 @@ Version="0.0.1.0" /> - Windows Terminal Dev + ms-resource:AppStoreNameDev A Lone Developer Images\StoreLogo.png @@ -30,7 +30,7 @@ - + @@ -87,7 +87,7 @@ diff --git a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest index 3c0dc42e2be..a55af61e2a0 100644 --- a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest @@ -21,7 +21,7 @@ Version="0.5.0.0" /> - Windows Terminal Preview + ms-resource:AppStoreNamePre Microsoft Corporation Images\StoreLogo.png @@ -31,7 +31,95 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -88,7 +176,7 @@ diff --git a/src/cascadia/CascadiaPackage/Package.appxmanifest b/src/cascadia/CascadiaPackage/Package.appxmanifest index 7b3ad6e4c12..cb99c9a07ea 100644 --- a/src/cascadia/CascadiaPackage/Package.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package.appxmanifest @@ -21,7 +21,7 @@ Version="1.0.0.0" /> - Windows Terminal + ms-resource:AppStoreName Microsoft Corporation Images\StoreLogo.png @@ -31,7 +31,95 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -88,7 +176,7 @@ diff --git a/src/cascadia/CascadiaPackage/Resources/Resources.resw b/src/cascadia/CascadiaPackage/Resources/Resources.resw index 94a910fd0e9..07e77609519 100644 --- a/src/cascadia/CascadiaPackage/Resources/Resources.resw +++ b/src/cascadia/CascadiaPackage/Resources/Resources.resw @@ -117,22 +117,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Windows Terminal - - - Windows Terminal Dev - - - Windows Terminal Preview - - - Terminal - - - Terminal Dev - - - Terminal Preview - - \ No newline at end of file + diff --git a/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw b/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw index c2d6436947f..eced69ff25a 100644 --- a/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw +++ b/src/cascadia/CascadiaPackage/Resources/en-US/Resources.resw @@ -117,13 +117,52 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Terminal + {Locked} + + + Terminal Dev + {Locked} + + + Terminal Preview + {Locked} + + + Windows Terminal + {Locked} + + + Windows Terminal Dev + {Locked} + + + Windows Terminal Preview + {Locked} + + + Terminal + {Locked} + + + Terminal Dev + {Locked} + + + Terminal Preview + {Locked} + The New Windows Terminal + {Locked} The Windows Terminal, but Unofficial + {Locked} Windows Terminal with a preview of upcoming features + {Locked} - \ No newline at end of file + diff --git a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj index f383f371001..2ad3e5508e3 100644 --- a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj @@ -81,8 +81,8 @@ If you don't have this, then you'll see an error like "(init.obj) : error LNK2005: DllMain already defined in MSVCRTD.lib(dll_dllmain_stub.obj)" --> - /INCLUDE:_DllMain@12 - /INCLUDE:DllMain + %(AdditionalOptions) /INCLUDE:_DllMain@12 + %(AdditionalOptions) /INCLUDE:DllMain @@ -99,10 +99,10 @@ x86 $(Platform) - <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\runtimes\win10-$(Native-Platform)\native\" + <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\" - + diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp index 100634874c8..ad2463bbb70 100644 --- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp @@ -38,6 +38,7 @@ namespace SettingsModelLocalTests TEST_METHOD(TryCreateWinRTType); TEST_METHOD(TestTerminalArgsForBinding); TEST_METHOD(CommandLineToArgvW); + TEST_METHOD(NormalizeCommandLine); TEST_METHOD(GetProfileForArgsWithCommandline); TEST_METHOD(MakeSettingsForProfile); TEST_METHOD(MakeSettingsForDefaultProfileThatDoesntExist); @@ -84,7 +85,8 @@ namespace SettingsModelLocalTests for (int i = 0; i < expectedArgc; ++i) { const bool useQuotes = static_cast(rng(2)); - const auto count = static_cast(rng(64)); + // We need to ensure there is at least one character + const auto count = static_cast(rng(64) + 1); const auto ch = static_cast(rng('z' - 'a' + 1) + 'a'); if (i != 0) @@ -106,6 +108,7 @@ namespace SettingsModelLocalTests input.push_back(L'"'); } } + Log::Comment(NoThrowString().Format(input.c_str())); int argc; wil::unique_hlocal_ptr argv{ ::CommandLineToArgvW(input.c_str(), &argc) }; @@ -120,6 +123,67 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(0, memcmp(beg, expectedArgv.data(), expectedArgv.size())); } + // This unit test covers GH#12345. + // * paths with more than 1 whitespace + // * paths sharing a common prefix with another directory + void TerminalSettingsTests::NormalizeCommandLine() + { + using namespace std::string_literals; + + static constexpr auto touch = [](const auto& path) { + std::ofstream file{ path }; + }; + + std::wstring guid; + { + GUID g{}; + THROW_IF_FAILED(CoCreateGuid(&g)); + guid = fmt::format( + L"{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", + g.Data1, + g.Data2, + g.Data3, + g.Data4[0], + g.Data4[1], + g.Data4[2], + g.Data4[3], + g.Data4[4], + g.Data4[5], + g.Data4[6], + g.Data4[7]); + } + + const auto tmpdir = std::filesystem::temp_directory_path(); + const auto dir1 = tmpdir / guid; + const auto dir2 = tmpdir / (guid + L" two"); + const auto file1 = dir1 / L"file 1.exe"; + const auto file2 = dir2 / L"file 2.exe"; + + const auto cleanup = wil::scope_exit([&]() { + std::error_code ec; + remove_all(dir1, ec); + remove_all(dir2, ec); + }); + + create_directory(dir1); + create_directory(dir2); + touch(file1); + touch(file2); + + { + const auto commandLine = file2.native() + LR"( -foo "bar1 bar2" -baz)"s; + const auto expected = file2.native() + L"\0-foo\0bar1 bar2\0-baz"s; + const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine.c_str()); + VERIFY_ARE_EQUAL(expected, actual); + } + { + const auto commandLine = L"C:\\"; + const auto expected = L"C:\\"; + const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine); + VERIFY_ARE_EQUAL(expected, actual); + } + } + void TerminalSettingsTests::GetProfileForArgsWithCommandline() { // I'm exclusively using cmd.exe as I know exactly where it resides at. @@ -145,6 +209,10 @@ namespace SettingsModelLocalTests "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}", "commandline": "cmd.exe /A /C", "connectionType": "{9a9977a7-1fe0-49c0-b6c0-13a0cd1c98a1}" + }, + { + "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}", + "commandline": "C:\\invalid.exe", } ] } @@ -163,7 +231,7 @@ namespace SettingsModelLocalTests TestCase{ L"cmd.exe", 0 }, // SearchPathW() normalization + case insensitive matching. TestCase{ L"cmd.exe /a", 1 }, - TestCase{ L"C:\\Windows\\System32\\cmd.exe /A", 1 }, + TestCase{ L"%SystemRoot%\\System32\\cmd.exe /A", 1 }, // Test that we don't pick the equally long but different "/A /B" variant. TestCase{ L"C:\\Windows\\System32\\cmd.exe /A /C", 1 }, // Test that we don't pick the shorter "/A" variant, @@ -173,6 +241,9 @@ namespace SettingsModelLocalTests // Ignore profiles with a connection type, like the Azure cloud shell. // Instead it should pick any other prefix. TestCase{ L"C:\\Windows\\System32\\cmd.exe /A /C", 1 }, + // Failure to normalize a path (e.g. because the path doesn't exist) + // should yield the unmodified input string (see NormalizeCommandLine). + TestCase{ L"C:\\invalid.exe /A /B", 4 }, // Return base layer profile for missing profiles. TestCase{ L"C:\\Windows\\regedit.exe", -1 }, }; diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj index 6217790a895..d44431a5f89 100644 --- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj @@ -92,11 +92,11 @@ x86 $(Platform) - <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\runtimes\win10-$(Native-Platform)\native\" + <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\" - + diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 5197b0bd07f..a97fe8064b4 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -123,7 +123,7 @@ - + diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 4cafee145a0..8ce04464cda 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -252,8 +252,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_peasant) { - // Inform the monarch of the time we were last activated - _monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs()); + if (const auto& lastActivated{ _peasant.GetLastActivatedArgs() }) + { + // Inform the monarch of the time we were last activated + _monarch.HandleActivatePeasant(lastActivated); + } } if (!_isKing) @@ -539,10 +542,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void WindowManager::SummonAllWindows() { - if constexpr (Feature_NotificationIcon::IsEnabled()) - { - _monarch.SummonAllWindows(); - } + _monarch.SummonAllWindows(); } Windows::Foundation::Collections::IVectorView WindowManager::GetPeasantInfos() diff --git a/src/cascadia/Remoting/packages.config b/src/cascadia/Remoting/packages.config index 2d79f9f5adb..64e09e4c8e5 100644 --- a/src/cascadia/Remoting/packages.config +++ b/src/cascadia/Remoting/packages.config @@ -1,5 +1,5 @@ - + diff --git a/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp b/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp index c43dcdf399a..84aba407f74 100644 --- a/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp +++ b/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp @@ -70,12 +70,14 @@ namespace winrt::TerminalApp::implementation // Apply the reverts in reverse order - If we had multiple previews // stacked on top of each other, then this will ensure the first one in // is the last one out. - for (auto i{ _restorePreviewFuncs.rbegin() }; i < _restorePreviewFuncs.rend(); i++) + const auto cleanup = wil::scope_exit([this]() { + _restorePreviewFuncs.clear(); + }); + + for (const auto& f : _restorePreviewFuncs) { - auto f = *i; f(); } - _restorePreviewFuncs.clear(); } // Method Description: @@ -94,6 +96,8 @@ namespace winrt::TerminalApp::implementation { if (const auto& scheme{ _settings.GlobalSettings().ColorSchemes().TryLookup(args.SchemeName()) }) { + const auto backup = _restorePreviewFuncs.empty(); + _ApplyToActiveControls([&](const auto& control) { // Stash a copy of the current scheme. auto originalScheme{ control.ColorScheme() }; @@ -101,34 +105,38 @@ namespace winrt::TerminalApp::implementation // Apply the new scheme. control.ColorScheme(scheme.ToCoreScheme()); - // Each control will emplace a revert into the - // _restorePreviewFuncs for itself. - _restorePreviewFuncs.emplace_back([=]() { - // On dismiss, restore the original scheme. - control.ColorScheme(originalScheme); - }); + if (backup) + { + // Each control will emplace a revert into the + // _restorePreviewFuncs for itself. + _restorePreviewFuncs.emplace_back([=]() { + // On dismiss, restore the original scheme. + control.ColorScheme(originalScheme); + }); + } }); } } void TerminalPage::_PreviewAdjustOpacity(const Settings::Model::AdjustOpacityArgs& args) { - // Clear the saved preview funcs because we don't need to add a restore each time - // the preview changes, we only need to be able to restore the last one. - _restorePreviewFuncs.clear(); + const auto backup = _restorePreviewFuncs.empty(); _ApplyToActiveControls([&](const auto& control) { // Stash a copy of the original opacity. auto originalOpacity{ control.BackgroundOpacity() }; // Apply the new opacity - control.AdjustOpacity(args.Opacity(), args.Relative()); + control.AdjustOpacity(args.Opacity() / 100.0, args.Relative()); - _restorePreviewFuncs.emplace_back([=]() { - // On dismiss: - // Don't adjust relatively, just set outright. - control.AdjustOpacity(::base::saturated_cast(originalOpacity * 100), false); - }); + if (backup) + { + _restorePreviewFuncs.emplace_back([=]() { + // On dismiss: + // Don't adjust relatively, just set outright. + control.AdjustOpacity(originalOpacity, false); + }); + } }); } diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index 251028dee8f..074aed6ab1c 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -21,7 +21,7 @@ + ControlsResourcesVersion="Version2" /> + + + Color="#202020" /> + + + - + + + Color="#E8E8E8" /> - + diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index c8dbacbcbde..dd01c2a2e22 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -709,7 +709,6 @@ namespace winrt::TerminalApp::implementation // This might cause a UAC prompt. The elevation is performed on a // background thread, as to not block the UI thread. // Arguments: - // - elevate: If true, launch the new Terminal elevated using `runas` // - newTerminalArgs: A NewTerminalArgs describing the terminal instance // that should be spawned. The Profile should be filled in with the GUID // of the profile we want to launch. @@ -717,8 +716,7 @@ namespace winrt::TerminalApp::implementation // - // Important: Don't take the param by reference, since we'll be doing work // on another thread. - fire_and_forget TerminalPage::_OpenNewWindow(const bool elevate, - const NewTerminalArgs newTerminalArgs) + fire_and_forget TerminalPage::_OpenNewWindow(const NewTerminalArgs newTerminalArgs) { // Hop to the BG thread co_await winrt::resume_background(); @@ -745,9 +743,8 @@ namespace winrt::TerminalApp::implementation SHELLEXECUTEINFOW seInfo{ 0 }; seInfo.cbSize = sizeof(seInfo); seInfo.fMask = SEE_MASK_NOASYNC; - // `runas` will cause the shell to launch this child process elevated. // `open` will just run the executable normally. - seInfo.lpVerb = elevate ? L"runas" : L"open"; + seInfo.lpVerb = L"open"; seInfo.lpFile = exePath.c_str(); seInfo.lpParameters = cmdline.c_str(); seInfo.nShow = SW_SHOWNORMAL; @@ -781,7 +778,7 @@ namespace winrt::TerminalApp::implementation // Manually fill in the evaluated profile. newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid())); - _OpenNewWindow(false, newTerminalArgs); + _OpenNewWindow(newTerminalArgs); actionArgs.Handled(true); } @@ -923,7 +920,10 @@ namespace winrt::TerminalApp::implementation // If we didn't have args, or the args weren't ExportBufferArgs (somehow) _ExportTab(*activeTab, L""); - args.Handled(true); + if (args) + { + args.Handled(true); + } } } @@ -967,7 +967,7 @@ namespace winrt::TerminalApp::implementation if (const auto& realArgs = args.ActionArgs().try_as()) { const auto res = _ApplyToActiveControls([&](auto& control) { - control.AdjustOpacity(realArgs.Opacity(), realArgs.Relative()); + control.AdjustOpacity(realArgs.Opacity() / 100.0, realArgs.Relative()); }); args.Handled(res); } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index ec6ebf50fc5..71698f7c4c2 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -396,6 +396,14 @@ namespace winrt::TerminalApp::implementation } } + // Method Description: + // - Returns true if there is no dialog currently being shown (meaning that we can show a dialog) + // - Returns false if there is a dialog currently being shown (meaning that we cannot show another dialog) + bool AppLogic::CanShowDialog() + { + return (_dialog == nullptr); + } + // Method Description: // - Displays a dialog for errors found while loading or validating the // settings. Uses the resources under the provided title and content keys @@ -504,8 +512,26 @@ namespace winrt::TerminalApp::implementation if (keyboardServiceIsDisabled) { _root->ShowKeyboardServiceWarning(); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "KeyboardServiceWasDisabled", + TraceLoggingDescription("Event emitted when the keyboard service is disabled, and we warned them about it"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); } } + else + { + // For when the warning was disabled in the settings + + TraceLoggingWrite( + g_hTerminalAppProvider, + "KeyboardServiceWarningWasDisabledBySetting", + TraceLoggingDescription("Event emitted when the user has disabled the KB service warning"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + } if (FAILED(_settingsLoadedResult)) { @@ -1074,18 +1100,18 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Gets the title of the currently focused terminal control. If there - // isn't a control selected for any reason, returns "Windows Terminal" + // isn't a control selected for any reason, returns "Terminal" // Arguments: // - // Return Value: - // - the title of the focused control if there is one, else "Windows Terminal" + // - the title of the focused control if there is one, else "Terminal" hstring AppLogic::Title() { if (_root) { return _root->Title(); } - return { L"Windows Terminal" }; + return { L"Terminal" }; } // Method Description: @@ -1472,6 +1498,24 @@ namespace winrt::TerminalApp::implementation return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; } + bool AppLogic::ShouldImmediatelyHandoffToElevated() + { + if (!_loadedInitialSettings) + { + // Load settings if we haven't already + LoadSettings(); + } + + return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; + } + void AppLogic::HandoffToElevated() + { + if (_root) + { + _root->HandoffToElevated(_settings); + } + } + void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) { std::vector converted; @@ -1569,38 +1613,24 @@ namespace winrt::TerminalApp::implementation bool AppLogic::GetMinimizeToNotificationArea() { - if constexpr (Feature_NotificationIcon::IsEnabled()) - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - LoadSettings(); - } - - return _settings.GlobalSettings().MinimizeToNotificationArea(); - } - else + if (!_loadedInitialSettings) { - return false; + // Load settings if we haven't already + LoadSettings(); } + + return _settings.GlobalSettings().MinimizeToNotificationArea(); } bool AppLogic::GetAlwaysShowNotificationIcon() { - if constexpr (Feature_NotificationIcon::IsEnabled()) - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - LoadSettings(); - } - - return _settings.GlobalSettings().AlwaysShowNotificationIcon(); - } - else + if (!_loadedInitialSettings) { - return false; + // Load settings if we haven't already + LoadSettings(); } + + return _settings.GlobalSettings().AlwaysShowNotificationIcon(); } bool AppLogic::GetShowTitleInTitlebar() diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 19e891843b9..582866704b4 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -81,6 +81,8 @@ namespace winrt::TerminalApp::implementation bool AlwaysOnTop() const; bool ShouldUsePersistedLayout(); + bool ShouldImmediatelyHandoffToElevated(); + void HandoffToElevated(); hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); void IdentifyWindow(); @@ -120,6 +122,7 @@ namespace winrt::TerminalApp::implementation bool GetShowTitleInTitlebar(); winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); + bool CanShowDialog(); void DismissDialog(); Windows::Foundation::Collections::IMapView GlobalHotkeys(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 9121c3ccf9e..df0f7b3cd47 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -33,6 +33,8 @@ namespace TerminalApp SystemMenuItemHandler Handler { get; }; }; + // See IDialogPresenter and TerminalPage's DialogPresenter for more + // information. [default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter { AppLogic(); @@ -93,6 +95,8 @@ namespace TerminalApp TaskbarState TaskbarState{ get; }; Boolean ShouldUsePersistedLayout(); + Boolean ShouldImmediatelyHandoffToElevated(); + void HandoffToElevated(); String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); @@ -104,11 +108,6 @@ namespace TerminalApp Windows.Foundation.Collections.IMapView GlobalHotkeys(); - // See IDialogPresenter and TerminalPage's DialogPresenter for more - // information. - Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); - void DismissDialog(); - event Windows.Foundation.TypedEventHandler SetTitleBarContent; event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler LastTabClosed; diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp index a4b4c0b8f18..68a8a6c003c 100644 --- a/src/cascadia/TerminalApp/CommandPalette.cpp +++ b/src/cascadia/TerminalApp/CommandPalette.cpp @@ -164,11 +164,16 @@ namespace winrt::TerminalApp::implementation // - the approximate number of items visible in the list (in other words the size of the page) uint32_t CommandPalette::_getNumVisibleItems() { - const auto container = _filteredActionsView().ContainerFromIndex(0); - const auto item = container.try_as(); - const auto itemHeight = ::base::saturated_cast(item.ActualHeight()); - const auto listHeight = ::base::saturated_cast(_filteredActionsView().ActualHeight()); - return listHeight / itemHeight; + if (const auto container = _filteredActionsView().ContainerFromIndex(0)) + { + if (const auto item = container.try_as()) + { + const auto itemHeight = ::base::saturated_cast(item.ActualHeight()); + const auto listHeight = ::base::saturated_cast(_filteredActionsView().ActualHeight()); + return listHeight / itemHeight; + } + } + return 0; } // Method Description: @@ -822,16 +827,15 @@ namespace winrt::TerminalApp::implementation { const auto currentNeedleHasResults{ _filteredActions.Size() > 0 }; _noMatchesText().Visibility(currentNeedleHasResults ? Visibility::Collapsed : Visibility::Visible); - if (!currentNeedleHasResults) + if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(_searchBox()) }) { - if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(_searchBox()) }) - { - automationPeer.RaiseNotificationEvent( - Automation::Peers::AutomationNotificationKind::ActionCompleted, - Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent, - NoMatchesText(), // NoMatchesText contains the right text for the current mode - L"CommandPaletteResultAnnouncement" /* unique name for this notification */); - } + automationPeer.RaiseNotificationEvent( + Automation::Peers::AutomationNotificationKind::ActionCompleted, + Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent, + currentNeedleHasResults ? + winrt::hstring{ fmt::format(std::wstring_view{ RS_(L"CommandPalette_MatchesAvailable") }, _filteredActions.Size()) } : + NoMatchesText(), // what to announce if results were found + L"CommandPaletteResultAnnouncement" /* unique name for this group of notifications */); } } else diff --git a/src/cascadia/TerminalApp/CommandPalette.xaml b/src/cascadia/TerminalApp/CommandPalette.xaml index 29dbfb90094..11fa61807c0 100644 --- a/src/cascadia/TerminalApp/CommandPalette.xaml +++ b/src/cascadia/TerminalApp/CommandPalette.xaml @@ -145,7 +145,7 @@ @@ -199,20 +199,20 @@ Orientation="Horizontal"> @@ -445,7 +445,7 @@ Background="Transparent" Click="_moveBackButtonClicked" ClickMode="Press"> - diff --git a/src/cascadia/TerminalApp/Jumplist.cpp b/src/cascadia/TerminalApp/Jumplist.cpp index 3452da5449c..dcbcef030ac 100644 --- a/src/cascadia/TerminalApp/Jumplist.cpp +++ b/src/cascadia/TerminalApp/Jumplist.cpp @@ -62,6 +62,19 @@ static std::wstring _normalizeIconPath(std::wstring_view path) // - winrt::fire_and_forget Jumplist::UpdateJumplist(const CascadiaSettings& settings) noexcept { + if (!settings) + { + // By all accounts, this shouldn't be null. Seemingly however (GH + // #12360), it sometimes is. So just check this case here and log a + // message. + TraceLoggingWrite(g_hTerminalAppProvider, + "Jumplist_UpdateJumplist_NullSettings", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + co_return; + } + // make sure to capture the settings _before_ the co_await const auto strongSettings = settings; diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 2fcdab15b9b..2bef061af7a 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -33,7 +33,6 @@ static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Wi winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr }; winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr }; -winrt::Windows::Media::Playback::MediaPlayer Pane::s_bellPlayer = { nullptr }; Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) : _control{ control }, @@ -70,36 +69,6 @@ Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFo _FocusFirstChild(); e.Handled(true); }); - - if (!s_bellPlayer) - { - try - { - s_bellPlayer = winrt::Windows::Media::Playback::MediaPlayer(); - if (s_bellPlayer) - { - // BODGY - // - // Manually leak a ref to the MediaPlayer we just instantiated. - // We're doing this just like the way we do in AppHost with the - // App itself. - // - // We have to do this because there's some bug in the OS with - // the way a MediaPlayer gets torn down. At time of writing (Nov - // 2021), if you search for `remove_SoundLevelChanged` in the OS - // repo, you'll find a pile of bugs. - // - // We tried moving the MediaPlayer singleton up to the - // TerminalPage, but alas, that teardown had the same problem. - // So _whatever_. We'll leak it here. It needs to last the - // lifetime of the app anyways, and it'll get cleaned up when the - // Terminal is closed, so whatever. - winrt::Windows::Media::Playback::MediaPlayer p{ s_bellPlayer }; - ::winrt::detach_abi(p); - } - } - CATCH_LOG(); - } } Pane::Pane(std::shared_ptr first, @@ -1061,17 +1030,54 @@ void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundatio winrt::fire_and_forget Pane::_playBellSound(winrt::Windows::Foundation::Uri uri) { - auto weakThis{ shared_from_this() }; + auto weakThis{ weak_from_this() }; co_await winrt::resume_foreground(_root.Dispatcher()); - if (auto pane{ weakThis.get() }) + if (auto pane{ weakThis.lock() }) { - if (s_bellPlayer) + // BODGY + // GH#12258: We learned that if you leave the MediaPlayer open, and + // press the media keys (like play/pause), then the OS will _replay the + // bell_. So we have to re-create the MediaPlayer each time we want to + // play the bell, to make sure a subsequent play doesn't come through + // and reactivate the old one. + + if (!_bellPlayer) + { + // The MediaPlayer might not exist on Windows N SKU. + try + { + _bellPlayer = winrt::Windows::Media::Playback::MediaPlayer(); + } + CATCH_LOG(); + } + if (_bellPlayer) { const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) }; const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) }; - s_bellPlayer.Source(item); - s_bellPlayer.Play(); + _bellPlayer.Source(item); + _bellPlayer.Play(); + + // This lambda will clean up the bell player when we're done with it. + auto weakThis2{ weak_from_this() }; + _mediaEndedRevoker = _bellPlayer.MediaEnded(winrt::auto_revoke, [weakThis2](auto&&, auto&&) { + if (auto self{ weakThis2.lock() }) + { + if (self->_bellPlayer) + { + // We need to make sure clear out the current track + // that's being played, again, so that the system can't + // come through and replay it. In testing, we needed to + // do this, closing the MediaPlayer alone wasn't good + // enough. + self->_bellPlayer.Pause(); + self->_bellPlayer.Source(nullptr); + self->_bellPlayer.Close(); + } + self->_mediaEndedRevoker.revoke(); + self->_bellPlayer = nullptr; + } + }); } } } @@ -1173,6 +1179,19 @@ void Pane::Shutdown() // Lock the create/close lock so that another operation won't concurrently // modify our tree std::unique_lock lock{ _createCloseLock }; + + // Clear out our media player callbacks, and stop any playing media. This + // will prevent the callback from being triggered after we've closed, and + // also make sure that our sound stops when we're closed. + _mediaEndedRevoker.revoke(); + if (_bellPlayer) + { + _bellPlayer.Pause(); + _bellPlayer.Source(nullptr); + _bellPlayer.Close(); + } + _bellPlayer = nullptr; + if (_IsLeaf()) { _control.Close(); diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index acef331a379..67f03d1b0bc 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -241,7 +241,8 @@ class Pane : public std::enable_shared_from_this bool _zoomed{ false }; - static winrt::Windows::Media::Playback::MediaPlayer s_bellPlayer; + winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; + winrt::Windows::Media::Playback::MediaPlayer::MediaEnded_revoker _mediaEndedRevoker; bool _IsLeaf() const noexcept; bool _HasFocusedChild() const noexcept; diff --git a/src/cascadia/TerminalApp/Resources/en-US/ContextMenu.resw b/src/cascadia/TerminalApp/Resources/en-US/ContextMenu.resw index d2f7c335235..3277f3ad4f0 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/ContextMenu.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/ContextMenu.resw @@ -117,6 +117,46 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Terminal + + + Terminal Dev + {Locked} The dev build will never be seen in multiple languages + + + Terminal Preview + + + Windows Terminal + + + Windows Terminal Dev + {Locked} The dev build will never be seen in multiple languages + + + Windows Terminal Preview + + + Terminal + + + Terminal Dev + {Locked} The dev build will never be seen in multiple languages + + + Terminal Preview + + + The New Windows Terminal + + + The Windows Terminal, but Unofficial + {Locked} The dev build will never be seen in multiple languages + + + Windows Terminal with a preview of upcoming features + Open in Terminal (Dev) {Locked} The dev build will never be seen in multiple languages diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 88dd2624e1a..b2133af1816 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -405,6 +405,9 @@ Shift+Click to open a new window + + Ctrl+Click to open as administrator + Close @@ -725,4 +728,8 @@ Open Settings This is a call-to-action hyperlink; it will open the settings. + + Suggestions found: {0} + {0} will be replaced with a number. + diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp index fc2f76d94b5..4eb0457d1b1 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -98,7 +98,6 @@ namespace winrt::TerminalApp::implementation if (auto tab{ weakThis.get() }) { - auto fontFamily = winrt::WUX::Media::FontFamily(L"Segoe MDL2 Assets"); 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... diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp index ef17fe3eb95..6cda2158ae3 100644 --- a/src/cascadia/TerminalApp/TabBase.cpp +++ b/src/cascadia/TerminalApp/TabBase.cpp @@ -90,7 +90,7 @@ namespace winrt::TerminalApp::implementation // Close Controls::MenuFlyoutItem closeTabMenuItem; Controls::FontIcon closeSymbol; - closeSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + closeSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); closeSymbol.Glyph(L"\xE711"); closeTabMenuItem.Click([weakThis](auto&&, auto&&) { @@ -251,4 +251,5 @@ namespace winrt::TerminalApp::implementation } }); } + } diff --git a/src/cascadia/TerminalApp/TabHeaderControl.xaml b/src/cascadia/TerminalApp/TabHeaderControl.xaml index 1a69fdf785f..f24f9e0d2bf 100644 --- a/src/cascadia/TerminalApp/TabHeaderControl.xaml +++ b/src/cascadia/TerminalApp/TabHeaderControl.xaml @@ -48,19 +48,19 @@ --> diff --git a/src/cascadia/TerminalApp/TabRowControl.xaml b/src/cascadia/TerminalApp/TabRowControl.xaml index 0cce26ef313..b5d300aaefe 100644 --- a/src/cascadia/TerminalApp/TabRowControl.xaml +++ b/src/cascadia/TerminalApp/TabRowControl.xaml @@ -24,7 +24,7 @@ diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 4cb06143311..40fad5cce81 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -400,13 +400,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 4d0e4584054..8f79e9b8090 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -156,7 +156,6 @@ namespace winrt::TerminalApp::implementation if (_settings.GlobalSettings().UseAcrylicInTabRow()) { const auto res = Application::Current().Resources(); - const auto lightKey = winrt::box_value(L"Light"); const auto darkKey = winrt::box_value(L"Dark"); const auto tabViewBackgroundKey = winrt::box_value(L"TabViewBackground"); @@ -224,7 +223,7 @@ namespace winrt::TerminalApp::implementation _newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) { if (auto page{ weakThis.get() }) { - page->_OpenNewTerminal(NewTerminalArgs()); + page->_OpenNewTerminalViaDropdown(NewTerminalArgs()); } }); _newTabButton.Drop([weakThis{ get_weak() }](Windows::Foundation::IInspectable const&, winrt::Windows::UI::Xaml::DragEventArgs e) { @@ -298,8 +297,118 @@ namespace winrt::TerminalApp::implementation // - true if the ApplicationState should be used. bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const { - return Feature_PersistedWindowLayout::IsEnabled() && - settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + } + + // Method Description: + // - This is a bit of trickiness: If we're running unelevated, and the user + // passed in only --elevate actions, the we don't _actually_ want to + // restore the layouts here. We're not _actually_ about to create the + // window. We're simply going to toss the commandlines + // Arguments: + // - + // Return Value: + // - true if we're not elevated but all relevant pane-spawning actions are elevated + bool TerminalPage::ShouldImmediatelyHandoffToElevated(const CascadiaSettings& settings) const + { + // 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) + { + // there aren't startup actions, or we're elevated. In that case, go for it. + return false; + } + + // Check that there's at least one action that's not just an elevated newTab action. + for (const auto& action : _startupActions) + { + NewTerminalArgs newTerminalArgs{ nullptr }; + + if (action.Action() == ShortcutAction::NewTab) + { + const auto& args{ action.Args().try_as() }; + if (args) + { + newTerminalArgs = args.TerminalArgs(); + } + else + { + // This was a nt action that didn't have any args. The default + // profile may want to be elevated, so don't just early return. + } + } + else if (action.Action() == ShortcutAction::SplitPane) + { + const auto& args{ action.Args().try_as() }; + if (args) + { + newTerminalArgs = args.TerminalArgs(); + } + else + { + // This was a nt action that didn't have any args. The default + // profile may want to be elevated, so don't just early return. + } + } + else + { + // This was not a new tab or split pane action. + // This doesn't affect the outcome + continue; + } + + // It's possible that newTerminalArgs is null here. + // GetProfileForArgs should be resilient to that. + const auto profile{ settings.GetProfileForArgs(newTerminalArgs) }; + if (profile.Elevate()) + { + continue; + } + + // The profile didn't want to be elevated, and we aren't elevated. + // We're going to open at least one tab, so return false. + return false; + } + return true; + } + + // Method Description: + // - Escape hatch for immediately dispatching requests to elevated windows + // when first launched. At this point in startup, the window doesn't exist + // yet, XAML hasn't been started, but we need to dispatch these actions. + // We can't just go through ProcessStartupActions, because that processes + // the actions async using the XAML dispatcher (which doesn't exist yet) + // - DON'T CALL THIS if you haven't already checked + // ShouldImmediatelyHandoffToElevated. If you're thinking about calling + // this outside of the one place it's used, that's probably the wrong + // solution. + // Arguments: + // - settings: the settings we should use for dispatching these actions. At + // this point in startup, we hadn't otherwise been initialized with these, + // so use them now. + // Return Value: + // - + void TerminalPage::HandoffToElevated(const CascadiaSettings& settings) + { + if (!_startupActions) + { + return; + } + + // Hookup our event handlers to the ShortcutActionDispatch + _settings = settings; + _HookupKeyBindings(_settings.ActionMap()); + _RegisterActionCallbacks(); + + for (const auto& action : _startupActions) + { + // only process new tabs and split panes. They're all going to the elevated window anyways. + if (action.Action() == ShortcutAction::NewTab || action.Action() == ShortcutAction::SplitPane) + { + _actionDispatch->DoAction(action); + } + } } // Method Description; @@ -347,7 +456,7 @@ namespace winrt::TerminalApp::implementation NewTerminalArgs args; args.StartingDirectory(winrt::hstring{ path.wstring() }); - this->_OpenNewTerminal(args); + this->_OpenNewTerminalViaDropdown(args); TraceLoggingWrite( g_hTerminalAppProvider, @@ -556,7 +665,11 @@ namespace winrt::TerminalApp::implementation // However, we need to make sure to close this window in that scenario. // Since there aren't any _tabs_ in this window, we won't ever get a // closed event. So do it manually. - if (_tabs.Size() == 0) + // + // GH#12267: Make sure that we don't instantly close ourselves when + // we're readying to accept a defterm connection. In that case, we don't + // have a tab yet, but will once we're initialized. + if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener)) { _LastTabClosedHandlers(*this, nullptr); } @@ -572,10 +685,7 @@ namespace winrt::TerminalApp::implementation // Notes link, and privacy policy link. void TerminalPage::_ShowAboutDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - presenter.ShowDialog(FindName(L"AboutDialog").try_as()); - } + _ShowDialogHelper(L"AboutDialog"); } winrt::hstring TerminalPage::ApplicationDisplayName() @@ -595,6 +705,33 @@ namespace winrt::TerminalApp::implementation ShellExecute(nullptr, nullptr, currentPath.c_str(), nullptr, nullptr, SW_SHOW); } + // Method description: + // - Called when the user closes a content dialog + // - Tells the presenter to update its knowledge of whether there is a content dialog open + void TerminalPage::_DialogCloseClick(const IInspectable&, + const ContentDialogButtonClickEventArgs&) + { + if (auto presenter{ _dialogPresenter.get() }) + { + presenter.DismissDialog(); + } + } + + // Method Description: + // - Helper to show a content dialog + // - We only open a content dialog if there isn't one open already + winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowDialogHelper(const std::wstring_view& name) + { + if (auto presenter{ _dialogPresenter.get() }) + { + if (presenter.CanShowDialog()) + { + co_return co_await presenter.ShowDialog(FindName(name).try_as()); + } + } + co_return ContentDialogResult::None; + } + // Method Description: // - Displays a dialog to warn the user that they are about to close all open windows. // Once the user clicks the OK button, shut down the application. @@ -603,11 +740,7 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowQuitDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"QuitDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"QuitDialog"); } // Method Description: @@ -619,22 +752,14 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowCloseWarningDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"CloseAllDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"CloseAllDialog"); } // Method Description: // - Displays a dialog for warnings found while closing the terminal tab marked as read-only winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowCloseReadOnlyDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"CloseReadOnlyDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"CloseReadOnlyDialog"); } // Method Description: @@ -647,11 +772,7 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowMultiLinePasteWarningDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"MultiLinePasteDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"MultiLinePasteDialog"); } // Method Description: @@ -662,11 +783,7 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowLargePasteWarningDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"LargePasteDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"LargePasteDialog"); } // Method Description: @@ -731,6 +848,9 @@ namespace winrt::TerminalApp::implementation auto newWindowRun = WUX::Documents::Run(); newWindowRun.Text(RS_(L"NewWindowRun/Text")); newWindowRun.FontStyle(FontStyle::Italic); + auto elevatedRun = WUX::Documents::Run(); + elevatedRun.Text(RS_(L"ElevatedRun/Text")); + elevatedRun.FontStyle(FontStyle::Italic); auto textBlock = WUX::Controls::TextBlock{}; textBlock.Inlines().Append(newTabRun); @@ -738,6 +858,8 @@ namespace winrt::TerminalApp::implementation textBlock.Inlines().Append(newPaneRun); textBlock.Inlines().Append(WUX::Documents::LineBreak{}); textBlock.Inlines().Append(newWindowRun); + textBlock.Inlines().Append(WUX::Documents::LineBreak{}); + textBlock.Inlines().Append(elevatedRun); auto toolTip = WUX::Controls::ToolTip{}; toolTip.Content(textBlock); @@ -747,7 +869,7 @@ namespace winrt::TerminalApp::implementation if (auto page{ weakThis.get() }) { NewTerminalArgs newTerminalArgs{ profileIndex }; - page->_OpenNewTerminal(newTerminalArgs); + page->_OpenNewTerminalViaDropdown(newTerminalArgs); } }); newTabFlyout.Items().Append(profileMenuItem); @@ -794,7 +916,7 @@ namespace winrt::TerminalApp::implementation WUX::Controls::FontIcon commandPaletteIcon{}; commandPaletteIcon.Glyph(L"\xE945"); - commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); commandPaletteFlyout.Icon(commandPaletteIcon); commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick }); @@ -841,7 +963,7 @@ namespace winrt::TerminalApp::implementation _newTabButton.Flyout().ShowAt(_newTabButton); } - void TerminalPage::_OpenNewTerminal(const NewTerminalArgs newTerminalArgs) + void TerminalPage::_OpenNewTerminalViaDropdown(const NewTerminalArgs newTerminalArgs) { // if alt is pressed, open a pane const CoreWindow window = CoreWindow::GetForCurrentThread(); @@ -857,12 +979,21 @@ namespace winrt::TerminalApp::implementation WI_IsFlagSet(lShiftState, CoreVirtualKeyStates::Down) || WI_IsFlagSet(rShiftState, CoreVirtualKeyStates::Down) }; + const auto ctrlState{ window.GetKeyState(VirtualKey::Control) }; + const auto rCtrlState = window.GetKeyState(VirtualKey::RightControl); + const auto lCtrlState = window.GetKeyState(VirtualKey::LeftControl); + const auto ctrlPressed{ WI_IsFlagSet(ctrlState, CoreVirtualKeyStates::Down) || + WI_IsFlagSet(rCtrlState, CoreVirtualKeyStates::Down) || + WI_IsFlagSet(lCtrlState, CoreVirtualKeyStates::Down) }; + // Check for DebugTap bool debugTap = this->_settings.GlobalSettings().DebugFeaturesEnabled() && WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) && WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down); - if (shiftPressed && !debugTap) + const bool dispatchToElevatedWindow = ctrlPressed && !IsElevated(); + + if ((shiftPressed || dispatchToElevatedWindow) && !debugTap) { // Manually fill in the evaluated profile. if (newTerminalArgs.ProfileIndex() != nullptr) @@ -874,7 +1005,15 @@ namespace winrt::TerminalApp::implementation newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid())); } } - this->_OpenNewWindow(false, newTerminalArgs); + + if (dispatchToElevatedWindow) + { + _OpenElevatedWT(newTerminalArgs); + } + else + { + _OpenNewWindow(newTerminalArgs); + } } else { @@ -1061,6 +1200,12 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Called when the users pressed keyBindings while CommandPalette is open. + // - As of GH#8480, this is also bound to the TabRowControl's KeyUp event. + // That should only fire when focus is in the tab row, which is hard to + // do. Notably, that's possible: + // - When you have enough tabs to make the little scroll arrows appear, + // click one, then hit tab + // - When Narrator is in Scan mode (which is the a11y bug we're fixing here) // - This method is effectively an extract of TermControl::_KeyHandler and TermControl::_TryHandleKeyBinding. // Arguments: // - e: the KeyRoutedEventArgs containing info about the keystroke. @@ -1078,7 +1223,7 @@ namespace winrt::TerminalApp::implementation // message without vkey or scanCode if a user drags a tab. // The KeyChord constructor has a debug assertion ensuring that all KeyChord // either have a valid vkey/scanCode. This is important, because this prevents - // accidential insertion of invalid KeyChords into classes like ActionMap. + // accidental insertion of invalid KeyChords into classes like ActionMap. if (!vkey && !scanCode) { return; @@ -1638,7 +1783,30 @@ namespace winrt::TerminalApp::implementation std::shared_ptr newPane) { const auto focusedTab{ _GetFocusedTabImpl() }; - _SplitPane(*focusedTab, splitDirection, splitSize, newPane); + + // Clever hack for a crash in startup, with multiple sub-commands. Say + // you have the following commandline: + // + // wtd nt -p "elevated cmd" ; sp -p "elevated cmd" ; sp -p "Command Prompt" + // + // Where "elevated cmd" is an elevated profile. + // + // In that scenario, we won't dump off the commandline immediately to an + // elevated window, because it's got the final unelevated split in it. + // However, when we get to that command, there won't be a tab yet. So + // we'd crash right about here. + // + // Instead, let's just promote this first split to be a tab instead. + // Crash avoided, and we don't need to worry about inserting a new-tab + // command in at the start. + if (!focusedTab && _tabs.Size() == 0) + { + _CreateNewTabFromPane(newPane); + } + else + { + _SplitPane(*focusedTab, splitDirection, splitSize, newPane); + } } // Method Description: @@ -1750,11 +1918,11 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Gets the title of the currently focused terminal control. If there - // isn't a control selected for any reason, returns "Windows Terminal" + // isn't a control selected for any reason, returns "Terminal" // Arguments: // - // Return Value: - // - the title of the focused control if there is one, else "Windows Terminal" + // - the title of the focused control if there is one, else "Terminal" hstring TerminalPage::Title() { if (_settings.GlobalSettings().ShowTitleInTitlebar()) @@ -1772,7 +1940,7 @@ namespace winrt::TerminalApp::implementation CATCH_LOG(); } } - return { L"Windows Terminal" }; + return { L"Terminal" }; } // Method Description: @@ -1970,7 +2138,9 @@ namespace winrt::TerminalApp::implementation } } - bool warnMultiLine = _settings.GlobalSettings().WarnAboutMultiLinePaste(); + // If the requesting terminal is in bracketed paste mode, then we don't need to warn about a multi-line paste. + bool warnMultiLine = _settings.GlobalSettings().WarnAboutMultiLinePaste() && + !eventArgs.BracketedPasteEnabled(); if (warnMultiLine) { const auto isNewLineLambda = [](auto c) { return c == L'\n' || c == L'\r'; }; @@ -1986,13 +2156,6 @@ namespace winrt::TerminalApp::implementation { co_await winrt::resume_foreground(Dispatcher()); - if (warnMultiLine) - { - const auto focusedTab = _GetFocusedTabImpl(); - // Do not warn about multi line pasting if the current tab has bracketed paste enabled. - warnMultiLine = warnMultiLine && !focusedTab->GetActiveTerminalControl().BracketedPasteEnabled(); - } - // We have to initialize the dialog here to be able to change the text of the text block within it FindName(L"MultiLinePasteDialog").try_as(); ClipboardText().Text(text); @@ -2934,9 +3097,12 @@ namespace winrt::TerminalApp::implementation { NewTerminalArgs newTerminalArgs; newTerminalArgs.Commandline(connection.Commandline()); - const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) }; - const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) }; - + // GH #12370: We absolutely cannot allow a defterm connection to + // auto-elevate. Defterm doesn't work for elevated scenarios in the + // first place. If we try accepting the connection, the spawning an + // elevated version of the Terminal with that profile... that's a + // recipe for disaster. We won't ever open up a tab in this window. + newTerminalArgs.Elevate(false); _CreateNewTabFromPane(_MakePane(newTerminalArgs, false, connection)); // Request a summon of this window to the foreground diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 91c76fd1040..2e48d9db883 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -64,6 +64,8 @@ namespace winrt::TerminalApp::implementation void Create(); bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); std::optional LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout(); @@ -213,12 +215,15 @@ namespace winrt::TerminalApp::implementation std::shared_ptr _windowIdToast{ nullptr }; std::shared_ptr _windowRenameFailedToast{ nullptr }; + winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); + void _ShowAboutDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowQuitDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseWarningDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseReadOnlyDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowMultiLinePasteWarningDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowLargePasteWarningDialog(); + void _DialogCloseClick(const IInspectable& sender, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& eventArgs); void _CreateNewTabFlyout(); void _OpenNewTabDropdown(); @@ -226,9 +231,9 @@ namespace winrt::TerminalApp::implementation void _CreateNewTabFromPane(std::shared_ptr pane); winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings); - winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); + winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); - void _OpenNewTerminal(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); + void _OpenNewTerminalViaDropdown(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs); bool _displayingCloseDialog{ false }; void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 979483d0ad8..b726fcf23ef 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -14,6 +14,8 @@ namespace TerminalApp interface IDialogPresenter { Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); + Boolean CanShowDialog(); + void DismissDialog(); }; [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index e4be0e54a06..5ca8ed25858 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -21,9 +21,11 @@ + Grid.Row="0" + KeyUp="_KeyDownHandler" /> - + @@ -94,21 +97,25 @@ @@ -143,6 +152,7 @@ diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 14ba0743d12..bacba947c5f 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1178,7 +1178,7 @@ namespace winrt::TerminalApp::implementation // "Color..." Controls::MenuFlyoutItem chooseColorMenuItem; Controls::FontIcon colorPickSymbol; - colorPickSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + colorPickSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); colorPickSymbol.Glyph(L"\xE790"); chooseColorMenuItem.Click([weakThis](auto&&, auto&&) { @@ -1209,7 +1209,7 @@ namespace winrt::TerminalApp::implementation { // "Rename Tab" Controls::FontIcon renameTabSymbol; - renameTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + renameTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); renameTabSymbol.Glyph(L"\xE8AC"); // Rename renameTabMenuItem.Click([weakThis](auto&&, auto&&) { @@ -1226,7 +1226,7 @@ namespace winrt::TerminalApp::implementation { // "Duplicate Tab" Controls::FontIcon duplicateTabSymbol; - duplicateTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + duplicateTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); duplicateTabSymbol.Glyph(L"\xF5ED"); duplicateTabMenuItem.Click([weakThis](auto&&, auto&&) { @@ -1243,7 +1243,7 @@ namespace winrt::TerminalApp::implementation { // "Split Tab" Controls::FontIcon splitTabSymbol; - splitTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + splitTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); splitTabSymbol.Glyph(L"\xF246"); // ViewDashboard splitTabMenuItem.Click([weakThis](auto&&, auto&&) { @@ -1260,7 +1260,7 @@ namespace winrt::TerminalApp::implementation { // "Split Tab" Controls::FontIcon exportTabSymbol; - exportTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + exportTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); exportTabSymbol.Glyph(L"\xE74E"); // Save exportTabMenuItem.Click([weakThis](auto&&, auto&&) { @@ -1415,7 +1415,7 @@ namespace winrt::TerminalApp::implementation selectedTabBrush.Color(color); // currently if a tab has a custom color, a deselected state is - // signified by using the same color with a bit ot transparency + // signified by using the same color with a bit of transparency auto deselectedTabColor = color; deselectedTabColor.A = 64; deselectedTabBrush.Color(deselectedTabColor); diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj index a5d86577692..ef10edfa434 100644 --- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj @@ -89,13 +89,13 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/cascadia/TerminalApp/packages.config b/src/cascadia/TerminalApp/packages.config index aece6932a3b..62c921a4fac 100644 --- a/src/cascadia/TerminalApp/packages.config +++ b/src/cascadia/TerminalApp/packages.config @@ -1,7 +1,8 @@ + - + diff --git a/src/cascadia/TerminalConnection/packages.config b/src/cascadia/TerminalConnection/packages.config index 5691dc1ad15..18ba2c234dc 100644 --- a/src/cascadia/TerminalConnection/packages.config +++ b/src/cascadia/TerminalConnection/packages.config @@ -2,5 +2,5 @@ - + diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index f8e0d436830..6bca14aa18c 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -478,6 +478,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (_renderEngine) { _renderEngine->EnableTransparentBackground(_isBackgroundTransparent()); + _renderer->NotifyPaintFrame(); } auto eventArgs = winrt::make_self(newOpacity); @@ -916,16 +917,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - const auto dpi = (float)(scale * USER_DEFAULT_SCREEN_DPI); - const auto actualFontOldSize = _actualFont.GetSize(); auto lock = _terminal->LockForWriting(); _compositionScale = scale; - _renderer->TriggerFontChange(::base::saturated_cast(dpi), - _desiredFont, - _actualFont); + // _updateFont relies on the new _compositionScale set above + _updateFont(); const auto actualFontNewSize = _actualFont.GetSize(); if (actualFontNewSize != actualFontOldSize) @@ -1315,12 +1313,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation ::Search search(*GetUiaData(), text.c_str(), direction, sensitivity); auto lock = _terminal->LockForWriting(); - if (search.FindNext()) + const bool foundMatch{ search.FindNext() }; + if (foundMatch) { _terminal->SetBlockSelection(false); search.Select(); _renderer->TriggerSelection(); } + + // Raise a FoundMatch event, which the control will use to notify + // narrator if there was any results in the buffer + auto foundResults = winrt::make_self(foundMatch); + _FoundMatchHandlers(*this, *foundResults); } // Method Description: @@ -1718,9 +1722,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _settings->HasUnfocusedAppearance(); } - void ControlCore::AdjustOpacity(const int32_t& opacity, const bool& relative) + void ControlCore::AdjustOpacity(const double opacityAdjust, const bool relative) { - const double opacityAdjust = static_cast(opacity) / 100.0; if (relative) { AdjustOpacity(opacityAdjust); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 7c9a668c3a5..2a86c2585cd 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -166,7 +166,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation static bool IsVintageOpacityAvailable() noexcept; - void AdjustOpacity(const int32_t& opacity, const bool& relative); + void AdjustOpacity(const double opacity, const bool relative); RUNTIME_SETTING(double, Opacity, _settings->Opacity()); RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic()); @@ -191,6 +191,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs); TYPED_EVENT(TransparencyChanged, IInspectable, Control::TransparencyChangedEventArgs); TYPED_EVENT(ReceivedOutput, IInspectable, IInspectable); + TYPED_EVENT(FoundMatch, IInspectable, Control::FoundResultsArgs); // clang-format on private: diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index c23461412e2..a8b3e14234d 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -54,6 +54,7 @@ namespace Microsoft.Terminal.Control String FontFaceName { get; }; UInt16 FontWeight { get; }; Double Opacity { get; }; + Boolean UseAcrylic { get; }; Boolean TrySendKeyEvent(Int16 vkey, Int16 scanCode, @@ -98,7 +99,7 @@ namespace Microsoft.Terminal.Control String ReadEntireBuffer(); - void AdjustOpacity(Int32 Opacity, Boolean relative); + void AdjustOpacity(Double Opacity, Boolean relative); event FontSizeChangedEventArgs FontSizeChanged; @@ -118,6 +119,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler RaiseNotice; event Windows.Foundation.TypedEventHandler TransparencyChanged; event Windows.Foundation.TypedEventHandler ReceivedOutput; + event Windows.Foundation.TypedEventHandler FoundMatch; }; } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 70c34674a8f..de992e1b379 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -174,7 +174,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // clipboardDataHandler. This is called when the clipboard data is // loaded. auto clipboardDataHandler = std::bind(&ControlInteractivity::_sendPastedTextToConnection, this, std::placeholders::_1); - auto pasteArgs = winrt::make_self(clipboardDataHandler); + auto pasteArgs = winrt::make_self(clipboardDataHandler, _core->BracketedPasteEnabled()); // send paste event up to TermApp _PasteFromClipboardHandlers(*this, *pasteArgs); diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index f4b4d9bc786..28417c4eb33 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -11,3 +11,4 @@ #include "ScrollPositionChangedArgs.g.cpp" #include "RendererWarningArgs.g.cpp" #include "TransparencyChangedEventArgs.g.cpp" +#include "FoundResultsArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 27dc2c6473f..0b9e80b0882 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -11,6 +11,7 @@ #include "ScrollPositionChangedArgs.g.h" #include "RendererWarningArgs.g.h" #include "TransparencyChangedEventArgs.g.h" +#include "FoundResultsArgs.g.h" namespace winrt::Microsoft::Terminal::Control::implementation { @@ -53,14 +54,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation struct PasteFromClipboardEventArgs : public PasteFromClipboardEventArgsT { public: - PasteFromClipboardEventArgs(std::function clipboardDataHandler) : - m_clipboardDataHandler(clipboardDataHandler) {} + PasteFromClipboardEventArgs(std::function clipboardDataHandler, bool bracketedPasteEnabled) : + m_clipboardDataHandler(clipboardDataHandler), + _BracketedPasteEnabled{ bracketedPasteEnabled } {} void HandleClipboardData(hstring value) { m_clipboardDataHandler(value); }; + WINRT_PROPERTY(bool, BracketedPasteEnabled, false); + private: std::function m_clipboardDataHandler; }; @@ -130,4 +134,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(double, Opacity); }; + + struct FoundResultsArgs : public FoundResultsArgsT + { + public: + FoundResultsArgs(const bool foundMatch) : + _FoundMatch(foundMatch) + { + } + + WINRT_PROPERTY(bool, FoundMatch); + }; } diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index dae093f51f8..377c6535cf4 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -30,6 +30,7 @@ namespace Microsoft.Terminal.Control runtimeclass PasteFromClipboardEventArgs { void HandleClipboardData(String data); + Boolean BracketedPasteEnabled { get; }; } runtimeclass OpenHyperlinkEventArgs @@ -67,4 +68,10 @@ namespace Microsoft.Terminal.Control { Double Opacity { get; }; } + + + runtimeclass FoundResultsArgs + { + Boolean FoundMatch { get; }; + } } diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw index f2fde4b9dc5..4c17742c1ce 100644 --- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw @@ -200,4 +200,12 @@ Please either install the missing font or choose another one. Read-only mode is enabled. + + Results found + Announced to a screen reader when the user searches for some text and there are matches for that text in the terminal. + + + No results found + Announced to a screen reader when the user searches for some text and there are no matches for that text in the terminal. + diff --git a/src/cascadia/TerminalControl/SearchBoxControl.xaml b/src/cascadia/TerminalControl/SearchBoxControl.xaml index a2fbf6e8969..e61bfbd6453 100644 --- a/src/cascadia/TerminalControl/SearchBoxControl.xaml +++ b/src/cascadia/TerminalControl/SearchBoxControl.xaml @@ -255,7 +255,7 @@ Click="GoBackwardClicked" IsChecked="True" Style="{StaticResource ToggleButtonStyle}"> - @@ -263,7 +263,7 @@ x:Uid="SearchBox_SearchForwards" Click="GoForwardClicked" Style="{StaticResource ToggleButtonStyle}"> - @@ -278,7 +278,7 @@ x:Uid="SearchBox_Close" Click="CloseClick" Style="{ThemeResource ButtonStyle}"> - diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index aa6f3567e80..b6bb9e6b98b 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -82,6 +82,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged }); _core.RaiseNotice({ this, &TermControl::_coreRaisedNotice }); _core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged }); + _core.FoundMatch({ this, &TermControl::_coreFoundMatch }); _interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); _interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); @@ -451,7 +452,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation { auto settings{ _core.Settings() }; auto bgColor = til::color{ _core.FocusedAppearance().DefaultBackground() }; - if (settings.UseAcrylic()) + // GH#11743: Make sure to use the Core's current UseAcrylic value, not + // the one from the settings. The Core's runtime UseAcrylic may have + // changed from what was in the original settings. + if (_core.UseAcrylic()) { // See if we've already got an acrylic background brush // to avoid the flicker when setting up a new one @@ -536,12 +540,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_changeBackgroundOpacity() { const auto opacity{ _core.Opacity() }; + const auto useAcrylic{ _core.UseAcrylic() }; + + // GH#11743, #11619: If we're changing whether or not acrylic is used, + // then just entirely reinitialize the brush. The primary way that this + // happens is on Windows 10, where we need to enable acrylic when the + // user asks for <100% opacity. Even when we remove this Windows 10 + // fallback, we may still need this for something like changing if + // acrylic is enabled at runtime (GH#2531) if (auto acrylic = RootGrid().Background().try_as()) { + if (!useAcrylic) + { + _InitializeBackgroundBrush(); + return; + } acrylic.TintOpacity(opacity); } else if (auto solidColor = RootGrid().Background().try_as()) { + if (useAcrylic) + { + _InitializeBackgroundBrush(); + return; + } solidColor.Opacity(opacity); } } @@ -914,7 +936,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // message without vkey or scanCode if a user drags a tab. // The KeyChord constructor has a debug assertion ensuring that all KeyChord // either have a valid vkey/scanCode. This is important, because this prevents - // accidential insertion of invalid KeyChords into classes like ActionMap. + // accidental insertion of invalid KeyChords into classes like ActionMap. if (!vkey && !scanCode) { e.Handled(true); @@ -2713,7 +2735,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core.ColorScheme(scheme); } - void TermControl::AdjustOpacity(const int32_t& opacity, const bool& relative) + void TermControl::AdjustOpacity(const double opacity, const bool relative) { _core.AdjustOpacity(opacity, relative); } @@ -2726,4 +2748,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return _core.Opacity(); } + + // Method Description: + // - Called when the core raises a FoundMatch event. That's done in response + // to us starting a search query with ControlCore::Search. + // - The args will tell us if there were or were not any results for that + // particular search. We'll use that to control what to announce to + // Narrator. When we have more elaborate search information to report, we + // may want to report that here. (see GH #3920) + // Arguments: + // - args: contains information about the results that were or were not found. + // Return Value: + // - + void TermControl::_coreFoundMatch(const IInspectable& /*sender*/, const Control::FoundResultsArgs& args) + { + if (auto automationPeer{ Automation::Peers::FrameworkElementAutomationPeer::FromElement(*this) }) + { + automationPeer.RaiseNotificationEvent( + Automation::Peers::AutomationNotificationKind::ActionCompleted, + Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent, + args.FoundMatch() ? RS_(L"SearchBox_MatchesAvailable") : RS_(L"SearchBox_NoMatches"), // what to announce if results were found + L"SearchBoxResultAnnouncement" /* unique name for this group of notifications */); + } + } + } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 4c360913b4c..95aa82cdb92 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -110,7 +110,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept; - void AdjustOpacity(const int32_t& opacity, const bool& relative); + void AdjustOpacity(const double opacity, const bool relative); // -------------------------------- WinRT Events --------------------------------- // clang-format off @@ -275,6 +275,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args); void _coreWarningBell(const IInspectable& sender, const IInspectable& args); + void _coreFoundMatch(const IInspectable& sender, const Control::FoundResultsArgs& args); }; } diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 4862c408b3d..307dd965530 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -73,7 +73,7 @@ namespace Microsoft.Terminal.Control String ReadEntireBuffer(); - void AdjustOpacity(Int32 Opacity, Boolean relative); + void AdjustOpacity(Double Opacity, Boolean relative); // You'd think this should just be "Opacity", but UIElement already // defines an "Opacity", which we're actually not setting at all. We're diff --git a/src/cascadia/TerminalControl/packages.config b/src/cascadia/TerminalControl/packages.config index 2d79f9f5adb..64e09e4c8e5 100644 --- a/src/cascadia/TerminalControl/packages.config +++ b/src/cascadia/TerminalControl/packages.config @@ -1,5 +1,5 @@ - + diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index 4e463c16664..ebff6fd2072 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -19,54 +19,53 @@ namespace Microsoft::Terminal::Core ITerminalApi& operator=(const ITerminalApi&) = default; ITerminalApi& operator=(ITerminalApi&&) = default; - virtual bool PrintString(std::wstring_view string) noexcept = 0; - virtual bool ExecuteChar(wchar_t wch) noexcept = 0; + virtual void PrintString(std::wstring_view string) = 0; - virtual TextAttribute GetTextAttributes() const noexcept = 0; - virtual void SetTextAttributes(const TextAttribute& attrs) noexcept = 0; + virtual TextAttribute GetTextAttributes() const = 0; + virtual void SetTextAttributes(const TextAttribute& attrs) = 0; - virtual Microsoft::Console::Types::Viewport GetBufferSize() noexcept = 0; - virtual bool SetCursorPosition(short x, short y) noexcept = 0; - virtual COORD GetCursorPosition() noexcept = 0; - virtual bool SetCursorVisibility(const bool visible) noexcept = 0; - virtual bool CursorLineFeed(const bool withReturn) noexcept = 0; - virtual bool EnableCursorBlinking(const bool enable) noexcept = 0; + virtual Microsoft::Console::Types::Viewport GetBufferSize() = 0; + virtual void SetCursorPosition(short x, short y) = 0; + virtual COORD GetCursorPosition() = 0; + virtual void SetCursorVisibility(const bool visible) = 0; + virtual void CursorLineFeed(const bool withReturn) = 0; + virtual void EnableCursorBlinking(const bool enable) = 0; - virtual bool DeleteCharacter(const size_t count) noexcept = 0; - virtual bool InsertCharacter(const size_t count) noexcept = 0; - virtual bool EraseCharacters(const size_t numChars) noexcept = 0; - virtual bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept = 0; - virtual bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept = 0; + virtual void DeleteCharacter(const size_t count) = 0; + virtual void InsertCharacter(const size_t count) = 0; + virtual void EraseCharacters(const size_t numChars) = 0; + virtual bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) = 0; + virtual bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) = 0; - virtual bool WarningBell() noexcept = 0; - virtual bool SetWindowTitle(std::wstring_view title) noexcept = 0; + virtual void WarningBell() = 0; + virtual void SetWindowTitle(std::wstring_view title) = 0; - virtual COLORREF GetColorTableEntry(const size_t tableIndex) const noexcept = 0; - virtual bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept = 0; - virtual void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) noexcept = 0; + virtual COLORREF GetColorTableEntry(const size_t tableIndex) const = 0; + virtual void SetColorTableEntry(const size_t tableIndex, const COLORREF color) = 0; + virtual void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) = 0; - virtual bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept = 0; + virtual void SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) = 0; - virtual bool SetInputMode(const ::Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) noexcept = 0; - virtual bool SetRenderMode(const ::Microsoft::Console::Render::RenderSettings::Mode mode, const bool enabled) noexcept = 0; + virtual void SetInputMode(const ::Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) = 0; + virtual void SetRenderMode(const ::Microsoft::Console::Render::RenderSettings::Mode mode, const bool enabled) = 0; - virtual bool EnableXtermBracketedPasteMode(const bool enabled) noexcept = 0; + virtual void EnableXtermBracketedPasteMode(const bool enabled) = 0; virtual bool IsXtermBracketedPasteModeEnabled() const = 0; virtual bool IsVtInputEnabled() const = 0; - virtual bool CopyToClipboard(std::wstring_view content) noexcept = 0; + virtual void CopyToClipboard(std::wstring_view content) = 0; - virtual bool AddHyperlink(std::wstring_view uri, std::wstring_view params) noexcept = 0; - virtual bool EndHyperlink() noexcept = 0; + virtual void AddHyperlink(std::wstring_view uri, std::wstring_view params) = 0; + virtual void EndHyperlink() = 0; - virtual bool SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) noexcept = 0; + virtual void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) = 0; - virtual bool SetWorkingDirectory(std::wstring_view uri) noexcept = 0; - virtual std::wstring_view GetWorkingDirectory() noexcept = 0; + virtual void SetWorkingDirectory(std::wstring_view uri) = 0; + virtual std::wstring_view GetWorkingDirectory() = 0; - virtual bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept = 0; - virtual bool PopGraphicsRendition() noexcept = 0; + virtual void PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) = 0; + virtual void PopGraphicsRendition() = 0; protected: ITerminalApi() = default; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index bcb6fa07b3f..d03bcbb3abb 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -431,7 +431,21 @@ void Terminal::Write(std::wstring_view stringView) { auto lock = LockForWriting(); + auto& cursor = _buffer->GetCursor(); + const til::point cursorPosBefore{ cursor.GetPosition() }; + _stateMachine->ProcessString(stringView); + + const til::point cursorPosAfter{ cursor.GetPosition() }; + + // Firing the CursorPositionChanged event is very expensive so we try not to + // do that when the cursor does not need to be redrawn. We don't do this + // inside _AdjustCursorPosition, only once we're done writing the whole run + // of output. + if (cursorPosBefore != cursorPosAfter) + { + _NotifyTerminalCursorPositionChanged(); + } } void Terminal::WritePastedText(std::wstring_view stringView) @@ -1093,13 +1107,6 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition) COORD delta{ 0, gsl::narrow_cast(-rowsPushedOffTopOfBuffer) }; _buffer->GetRenderTarget().TriggerScroll(&delta); } - - // Firing the CursorPositionChanged event is very expensive so we try not to do that when - // the cursor does not need to be redrawn. - if (!cursor.IsDeferDrawing()) - { - _NotifyTerminalCursorPositionChanged(); - } } void Terminal::UserScrollViewport(const int viewTop) diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 9fbc4700718..5a6d328ecf8 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -95,47 +95,46 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region ITerminalApi // These methods are defined in TerminalApi.cpp - bool PrintString(std::wstring_view stringView) noexcept override; - bool ExecuteChar(wchar_t wch) noexcept override; - TextAttribute GetTextAttributes() const noexcept override; - void SetTextAttributes(const TextAttribute& attrs) noexcept override; - Microsoft::Console::Types::Viewport GetBufferSize() noexcept override; - bool SetCursorPosition(short x, short y) noexcept override; - COORD GetCursorPosition() noexcept override; - bool SetCursorVisibility(const bool visible) noexcept override; - bool EnableCursorBlinking(const bool enable) noexcept override; - bool CursorLineFeed(const bool withReturn) noexcept override; - bool DeleteCharacter(const size_t count) noexcept override; - bool InsertCharacter(const size_t count) noexcept override; - bool EraseCharacters(const size_t numChars) noexcept override; - bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; - bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; - bool WarningBell() noexcept override; - bool SetWindowTitle(std::wstring_view title) noexcept override; - COLORREF GetColorTableEntry(const size_t tableIndex) const noexcept override; - bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept override; - void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) noexcept override; - bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override; - - bool SetInputMode(const ::Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) noexcept override; - bool SetRenderMode(const ::Microsoft::Console::Render::RenderSettings::Mode mode, const bool enabled) noexcept override; - - bool EnableXtermBracketedPasteMode(const bool enabled) noexcept override; - bool IsXtermBracketedPasteModeEnabled() const noexcept override; - - bool IsVtInputEnabled() const noexcept override; - - bool CopyToClipboard(std::wstring_view content) noexcept override; - - bool AddHyperlink(std::wstring_view uri, std::wstring_view params) noexcept override; - bool EndHyperlink() noexcept override; - - bool SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) noexcept override; - bool SetWorkingDirectory(std::wstring_view uri) noexcept override; - std::wstring_view GetWorkingDirectory() noexcept override; - - bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override; - bool PopGraphicsRendition() noexcept override; + void PrintString(std::wstring_view stringView) override; + TextAttribute GetTextAttributes() const override; + void SetTextAttributes(const TextAttribute& attrs) override; + Microsoft::Console::Types::Viewport GetBufferSize() override; + void SetCursorPosition(short x, short y) override; + COORD GetCursorPosition() override; + void SetCursorVisibility(const bool visible) override; + void EnableCursorBlinking(const bool enable) override; + void CursorLineFeed(const bool withReturn) override; + void DeleteCharacter(const size_t count) override; + void InsertCharacter(const size_t count) override; + void EraseCharacters(const size_t numChars) override; + bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; + bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; + void WarningBell() override; + void SetWindowTitle(std::wstring_view title) override; + COLORREF GetColorTableEntry(const size_t tableIndex) const override; + void SetColorTableEntry(const size_t tableIndex, const COLORREF color) override; + void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) override; + void SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) override; + + void SetInputMode(const ::Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) override; + void SetRenderMode(const ::Microsoft::Console::Render::RenderSettings::Mode mode, const bool enabled) override; + + void EnableXtermBracketedPasteMode(const bool enabled) override; + bool IsXtermBracketedPasteModeEnabled() const override; + + bool IsVtInputEnabled() const override; + + void CopyToClipboard(std::wstring_view content) override; + + void AddHyperlink(std::wstring_view uri, std::wstring_view params) override; + void EndHyperlink() override; + + void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override; + void SetWorkingDirectory(std::wstring_view uri) override; + std::wstring_view GetWorkingDirectory() override; + + void PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) override; + void PopGraphicsRendition() override; #pragma endregion diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index e2e5eb35253..6e89bce460a 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -11,39 +11,27 @@ using namespace Microsoft::Console::Types; using namespace Microsoft::Console::VirtualTerminal; // Print puts the text in the buffer and moves the cursor -bool Terminal::PrintString(std::wstring_view stringView) noexcept -try +void Terminal::PrintString(std::wstring_view stringView) { _WriteBuffer(stringView); - return true; -} -CATCH_RETURN_FALSE() - -bool Terminal::ExecuteChar(wchar_t wch) noexcept -try -{ - _WriteBuffer({ &wch, 1 }); - return true; } -CATCH_RETURN_FALSE() -TextAttribute Terminal::GetTextAttributes() const noexcept +TextAttribute Terminal::GetTextAttributes() const { return _buffer->GetCurrentAttributes(); } -void Terminal::SetTextAttributes(const TextAttribute& attrs) noexcept +void Terminal::SetTextAttributes(const TextAttribute& attrs) { _buffer->SetCurrentAttributes(attrs); } -Viewport Terminal::GetBufferSize() noexcept +Viewport Terminal::GetBufferSize() { return _buffer->GetSize(); } -bool Terminal::SetCursorPosition(short x, short y) noexcept -try +void Terminal::SetCursorPosition(short x, short y) { const auto viewport = _GetMutableViewport(); const auto viewOrigin = viewport.Origin(); @@ -52,12 +40,9 @@ try COORD newPos{ absoluteX, absoluteY }; viewport.Clamp(newPos); _buffer->GetCursor().SetPosition(newPos); - - return true; } -CATCH_RETURN_FALSE() -COORD Terminal::GetCursorPosition() noexcept +COORD Terminal::GetCursorPosition() { const auto absoluteCursorPos = _buffer->GetCursor().GetPosition(); const auto viewport = _GetMutableViewport(); @@ -75,9 +60,8 @@ COORD Terminal::GetCursorPosition() noexcept // Arguments: // - withReturn, set to true if a carriage return should be performed as well. // Return value: -// - true if succeeded, false otherwise -bool Terminal::CursorLineFeed(const bool withReturn) noexcept -try +// - +void Terminal::CursorLineFeed(const bool withReturn) { auto cursorPos = _buffer->GetCursor().GetPosition(); @@ -91,10 +75,7 @@ try cursorPos.X = 0; } _AdjustCursorPosition(cursorPos); - - return true; } -CATCH_RETURN_FALSE() // Method Description: // - deletes count characters starting from the cursor's current position @@ -105,24 +86,17 @@ CATCH_RETURN_FALSE() // Arguments: // - count, the number of characters to delete // Return value: -// - true if succeeded, false otherwise -bool Terminal::DeleteCharacter(const size_t count) noexcept -try +// - +void Terminal::DeleteCharacter(const size_t count) { SHORT dist; - if (!SUCCEEDED(SizeTToShort(count, &dist))) - { - return false; - } + THROW_IF_FAILED(SizeTToShort(count, &dist)); const auto cursorPos = _buffer->GetCursor().GetPosition(); const auto copyToPos = cursorPos; const COORD copyFromPos{ cursorPos.X + dist, cursorPos.Y }; const auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X; SHORT width; - if (!SUCCEEDED(UIntToShort(sourceWidth, &width))) - { - return false; - } + THROW_IF_FAILED(UIntToShort(sourceWidth, &width)); // Get a rectangle of the source auto source = Viewport::FromDimensions(copyFromPos, width, 1); @@ -140,10 +114,7 @@ try const auto data = OutputCell(*(_buffer->GetCellDataAt(sourcePos))); _buffer->Write(OutputCellIterator({ &data, 1 }), targetPos); } while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection)); - - return true; } -CATCH_RETURN_FALSE() // Method Description: // - Inserts count spaces starting from the cursor's current position, moving over the existing text @@ -153,28 +124,21 @@ CATCH_RETURN_FALSE() // Arguments: // - count, the number of spaces to insert // Return value: -// - true if succeeded, false otherwise -bool Terminal::InsertCharacter(const size_t count) noexcept -try +// - +void Terminal::InsertCharacter(const size_t count) { // NOTE: the code below is _extremely_ similar to DeleteCharacter // We will want to use this same logic and implement a helper function instead // that does the 'move a region from here to there' operation // TODO: Github issue #2163 SHORT dist; - if (!SUCCEEDED(SizeTToShort(count, &dist))) - { - return false; - } + THROW_IF_FAILED(SizeTToShort(count, &dist)); const auto cursorPos = _buffer->GetCursor().GetPosition(); const auto copyFromPos = cursorPos; const COORD copyToPos{ cursorPos.X + dist, cursorPos.Y }; const auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X; SHORT width; - if (!SUCCEEDED(UIntToShort(sourceWidth, &width))) - { - return false; - } + THROW_IF_FAILED(UIntToShort(sourceWidth, &width)); // Get a rectangle of the source auto source = Viewport::FromDimensions(copyFromPos, width, 1); @@ -195,13 +159,9 @@ try } while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection)); const auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), dist); _buffer->Write(eraseIter, cursorPos); - - return true; } -CATCH_RETURN_FALSE() -bool Terminal::EraseCharacters(const size_t numChars) noexcept -try +void Terminal::EraseCharacters(const size_t numChars) { const auto absoluteCursorPos = _buffer->GetCursor().GetPosition(); const auto viewport = _GetMutableViewport(); @@ -209,9 +169,7 @@ try const short fillLimit = std::min(static_cast(numChars), distanceToRight); const auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), fillLimit); _buffer->Write(eraseIter, absoluteCursorPos); - return true; } -CATCH_RETURN_FALSE() // Method description: // - erases a line of text, either from @@ -223,8 +181,7 @@ CATCH_RETURN_FALSE() // - the erase type // Return value: // - true if succeeded, false otherwise -bool Terminal::EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept -try +bool Terminal::EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) { const auto cursorPos = _buffer->GetCursor().GetPosition(); const auto viewport = _GetMutableViewport(); @@ -257,7 +214,6 @@ try _buffer->Write(eraseIter, startPos, false); return true; } -CATCH_RETURN_FALSE() // Method description: // - erases text in the buffer in two ways depending on erase type @@ -267,8 +223,7 @@ CATCH_RETURN_FALSE() // - the erase type // Return Value: // - true if succeeded, false otherwise -bool Terminal::EraseInDisplay(const DispatchTypes::EraseType eraseType) noexcept -try +bool Terminal::EraseInDisplay(const DispatchTypes::EraseType eraseType) { // Store the relative cursor position so we can restore it later after we move the viewport const auto cursorPos = _buffer->GetCursor().GetPosition(); @@ -341,27 +296,20 @@ try return true; } -CATCH_RETURN_FALSE() -bool Terminal::WarningBell() noexcept -try +void Terminal::WarningBell() { _pfnWarningBell(); - return true; } -CATCH_RETURN_FALSE() -bool Terminal::SetWindowTitle(std::wstring_view title) noexcept -try +void Terminal::SetWindowTitle(std::wstring_view title) { if (!_suppressApplicationTitle) { _title.emplace(title); _pfnTitleChanged(_title.value()); } - return true; } -CATCH_RETURN_FALSE() // Method Description: // - Retrieves the value in the colortable at the specified index. @@ -369,15 +317,10 @@ CATCH_RETURN_FALSE() // - tableIndex: the index of the color table to retrieve. // Return Value: // - the COLORREF value for the color at that index in the table. -COLORREF Terminal::GetColorTableEntry(const size_t tableIndex) const noexcept -try +COLORREF Terminal::GetColorTableEntry(const size_t tableIndex) const { return _renderSettings.GetColorTableEntry(tableIndex); } -catch (...) -{ - return INVALID_COLOR; -} // Method Description: // - Updates the value in the colortable at index tableIndex to the new color @@ -386,9 +329,8 @@ catch (...) // - tableIndex: the index of the color table to update. // - color: the new COLORREF to use as that color table value. // Return Value: -// - true iff we successfully updated the color table entry. -bool Terminal::SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept -try +// - +void Terminal::SetColorTableEntry(const size_t tableIndex, const COLORREF color) { _renderSettings.SetColorTableEntry(tableIndex, color); @@ -399,9 +341,7 @@ try // Repaint everything - the colors might have changed _buffer->GetRenderTarget().TriggerRedrawAll(); - return true; } -CATCH_RETURN_FALSE() // Method Description: // - Sets the position in the color table for the given color alias. @@ -410,7 +350,7 @@ CATCH_RETURN_FALSE() // - tableIndex: the new position of the alias in the color table. // Return Value: // - -void Terminal::SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) noexcept +void Terminal::SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) { _renderSettings.SetColorAliasIndex(alias, tableIndex); } @@ -420,8 +360,8 @@ void Terminal::SetColorAliasIndex(const ColorAlias alias, const size_t tableInde // Arguments: // - cursorStyle: the style to be set for the cursor // Return Value: -// - true iff we successfully set the cursor style -bool Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) noexcept +// - +void Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) { CursorType finalCursorType = CursorType::Legacy; bool shouldBlink = false; @@ -459,58 +399,48 @@ bool Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) noex default: // Invalid argument should be ignored. - return true; + return; } _buffer->GetCursor().SetType(finalCursorType); _buffer->GetCursor().SetBlinkingAllowed(shouldBlink); - - return true; } -bool Terminal::SetInputMode(const TerminalInput::Mode mode, const bool enabled) noexcept -try +void Terminal::SetInputMode(const TerminalInput::Mode mode, const bool enabled) { _terminalInput->SetInputMode(mode, enabled); - return true; } -CATCH_RETURN_FALSE() -bool Terminal::SetRenderMode(const RenderSettings::Mode mode, const bool enabled) noexcept -try +void Terminal::SetRenderMode(const RenderSettings::Mode mode, const bool enabled) { _renderSettings.SetRenderMode(mode, enabled); // Repaint everything - the colors will have changed _buffer->GetRenderTarget().TriggerRedrawAll(); - return true; } -CATCH_RETURN_FALSE() -bool Terminal::EnableXtermBracketedPasteMode(const bool enabled) noexcept +void Terminal::EnableXtermBracketedPasteMode(const bool enabled) { _bracketedPasteMode = enabled; - return true; } -bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept +bool Terminal::IsXtermBracketedPasteModeEnabled() const { return _bracketedPasteMode; } -bool Terminal::IsVtInputEnabled() const noexcept +bool Terminal::IsVtInputEnabled() const { // We should never be getting this call in Terminal. FAIL_FAST(); } -bool Terminal::SetCursorVisibility(const bool visible) noexcept +void Terminal::SetCursorVisibility(const bool visible) { _buffer->GetCursor().SetIsVisible(visible); - return true; } -bool Terminal::EnableCursorBlinking(const bool enable) noexcept +void Terminal::EnableCursorBlinking(const bool enable) { _buffer->GetCursor().SetBlinkingAllowed(enable); @@ -521,17 +451,12 @@ bool Terminal::EnableCursorBlinking(const bool enable) noexcept // cursor visibility property controls whether the user can see it or not. // (Yes, the cursor can be On and NOT Visible) _buffer->GetCursor().SetIsOn(true); - return true; } -bool Terminal::CopyToClipboard(std::wstring_view content) noexcept -try +void Terminal::CopyToClipboard(std::wstring_view content) { _pfnCopyToClipboard(content); - - return true; } -CATCH_RETURN_FALSE() // Method Description: // - Updates the buffer's current text attributes to start a hyperlink @@ -539,27 +464,25 @@ CATCH_RETURN_FALSE() // - The hyperlink URI // - The customID provided (if there was one) // Return Value: -// - true -bool Terminal::AddHyperlink(std::wstring_view uri, std::wstring_view params) noexcept +// - +void Terminal::AddHyperlink(std::wstring_view uri, std::wstring_view params) { auto attr = _buffer->GetCurrentAttributes(); const auto id = _buffer->GetHyperlinkId(uri, params); attr.SetHyperlinkId(id); _buffer->SetCurrentAttributes(attr); _buffer->AddHyperlinkToMap(uri, id); - return true; } // Method Description: // - Updates the buffer's current text attributes to end a hyperlink // Return Value: -// - true -bool Terminal::EndHyperlink() noexcept +// - +void Terminal::EndHyperlink() { auto attr = _buffer->GetCurrentAttributes(); attr.SetHyperlinkId(0); _buffer->SetCurrentAttributes(attr); - return true; } // Method Description: @@ -568,8 +491,8 @@ bool Terminal::EndHyperlink() noexcept // - state: indicates the progress state // - progress: indicates the progress value // Return Value: -// - true -bool Terminal::SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) noexcept +// - +void Terminal::SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) { _taskbarState = static_cast(state); @@ -610,16 +533,14 @@ bool Terminal::SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::D { _pfnTaskbarProgressChanged(); } - return true; } -bool Terminal::SetWorkingDirectory(std::wstring_view uri) noexcept +void Terminal::SetWorkingDirectory(std::wstring_view uri) { _workingDirectory = uri; - return true; } -std::wstring_view Terminal::GetWorkingDirectory() noexcept +std::wstring_view Terminal::GetWorkingDirectory() { return _workingDirectory; } @@ -631,11 +552,10 @@ std::wstring_view Terminal::GetWorkingDirectory() noexcept // should be saved. Only a small subset of GraphicsOptions are actually supported; // others are ignored. If no options are specified, all attributes are stored. // Return Value: -// - true -bool Terminal::PushGraphicsRendition(const VTParameters options) noexcept +// - +void Terminal::PushGraphicsRendition(const VTParameters options) { _sgrStack.Push(_buffer->GetCurrentAttributes(), options); - return true; } // Method Description: @@ -644,10 +564,9 @@ bool Terminal::PushGraphicsRendition(const VTParameters options) noexcept // Arguments: // - // Return Value: -// - true -bool Terminal::PopGraphicsRendition() noexcept +// - +void Terminal::PopGraphicsRendition() { const TextAttribute current = _buffer->GetCurrentAttributes(); _buffer->SetCurrentAttributes(_sgrStack.Pop(current)); - return true; } diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index 0c3dae74a8a..6adf0daed61 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -19,77 +19,69 @@ TerminalDispatch::TerminalDispatch(ITerminalApi& terminalApi) noexcept : { } -void TerminalDispatch::Execute(const wchar_t wchControl) noexcept -{ - _terminalApi.ExecuteChar(wchControl); -} - -void TerminalDispatch::Print(const wchar_t wchPrintable) noexcept +void TerminalDispatch::Print(const wchar_t wchPrintable) { _terminalApi.PrintString({ &wchPrintable, 1 }); } -void TerminalDispatch::PrintString(const std::wstring_view string) noexcept +void TerminalDispatch::PrintString(const std::wstring_view string) { _terminalApi.PrintString(string); } bool TerminalDispatch::CursorPosition(const size_t line, - const size_t column) noexcept -try + const size_t column) { SHORT x{ 0 }; SHORT y{ 0 }; - RETURN_BOOL_IF_FALSE(SUCCEEDED(SizeTToShort(column, &x)) && - SUCCEEDED(SizeTToShort(line, &y))); + THROW_IF_FAILED(SizeTToShort(column, &x)); + THROW_IF_FAILED(SizeTToShort(line, &y)); - RETURN_BOOL_IF_FALSE(SUCCEEDED(ShortSub(x, 1, &x)) && - SUCCEEDED(ShortSub(y, 1, &y))); + THROW_IF_FAILED(ShortSub(x, 1, &x)); + THROW_IF_FAILED(ShortSub(y, 1, &y)); - return _terminalApi.SetCursorPosition(x, y); + _terminalApi.SetCursorPosition(x, y); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::CursorVisibility(const bool isVisible) noexcept +bool TerminalDispatch::CursorVisibility(const bool isVisible) { - return _terminalApi.SetCursorVisibility(isVisible); + _terminalApi.SetCursorVisibility(isVisible); + return true; } -bool TerminalDispatch::EnableCursorBlinking(const bool enable) noexcept +bool TerminalDispatch::EnableCursorBlinking(const bool enable) { - return _terminalApi.EnableCursorBlinking(enable); + _terminalApi.EnableCursorBlinking(enable); + return true; } -bool TerminalDispatch::CursorForward(const size_t distance) noexcept -try +bool TerminalDispatch::CursorForward(const size_t distance) { const auto cursorPos = _terminalApi.GetCursorPosition(); const COORD newCursorPos{ cursorPos.X + gsl::narrow(distance), cursorPos.Y }; - return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); + _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::CursorBackward(const size_t distance) noexcept -try +bool TerminalDispatch::CursorBackward(const size_t distance) { const auto cursorPos = _terminalApi.GetCursorPosition(); const COORD newCursorPos{ cursorPos.X - gsl::narrow(distance), cursorPos.Y }; - return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); + _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::CursorUp(const size_t distance) noexcept -try +bool TerminalDispatch::CursorUp(const size_t distance) { const auto cursorPos = _terminalApi.GetCursorPosition(); const COORD newCursorPos{ cursorPos.X, cursorPos.Y + gsl::narrow(distance) }; - return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); + _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) noexcept -try +bool TerminalDispatch::LineFeed(const DispatchTypes::LineFeedType lineFeedType) { switch (lineFeedType) { @@ -97,45 +89,42 @@ try // There is currently no need for mode-specific line feeds in the Terminal, // so for now we just treat them as a line feed without carriage return. case DispatchTypes::LineFeedType::WithoutReturn: - return _terminalApi.CursorLineFeed(false); + _terminalApi.CursorLineFeed(false); + return true; case DispatchTypes::LineFeedType::WithReturn: - return _terminalApi.CursorLineFeed(true); + _terminalApi.CursorLineFeed(true); + return true; default: return false; } } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::EraseCharacters(const size_t numChars) noexcept -try +bool TerminalDispatch::EraseCharacters(const size_t numChars) { - return _terminalApi.EraseCharacters(numChars); + _terminalApi.EraseCharacters(numChars); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::WarningBell() noexcept -try +bool TerminalDispatch::WarningBell() { - return _terminalApi.WarningBell(); + _terminalApi.WarningBell(); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::CarriageReturn() noexcept -try +bool TerminalDispatch::CarriageReturn() { const auto cursorPos = _terminalApi.GetCursorPosition(); - return _terminalApi.SetCursorPosition(0, cursorPos.Y); + _terminalApi.SetCursorPosition(0, cursorPos.Y); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::SetWindowTitle(std::wstring_view title) noexcept -try +bool TerminalDispatch::SetWindowTitle(std::wstring_view title) { - return _terminalApi.SetWindowTitle(title); + _terminalApi.SetWindowTitle(title); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::HorizontalTabSet() noexcept +bool TerminalDispatch::HorizontalTabSet() { const auto width = _terminalApi.GetBufferSize().Dimensions().X; const auto column = _terminalApi.GetCursorPosition().X; @@ -145,7 +134,7 @@ bool TerminalDispatch::HorizontalTabSet() noexcept return true; } -bool TerminalDispatch::ForwardTab(const size_t numTabs) noexcept +bool TerminalDispatch::ForwardTab(const size_t numTabs) { const auto width = _terminalApi.GetBufferSize().Dimensions().X; const auto cursorPosition = _terminalApi.GetCursorPosition(); @@ -162,10 +151,11 @@ bool TerminalDispatch::ForwardTab(const size_t numTabs) noexcept } } - return _terminalApi.SetCursorPosition(column, row); + _terminalApi.SetCursorPosition(column, row); + return true; } -bool TerminalDispatch::BackwardsTab(const size_t numTabs) noexcept +bool TerminalDispatch::BackwardsTab(const size_t numTabs) { const auto width = _terminalApi.GetBufferSize().Dimensions().X; const auto cursorPosition = _terminalApi.GetCursorPosition(); @@ -182,25 +172,23 @@ bool TerminalDispatch::BackwardsTab(const size_t numTabs) noexcept } } - return _terminalApi.SetCursorPosition(column, row); + _terminalApi.SetCursorPosition(column, row); + return true; } -bool TerminalDispatch::TabClear(const DispatchTypes::TabClearType clearType) noexcept +bool TerminalDispatch::TabClear(const DispatchTypes::TabClearType clearType) { - bool success = false; switch (clearType) { case DispatchTypes::TabClearType::ClearCurrentColumn: - success = _ClearSingleTabStop(); - break; + _ClearSingleTabStop(); + return true; case DispatchTypes::TabClearType::ClearAllColumns: - success = _ClearAllTabStops(); - break; + _ClearAllTabStops(); + return true; default: - success = false; - break; + return false; } - return success; } // Method Description: @@ -209,63 +197,57 @@ bool TerminalDispatch::TabClear(const DispatchTypes::TabClearType clearType) noe // - tableIndex: The VT color table index // - color: The new RGB color value to use. // Return Value: -// True if handled successfully. False otherwise. +// - True. bool TerminalDispatch::SetColorTableEntry(const size_t tableIndex, - const DWORD color) noexcept -try + const DWORD color) { - return _terminalApi.SetColorTableEntry(tableIndex, color); + _terminalApi.SetColorTableEntry(tableIndex, color); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) noexcept -try +bool TerminalDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) { - return _terminalApi.SetCursorStyle(cursorStyle); + _terminalApi.SetCursorStyle(cursorStyle); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::SetCursorColor(const DWORD color) noexcept -try +bool TerminalDispatch::SetCursorColor(const DWORD color) { - return _terminalApi.SetColorTableEntry(TextColor::CURSOR_COLOR, color); + _terminalApi.SetColorTableEntry(TextColor::CURSOR_COLOR, color); + return true; } -CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::SetClipboard(std::wstring_view content) noexcept -try +bool TerminalDispatch::SetClipboard(std::wstring_view content) { - return _terminalApi.CopyToClipboard(content); + _terminalApi.CopyToClipboard(content); + return true; } -CATCH_LOG_RETURN_FALSE() // Method Description: // - Sets the default foreground color to a new value // Arguments: // - color: The new RGB color value to use, in 0x00BBGGRR form // Return Value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::SetDefaultForeground(const DWORD color) noexcept -try +// - True. +bool TerminalDispatch::SetDefaultForeground(const DWORD color) { _terminalApi.SetColorAliasIndex(ColorAlias::DefaultForeground, TextColor::DEFAULT_FOREGROUND); - return _terminalApi.SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, color); + _terminalApi.SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, color); + return true; } -CATCH_LOG_RETURN_FALSE() // Method Description: // - Sets the default background color to a new value // Arguments: // - color: The new RGB color value to use, in 0x00BBGGRR form // Return Value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::SetDefaultBackground(const DWORD color) noexcept -try +// - True. +bool TerminalDispatch::SetDefaultBackground(const DWORD color) { _terminalApi.SetColorAliasIndex(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND); - return _terminalApi.SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, color); + _terminalApi.SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, color); + return true; } -CATCH_LOG_RETURN_FALSE() // Method Description: // - Erases characters in the buffer depending on the erase type @@ -273,38 +255,34 @@ CATCH_LOG_RETURN_FALSE() // - eraseType: the erase type (from beginning, to end, or all) // Return Value: // True if handled successfully. False otherwise. -bool TerminalDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) noexcept -try +bool TerminalDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) { return _terminalApi.EraseInLine(eraseType); } -CATCH_LOG_RETURN_FALSE() // Method Description: // - Deletes count number of characters starting from where the cursor is currently // Arguments: // - count, the number of characters to delete // Return Value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::DeleteCharacter(const size_t count) noexcept -try +// - True. +bool TerminalDispatch::DeleteCharacter(const size_t count) { - return _terminalApi.DeleteCharacter(count); + _terminalApi.DeleteCharacter(count); + return true; } -CATCH_LOG_RETURN_FALSE() // Method Description: // - Adds count number of spaces starting from where the cursor is currently // Arguments: // - count, the number of spaces to add // Return Value: -// True if handled successfully, false otherwise -bool TerminalDispatch::InsertCharacter(const size_t count) noexcept -try +// - True. +bool TerminalDispatch::InsertCharacter(const size_t count) { - return _terminalApi.InsertCharacter(count); + _terminalApi.InsertCharacter(count); + return true; } -CATCH_LOG_RETURN_FALSE() // Method Description: // - Moves the viewport and erases text from the buffer depending on the eraseType @@ -312,19 +290,17 @@ CATCH_LOG_RETURN_FALSE() // - eraseType: the desired erase type // Return Value: // True if handled successfully. False otherwise -bool TerminalDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) noexcept -try +bool TerminalDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) { return _terminalApi.EraseInDisplay(eraseType); } -CATCH_LOG_RETURN_FALSE() // - DECKPAM, DECKPNM - Sets the keypad input mode to either Application mode or Numeric mode (true, false respectively) // Arguments: // - applicationMode - set to true to enable Application Mode Input, false for Numeric Mode Input. // Return Value: -// - True if handled successfully. False otherwise. -bool TerminalDispatch::SetKeypadMode(const bool applicationMode) noexcept +// - True. +bool TerminalDispatch::SetKeypadMode(const bool applicationMode) { _terminalApi.SetInputMode(TerminalInput::Mode::Keypad, applicationMode); return true; @@ -334,8 +310,8 @@ bool TerminalDispatch::SetKeypadMode(const bool applicationMode) noexcept // Arguments: // - applicationMode - set to true to enable Application Mode Input, false for Normal Mode Input. // Return Value: -// - True if handled successfully. False otherwise. -bool TerminalDispatch::SetCursorKeysMode(const bool applicationMode) noexcept +// - True. +bool TerminalDispatch::SetCursorKeysMode(const bool applicationMode) { _terminalApi.SetInputMode(TerminalInput::Mode::CursorKey, applicationMode); return true; @@ -347,10 +323,11 @@ bool TerminalDispatch::SetCursorKeysMode(const bool applicationMode) noexcept // Arguments: // - reverseMode - set to true to enable reverse screen mode, false for normal mode. // Return Value: -// - True if handled successfully. False otherwise. -bool TerminalDispatch::SetScreenMode(const bool reverseMode) noexcept +// - True. +bool TerminalDispatch::SetScreenMode(const bool reverseMode) { - return _terminalApi.SetRenderMode(RenderSettings::Mode::ScreenReversed, reverseMode); + _terminalApi.SetRenderMode(RenderSettings::Mode::ScreenReversed, reverseMode); + return true; } // Method Description: @@ -359,8 +336,8 @@ bool TerminalDispatch::SetScreenMode(const bool reverseMode) noexcept // Arguments: // - win32InputMode - set to true to enable win32-input-mode, false to disable. // Return Value: -// - True if handled successfully. False otherwise. -bool TerminalDispatch::EnableWin32InputMode(const bool win32Mode) noexcept +// - True. +bool TerminalDispatch::EnableWin32InputMode(const bool win32Mode) { _terminalApi.SetInputMode(TerminalInput::Mode::Win32, win32Mode); return true; @@ -371,8 +348,8 @@ bool TerminalDispatch::EnableWin32InputMode(const bool win32Mode) noexcept //Arguments: // - enabled - true to enable, false to disable. // Return value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::EnableVT200MouseMode(const bool enabled) noexcept +// - True. +bool TerminalDispatch::EnableVT200MouseMode(const bool enabled) { _terminalApi.SetInputMode(TerminalInput::Mode::DefaultMouseTracking, enabled); return true; @@ -384,8 +361,8 @@ bool TerminalDispatch::EnableVT200MouseMode(const bool enabled) noexcept //Arguments: // - enabled - true to enable, false to disable. // Return value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) noexcept +// - True. +bool TerminalDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) { _terminalApi.SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, enabled); return true; @@ -397,8 +374,8 @@ bool TerminalDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) noexcept //Arguments: // - enabled - true to enable, false to disable. // Return value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::EnableSGRExtendedMouseMode(const bool enabled) noexcept +// - True. +bool TerminalDispatch::EnableSGRExtendedMouseMode(const bool enabled) { _terminalApi.SetInputMode(TerminalInput::Mode::SgrMouseEncoding, enabled); return true; @@ -409,8 +386,8 @@ bool TerminalDispatch::EnableSGRExtendedMouseMode(const bool enabled) noexcept //Arguments: // - enabled - true to enable, false to disable. // Return value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::EnableButtonEventMouseMode(const bool enabled) noexcept +// - True. +bool TerminalDispatch::EnableButtonEventMouseMode(const bool enabled) { _terminalApi.SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, enabled); return true; @@ -422,8 +399,8 @@ bool TerminalDispatch::EnableButtonEventMouseMode(const bool enabled) noexcept //Arguments: // - enabled - true to enable, false to disable. // Return value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::EnableAnyEventMouseMode(const bool enabled) noexcept +// - True. +bool TerminalDispatch::EnableAnyEventMouseMode(const bool enabled) { _terminalApi.SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enabled); return true; @@ -435,8 +412,8 @@ bool TerminalDispatch::EnableAnyEventMouseMode(const bool enabled) noexcept //Arguments: // - enabled - true to enable, false to disable. // Return value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::EnableAlternateScroll(const bool enabled) noexcept +// - True. +bool TerminalDispatch::EnableAlternateScroll(const bool enabled) { _terminalApi.SetInputMode(TerminalInput::Mode::AlternateScroll, enabled); return true; @@ -448,19 +425,19 @@ bool TerminalDispatch::EnableAlternateScroll(const bool enabled) noexcept //Arguments: // - enabled - true to enable, false to disable. // Return value: -// True if handled successfully. False otherwise. -bool TerminalDispatch::EnableXtermBracketedPasteMode(const bool enabled) noexcept +// - True. +bool TerminalDispatch::EnableXtermBracketedPasteMode(const bool enabled) { _terminalApi.EnableXtermBracketedPasteMode(enabled); return true; } -bool TerminalDispatch::SetMode(const DispatchTypes::ModeParams param) noexcept +bool TerminalDispatch::SetMode(const DispatchTypes::ModeParams param) { return _ModeParamsHelper(param, true); } -bool TerminalDispatch::ResetMode(const DispatchTypes::ModeParams param) noexcept +bool TerminalDispatch::ResetMode(const DispatchTypes::ModeParams param) { return _ModeParamsHelper(param, false); } @@ -472,18 +449,20 @@ bool TerminalDispatch::ResetMode(const DispatchTypes::ModeParams param) noexcept // - params - the optional custom ID // Return Value: // - true -bool TerminalDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring_view params) noexcept +bool TerminalDispatch::AddHyperlink(const std::wstring_view uri, const std::wstring_view params) { - return _terminalApi.AddHyperlink(uri, params); + _terminalApi.AddHyperlink(uri, params); + return true; } // Method Description: // - End a hyperlink // Return Value: // - true -bool TerminalDispatch::EndHyperlink() noexcept +bool TerminalDispatch::EndHyperlink() { - return _terminalApi.EndHyperlink(); + _terminalApi.EndHyperlink(); + return true; } // Method Description: @@ -493,7 +472,7 @@ bool TerminalDispatch::EndHyperlink() noexcept // - string: contains the parameters that define which action we do // Return Value: // - true -bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) noexcept +bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) { unsigned int state = 0; unsigned int progress = 0; @@ -538,7 +517,8 @@ bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) noexcept // progress is greater than the maximum allowed value, clamp it to the max progress = TaskbarMaxProgress; } - return _terminalApi.SetTaskbarProgress(static_cast(state), progress); + _terminalApi.SetTaskbarProgress(static_cast(state), progress); + return true; } // 9 is SetWorkingDirectory, which informs the terminal about the current working directory. else if (subParam == 9) @@ -550,14 +530,15 @@ bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) noexcept // An example: 9;"D:/" if (path.at(0) == L'"' && path.at(path.size() - 1) == L'"' && path.size() >= 3) { - return _terminalApi.SetWorkingDirectory(path.substr(1, path.size() - 2)); + _terminalApi.SetWorkingDirectory(path.substr(1, path.size() - 2)); } else { // If we fail to find the surrounding quotation marks, we'll give the path a try anyway. // ConEmu also does this. - return _terminalApi.SetWorkingDirectory(path); + _terminalApi.SetWorkingDirectory(path); } + return true; } } @@ -571,7 +552,7 @@ bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) noexcept // - enable - True for set, false for unset. // Return Value: // - True if handled successfully. False otherwise. -bool TerminalDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable) noexcept +bool TerminalDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable) { bool success = false; switch (param) @@ -621,24 +602,22 @@ bool TerminalDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, return success; } -bool TerminalDispatch::_ClearSingleTabStop() noexcept +void TerminalDispatch::_ClearSingleTabStop() { const auto width = _terminalApi.GetBufferSize().Dimensions().X; const auto column = _terminalApi.GetCursorPosition().X; _InitTabStopsForWidth(width); _tabStopColumns.at(column) = false; - return true; } -bool TerminalDispatch::_ClearAllTabStops() noexcept +void TerminalDispatch::_ClearAllTabStops() { _tabStopColumns.clear(); _initDefaultTabStops = false; - return true; } -void TerminalDispatch::_ResetTabStops() noexcept +void TerminalDispatch::_ResetTabStops() { _tabStopColumns.clear(); _initDefaultTabStops = true; @@ -663,7 +642,7 @@ void TerminalDispatch::_InitTabStopsForWidth(const size_t width) } } -bool TerminalDispatch::SoftReset() noexcept +bool TerminalDispatch::SoftReset() { // TODO:GH#1883 much of this method is not yet implemented in the Terminal, // because the Terminal _doesn't need to_ yet. The terminal is only ever @@ -675,23 +654,23 @@ bool TerminalDispatch::SoftReset() noexcept // This code is left here (from its original form in conhost) as a reminder // of what needs to be done. - bool success = CursorVisibility(true); // Cursor enabled. - // success = SetOriginMode(false) && success; // Absolute cursor addressing. - // success = SetAutoWrapMode(true) && success; // Wrap at end of line. - success = SetCursorKeysMode(false) && success; // Normal characters. - success = SetKeypadMode(false) && success; // Numeric characters. + CursorVisibility(true); // Cursor enabled. + // SetOriginMode(false); // Absolute cursor addressing. + // SetAutoWrapMode(true); // Wrap at end of line. + SetCursorKeysMode(false); // Normal characters. + SetKeypadMode(false); // Numeric characters. // // Top margin = 1; bottom margin = page length. - // success = _DoSetTopBottomScrollingMargins(0, 0) && success; + // _DoSetTopBottomScrollingMargins(0, 0); // _termOutput = {}; // Reset all character set designations. // if (_initialCodePage.has_value()) // { // // Restore initial code page if previously changed by a DOCS sequence. - // success = _pConApi->SetConsoleOutputCP(_initialCodePage.value()) && success; + // _pConApi->SetConsoleOutputCP(_initialCodePage.value()); // } - success = SetGraphicsRendition({}) && success; // Normal rendition. + SetGraphicsRendition({}); // Normal rendition. // // Reset the saved cursor state. // // Note that XTerm only resets the main buffer state, but that @@ -699,10 +678,10 @@ bool TerminalDispatch::SoftReset() noexcept // _savedCursorState.at(0) = {}; // Main buffer // _savedCursorState.at(1) = {}; // Alt buffer - return success; + return true; } -bool TerminalDispatch::HardReset() noexcept +bool TerminalDispatch::HardReset() { // TODO:GH#1883 much of this method is not yet implemented in the Terminal, // because the Terminal _doesn't need to_ yet. The terminal is only ever @@ -713,35 +692,33 @@ bool TerminalDispatch::HardReset() noexcept // This code is left here (from its original form in conhost) as a reminder // of what needs to be done. - bool success = true; - // // If in the alt buffer, switch back to main before doing anything else. // if (_usingAltBuffer) // { - // success = _pConApi->PrivateUseMainScreenBuffer(); - // _usingAltBuffer = !success; + // _pConApi->PrivateUseMainScreenBuffer(); + // _usingAltBuffer = false; // } // Sets the SGR state to normal - this must be done before EraseInDisplay // to ensure that it clears with the default background color. - success = SoftReset() && success; + SoftReset(); // Clears the screen - Needs to be done in two operations. - success = EraseInDisplay(DispatchTypes::EraseType::All) && success; - success = EraseInDisplay(DispatchTypes::EraseType::Scrollback) && success; + EraseInDisplay(DispatchTypes::EraseType::All); + EraseInDisplay(DispatchTypes::EraseType::Scrollback); // Set the DECSCNM screen mode back to normal. - success = SetScreenMode(false) && success; + SetScreenMode(false); // Cursor to 1,1 - the Soft Reset guarantees this is absolute - success = CursorPosition(1, 1) && success; + CursorPosition(1, 1); // Reset the mouse mode - success = EnableSGRExtendedMouseMode(false) && success; - success = EnableAnyEventMouseMode(false) && success; + EnableSGRExtendedMouseMode(false); + EnableAnyEventMouseMode(false); // Delete all current tab stops and reapply _ResetTabStops(); - return success; + return true; } diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index 7c0d14b9ce7..1ec29bf8e08 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -12,74 +12,73 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc public: TerminalDispatch(::Microsoft::Terminal::Core::ITerminalApi& terminalApi) noexcept; - void Execute(const wchar_t wchControl) noexcept override; - void Print(const wchar_t wchPrintable) noexcept override; - void PrintString(const std::wstring_view string) noexcept override; + void Print(const wchar_t wchPrintable) override; + void PrintString(const std::wstring_view string) override; - bool SetGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override; + bool SetGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) override; - bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) noexcept override; - bool PopGraphicsRendition() noexcept override; + bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::VTParameters options) override; + bool PopGraphicsRendition() override; bool CursorPosition(const size_t line, - const size_t column) noexcept override; // CUP + const size_t column) override; // CUP - bool EnableWin32InputMode(const bool win32InputMode) noexcept override; // win32-input-mode + bool EnableWin32InputMode(const bool win32InputMode) override; // win32-input-mode - bool CursorVisibility(const bool isVisible) noexcept override; // DECTCEM - bool EnableCursorBlinking(const bool enable) noexcept override; // ATT610 + bool CursorVisibility(const bool isVisible) override; // DECTCEM + bool EnableCursorBlinking(const bool enable) override; // ATT610 - bool CursorForward(const size_t distance) noexcept override; - bool CursorBackward(const size_t distance) noexcept override; - bool CursorUp(const size_t distance) noexcept override; + bool CursorForward(const size_t distance) override; + bool CursorBackward(const size_t distance) override; + bool CursorUp(const size_t distance) override; - bool LineFeed(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::LineFeedType lineFeedType) noexcept override; + bool LineFeed(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::LineFeedType lineFeedType) override; - bool EraseCharacters(const size_t numChars) noexcept override; - bool WarningBell() noexcept override; - bool CarriageReturn() noexcept override; - bool SetWindowTitle(std::wstring_view title) noexcept override; + bool EraseCharacters(const size_t numChars) override; + bool WarningBell() override; + bool CarriageReturn() override; + bool SetWindowTitle(std::wstring_view title) override; - bool HorizontalTabSet() noexcept override; // HTS - bool ForwardTab(const size_t numTabs) noexcept override; // CHT, HT - bool BackwardsTab(const size_t numTabs) noexcept override; // CBT - bool TabClear(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TabClearType clearType) noexcept override; // TBC + bool HorizontalTabSet() override; // HTS + bool ForwardTab(const size_t numTabs) override; // CHT, HT + bool BackwardsTab(const size_t numTabs) override; // CBT + bool TabClear(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TabClearType clearType) override; // TBC - bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept override; - bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override; - bool SetCursorColor(const DWORD color) noexcept override; + bool SetColorTableEntry(const size_t tableIndex, const DWORD color) override; + bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) override; + bool SetCursorColor(const DWORD color) override; - bool SetClipboard(std::wstring_view content) noexcept override; + bool SetClipboard(std::wstring_view content) override; - bool SetDefaultForeground(const DWORD color) noexcept override; - bool SetDefaultBackground(const DWORD color) noexcept override; - bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; // ED - bool DeleteCharacter(const size_t count) noexcept override; - bool InsertCharacter(const size_t count) noexcept override; - bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; + bool SetDefaultForeground(const DWORD color) override; + bool SetDefaultBackground(const DWORD color) override; + bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; // ED + bool DeleteCharacter(const size_t count) override; + bool InsertCharacter(const size_t count) override; + bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; - bool SetCursorKeysMode(const bool applicationMode) noexcept override; // DECCKM - bool SetKeypadMode(const bool applicationMode) noexcept override; // DECKPAM, DECKPNM - bool SetScreenMode(const bool reverseMode) noexcept override; // DECSCNM + bool SetCursorKeysMode(const bool applicationMode) override; // DECCKM + bool SetKeypadMode(const bool applicationMode) override; // DECKPAM, DECKPNM + bool SetScreenMode(const bool reverseMode) override; // DECSCNM - bool SoftReset() noexcept override; // DECSTR - bool HardReset() noexcept override; // RIS + bool SoftReset() override; // DECSTR + bool HardReset() override; // RIS - bool EnableVT200MouseMode(const bool enabled) noexcept override; // ?1000 - bool EnableUTF8ExtendedMouseMode(const bool enabled) noexcept override; // ?1005 - bool EnableSGRExtendedMouseMode(const bool enabled) noexcept override; // ?1006 - bool EnableButtonEventMouseMode(const bool enabled) noexcept override; // ?1002 - bool EnableAnyEventMouseMode(const bool enabled) noexcept override; // ?1003 - bool EnableAlternateScroll(const bool enabled) noexcept override; // ?1007 - bool EnableXtermBracketedPasteMode(const bool enabled) noexcept override; // ?2004 + bool EnableVT200MouseMode(const bool enabled) override; // ?1000 + bool EnableUTF8ExtendedMouseMode(const bool enabled) override; // ?1005 + bool EnableSGRExtendedMouseMode(const bool enabled) override; // ?1006 + bool EnableButtonEventMouseMode(const bool enabled) override; // ?1002 + bool EnableAnyEventMouseMode(const bool enabled) override; // ?1003 + bool EnableAlternateScroll(const bool enabled) override; // ?1007 + bool EnableXtermBracketedPasteMode(const bool enabled) override; // ?2004 - bool SetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) noexcept override; // DECSET - bool ResetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) noexcept override; // DECRST + bool SetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) override; // DECSET + bool ResetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) override; // DECRST - bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) noexcept override; - bool EndHyperlink() noexcept override; + bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) override; + bool EndHyperlink() override; - bool DoConEmuAction(const std::wstring_view string) noexcept override; + bool DoConEmuAction(const std::wstring_view string) override; private: ::Microsoft::Terminal::Core::ITerminalApi& _terminalApi; @@ -89,12 +88,12 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc size_t _SetRgbColorsHelper(const ::Microsoft::Console::VirtualTerminal::VTParameters options, TextAttribute& attr, - const bool isForeground) noexcept; + const bool isForeground); - bool _ModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams param, const bool enable) noexcept; + bool _ModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams param, const bool enable); - bool _ClearSingleTabStop() noexcept; - bool _ClearAllTabStops() noexcept; - void _ResetTabStops() noexcept; + void _ClearSingleTabStop(); + void _ClearAllTabStops(); + void _ResetTabStops(); void _InitTabStopsForWidth(const size_t width); }; diff --git a/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp b/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp index 3f34e768ced..996751c93f2 100644 --- a/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp @@ -20,7 +20,7 @@ using namespace Microsoft::Console::VirtualTerminal::DispatchTypes; // - The number of options consumed, not including the initial 38/48. size_t TerminalDispatch::_SetRgbColorsHelper(const VTParameters options, TextAttribute& attr, - const bool isForeground) noexcept + const bool isForeground) { size_t optionsConsumed = 1; const DispatchTypes::GraphicsOptions typeOpt = options.at(0); @@ -67,7 +67,7 @@ size_t TerminalDispatch::_SetRgbColorsHelper(const VTParameters options, // one at a time by setting or removing flags in the font style properties. // Return Value: // - True if handled successfully. False otherwise. -bool TerminalDispatch::SetGraphicsRendition(const VTParameters options) noexcept +bool TerminalDispatch::SetGraphicsRendition(const VTParameters options) { TextAttribute attr = _terminalApi.GetTextAttributes(); @@ -88,14 +88,14 @@ bool TerminalDispatch::SetGraphicsRendition(const VTParameters options) noexcept case BackgroundDefault: attr.SetDefaultBackground(); break; - case BoldBright: - attr.SetBold(true); + case Intense: + attr.SetIntense(true); break; case RGBColorOrFaint: attr.SetFaint(true); break; - case NotBoldOrFaint: - attr.SetBold(false); + case NotIntenseOrFaint: + attr.SetIntense(false); attr.SetFaint(false); break; case Italics: @@ -254,12 +254,14 @@ bool TerminalDispatch::SetGraphicsRendition(const VTParameters options) noexcept return true; } -bool TerminalDispatch::PushGraphicsRendition(const VTParameters options) noexcept +bool TerminalDispatch::PushGraphicsRendition(const VTParameters options) { - return _terminalApi.PushGraphicsRendition(options); + _terminalApi.PushGraphicsRendition(options); + return true; } -bool TerminalDispatch::PopGraphicsRendition() noexcept +bool TerminalDispatch::PopGraphicsRendition() { - return _terminalApi.PopGraphicsRendition(); + _terminalApi.PopGraphicsRendition(); + return true; } diff --git a/src/cascadia/TerminalSettingsEditor/Actions.xaml b/src/cascadia/TerminalSettingsEditor/Actions.xaml index 35a59cc8f28..5d46d5da020 100644 --- a/src/cascadia/TerminalSettingsEditor/Actions.xaml +++ b/src/cascadia/TerminalSettingsEditor/Actions.xaml @@ -375,28 +375,30 @@ - - - - - - - + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/AddProfile.cpp b/src/cascadia/TerminalSettingsEditor/AddProfile.cpp index ca0fe062fb1..bb2156c646b 100644 --- a/src/cascadia/TerminalSettingsEditor/AddProfile.cpp +++ b/src/cascadia/TerminalSettingsEditor/AddProfile.cpp @@ -7,9 +7,12 @@ #include "AddProfilePageNavigationState.g.cpp" #include "EnumEntry.h" +#include + using namespace winrt::Windows::Foundation; using namespace winrt::Windows::System; using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Navigation; using namespace winrt::Microsoft::Terminal::Settings::Model; @@ -18,6 +21,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation AddProfile::AddProfile() { InitializeComponent(); + + Automation::AutomationProperties::SetName(AddNewButton(), RS_(L"AddProfile_AddNewTextBlock/Text")); + Automation::AutomationProperties::SetName(DuplicateButton(), RS_(L"AddProfile_DuplicateTextBlock/Text")); } void AddProfile::OnNavigatedTo(const NavigationEventArgs& e) diff --git a/src/cascadia/TerminalSettingsEditor/AddProfile.xaml b/src/cascadia/TerminalSettingsEditor/AddProfile.xaml index ead73a4eebb..09237dd1b59 100644 --- a/src/cascadia/TerminalSettingsEditor/AddProfile.xaml +++ b/src/cascadia/TerminalSettingsEditor/AddProfile.xaml @@ -23,27 +23,29 @@ - + + + - - + + @@ -65,25 +67,25 @@ - - + + - + + + diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.cpp b/src/cascadia/TerminalSettingsEditor/Appearances.cpp index a9add91b5c4..f22c11fbd26 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.cpp +++ b/src/cascadia/TerminalSettingsEditor/Appearances.cpp @@ -136,6 +136,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation INITIALIZE_BINDABLE_ENUM_SETTING(IntenseTextStyle, IntenseTextStyle, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle, L"Appearance_IntenseTextStyle", L"Content"); } + bool Appearances::ShowIndistinguishableColorsItem() const noexcept + { + return Feature_AdjustIndistinguishableText::IsEnabled(); + } + // Method Description: // - Searches through our list of monospace fonts to determine if the settings model's current font face is a monospace font bool Appearances::UsingMonospaceFont() const noexcept diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.h b/src/cascadia/TerminalSettingsEditor/Appearances.h index cadbfbd5e5f..c34e7d7ff61 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.h +++ b/src/cascadia/TerminalSettingsEditor/Appearances.h @@ -106,6 +106,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation public: Appearances(); + bool ShowIndistinguishableColorsItem() const noexcept; + // font face Windows::Foundation::IInspectable CurrentFontFace() const; @@ -129,16 +131,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation bool IsCustomFontWeight(); WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector, FontWeightList); - GETSET_BINDABLE_ENUM_SETTING(CursorShape, Microsoft::Terminal::Core::CursorStyle, Appearance(), CursorShape); + GETSET_BINDABLE_ENUM_SETTING(CursorShape, Microsoft::Terminal::Core::CursorStyle, Appearance().CursorShape); WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector, ColorSchemeList, nullptr); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); DEPENDENCY_PROPERTY(Editor::AppearanceViewModel, Appearance); WINRT_PROPERTY(Editor::ProfileViewModel, SourceProfile, nullptr); - GETSET_BINDABLE_ENUM_SETTING(BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch, Appearance(), BackgroundImageStretchMode); + GETSET_BINDABLE_ENUM_SETTING(BackgroundImageStretchMode, Windows::UI::Xaml::Media::Stretch, Appearance().BackgroundImageStretchMode); - GETSET_BINDABLE_ENUM_SETTING(IntenseTextStyle, Microsoft::Terminal::Settings::Model::IntenseStyle, Appearance(), IntenseTextStyle); + GETSET_BINDABLE_ENUM_SETTING(IntenseTextStyle, Microsoft::Terminal::Settings::Model::IntenseStyle, Appearance().IntenseTextStyle); private: bool _ShowAllFonts; diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.idl b/src/cascadia/TerminalSettingsEditor/Appearances.idl index 33e4567fda2..c7bd4706014 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.idl +++ b/src/cascadia/TerminalSettingsEditor/Appearances.idl @@ -56,6 +56,8 @@ namespace Microsoft.Terminal.Settings.Editor ProfileViewModel SourceProfile; static Windows.UI.Xaml.DependencyProperty AppearanceProperty { get; }; + Boolean ShowIndistinguishableColorsItem { get; }; + Boolean UsingMonospaceFont { get; }; Boolean ShowAllFonts; diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index 497b7349a2a..606cfa82698 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -41,11 +41,10 @@ + Style="{StaticResource TextBlockSubHeaderStyle}" /> @@ -66,7 +65,7 @@ HasSettingValue="{x:Bind Appearance.HasFontFace, Mode=OneWay}" SettingOverrideSource="{x:Bind Appearance.FontFaceOverrideSource, Mode=OneWay}" Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}"> - + - + SettingOverrideSource="{x:Bind Appearance.AdjustIndistinguishableColorsOverrideSource, Mode=OneWay}" + Visibility="{x:Bind ShowIndistinguishableColorsItem}"> + + Style="{StaticResource TextBlockSubHeaderStyle}" /> + SettingOverrideSource="{x:Bind Appearance.CursorShapeOverrideSource, Mode=OneWay}" + Style="{StaticResource ExpanderSettingContainerStyle}"> + Style="{StaticResource TextBlockSubHeaderStyle}" /> + SettingOverrideSource="{x:Bind Appearance.BackgroundImagePathOverrideSource, Mode=OneWay}" + Style="{StaticResource ExpanderSettingContainerStyle}"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp new file mode 100644 index 00000000000..79c0edffd76 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Profiles_Advanced.h" +#include "Profiles_Advanced.g.cpp" + +#include "EnumEntry.h" +#include +#include "..\WinRTUtils\inc\Utils.h" + +using namespace winrt::Windows::UI::Xaml::Navigation; + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + Profiles_Advanced::Profiles_Advanced() + { + InitializeComponent(); + } + + void Profiles_Advanced::OnNavigatedTo(const NavigationEventArgs& e) + { + auto state{ e.Parameter().as() }; + _Profile = state.Profile(); + } + + void Profiles_Advanced::OnNavigatedFrom(const NavigationEventArgs& /*e*/) + { + _ViewModelChangedRevoker.revoke(); + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h new file mode 100644 index 00000000000..1acd81d647a --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Profiles_Advanced.g.h" +#include "ViewModelHelpers.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct Profiles_Advanced : public HasScrollViewer, Profiles_AdvancedT + { + public: + Profiles_Advanced(); + + void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); + + private: + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; + }; +}; + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(Profiles_Advanced); +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.idl b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.idl new file mode 100644 index 00000000000..44a1cbca91a --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.idl @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Profiles.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + [default_interface] runtimeclass Profiles_Advanced : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Profiles_Advanced(); + ProfileViewModel Profile { get; }; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml new file mode 100644 index 00000000000..517cc3f9113 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp new file mode 100644 index 00000000000..8ca25d19faa --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Profiles_Appearance.h" +#include "Profiles_Appearance.g.cpp" +#include "Profiles.h" +#include "PreviewConnection.h" +#include "EnumEntry.h" + +#include +#include "..\WinRTUtils\inc\Utils.h" + +using namespace winrt::Windows::UI::Xaml; +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()) } + { + InitializeComponent(); + + _previewControl.IsEnabled(false); + _previewControl.AllowFocusWhenDisabled(false); + ControlPreview().Child(_previewControl); + } + + void Profiles_Appearance::OnNavigatedTo(const NavigationEventArgs& e) + { + auto state{ e.Parameter().as() }; + _Profile = state.Profile(); + + // generate the font list, if we don't have one + if (_Profile.CompleteFontList() || !_Profile.MonospaceFontList()) + { + ProfileViewModel::UpdateFontList(); + } + + // Subscribe to some changes in the view model + // These changes should force us to update our own set of "Current" members, + // and propagate those changes to the UI + _ViewModelChangedRevoker = _Profile.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) { + _previewControl.UpdateControlSettings(_Profile.TermSettings(), _Profile.TermSettings()); + }); + + // 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()); + }); + } + + void Profiles_Appearance::OnNavigatedFrom(const NavigationEventArgs& /*e*/) + { + _ViewModelChangedRevoker.revoke(); + _AppearanceViewModelChangedRevoker.revoke(); + } + + void Profiles_Appearance::CreateUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) + { + _Profile.CreateUnfocusedAppearance(); + } + + void Profiles_Appearance::DeleteUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) + { + _Profile.DeleteUnfocusedAppearance(); + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h new file mode 100644 index 00000000000..f85efca9c23 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Profiles_Appearance.g.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct Profiles_Appearance : public HasScrollViewer, Profiles_AppearanceT + { + public: + Profiles_Appearance(); + + void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + + void CreateUnfocusedAppearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void DeleteUnfocusedAppearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); + + private: + Microsoft::Terminal::Control::TermControl _previewControl; + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _AppearanceViewModelChangedRevoker; + }; +}; + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(Profiles_Appearance); +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.idl b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.idl new file mode 100644 index 00000000000..dff812912a7 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.idl @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Profiles.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + [default_interface] runtimeclass Profiles_Appearance : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Profiles_Appearance(); + ProfileViewModel Profile { get; }; + + Windows.UI.Xaml.Controls.Slider OpacitySlider { get; }; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml new file mode 100644 index 00000000000..a506c1e80cc --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp new file mode 100644 index 00000000000..1c75efabfc9 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Profiles_Base.h" +#include "Profiles_Base.g.cpp" +#include "Profiles.h" + +#include +#include "..\WinRTUtils\inc\Utils.h" + +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Xaml::Navigation; + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + Profiles_Base::Profiles_Base() + { + InitializeComponent(); + + const auto startingDirCheckboxTooltip{ ToolTipService::GetToolTip(StartingDirectoryUseParentCheckbox()) }; + Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value(startingDirCheckboxTooltip)); + + Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text")); + AppearanceNavigator().Content(box_value(RS_(L"Profile_Appearance/Header"))); + AdvancedNavigator().Content(box_value(RS_(L"Profile_Advanced/Header"))); + } + + void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e) + { + auto state{ e.Parameter().as() }; + _Profile = state.Profile(); + + // Check the use parent directory box if the starting directory is empty + if (_Profile.StartingDirectory().empty()) + { + StartingDirectoryUseParentCheckbox().IsChecked(true); + } + } + + void Profiles_Base::OnNavigatedFrom(const NavigationEventArgs& /*e*/) + { + _ViewModelChangedRevoker.revoke(); + } + + void Profiles_Base::Appearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*args*/) + { + _Profile.CurrentPage(ProfileSubPage::Appearance); + } + + void Profiles_Base::Advanced_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*args*/) + { + _Profile.CurrentPage(ProfileSubPage::Advanced); + } + + void Profiles_Base::DeleteConfirmation_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) + { + winrt::get_self(_Profile)->DeleteProfile(); + } + + fire_and_forget Profiles_Base::Commandline_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = { + { L"Executable Files (*.exe, *.cmd, *.bat)", L"*.exe;*.cmd;*.bat" }, + { L"All Files (*.*)", L"*.*" } + }; + + static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } }; + const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; + auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { + THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExecutables)); + try + { + auto folderShellItem{ winrt::capture(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) }; + dialog->SetDefaultFolder(folderShellItem.get()); + } + CATCH_LOG(); // non-fatal + THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes)); + THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed + THROW_IF_FAILED(dialog->SetDefaultExtension(L"exe;cmd;bat")); + }); + + if (!path.empty()) + { + _Profile.Commandline(path); + } + } + + fire_and_forget Profiles_Base::Icon_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; + auto file = co_await OpenImagePicker(parentHwnd); + if (!file.empty()) + { + _Profile.Icon(file); + } + } + + fire_and_forget Profiles_Base::StartingDirectory_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; + auto folder = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { + static constexpr winrt::guid clientGuidFolderPicker{ 0xAADAA433, 0xB04D, 0x4BAE, { 0xB1, 0xEA, 0x1E, 0x6C, 0xD1, 0xCD, 0xA6, 0x8B } }; + THROW_IF_FAILED(dialog->SetClientGuid(clientGuidFolderPicker)); + try + { + auto folderShellItem{ winrt::capture(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) }; + dialog->SetDefaultFolder(folderShellItem.get()); + } + CATCH_LOG(); // non-fatal + + DWORD flags{}; + THROW_IF_FAILED(dialog->GetOptions(&flags)); + THROW_IF_FAILED(dialog->SetOptions(flags | FOS_PICKFOLDERS)); // folders only + }); + + if (!folder.empty()) + { + _Profile.StartingDirectory(folder); + } + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.h b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h new file mode 100644 index 00000000000..778a2f675cc --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Profiles_Base.g.h" +#include "ViewModelHelpers.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct Profiles_Base : public HasScrollViewer, Profiles_BaseT + { + public: + Profiles_Base(); + + void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + + fire_and_forget StartingDirectory_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + fire_and_forget Icon_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + fire_and_forget Commandline_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void Appearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void Advanced_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void DeleteConfirmation_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); + + private: + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; + }; +}; + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(Profiles_Base); +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.idl b/src/cascadia/TerminalSettingsEditor/Profiles_Base.idl new file mode 100644 index 00000000000..a0cd1b88aba --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.idl @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Profiles.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + [default_interface] runtimeclass Profiles_Base : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Profiles_Base(); + ProfileViewModel Profile { get; }; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml new file mode 100644 index 00000000000..4f593012110 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Rendering.xaml b/src/cascadia/TerminalSettingsEditor/Rendering.xaml index 3746a444c5e..72f39027bca 100644 --- a/src/cascadia/TerminalSettingsEditor/Rendering.xaml +++ b/src/cascadia/TerminalSettingsEditor/Rendering.xaml @@ -25,12 +25,14 @@ - + - + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 25af7bfbe4c..a069c7de6ec 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -279,22 +279,14 @@ When disabled, the terminal will render only the updates to the screen between frames. A description for what the "force full repaint" setting does. Presented near "Globals_ForceFullRepaint.Header". - + Columns Header for a control to choose the number of columns in the terminal's text grid. - - The number of columns displayed in the window upon first load. Measured in characters. - A description for what the "columns" setting does. Presented near "Globals_InitialCols.Header". - - + Rows Header for a control to choose the number of rows in the terminal's text grid. - - The number of rows displayed in the window upon first load. Measured in characters. - A description for what the "rows" setting does. Presented near "Globals_InitialRows.Header". - Initial Columns Name for a control to choose the number of columns in the terminal's text grid. @@ -372,19 +364,19 @@ A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Show acrylic in tab row (requires relaunch) - Header for a control to toggle whether acrylic shows in the tab row. Changing this setting requires the user to relaunch the app. + Use acrylic material in the tab row (requires relaunch) + Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic When checked, the tab row will have the acrylic material. - A description for what the "use acrylic in tab row" setting does. Presented near "Globals_AcrylicTabRow.Header". + A description for the "Globals_AcrylicTabRow.Header" setting does. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic Use active terminal title as application title Header for a control to toggle whether the terminal's title is shown as the application title, or not. - When disabled, the title bar will be 'Windows Terminal'. + When disabled, the title bar will be 'Terminal'. A description for what the "show title in titlebar" setting does. Presented near "Globals_ShowTitleInTitlebar.Header".{Locked="Windows"} @@ -408,7 +400,7 @@ Header for a control to toggle whether the app should launch when the user's machine starts up, or not. - When enabled, this enables the launch of Windows Terminal at machine startup. + When enabled, this enables the launch of Terminal at machine startup. A description for what the "start on user login" setting does. Presented near "Globals_StartOnUserLogin.Header". @@ -416,7 +408,7 @@ Header for a control to toggle if the app will always be presented on top of other windows, or is treated normally (when disabled). - Windows Terminal will always be the topmost window on the desktop. + Terminal will always be the topmost window on the desktop. A description for what the "always on top" setting does. Presented near "Globals_AlwaysOnTop.Header". @@ -471,18 +463,34 @@ Appearance Header for the "appearance" menu item. This navigates to a page that lets you see and modify settings related to the app's appearance. + + Appearance + Tooltip for the "appearance" menu item. + Color schemes Header for the "color schemes" menu item. This navigates to a page that lets you see and modify schemes of colors that can be used by the terminal. + + Color schemes + Tooltip for the "color schemes" menu item. + Interaction Header for the "interaction" menu item. This navigates to a page that lets you see and modify settings related to the user's mouse and touch interactions with the app. + + Interaction + Tooltip for the "interaction" menu item. + Startup Header for the "startup" menu item. This navigates to a page that lets you see and modify settings related to the app's launch experience (i.e. screen position, mode, etc.) + + Startup + Tooltip for the "startup" menu item. + Open JSON file Header for a menu item. This opens the JSON file that is used to log the app's settings. @@ -491,21 +499,29 @@ Defaults Header for the "defaults" menu item. This navigates to a page that lets you see and modify settings that affect profiles. This is the lowest layer of profile settings that all other profile settings are based on. If a profile doesn't define a setting, this page is responsible for figuring out what that setting is supposed to be. + + Defaults + Tooltip for the "profile defaults" menu item. + Rendering Header for the "rendering" menu item. This navigates to a page that lets you see and modify settings related to the app's rendering of text in the terminal. + + Rendering + Tooltip for the "rendering" menu item. + Actions Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app. - - Acrylic opacity - Header for a control to determine the level of opacity for the acrylic rendering material. The user can choose to make the acrylic background of the app more or less opaque. + + Actions + Tooltip for the "actions" menu item. - - Sets the transparency of the window. - A description for what the "acrylic opacity" setting does. Presented near "Profile_AcrylicOpacity.Header". + + Background opacity + Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. Background opacity @@ -527,6 +543,10 @@ By default Windows treats Ctrl+Alt as an alias for AltGr. When disabled, this behavior will be disabled. A description for what the "AltGr aliasing" setting does. Presented near "Profile_AltGrAliasing.Header". + + Text antialiasing + Name for a control to select the graphical anti-aliasing format of text. + Text antialiasing Header for a control to select the graphical anti-aliasing format of text. @@ -551,6 +571,14 @@ Appearance Header for a sub-page of profile settings focused on customizing the appearance of the profile. + + Background image path + Name for a control to determine the image presented on the background of the app. + + + Background image path + Name for a control to determine the image presented on the background of the app. + Background image path Header for a control to determine the image presented on the background of the app. @@ -559,6 +587,10 @@ File location of the image used in the background of the window. A description for what the "background image path" setting does. Presented near "Profile_BackgroundImage". + + Background image alignment + Name for a control to choose the visual alignment of the image presented on the background of the app. + Background image alignment Header for a control to choose the visual alignment of the image presented on the background of the app. @@ -607,6 +639,10 @@ Browse... Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. + + Background image opacity + Name for a control to choose the opacity of the image presented on the background of the app. + Background image opacity Header for a control to choose the opacity of the image presented on the background of the app. @@ -615,6 +651,10 @@ Sets the transparency of the background image. A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". + + Background image stretch mode + Name for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. + Background image stretch mode Header for a control to choose the stretch mode of the image presented on the background of the app. Stretch mode is how the image is resized to fill its allocated space. @@ -639,6 +679,10 @@ Uniform to fill An option to choose from for the "background image stretch mode" setting. When selected, the content is resized to fill the destination dimensions while it preserves its native aspect ratio. But if the aspect ratio of the destination differs, the image is clipped to fit in the space (to fill the space) + + Profile termination behavior + Name for a control to select the behavior of a terminal session (a profile) when it closes. + Profile termination behavior Header for a control to select the behavior of a terminal session (a profile) when it closes. @@ -663,6 +707,10 @@ Name of the color scheme to use. A description for what the "color scheme" setting does. Presented near "Profile_ColorScheme". + + Command line + Name for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. + Command line Header for a control to determine commandline executable (i.e. a .exe file) to run when a terminal session of this profile is launched. @@ -687,6 +735,10 @@ Sets the percentage height of the cursor starting from the bottom. Only works with the vintage cursor shape. A description for what the "cursor height" setting does. Presented near "Profile_CursorHeight". + + Cursor shape + Name for a control to select the shape of the text cursor. + Cursor shape Header for a control to select the shape of the text cursor. @@ -739,6 +791,14 @@ Size of the font in points. A description for what the "font size" setting does. Presented near "Profile_FontSize". + + Font weight + Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. + + + Font weight + Name for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. + Font weight Header for a control to select the weight (i.e. bold, thin, etc.) of the text in the app. @@ -759,6 +819,14 @@ If enabled, the profile will not appear in the list of profiles. This can be used to hide default profiles and dynamically generated profiles, while leaving them in your settings file. A description for what the "hidden" setting does. Presented near "Profile_Hidden". + + Run this profile as Administrator + Header for a control to toggle whether the profile should always open elevated (in an admin window) + + + If enabled, the profile will open in an Admin terminal window automatically. If the current window is already running as admin, it'll open in this window. + A description for what the "elevate" setting does. Presented near "Profile_Elevate". + History size Header for a control to determine how many lines of text can be saved in a session. In terminals, the "history" is the output generated within your session. @@ -771,6 +839,10 @@ The number of lines above the ones displayed in the window you can scroll back to. A description for what the "history size" setting does. Presented near "Profile_HistorySize". + + Icon + Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. + Icon Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one. @@ -787,6 +859,10 @@ Browse... Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window. + + Padding + Name for a control to determine the amount of space between text in a terminal and the edge of the window. + Padding Header for a control to determine the amount of space between text in a terminal and the edge of the window. The space can be any combination of the top, bottom, left, and right side of the window. @@ -811,6 +887,10 @@ When enabled, enables automatic adjustment of indistinguishable colors, which will, only when necessary, adjust the foreground color's lightness to make it more visible (based on the background color). A description for what the "adjust indistinguishable colors" setting does. Presented near "Profile_AdjustIndistinguishableColors". + + Scrollbar visibility + Name for a control to select the visibility of the scrollbar in a session. + Scrollbar visibility Header for a control to select the visibility of the scrollbar in a session. @@ -827,6 +907,10 @@ Scroll to input when typing Header for a control to toggle if keyboard input should automatically scroll to where the input was placed. + + Starting directory + Name for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. + Starting directory Header for a control to determine the directory the session opens it at launch. This is on a text box that accepts folder paths. @@ -848,7 +932,7 @@ A supplementary setting to the "starting directory" setting. "Parent" refers to the parent process of the current process. - If enabled, this profile will spawn in the directory from which Windows Terminal was launched. + If enabled, this profile will spawn in the directory from which Terminal was launched. A description for what the supplementary "use parent process directory" setting does. Presented near "Profile_StartingDirectoryUseParentCheckbox". @@ -859,6 +943,10 @@ Use the tab title to override the default title of the tab and suppress any title change messages from the application. A description for what the "suppress application title" setting does. Presented near "Profile_SuppressApplicationTitle". + + Tab title + Name for a control to determine the title of the tab. This is represented using a text box. + Tab title Header for a control to determine the title of the tab. This is represented using a text box. @@ -868,16 +956,24 @@ A description for what the "tab title" setting does. Presented near "Profile_TabTitle". - Unfocused Appearance + Unfocused appearance The header for the section where the unfocused appearance settings can be changed. + + Add + Button label that adds an unfocused appearance for this profile. + + + Delete + Button label that deletes the unfocused appearance for this profile. + - Enable acrylic - Header for a control to toggle the acrylic-like rendering of the background. The acrylic material creates a translucent texture. + Enable acrylic material + Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic Applies a translucent texture to the background of the window. - A description for what the "enable acrylic" setting does. Presented near "Profile_UseAcrylic". + A description for what the "Profile_UseAcrylic" setting does. Use desktop wallpaper @@ -931,6 +1027,10 @@ Maximized full screen focus An option to choose from for the "launch mode" setting. Opens the app maximized in full screen and in focus mode. + + Bell notification style + Name for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). + Bell notification style Header for a control to select the how the app notifies the user. "Bell" is the common term in terminals for the BEL character (like the metal device used to chime). @@ -1003,10 +1103,14 @@ Thin This is the formal name for a font weight. - + Launch size Header for a group of settings that control the size of the app. Presented near "Globals_InitialCols" and "Globals_InitialRows". + + The number of rows and columns displayed in the window upon first load. Measured in characters. + A description for what the "rows" and "columns" settings do. Presented near "Globals_LaunchSize.Header". + All An option to choose from for the "bell style" setting. When selected, a combination of the other bell styles is used to notify the user. @@ -1046,17 +1150,17 @@ The name of the profile that appears in the dropdown. A description for what the "name" setting does. Presented near "Profile_Name". + + Name + Name for a control to determine the name of the profile. This is a text box. + Name Header for a control to determine the name of the profile. This is a text box. - - Acrylic - Header for a group of settings related to the acrylic texture rendering on the background of the app. - Transparency - Header for a group of settings related to transparency, including the acrylic texture rendering on the background of the app. + Header for a group of settings related to transparency, including the acrylic material background of the app. Background image @@ -1066,6 +1170,10 @@ Cursor Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". + + Additional settings + Header for the buttons that navigate to additional settings for the profile. + Text Header for a group of settings that control the appearance of text in the app. @@ -1210,10 +1318,18 @@ If enabled, show all installed fonts in the list above. Otherwise, only show the list of monospace fonts. A description for what the supplementary "show all fonts" setting does. Presented near "Profile_FontFaceShowAllFonts". + + Create Appearance + Name for a control which creates an the unfocused appearance settings for this profile. + Create an unfocused appearance for this profile. This will be the appearance of the profile when it is inactive. A description for what the create unfocused appearance button does. + + Delete Appearance + Name for a control which deletes an the unfocused appearance settings for this profile. + Delete the unfocused appearance for this profile. A description for what the delete unfocused appearance button does. @@ -1286,6 +1402,10 @@ Text Formatting Header for a control to how text is formatted + + Intense text style + Name for a control to select how "intense" text is formatted (bold, bright, both or none) + Intense text style Header for a control to select how "intense" text is formatted (bold, bright, both or none) diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp index 4403827834c..3d1e55c1e83 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.cpp @@ -12,6 +12,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { DependencyProperty SettingContainer::_HeaderProperty{ nullptr }; DependencyProperty SettingContainer::_HelpTextProperty{ nullptr }; + DependencyProperty SettingContainer::_CurrentValueProperty{ nullptr }; DependencyProperty SettingContainer::_HasSettingValueProperty{ nullptr }; DependencyProperty SettingContainer::_SettingOverrideSourceProperty{ nullptr }; @@ -43,6 +44,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation xaml_typename(), PropertyMetadata{ box_value(L"") }); } + if (!_CurrentValueProperty) + { + _CurrentValueProperty = + DependencyProperty::Register( + L"CurrentValue", + xaml_typename(), + xaml_typename(), + PropertyMetadata{ box_value(L"") }); + } if (!_HasSettingValueProperty) { _HasSettingValueProperty = @@ -135,6 +145,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } } + + if (HelpText().empty()) + { + if (const auto& child{ GetTemplateChild(L"HelpTextBlock") }) + { + if (const auto& textBlock{ child.try_as() }) + { + textBlock.Visibility(Visibility::Collapsed); + } + } + } } // Method Description: diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.h b/src/cascadia/TerminalSettingsEditor/SettingContainer.h index de9570e7f11..7accd8735b9 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.h +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.h @@ -31,6 +31,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Header); DEPENDENCY_PROPERTY(hstring, HelpText); + DEPENDENCY_PROPERTY(hstring, CurrentValue); DEPENDENCY_PROPERTY(bool, HasSettingValue); DEPENDENCY_PROPERTY(IInspectable, SettingOverrideSource); TYPED_EVENT(ClearSettingValue, Editor::SettingContainer, Windows::Foundation::IInspectable); diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainer.idl b/src/cascadia/TerminalSettingsEditor/SettingContainer.idl index 3c75cd2dc33..39638ca4fe8 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainer.idl +++ b/src/cascadia/TerminalSettingsEditor/SettingContainer.idl @@ -13,6 +13,9 @@ namespace Microsoft.Terminal.Settings.Editor String HelpText; static Windows.UI.Xaml.DependencyProperty HelpTextProperty { get; }; + String CurrentValue; + static Windows.UI.Xaml.DependencyProperty CurrentValueProperty { get; }; + Boolean HasSettingValue; static Windows.UI.Xaml.DependencyProperty HasSettingValueProperty { get; }; diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml b/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml index 717e6e80855..5efb9a2ebb6 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml +++ b/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml @@ -7,12 +7,51 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:Microsoft.Terminal.Settings.Editor" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:muxc="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> - + + #0F000000 + + + + + + + #19000000 + + + + + + + + + + + + - - - +