Compare commits

..

25 Commits

Author SHA1 Message Date
Dustin L. Howett
080540983c Migrate spelling-0.0.21 changes from main 2021-08-25 08:25:20 -05:00
Mike Griese
5c17603a94 more cleanup 2021-08-25 08:25:20 -05:00
Mike Griese
354e4b00a3 BODGY, don't raise an event on destruction, that would be too... 2021-08-24 16:11:14 -05:00
Mike Griese
8707c03715 simplify the interface here a bit 2021-08-24 15:18:36 -05:00
Mike Griese
31b2763be5 move the content process main to another file as well 2021-08-24 15:07:26 -05:00
Mike Griese
a000d81fa9 Move the content process handling to a separate file in TermControl project 2021-08-24 12:33:53 -05:00
Mike Griese
d36a08186c add a dialog internally to the bounds of the control, not outside of the control 2021-08-24 12:20:36 -05:00
Mike Griese
3b1bb455d8 some cleanup 2021-08-24 10:40:30 -05:00
Mike Griese
901bc78966 Merge remote-tracking branch 'origin/main' into dev/migrie/oop/infinity-war 2021-08-24 10:05:34 -05:00
Mike Griese
4150609b42 This doesn't immediately crash, but it does crash when you start asking for the actual text ranges. That's not what you want. 2021-08-16 13:04:55 -05:00
Mike Griese
d5920a8c69 Revert "This too didn't work. Creating the XAML thing not on the XAML thing isn't going to work"
This reverts commit fd364db727.
2021-08-16 11:17:16 -05:00
Mike Griese
fd364db727 This too didn't work. Creating the XAML thing not on the XAML thing isn't going to work 2021-08-16 11:17:05 -05:00
Mike Griese
3a0fbd9f59 Revert "this was the part where I realized I dun goofed"
This reverts commit 64533c838a.
2021-08-16 10:18:54 -05:00
Mike Griese
64533c838a this was the part where I realized I dun goofed 2021-08-16 10:18:47 -05:00
Mike Griese
aa6b08118f The sample app has a hard time loading TermControl resources so we're just going to disable this for now 2021-08-16 10:18:16 -05:00
Mike Griese
b0b44410c6 a fix for a crash when closing 2021-08-16 10:17:41 -05:00
Mike Griese
b541179333 This works to kill the content and have the app live 2021-08-12 12:55:03 -05:00
Mike Griese
56f1223dc5 Add a kill button for manually killing the content 2021-08-12 11:28:01 -05:00
Mike Griese
88d974280d You know, there's 0% chance that this is the right pattern for this, but it _works_ 2021-08-12 10:32:24 -05:00
Mike Griese
d84a31801a some short-circuits for these inits, to make them more stable 2021-08-12 10:00:57 -05:00
Mike Griese
6910677a11 A close button, and more logging 2021-08-12 09:47:50 -05:00
Mike Griese
9331cc8e59 Add a signal that the content can use to tell the window it's ready 2021-08-12 08:45:43 -05:00
Mike Griese
2f64db2765 Some comments because everything is hard 2021-08-10 16:25:44 -05:00
Mike Griese
4cc3d39de9 I believe this merges the buisness of connection-factory, though there are many issues. 2021-08-10 16:17:27 -05:00
Mike Griese
d8dcb6f570 I think this merges the-whole-thing into this branch. The remote control doesn't render right, but I think that's because the actual HEAD of all this work is in connection-factory 2021-08-10 10:25:29 -05:00
161 changed files with 1763 additions and 3271 deletions

View File

@@ -21,7 +21,7 @@ Write-Host "Checking test results..."
$queryUri = GetQueryTestRunsUri -CollectionUri $CollectionUri -TeamProject $TeamProject -BuildUri $BuildUri -IncludeRunDetails
Write-Host "queryUri = $queryUri"
$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
$testRuns = Invoke-RestMethod -Uri $queryUri -Method Get -Headers $azureDevOpsRestApiHeaders
[System.Collections.Generic.List[string]]$failingTests = @()
[System.Collections.Generic.List[string]]$unreliableTests = @()
[System.Collections.Generic.List[string]]$unexpectedResultTest = @()
@@ -50,7 +50,7 @@ foreach ($testRun in ($testRuns.value | Sort-Object -Property "completedDate" -D
$totalTestsExecutedCount += $testRun.totalTests
$testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
$testResults = Invoke-RestMethodWithRetries "$($testRun.url)/results?api-version=5.0" -Headers $azureDevOpsRestApiHeaders
$testResults = Invoke-RestMethod -Uri "$($testRun.url)/results?api-version=5.0" -Method Get -Headers $azureDevOpsRestApiHeaders
foreach ($testResult in $testResults.value)
{

View File

@@ -20,31 +20,13 @@ function Generate-File-Links
Out-File -FilePath $helixLinkFile -Append -InputObject "<ul>"
foreach($file in $files)
{
$url = Append-HelixAccessTokenToUrl $file.Link "{Your-Helix-Access-Token-Here}"
Out-File -FilePath $helixLinkFile -Append -InputObject "<li>$($url)</li>"
Out-File -FilePath $helixLinkFile -Append -InputObject "<li><a href=$($file.Link)>$($file.Name)</a></li>"
}
Out-File -FilePath $helixLinkFile -Append -InputObject "</ul>"
Out-File -FilePath $helixLinkFile -Append -InputObject "</div>"
}
}
function Append-HelixAccessTokenToUrl
{
Param ([string]$url, [string]$token)
if($token)
{
if($url.Contains("?"))
{
$url = "$($url)&access_token=$($token)"
}
else
{
$url = "$($url)?access_token=$($token)"
}
}
return $url
}
#Create output directory
New-Item $OutputFolder -ItemType Directory
@@ -81,8 +63,7 @@ foreach ($testRun in $testRuns.value)
if (-not $workItems.Contains($workItem))
{
$workItems.Add($workItem)
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files"
$filesQueryUri = Append-HelixAccessTokenToUrl $filesQueryUri $helixAccessToken
$filesQueryUri = "https://helix.dot.net/api/2019-06-17/jobs/$helixJobId/workitems/$helixWorkItemName/files$accessTokenParam"
$files = Invoke-RestMethodWithRetries $filesQueryUri
$screenShots = $files | where { $_.Name.EndsWith(".jpg") }
@@ -121,7 +102,6 @@ foreach ($testRun in $testRuns.value)
Write-Host "Downloading $link to $destination"
$link = Append-HelixAccessTokenToUrl $link $HelixAccessToken
Download-FileWithRetries $link $destination
}
}

View File

@@ -23,7 +23,7 @@ Write-Host "queryUri = $queryUri"
# To account for unreliable tests, we'll iterate through all of the tests associated with this build, check to see any tests that were unreliable
# (denoted by being marked as "skipped"), and if so, we'll instead mark those tests with a warning and enumerate all of the attempted runs
# with their pass/fail states as well as any relevant error messages for failed attempts.
$testRuns = Invoke-RestMethodWithRetries $queryUri -Headers $azureDevOpsRestApiHeaders
$testRuns = Invoke-RestMethod -Uri $queryUri -Method Get -Headers $azureDevOpsRestApiHeaders
$timesSeenByRunName = @{}
@@ -32,10 +32,10 @@ foreach ($testRun in $testRuns.value)
$testRunResultsUri = "$($testRun.url)/results?api-version=5.0"
Write-Host "Marking test run `"$($testRun.name)`" as in progress so we can change its results to account for unreliable tests."
Invoke-RestMethod "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "InProgress" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
Invoke-RestMethod -Uri "$($testRun.url)?api-version=5.0" -Method Patch -Body (ConvertTo-Json @{ "state" = "InProgress" }) -Headers $azureDevOpsRestApiHeaders -ContentType "application/json" | Out-Null
Write-Host "Retrieving test results..."
$testResults = Invoke-RestMethodWithRetries $testRunResultsUri -Headers $azureDevOpsRestApiHeaders
$testResults = Invoke-RestMethod -Uri $testRunResultsUri -Method Get -Headers $azureDevOpsRestApiHeaders
foreach ($testResult in $testResults.value)
{
@@ -54,8 +54,7 @@ foreach ($testRun in $testRuns.value)
Write-Host " Test $($testResult.testCaseTitle) was detected as unreliable. Updating..."
# The errorMessage field contains a link to the JSON-encoded rerun result data.
$resultsJson = Download-StringWithRetries "Error results" $testResult.errorMessage
$rerunResults = ConvertFrom-Json $resultsJson
$rerunResults = ConvertFrom-Json (New-Object System.Net.WebClient).DownloadString($testResult.errorMessage)
[System.Collections.Generic.List[System.Collections.Hashtable]]$rerunDataList = @()
$attemptCount = 0
$passCount = 0

View File

@@ -152,7 +152,7 @@ jobs:
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 }} /t:Terminal\CascadiaPackage;Terminal\WindowsTerminalUniversal /p:WindowsTerminalReleaseBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: true
@@ -194,7 +194,7 @@ jobs:
inputs:
solution: '**\OpenConsole.sln'
vsVersion: 16.0
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }};PGOBuildMode=${{ parameters.pgoBuildMode }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
- task: PowerShell@2

View File

@@ -3,7 +3,7 @@ parameters:
platform: ''
additionalBuildArguments: ''
minimumExpectedTestsExecutedCount: 1 # Sanity check for minimum expected tests to be reported
rerunPassesRequiredToAvoidFailure: 5
rerunPassesRequiredToAvoidFailure: 0
jobs:
- job: Build${{ parameters.platform }}${{ parameters.configuration }}

View File

@@ -11,7 +11,7 @@ jobs:
clean: true
- task: PowerShell@2
displayName: 'Code Formatting Check'
displayName: 'Code Formattting Check'
inputs:
targetType: filePath
filePath: '.\build\scripts\Invoke-FormattingCheck.ps1'

View File

@@ -22,7 +22,6 @@ jobs:
condition: succeededOrFailed()
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
HelixAccessToken: $(HelixApiAccessToken)
inputs:
targetType: filePath
filePath: build\Helix\UpdateUnreliableTests.ps1
@@ -33,7 +32,6 @@ jobs:
condition: succeededOrFailed()
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
HelixAccessToken: $(HelixApiAccessToken)
inputs:
targetType: filePath
filePath: build\Helix\OutputTestResults.ps1

View File

@@ -15,7 +15,6 @@ parameters:
# 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'
jobs:
- job: ${{ parameters.name }}
@@ -30,11 +29,11 @@ jobs:
buildConfiguration: ${{ parameters.configuration }}
buildPlatform: ${{ parameters.platform }}
openHelixTargetQueues: ${{ parameters.openHelixTargetQueues }}
closedHelixTargetQueues: ${{ parameters.closedHelixTargetQueues }}
artifactsDir: $(Build.SourcesDirectory)\Artifacts
taefPath: $(Build.SourcesDirectory)\build\Helix\packages\Microsoft.Taef.10.60.210621002\build\Binaries\$(buildPlatform)
helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}'
steps:
- task: CmdLine@1
displayName: 'Display build machine environment variables'
@@ -141,7 +140,6 @@ jobs:
- task: DotNetCoreCLI@2
displayName: 'Run tests in Helix (open queues)'
condition: and(succeeded(),eq(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
inputs:
@@ -149,15 +147,3 @@ jobs:
projects: build\Helix\RunTestsInHelix.proj
custom: msbuild
arguments: '$(helixCommonArgs) /p:IsExternal=true /p:Creator=Terminal /p:HelixTargetQueues=$(openHelixTargetQueues)'
- task: DotNetCoreCLI@2
displayName: 'Run tests in Helix (closed queues)'
condition: and(succeeded(),ne(variables['System.CollectionUri'],'https://dev.azure.com/ms/'))
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
HelixAccessToken: $(HelixApiAccessToken)
inputs:
command: custom
projects: build\Helix\RunTestsInHelix.proj
custom: msbuild
arguments: '$(helixCommonArgs) /p:HelixTargetQueues=$(closedHelixTargetQueues)'

View File

@@ -20,15 +20,11 @@ jobs:
inputs:
artifactName: ${{ parameters.pgoArtifact }}
downloadPath: $(artifactsPath)
- task: NuGetAuthenticate@0
inputs:
nuGetServiceConnections: 'Terminal Public Artifact Feed'
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 5.8.0'
displayName: 'Use NuGet 5.2.0'
inputs:
versionSpec: 5.8.0
versionSpec: 5.2.0
- task: CopyFiles@2
displayName: 'Copy pgd files to NuGet build directory'
@@ -62,11 +58,5 @@ jobs:
displayName: 'NuGet push'
inputs:
command: push
nuGetFeedType: external
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg
# The actual URL and PAT for this feed is configured at
# https://microsoft.visualstudio.com/Dart/_settings/adminservices
# This is the name of that connection
publishFeedCredentials: 'Terminal Public Artifact Feed'
feedsToUse: config
nugetConfigPath: '$(Build.SourcesDirectory)/NuGet.config'
publishVstsFeed: Terminal/TerminalDependencies
packagesToPush: $(Build.ArtifactStagingDirectory)/*.nupkg

View File

@@ -5,7 +5,7 @@
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2021</XesBaseYearForStoreVersion>
<VersionMajor>1</VersionMajor>
<VersionMinor>12</VersionMinor>
<VersionMinor>11</VersionMinor>
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -215,22 +215,6 @@
"type": "integer"
}
]
},
"features": {
"description": "Sets the DWrite font features for the given font. For example, { \"ss01\": 1, \"liga\":0 } will enable ss01 and disable ligatures.",
"type": "object",
"patternProperties": {
"^(([A-Za-z0-9]){4})$": { "type": "integer" }
},
"additionalProperties": false
},
"axes": {
"description": "Sets the DWrite font axes for the given font. For example, { \"wght\": 200 } will set the font weight to 200.",
"type": "object",
"patternProperties": {
"^([A-Za-z]{4})$": { "type": "number" }
},
"additionalProperties": false
}
},
"type": "object"
@@ -262,14 +246,12 @@
"findMatch",
"focusPane",
"globalSummon",
"highlightCursor",
"identifyWindow",
"identifyWindows",
"moveFocus",
"movePane",
"swapPane",
"moveTab",
"multipleActions",
"newTab",
"newWindow",
"nextTab",
@@ -317,8 +299,7 @@
"down",
"previous",
"nextInOrder",
"previousInOrder",
"first"
"previousInOrder"
],
"type": "string"
},
@@ -827,24 +808,6 @@
],
"required": [ "direction" ]
},
"MultipleActionsAction": {
"description": "Arguments for the multiple actions command",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "multipleActions" },
"actions" : {
"$ref": "#/definitions/ShortcutAction",
"type": "array",
"minItems": 1,
"description": "A list of other actions."
}
}
}
],
"required": [ "actions" ]
},
"CommandPaletteAction": {
"description": "Arguments for a commandPalette action",
"allOf": [
@@ -1027,17 +990,6 @@
}
]
},
"HighlightCursorAction": {
"description": "The action to shine a spotlight on the current cursor location. If the cursor is off the screen, this action does nothing.",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "highlightCursor" }
}
}
]
},
"Keybinding": {
"additionalProperties": false,
"properties": {
@@ -1074,7 +1026,6 @@
{ "$ref": "#/definitions/FocusPaneAction" },
{ "$ref": "#/definitions/GlobalSummonAction" },
{ "$ref": "#/definitions/QuakeModeAction" },
{ "$ref": "#/definitions/HighlightCursorAction" },
{ "type": "null" }
]
},
@@ -1273,11 +1224,6 @@
"default": "false",
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
"type": "boolean"
},
"useAcrylicInTabRow": {
"default": "false",
"description": "When set to true, the tab row will have an acrylic background with 50% opacity.",
"type": "boolean"
},
"actions": {
"description": "Properties are specific to each custom action.",

View File

@@ -1,619 +0,0 @@
---
author: Mike Griese @zadjii-msft
created on: 2020-11-20
last updated: 2021-08-17
issue id: #1032
---
# Elevation Quality of Life Improvements
## Abstract
For a long time, we've been researching adding support to the Windows Terminal
for running both unelevated and elevated (admin) tabs side-by-side, in the same
window. However, after much research, we've determined that there isn't a safe
way to do this without opening the Terminal up as a potential
escalation-of-privilege vector.
Instead, we'll be adding a number of features to the Terminal to improve the
user experience of working in elevated scenarios. These improvements include:
* A visible indicator that the Terminal window is elevated ([#1939])
* Configuring the Terminal to always run elevated ([#632])
* Configuring a specific profile to always open elevated ([#632])
* Allowing new tabs, panes to be opened elevated directly from an unelevated
window
* Dynamic profile appearance that changes depending on if the Terminal is
elevated or not. ([#1939], [#8311])
## Background
_This section was originally authored in the [Process Model 2.0 Spec]. Please
refer to it there for its original context._
Let's presume that you're a user who wants to be able to open an elevated tab
within an otherwise unelevated Terminal window. We call this scenario "mixed
elevation" - the tabs within the Terminal can be running either unelevated _or_
elevated client applications.
It wouldn't be terribly difficult for the unelevated Terminal to request the
permission of the user to spawn an elevated client application. The user would
see a UAC prompt, they'd accept, and then they'd be able to have an elevated
shell alongside their unelevated tabs.
However, this creates an escalation of privilege vector. Now, there's an
unelevated window which is connected directly to an elevated process. At this
point, **any other unelevated application could send input to the Terminal's
`HWND`**. This would make it possible for another unelevated process to "drive"
the Terminal window, and send commands to the elevated client application.
It was initially theorized that the window/content model architecture would also
help enable "mixed elevation". With mixed elevation, tabs could run at different
integrity levels within the same terminal window. However, after investigation
and research, it has become apparent that this scenario is not possible to do
safely after all. There are numerous technical difficulties involved, and each
with their own security risks. At the end of the day, the team wouldn't be
comfortable shipping a mixed-elevation solution, because there's simply no way
for us to be confident that we haven't introduced an escalation-of-privilege
vector utilizing the Terminal. No matter how small the attack surface might be,
we wouldn't be confident that there are _no_ vectors for an attack.
Some things we considered during this investigation:
* If a user requests a new elevated tab from an otherwise unelevated window, we
could use UAC to create a new, elevated window process, and "move" all the
current tabs to that window process, as well as the new elevated client. Now,
the window process would be elevated, preventing it from input injection, and
it would still contains all the previously existing tabs. The original window
process could now be discarded, as the new elevated window process will
pretend to be the original window.
- However, it is unfortunately not possible with COM to have an elevated
client attach to an unelevated server that's registered at runtime. Even in
a packaged environment, the OS will reject the attempt to `CoCreateInstance`
the content process object. this will prevent elevated windows from
re-connecting to unelevated client processes.
- We could theoretically build an RPC tunnel between content and window
processes, and use the RPC connection to marshal the content process to the
elevated window. However, then _we_ would need to be responsible for
securing access the the RPC endpoint, and we feel even less confident doing
that.
- Attempts were also made to use a window-broker-content architecture, with
the broker process having a static CLSID in the registry, and having the
window and content processes at mixed elevation levels `CoCreateInstance`
that broker. This however _also_ did not work across elevation levels. This
may be due to a lack of Packaged COM support for mixed elevation levels.
It's also possible that the author forgot that packaged WinRT doesn't play
nicely with creating objects in an elevated context. The Terminal has
previously needed to manually manifest all its classes in a SxS manifest for
Unpackaged WinRT to allow the classes to be activated, rather than relying
on the packaged catalog. It's theoretically possible that doing that would
have allowed the broker to be activated across integrity levels.
Even if this approach did end up working, we would still need to be
responsible for securing the elevated windows so that an unelevated attacker
couldn't hijack a content process and trigger unexpected code in the window
process. We didn't feel confident that we could properly secure this channel
either.
We also considered allowing mixed content in windows that were _originally_
elevated. If the window is already elevated, then it can launch new unelevated
processes. We could allow elevated windows to still create unelevated
connections. However, we'd want to indicate per-pane what the elevation state
of each connection is. The user would then need to keep track themselves of
which terminal instances are elevated, and which are not.
This also marks a departure from the current behavior, where everything in an
elevated window would be elevated by default. The user would need to specify for
each thing in the elevated window that they'd want to create it elevated. Or the
Terminal would need to provide some setting like
`"autoElevateEverythingInAnElevatedWindow"`.
We cannot support mixed elevation when starting in a unelevated window.
Therefore, it doesn't make a lot of UX sense to support it in the other
direction. It's a cleaner UX story to just have everything in a single window at
the same elevation level.
## Solution Design
Instead of supporting mixed elevation in the same window, we'll introduce the
following features to the Terminal. These are meant as a way of improving the
quality of life for users who work in mixed-elevation (or even just elevated)
environments.
### Visible indicator for elevated windows
As requested in [#1939], it would be nice if it was easy to visibly identify if
a Terminal window was elevated or not.
One easy way of doing this is by adding a simple UAC shield to the left of the
tabs for elevated windows. This shield could be configured by the theme (see
[#3327]). We could provide the following states:
* Colored (the default)
* Monochrome
* Hidden, to hide the shield even on elevated windows. This is the current
behavior.
![UAC-shield-in-titlebar](UAC-shield-in-titlebar.png)
_figure 1: a monochrome UAC shield in the titlebar of the window, courtesy of @mdtauk_
We could also simplify this to only allow a boolean true/false for displaying
the shield. As we do often with other enums, we could define `true` to be the
same as the default appearance, and `false` to be the hidden option. As always,
the development of the Terminal is an iterative process, where we can
incrementally improve from no setting, to a boolean setting, to a enum-backed
one.
### Configuring a profile to always run elevated
Oftentimes, users might have a particular tool chain that only works when
running elevated. In these scenarios, it would be convenient for the user to be
able to identify that the profile should _always_ run elevated. That way, they
could open the profile from the dropdown menu of an otherwise unelevated window
and have the elevated window open with the profile automatically.
We'll be adding the `"elevate": true|false` setting as a per-profile setting,
with a default value of `false`. When set to `true`, we'll try to auto-elevate
the profile whenever it's launched. We'll check to see if this window is
elevated before creating the connection for this profile. If the window is not
elevated, then we'll create a new window with the requested elevation level to
handle the new connection.
`"elevate": false` will do nothing. If the window is already elevated, then the
profile won't open an un-elevated window.
If the user tries to open an `"elevate": true` profile in a window that's
already elevated, then a new tab/split will open in the existing window, rather
than spawning an additional elevated window.
There are three situations where we're creating new terminal instances: new
tabs, new splits, and new windows. Currently, these are all actions that are
also exposed in the `wt` commandline as subcommands. We can convert from the
commandline arguments into these actions already. Therefore, it shouldn't be too
challenging to convert these actions back into the equal commandline arguments.
For the following examples, let's assume the user is currently in an unelevated
Terminal window.
When the user tries to create a new elevated **tab**, we'll need to create a new
process, elevated, with the following commandline:
```
wt new-tab [args...]
```
When we create this new `wt` instance, it will obey the glomming rules as
specified in [Session Management Spec]. It might end up glomming to another
existing window at that elevation level, or possibly create its own window.
Similarly, for a new elevated **window**, we can make sure to pass the `-w new`
arguments to `wt`. These parameters indicate that we definitely want this
command to run in a new window, regardless of the current glomming settings.
```
wt -w new new-tab [args...]
```
However, creating a new **pane** is a little trickier. Invoking the `wt
split-pane [args...]` is straightforward enough.
<!-- Discussion notes follow:
If the current window doesn't have the same elevation level as the
requested profile, do we always want to just create a new split? If the command
ends up glomming to an existing window, does that even make sense? That invoking
an elevated split in an unelevated window would end up splitting the elevated
window? It's very possible that the user wanted a split in the tab they're
currently in, in the unelevated window, but they don't want a split in the
elevated window.
What if there's not space in the elevated window to create the split (but there
would be in the current window)? That would sure make it seem like nothing
happened, silently.
We could alternatively have cross-elevation splits default to always opening a
new tab. That might mitigate some of the odd behaviors. Until we actually have
support for running commands in existing windows, we'll always need to make a
new window when running elevated. We'll need to make the new window for new tabs
and splits, because there's no way to invoke another existing window.
A third proposal is to pop a warning dialog at the user when they try to open an
elevated split from and unelevated window. This dialog could be something like
> What you requested couldn't be completed. Do you want to:
> A. Make me a new tab instead.
> B. Forget it and cancel. I'll go fix my config.
I'm certainly leaning towards proposal 2 - always create a new tab. This is how
it's implemented in [#8514]. In that PR, this seems to work sensibly.
-->
After discussing with the team, we have decided that the most sensible approach
for handling a cross-elevation `split-pane` is to just create a new tab in the
elevated window. The user can always re-attach the pane as a split with the
`move-pane` command once the new pane in the elevated window.
#### Configure the Terminal to _always_ run elevated
`elevate` is a per-profile property, not a global property. If a user
wants to always have all instances of the Terminal run elevated, they
could set `"elevate": true` in their profile defaults. That would cause _all_
profiles they launch to always spawn as elevated windows.
#### `elevate` in Actions
Additionally, we'll add the `elevate` property to the `NewTerminalArgs` used in
the `newTab`, `splitPane`, and `newWindow` actions. This is similar to how other
properties of profiles can be overridden at launch time. This will allow
windows, tabs and panes to all be created specifically as elevated windows.
In the `NewTerminalArgs`, `elevate` will be an optional boolean, with the
following behavior:
* `null` (_default_): Don't modify the `elevate` property for this profile
* `true`: This launch should act like the profile had `"elevate": true` in its
properties.
* `false`: This launch should act like the profile had `"elevate": false` in its
properties.
We'll also add an iterable command for opening a profile in an
elevated tab, with the following json:
```jsonc
{
// New elevated tab...
"name": { "key": "NewElevatedTabParentCommandName", "icon": "UAC-Shield.png" },
"commands": [
{
"iterateOn": "profiles",
"icon": "${profile.icon}",
"name": "${profile.name}",
"command": { "action": "newTab", "profile": "${profile.name}", "elevated": true }
}
]
},
```
#### Elevation from the dropdown
Currently, the new tab dropdown supports opening a new pane by
<kbd>Alt+click</kbd>ing on a profile. We could similarly add support to open a
tab elevated with <kbd>Ctrl+click</kbd>. This is similar to the behavior of the
Windows taskbar. It supports creating an elevated instance of a program by
<kbd>Ctrl+click</kbd>ing on entries as well.
## Implementation Details
### Starting an elevated process from an unelevated process
It seems that we're able to create an elevated process by passing the `"runas"`
verb to
[`ShellExecute`](https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutea).
So we could use something like
```c++
ShellExecute(nullptr,
L"runas",
L"wt.exe",
L"-w new new-tab [args...]",
nullptr,
SW_SHOWNORMAL);
```
This will ask the shell to perform a UAC prompt before spawning `wt.exe` as an
elevated process.
> 👉 NOTE: This mechanism won't always work on non-Desktop SKUs of Windows. For
> more discussion, see [Elevation on OneCore SKUs](#Elevation-on-OneCore-SKUs).
## Potential Issues
<table>
<tr>
<td><strong>Accessibility</strong></td>
<td>
The set of changes proposed here are not expected to introduce any new
accessibility issues. Users can already create elevated Terminal windows. Making
it easier to create these windows doesn't really change our accessibility story.
</td>
</tr>
<tr>
<td><strong>Security</strong></td>
<td>
We won't be doing anything especially unique, so there aren't expected to be any
substantial security risks associated with these changes. Users can already
create elevated Terminal windows, so we're not really introducing any new
functionality, from a security perspective.
We're relying on the inherent security of the `runas` verb of `ShellExecute` to
prevent any sort of unexpected escalation-of-privilege.
<hr>
One security concern is the fact that the `settings.json` file is currently a
totally unsecured file. It's completely writable by any medium-IL process. That
means it's totally possible for a malicious program to change the file. The
malicious program could find a user's "Elevated PowerShell" profile, and change
the commandline to `malicious.exe`. The user might then think that their
"Elevated PowerShell" will run `powershell.exe` elevated, but will actually
auto-elevate this attacker.
If all we expose to the user is the name of the profile in the UAC dialog, then
there's no way for the user to be sure that the program that's about to be
launched is actually what they expect.
To help mitigate this, we should _always_ pass the evaluated `commandline` as a
part of the call to `ShellExecute`. the arguments that are passed to
`ShellExecute` are visible to the user, though they need to click the "More
Details" dropdown to reveal them.
We will need to mitigate this vulnerability regardless of adding support for the
auto-elevation of individual terminal tabs/panes. If a user is launching the
Terminal elevated (i.e. from the Win+X menu in Windows 11), then it's possible
for a malicious program to overwrite the `commandline` of their default profile.
The user may now unknowingly invoke this malicious program while thinking they
are simply launching the Terminal.
To deal with this more broadly, we will display a dialog within the Terminal
window before creating **any** elevated terminal instance. In that dialog, we'll
display the commandline that will be executed, so the user can very easily
confirm the commandline.
This will need to happen for all elevated terminal instances. For an elevated
Windows Terminal window, this means _all_ connections made by the Terminal.
Every time the user opens a new profile or a new commandline in a pane, we'll
need to prompt them first to confirm the commandline. This dialog within the
elevated window will also prevent an attacker from editing the `settings.json`
file while the user already has an elevated Terminal window open and hijacking a
profile.
The dialog options will certainly be annoying to users who don't want to be
taken out of their flow to confirm the commandline that they wish to launch.
There's precedent for a similar warning being implemented by VSCode, with their
[Workspace Trust] feature. They too faced a similar backlash when the feature
first shipped. However, in light of recent global cybersecurity attacks, this is
seen as an acceptable UX degradation in the name of application trust. We don't
want to provide an avenue that's too easy to abuse.
When the user confirms the commandline of this profile as something safe to run,
we'll add it to an elevated-only version of `state.json`. (see [#7972] for more
details). This elevated version of the file will only be accessible by the
elevated Terminal, so an attacker cannot hijack the contents of the file. This
will help mitigate the UX discomfort caused by prompting on every commandline
launched. This should mean that the discomfort is only limited to the first
elevated launch of a particular profile. Subsequent launches (without modifying
the `commandline`) will work as they always have.
The dialog for confirming these commandlines should have a link to the docs for
"Learn more...". Transparency in the face of this dialog should
mitigate some dissatisfaction.
The dialog will _not_ appear if the user does not have a split token - if the
user's PC does not have UAC enabled, then they're _already_ running as an
Administrator. Everything they do is elevated, so they shouldn't be prompted in
this way.
The Settings UI should also expose a way of viewing and removing these cached
entries. This page should only be populated in the elevated version of the
Terminal.
</td>
</tr>
<tr>
<td><strong>Reliability</strong></td>
<td>
No changes to our reliability are expected as a part of this change.
</td>
</tr>
<tr>
<td><strong>Compatibility</strong></td>
<td>
There are no serious compatibility concerns expected with this changelist. The
new `elevate` property will be unset by default, so users will heed to opt-in
to the new auto-elevating behavior.
There is one minor concern regarding introducing the UAC shield on the window.
We're planning on using themes to configure the appearance of the shield. That
means we'll need to ship themes before the user will be able to hide the shield
again.
</td>
</tr>
<tr>
<td><strong>Performance, Power, and Efficiency</strong></td>
<td>
No changes to our performance are expected as a part of this change.
</td>
</tr>
</table>
### Centennial Applications
In the past, we've had a notoriously rough time with the Centennial app
infrastructure and running the Terminal elevated. Notably, we've had to list all
our WinRT classes in our SxS manifest so they could be activated using
unpackaged WinRT while running elevated. Additionally, there are plenty of
issues running the Terminal in an "over the shoulder" elevation (OTS) scenario.
Specifically, we're concerned with the following scenario:
* the current user account has the Terminal installed,
* but they aren't an Administrator,
* the Administrator account doesn't have the Terminal installed.
In that scenario, the user can run into issues launching the Terminal in an
elevated context (even after entering the Admin's credentials in the UAC
prompt).
This spec proposes no new mitigations for dealing with these issues. It may in
fact make them more prevalent, by making elevated contexts more easily
accessible.
Unfortunately, these issues are OS bugs that are largely out of our own control.
We will continue to apply pressure to the centennial app team internally as we
encounter these issues. They are are team best equipped to resolve these issues.
### Default Terminal & auto-elevation
In the future, when we support setting the Terminal as the "default terminal
emulator" on Windows. When that lands, we will use the `profiles.defaults`
settings to create the tab where we'll be hosting the commandline client. If the user has
`"elevate": true` in their `profiles.defaults`, we'd usually try to
auto-elevate the profile. In this scenario, however, we can't do that. The
Terminal is being invoked on behalf of the client app launching, instead of the
Terminal invoking the client application.
**2021-08-17 edit**: Now that "defterm" has shipped, we're a little more aware
of some of the limitations with packaged COM and elevation boundaries. Defterm
cannot be used with elevated processes _at all_ currently (see [#10276]). When
an elevated commandline application is launched, it will always just appear in
`conhost.exe`. Furthermore, An unelevated peasant can't communicate with an
elevated monarch so we can't toss the connection to the elevated monarch and
have them handle it.
The simplest solution here is to just _always_ ignore the `elevate` property for
incoming defterm connections. This is not an ideal solution, and one that we're
willing to revisit if/when [#10276] is ever fixed.
### Elevation on OneCore SKUs
This spec proposes using `ShellExecute` to elevate the Terminal window. However,
not all Windows SKUs have support for `ShellExecute`. Notably, the non-Desktop
SKUs, which are often referred to as "OneCore" SKUs. On these platforms, we
won't be able to use `ShellExecute` to elevate the Terminal. There might not
even be the concept of multiple elevation levels, or different users, depending
on the SKU.
Fortunately, this is a mostly hypothetical concern for the moment. Desktop is
the only publicly supported SKU for the Terminal currently. If the Terminal ever
does become available on those SKUs, we can use these proposals as mitigations.
* If elevation is supported, there must be some other way of elevating a
process. We could always use that mechanism instead.
* If elevation isn't supported (I'm thinking 10X is one of these), then we could
instead display a warning dialog whenever a user tries to open an elevated
profile.
- We could take the warning a step further. We could add another settings
validation step. This would warn the user if they try to mark any profiles
or actions as `"elevate":true`
## Future considerations
* If we wanted to go even further down the visual differentiation route, we
could consider allowing the user to set an entirely different theme ([#3327])
based on the elevation state. Something like `elevatedTheme`, to pick another
theme from the set of themes. This would allow them to force elevated windows
to have a red titlebar, for example.
* Over the course of discussion concerning appearance objects ([#8345]), it
became clear that having separate "elevated" appearances defined for
`profile`s was overly complicated. This is left as a consideration for a
possible future extension that could handle this scenario in a cleaner way.
* Similarly, we're going to leave [#3637] "different profiles when elevated vs
unelevated" for the future. This also plays into the design of "configure the
new tab dropdown" ([#1571]), and reconciling those two designs is out-of-scope
for this particular release.
* Tangentially, we may want to have a separate Terminal icon we ship with the
UAC shield present on it. This would be especially useful for the tray icon.
Since there will be different tray icon instances for elevated and unelevated
windows, having unique icons may help users identify which is which.
### De-elevating a Terminal
the original version of this spec proposed that `"elevated":false` from an
elevated Terminal window should create a new unelevated Terminal instance. The
mechanism for doing this is described in [The Old New Thing: How can I launch an
unelevated process from my elevated process, redux].
This works well when the Terminal is running unpackaged. However, de-elevating a
process does not play well with packaged centennial applications. When asking
the OS to run the packaged application from an elevated context, the system will
still create the child process _elevated_. This means the packaged version of
the Terminal won't be able to create a new unelevated Terminal instance.
From an internal mail thread:
> App model intercepts the `CreateProcess` call and redirects it to a COM
> service. The parent of a packaged app is not the launching app, its some COM
> service. So none of the parent process nonsense will work because the
> parameters you passed to `CreateProcess` arent being used to create the
> process.
If this is fixed in the future, we could theoretically re-introduce de-elevating
a profile. The original spec proposed a `"elevated": bool?` setting, with the
following behaviors:
* `null` (_default_): Don't modify the elevation level when running this profile
* `true`: If the current window is unelevated, try to create a new elevated
window to host this connection.
* `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`.
### Change profile appearance for elevated windows
In [#3062] and [#8345], we're planning on allowing users to set different
appearances for a profile whether it's focused or not. We could do similar thing
to enable a profile to have a different appearance when elevated. In the
simplest case, this could allow the user to set `"background": "#ff0000"`. This
would make a profile always appear to have a red background when in an elevated
window.
The more specific details of this implementation are left to the spec
[Configuration object for profiles].
In discussion of that spec, we decided that it would be far too complicated to
try and overload the `unfocusedAppearance` machinery for differentiating between
elevated and unelevated versions of the same profile. Already, that would lead
to 4 states: [`appearance`, `unfocusedAppearance`, `elevatedAppearance`,
`elevatedUnfocusedAppearance`]. This would lead to a combinatorial explosion if
we decided in the future that there should also be other states for a profile.
This particular QoL improvement is currently being left as a future
consideration, should someone come up with a clever way of defining
elevated-specific settings.
<!--
Brainstorming notes for future readers:
You could have a profile that layers on an existing profile, with elevated-specific settings:
{
"name": "foo",
"background": "#0000ff",
"commandline": "cmd.exe /k echo I am unelevated"
},
{
"inheritsFrom": "foo",
"background": "#ff0000",
"elevate": true,
"commandline": "cmd.exe /k echo I am ELEVATED"
}
-->
<!-- Footnotes -->
[#632]: https://github.com/microsoft/terminal/issues/632
[#1032]: https://github.com/microsoft/terminal/issues/1032
[#1571]: https://github.com/microsoft/terminal/issues/1571
[#1939]: https://github.com/microsoft/terminal/issues/1939
[#3062]: https://github.com/microsoft/terminal/issues/3062
[#3327]: https://github.com/microsoft/terminal/issues/3327
[#3637]: https://github.com/microsoft/terminal/issues/3637
[#4472]: https://github.com/microsoft/terminal/issues/4472
[#5000]: https://github.com/microsoft/terminal/issues/5000
[#7972]: https://github.com/microsoft/terminal/pull/7972
[#8311]: https://github.com/microsoft/terminal/issues/8311
[#8345]: https://github.com/microsoft/terminal/issues/8345
[#8514]: https://github.com/microsoft/terminal/issues/8514
[#10276]: https://github.com/microsoft/terminal/issues/10276
[Process Model 2.0 Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0.md
[Configuration object for profiles]: https://github.com/microsoft/terminal/blob/main/doc/specs/Configuration%20object%20for%20profiles.md
[Session Management Spec]: https://github.com/microsoft/terminal/blob/main/doc/specs/%234472%20-%20Windows%20Terminal%20Session%20Management.md
[The Old New Thing: How can I launch an unelevated process from my elevated process, redux]: https://devblogs.microsoft.com/oldnewthing/20190425-00/?p=102443
[Workspace Trust]: https://code.visualstudio.com/docs/editor/workspace-trust

Binary file not shown.

Before

Width:  |  Height:  |  Size: 565 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -1,130 +0,0 @@
---
author: Kayla Cinnamon - cinnamon-msft
created on: 2021-03-04
last updated: 2021-03-09
issue id: 6900
---
# Actions Page
## Abstract
We need to represent actions inside the settings UI. This spec goes through the possible use cases and reasoning for including specific features for actions inside the settings UI.
## Background
### Inspiration
It would be ideal if we could get the settings UI to have parity with the JSON file. This will take some design work if we want every feature possible in relation to actions. There is also the option of not having parity with the JSON file in order to present a simpler UX.
### User Stories
All of these features are possible with the JSON file. This spec will go into discussion of which (possibly all) of these user stories need to be handled by the settings UI.
- Add key bindings to an action that does not already have keys assigned
- Edit key bindings for an action
- Remove key bindings from an action
- Add multiple key bindings for the same action
- Create an iterable action
- Create a nested action
- Choose which actions appear inside the command palette
- See all possible actions, regardless of keys
Commands with properties:
- sendInput has "input"
- closeOtherTabs has "index"
- closeTabsAfter has "index"
- renameTab has "title"*
- setTabColor has "color"*
- newWindow has "commandline", "startingDirectory", "tabTitle", "index", "profile"
- splitPane has "split", "commandline", "startingDirectory", "tabTitle", "index", "profile", "splitMode", "size"
- copy has "singleLine", "copyFormatting"
- scrollUp has "rowsToScroll"
- scrollDown has "rowsToScroll"
- setColorScheme has "colorScheme"
Majority of these commands listed above are intended for the command palette, so they wouldn't make much sense with keys assigned to them anyway.
### Future Considerations
One day we'll have actions that can be invoked by items in the dropdown menu. This setting will have to live somewhere. Also, once we get a status bar, people may want to invoke actions from there.
## Solution Design
### Proposal 1: Keyboard and Command Palette pages
Implement a Keyboard page in place of the Actions page. Also plan for a Command Palette page in the future if it's something that's heavily requested. The Command Palette page would cover the missing use cases listed below.
When users want to add a new key binding, the dropdown will list every action, regardless if it already has keys assigned. This page should show every key binding assigned to an action, even if there are multiple bindings to the same action.
Users will be able to view every possible action from the command palette if they'd like.
Use cases covered:
- Add key bindings to an action that does not already have keys assigned
- Edit key bindings for an action
- Remove key bindings from an action
- Add multiple key bindings for the same action
- See all actions that have keys assigned
Use cases missing:
- Create an iterable action
- Create a nested action
- Choose which actions appear inside the command palette
- See all possible actions, regardless of keys
* **Pros**:
- This allows people to edit their actions in most of their scenarios.
- This gives us some wiggle room to cover majority of the use cases we need and seeing if people want the other use cases that are missing.
* **Cons**:
- Unfortunately we couldn't cover every single use case with this design.
- You can't edit the properties that are on some commands, however the default commands from the command palette include options with properties anyway. For example "decrease font size" has the `delta` property already included.
### Proposal 2: Have everything on one Actions page
Implement an Actions page that allows you to create actions designed for the command palette as well as actions with keys.
Use cases covered:
- Add key bindings to an action that does not already have keys assigned
- Edit key bindings for an action
- Remove key bindings from an action
- Add multiple key bindings for the same action
- See all actions that have keys assigned
- Create an iterable action
- Create a nested action
- Choose which actions appear inside the command palette
- See all possible actions, regardless of keys
I could not come up with a UX design that wasn't too complicated or confusing for this scenario.
**Pros**:
- There is full parity with the JSON file.
**Cons**:
- Could not come up with a simplistic design to represent all of the use cases (which makes the settings UI not as enticing since it promotes ease of use).
## Conclusion
We considered Proposal 2, however the design became cluttered very quickly and we agreed to create two pages and start off with Proposal 1.
## UI/UX Design
![Click edit on key binding](./edit-click.png)
The Add new button is using the secondary color, to align with the button on the Color schemes page.
![Edit key binding](./edit-keys.png)
![Click add new](./add-click.png)
![Add key binding](./add-keys.png)
## Potential Issues
This design is not 1:1 with the JSON file, so actions that don't have keys will not appear on this page. Additionally, you can't add a new action without keys with this current design.
You also cannot specify properties on commands (like the `newTab` command) and these will have to be added through the JSON file. Considering there are only a few of these and we're planning to iterate on this and add a Command Palette page, we were okay with this decision.
## Resources
### Footnotes

View File

@@ -29,8 +29,8 @@ Below is the schedule for when milestones will be included in release builds of
| 2021-03-01 | [1.7] in Windows Terminal Preview<br>[1.6] in Windows Terminal | [Windows Terminal Preview 1.7 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-7-release/) |
| 2021-04-14 | [1.8] in Windows Terminal Preview<br>[1.7] in Windows Terminal | [Windows Terminal Preview 1.8 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-8-release/) |
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/) |
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/) |
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
| 2021-07-31 | 1.10 in Windows Terminal Preview<br>[1.9] in Windows Terminal | |
| 2021-08-30 | 1.11 in Windows Terminal Preview<br>1.10 in Windows Terminal | |
| 2021-10-31 | 1.12 in Windows Terminal Preview<br>1.11 in Windows Terminal | |
| 2021-11-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
| 2021-12-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
@@ -89,8 +89,6 @@ Feature Notes:
[1.7]: https://github.com/microsoft/terminal/milestone/32
[1.8]: https://github.com/microsoft/terminal/milestone/33
[1.9]: https://github.com/microsoft/terminal/milestone/34
[1.10]: https://github.com/microsoft/terminal/milestone/35
[1.11]: https://github.com/microsoft/terminal/milestone/36
[2.0]: https://github.com/microsoft/terminal/milestone/22
[#1564]: https://github.com/microsoft/terminal/issues/1564
[#6720]: https://github.com/microsoft/terminal/pull/6720

View File

@@ -4,7 +4,7 @@ This was originally imported by @Austin-Lamb in December 2020.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
## What should be done to update this in the future?
@@ -12,4 +12,4 @@ That provenance file is automatically read and inventoried by Microsoft systems
2. Take the parts you want, but leave most of it behind since it's HUGE and will bloat the repo to take it all. At the time of this writing, we only use small_vector.hpp and its dependencies as a header-only library.
3. Validate that the license in the root of the repository didn't change and update it if so. It is sitting in a version-specific subdirectory below this readme.
If it changed dramatically, ensure that it is still compatible with our license scheme. Also update the NOTICE file in the root of our repository to declare the third-party usage.
4. Submit the pull.
4. Submit the pull.

View File

@@ -4,7 +4,7 @@ This was originally imported by @miniksa in January 2020.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
## What should be done to update this in the future?

View File

@@ -4,7 +4,7 @@ This was originally imported by @miniksa in March 2020.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
## What should be done to update this in the future?

View File

@@ -4,7 +4,7 @@ This was originally imported by @DHowett-MSFT in April 2020.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
## What should be done to update this in the future?

View File

@@ -4,7 +4,7 @@ This was originally imported by @PankajBhojwani in September 2020.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
## What should be done to update this in the future?

View File

@@ -4,7 +4,7 @@ This was originally imported by @miniksa in March 2020.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.
## What should be done to update this in the future?

View File

@@ -4,4 +4,4 @@ This manifest anchors our usage of rgb.txt from the X11 distribution.
The provenance information (where it came from and which commit) is stored in the file `cgmanifest.json` in the same directory as this readme.
Please update the provenance information in that file when ingesting an updated version of the dependent library.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropriate governance standards.
That provenance file is automatically read and inventoried by Microsoft systems to ensure compliance with appropiate governance standards.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -8,5 +8,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
### Fonts Included
* Cascadia Code, Cascadia Mono (2108.26)
* from microsoft/cascadia-code@f91d08f703ee61cf4ae936b9700ca974de2748fe
* Cascadia Code, Cascadia Mono (2106.17)
* from microsoft/cascadia-code@fb0bce69c1c12f6c298b8bc1c1d181868f5daa9a

View File

@@ -140,12 +140,12 @@
<!-- **END VC LIBS HACK** -->
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,9 @@ Licensed under the MIT license.
#include <conattrs.hpp>
#include "MySettings.g.h"
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;
using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
namespace winrt::SampleApp::implementation
{
struct MySettings : MySettingsT<MySettings>
@@ -41,6 +44,8 @@ namespace winrt::SampleApp::implementation
winrt::Microsoft::Terminal::Core::ICoreAppearance UnfocusedAppearance() { return {}; };
WINRT_PROPERTY(bool, TrimBlockSelection, false);
WINRT_PROPERTY(bool, DetectURLs, true);
WINRT_PROPERTY(bool, IntenseIsBright, true);
// ------------------------ End of Core Settings -----------------------
WINRT_PROPERTY(winrt::hstring, ProfileName);
@@ -78,7 +83,10 @@ namespace winrt::SampleApp::implementation
WINRT_PROPERTY(winrt::hstring, PixelShaderPath);
WINRT_PROPERTY(bool, DetectURLs, true);
WINRT_PROPERTY(IFontFeatureMap, FontFeatures);
WINRT_PROPERTY(IFontAxesMap, FontAxes);
WINRT_PROPERTY(bool, IntenseIsBold, true);
private:
std::array<winrt::Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE> _ColorTable;

View File

@@ -147,13 +147,13 @@
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View File

@@ -80,13 +80,13 @@
</ItemGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
</packages>

View File

@@ -120,14 +120,14 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.4\build\native\Microsoft.VCRTForwarders.140.targets'))" />

View File

@@ -2,6 +2,6 @@
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
</packages>

View File

@@ -146,12 +146,12 @@
<!-- **END VC LIBS HACK** -->
<!-- This is required to get the package dependency in the AppXManifest. -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />

View File

@@ -98,10 +98,10 @@
<!-- From Microsoft.UI.Xaml.targets -->
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
</PropertyGroup>
<!-- We actually can just straight up reference MUX here, it's fine -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
</Project>

View File

@@ -42,8 +42,6 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestLayerProfileOnColorScheme);
TEST_METHOD(TestCommandlineToTitlePromotion);
TEST_CLASS_SETUP(ClassSetup)
{
return true;
@@ -65,7 +63,7 @@ namespace SettingsModelLocalTests
const std::string settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": { "list": [
"profiles": [
{
"name": "profile0",
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
@@ -84,9 +82,6 @@ namespace SettingsModelLocalTests
"commandline": "wsl.exe"
}
],
"defaults": {
"historySize": 29
} },
"keybindings": [
{ "keys": ["ctrl+a"], "command": { "action": "splitPane", "split": "vertical" } },
{ "keys": ["ctrl+b"], "command": { "action": "splitPane", "split": "vertical", "profile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" } },
@@ -222,18 +217,9 @@ namespace SettingsModelLocalTests
const auto profile{ settings.GetProfileForArgs(realArgs.TerminalArgs()) };
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr) };
const auto termSettings = settingsStruct.DefaultSettings();
if constexpr (Feature_ShowProfileDefaultsInSettings::IsEnabled())
{
// This action specified a command but no profile; it gets reassigned to the base profile
VERIFY_ARE_EQUAL(settings.ProfileDefaults(), profile);
VERIFY_ARE_EQUAL(29, termSettings.HistorySize());
}
else
{
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
}
VERIFY_ARE_EQUAL(guid0, profile.Guid());
VERIFY_ARE_EQUAL(L"foo.exe", termSettings.Commandline());
VERIFY_ARE_EQUAL(1, termSettings.HistorySize());
}
{
KeyChord kc{ true, false, false, false, static_cast<int32_t>('F'), 0 };
@@ -568,85 +554,4 @@ namespace SettingsModelLocalTests
VERIFY_ARE_EQUAL(ARGB(0, 0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme)
VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5->CursorColor()); // default
}
void TerminalSettingsTests::TestCommandlineToTitlePromotion()
{
const std::string settingsJson{ R"(
{
"defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"profiles": { "list": [
{
"name": "profile0",
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"historySize": 1,
"commandline": "cmd.exe"
},
],
"defaults": {
"historySize": 29
} }
})" };
const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
CascadiaSettings settings{ til::u8u16(settingsJson) };
{ // just a profile (profile wins)
NewTerminalArgs args{};
args.Profile(L"profile0");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
}
{ // profile and command line -> no promotion (profile wins)
NewTerminalArgs args{};
args.Profile(L"profile0");
args.Commandline(L"foo.exe");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"profile0", settingsStruct.DefaultSettings().StartingTitle());
}
{ // just a title -> it is propagated
NewTerminalArgs args{};
args.TabTitle(L"Analog Kid");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"Analog Kid", settingsStruct.DefaultSettings().StartingTitle());
}
{ // title and command line -> no promotion
NewTerminalArgs args{};
args.TabTitle(L"Digital Man");
args.Commandline(L"foo.exe");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"Digital Man", settingsStruct.DefaultSettings().StartingTitle());
}
{ // just a commandline -> promotion
NewTerminalArgs args{};
args.Commandline(L"foo.exe");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
}
// various typesof commandline follow
{
NewTerminalArgs args{};
args.Commandline(L"foo.exe bar");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"foo.exe", settingsStruct.DefaultSettings().StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L"\"foo exe.exe\" bar");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"foo exe.exe", settingsStruct.DefaultSettings().StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L"\"\" grand designs");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
}
{
NewTerminalArgs args{};
args.Commandline(L" imagine a man");
const auto settingsStruct{ TerminalSettings::CreateWithNewTerminalArgs(settings, args, nullptr) };
VERIFY_ARE_EQUAL(L"", settingsStruct.DefaultSettings().StartingTitle());
}
}
}

View File

@@ -92,11 +92,11 @@
<!-- From Microsoft.UI.Xaml.targets -->
<Native-Platform Condition="'$(Platform)' == 'Win32'">x86</Native-Platform>
<Native-Platform Condition="'$(Platform)' != 'Win32'">$(Platform)</Native-Platform>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
<_MUXBinRoot>&quot;$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\runtimes\win10-$(Native-Platform)\native\&quot;</_MUXBinRoot>
</PropertyGroup>
<!-- We actually can just straight up reference MUX here, it's fine -->
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
</Project>

View File

@@ -123,7 +123,7 @@
</Reference>
</ItemGroup>
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)\src\common.build.post.props" />

View File

@@ -20,7 +20,8 @@
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Include the MUX Controls resources -->
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"
ControlsResourcesVersion="Version1" />
<ResourceDictionary>
<!--
@@ -45,7 +46,7 @@
Color="{ThemeResource SystemErrorTextColor}" />
<!-- Suppress top padding -->
<Thickness x:Key="TabViewHeaderPadding">9,0,8,0</Thickness>
<Thickness x:Key="TabViewHeaderPadding">8,0,8,0</Thickness>
<!-- Remove when implementing WinUI 2.6 -->
<Thickness x:Key="FlyoutContentPadding">12</Thickness>

View File

@@ -874,47 +874,4 @@ namespace winrt::TerminalApp::implementation
}
}
}
void TerminalPage::_HandleHighlightCursor(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto termControl{ _GetActiveControl() })
{
termControl.HighlightCursor();
args.Handled(true);
}
}
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>())
{
if (const auto termControl{ _GetActiveControl() })
{
termControl.ClearBuffer(realArgs.Clear());
args.Handled(true);
}
}
}
}
void TerminalPage::_HandleMultipleActions(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<MultipleActionsArgs>())
{
for (const auto& action : realArgs.Actions())
{
_actionDispatch->DoAction(action);
}
args.Handled(true);
}
}
}
}

View File

@@ -399,10 +399,8 @@ static const std::map<std::string, FocusDirection> focusDirectionMap = {
{ "right", FocusDirection::Right },
{ "up", FocusDirection::Up },
{ "down", FocusDirection::Down },
{ "previous", FocusDirection::Previous },
{ "nextInOrder", FocusDirection::NextInOrder },
{ "previousInOrder", FocusDirection::PreviousInOrder },
{ "first", FocusDirection::First },
};
// Method Description:
@@ -605,6 +603,13 @@ NewTerminalArgs AppCommandlineArgs::_getNewTerminalArgs(AppCommandlineArgs::NewT
args.Profile(winrt::to_hstring(_profileName));
}
if (!*subcommand.profileNameOption && !_commandline.empty())
{
// If there's no profile, but there IS a command line, set the tab title to the first part of the command
// This will ensure that the tab we spawn has a name (since it didn't get one from its profile!)
args.TabTitle(winrt::to_hstring(til::at(_commandline, 0)));
}
if (*subcommand.startingDirectoryOption)
{
args.StartingDirectory(winrt::to_hstring(_startingDirectory));

View File

@@ -36,6 +36,7 @@ namespace winrt::TerminalApp::implementation
_allCommands = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_tabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_mruTabActions = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_commandLineHistory = winrt::single_threaded_vector<winrt::TerminalApp::FilteredCommand>();
_switchToMode(CommandPaletteMode::ActionMode);
@@ -244,17 +245,6 @@ namespace winrt::TerminalApp::implementation
_PreviewActionHandlers(*this, actionPaletteItem.Command());
}
}
else if (_currentMode == CommandPaletteMode::CommandlineMode)
{
if (filteredCommand)
{
SearchBoxPlaceholderText(filteredCommand.Item().Name());
}
else
{
SearchBoxPlaceholderText(RS_(L"CmdPalCommandlinePrompt"));
}
}
}
void CommandPalette::_previewKeyDownHandler(IInspectable const& /*sender*/,
@@ -375,17 +365,6 @@ namespace winrt::TerminalApp::implementation
_searchBox().PasteFromClipboard();
e.Handled(true);
}
else if (key == VirtualKey::Right && _currentMode == CommandPaletteMode::CommandlineMode)
{
if (const auto command{ _filteredActionsView().SelectedItem().try_as<winrt::TerminalApp::FilteredCommand>() })
{
_searchBox().Text(command.Item().Name());
_searchBox().Select(_searchBox().Text().size(), 0);
_searchBox().Focus(FocusState::Programmatic);
_filteredActionsView().SelectedIndex(-1);
e.Handled(true);
}
}
}
// Method Description:
@@ -608,7 +587,7 @@ namespace winrt::TerminalApp::implementation
case CommandPaletteMode::TabSwitchMode:
return _tabSwitcherMode == TabSwitcherMode::MostRecentlyUsed ? _mruTabActions : _tabActions;
case CommandPaletteMode::CommandlineMode:
return _loadRecentCommands();
return _commandLineHistory;
default:
return _allCommands;
}
@@ -741,10 +720,14 @@ namespace winrt::TerminalApp::implementation
// - <none>
void CommandPalette::_dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command)
{
const auto filteredCommand = command ? command : _buildCommandLineCommand(winrt::hstring(_getTrimmedInput()));
const auto filteredCommand = command ? command : _buildCommandLineCommand(_getTrimmedInput());
if (filteredCommand.has_value())
{
_updateRecentCommands(filteredCommand.value().Item().Name());
if (_commandLineHistory.Size() == CommandLineHistoryLength)
{
_commandLineHistory.RemoveAtEnd();
}
_commandLineHistory.InsertAt(0, filteredCommand.value());
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
@@ -761,14 +744,15 @@ namespace winrt::TerminalApp::implementation
}
}
std::optional<TerminalApp::FilteredCommand> CommandPalette::_buildCommandLineCommand(const hstring& commandLine)
std::optional<winrt::TerminalApp::FilteredCommand> CommandPalette::_buildCommandLineCommand(std::wstring const& commandLine)
{
if (commandLine.empty())
{
return std::nullopt;
}
auto commandLinePaletteItem{ winrt::make<CommandLinePaletteItem>(commandLine) };
winrt::hstring cl{ commandLine };
auto commandLinePaletteItem{ winrt::make<winrt::TerminalApp::implementation::CommandLinePaletteItem>(cl) };
return winrt::make<FilteredCommand>(commandLinePaletteItem);
}
@@ -1233,81 +1217,4 @@ namespace winrt::TerminalApp::implementation
itemContainer.DataContext(args.Item());
}
}
// Method Description:
// - Reads the list of recent commands from the persistent application state
// Return Value:
// - The list of FilteredCommand representing the ones stored in the state
IVector<TerminalApp::FilteredCommand> CommandPalette::_loadRecentCommands()
{
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
// If this is the first time we've opened the commandline mode and
// there aren't any recent commands, then just return an empty vector.
if (!recentCommands)
{
return single_threaded_vector<TerminalApp::FilteredCommand>();
}
std::vector<TerminalApp::FilteredCommand> parsedCommands;
parsedCommands.reserve(std::min(recentCommands.Size(), CommandLineHistoryLength));
for (const auto& c : recentCommands)
{
if (parsedCommands.size() >= CommandLineHistoryLength)
{
// Don't load more than CommandLineHistoryLength commands
break;
}
if (const auto parsedCommand = _buildCommandLineCommand(c))
{
parsedCommands.push_back(*parsedCommand);
}
}
return single_threaded_vector(std::move(parsedCommands));
}
// Method Description:
// - Update recent commands by putting the provided command as most recent.
// Upon race condition might override an update made by another window.
// Return Value:
// - <none>
void CommandPalette::_updateRecentCommands(const hstring& command)
{
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
// If this is the first time we've opened the commandline mode and
// there aren't any recent commands, then just store the new command.
if (!recentCommands)
{
ApplicationState::SharedInstance().RecentCommands(single_threaded_vector(std::move(std::vector{ command })));
return;
}
const auto numNewRecentCommands = std::min(recentCommands.Size() + 1, CommandLineHistoryLength);
std::vector<hstring> newRecentCommands;
newRecentCommands.reserve(numNewRecentCommands);
std::unordered_set<hstring> uniqueCommands;
uniqueCommands.reserve(numNewRecentCommands);
newRecentCommands.push_back(command);
uniqueCommands.insert(command);
for (const auto& c : recentCommands)
{
if (newRecentCommands.size() >= CommandLineHistoryLength)
{
// Don't store more than CommandLineHistoryLength commands
break;
}
if (uniqueCommands.emplace(c).second)
{
newRecentCommands.push_back(c);
}
}
ApplicationState::SharedInstance().RecentCommands(single_threaded_vector(std::move(newRecentCommands)));
}
}

View File

@@ -123,16 +123,15 @@ namespace winrt::TerminalApp::implementation
void _dispatchCommand(winrt::TerminalApp::FilteredCommand const& command);
void _dispatchCommandline(winrt::TerminalApp::FilteredCommand const& command);
void _switchToTab(winrt::TerminalApp::FilteredCommand const& command);
static std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(const winrt::hstring& commandLine);
std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(std::wstring const& commandLine);
void _dismissPalette();
void _scrollToIndex(uint32_t index);
uint32_t _getNumVisibleItems();
static constexpr uint32_t CommandLineHistoryLength = 20;
static Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _loadRecentCommands();
static void _updateRecentCommands(const winrt::hstring& command);
static constexpr int CommandLineHistoryLength = 10;
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandLineHistory{ nullptr };
::TerminalApp::AppCommandlineArgs _appArgs;
void _choosingItemContainer(Windows::UI::Xaml::Controls::ListViewBase const& sender, Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs const& args);

View File

@@ -9,7 +9,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
HorizontalAlignment="Left"
VerticalAlignment="Top"
d:DesignHeight="40"
d:DesignHeight="36"
d:DesignWidth="400"
Background="Transparent"
Orientation="Horizontal"
@@ -124,9 +124,8 @@
tabs will be flush with the top of the window. See GH#2541 for
details.
-->
<x:Double x:Key="CaptionButtonHeightWindowed">40.0</x:Double>
<!-- 32 + 1 to compensate for GH#10746 -->
<x:Double x:Key="CaptionButtonHeightMaximized">33.0</x:Double>
<x:Double x:Key="CaptionButtonHeightWindowed">36.0</x:Double>
<x:Double x:Key="CaptionButtonHeightMaximized">32.0</x:Double>
<Style x:Key="CaptionButton"
TargetType="Button">

View File

@@ -58,8 +58,8 @@ Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFo
_root.Background(s_unfocusedBorderBrush);
// Register an event with the control to have it inform us when it gains focus.
_gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
_lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler });
_gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
_lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler });
// When our border is tapped, make sure to transfer focus to our control.
// LOAD-BEARING: This will NOT work if the border's BorderBrush is set to
@@ -223,11 +223,10 @@ bool Pane::ResizePane(const ResizeDirection& direction)
// Arguments:
// - sourcePane: the pane to navigate from
// - direction: which direction to go in
// - mruPanes: the list of most recently used panes, in order
// Return Value:
// - The result of navigating from source according to direction, which may be
// nullptr (i.e. no pane was found in that direction).
std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> sourcePane, const FocusDirection& direction, const std::vector<uint32_t>& mruPanes)
std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> sourcePane, const FocusDirection& direction)
{
// Can't navigate anywhere if we are a leaf
if (_IsLeaf())
@@ -235,23 +234,12 @@ std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> source
return nullptr;
}
if (direction == FocusDirection::None)
// If the MRU previous pane is requested we can't move; the tab handles MRU
if (direction == FocusDirection::None || direction == FocusDirection::Previous)
{
return nullptr;
}
// Previous movement relies on the last used panes
if (direction == FocusDirection::Previous)
{
// If there is actually a previous pane.
if (mruPanes.size() > 1)
{
// This could return nullptr if the id is not actually in the tree.
return FindPane(mruPanes.at(1));
}
return nullptr;
}
// Check if we in-order traversal is requested
if (direction == FocusDirection::NextInOrder)
{
@@ -263,46 +251,22 @@ std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> source
return PreviousPane(sourcePane);
}
if (direction == FocusDirection::First)
{
std::shared_ptr<Pane> firstPane = nullptr;
WalkTree([&](auto p) {
if (p->_IsLeaf())
{
firstPane = p;
return true;
}
return false;
});
// Don't need to do any movement if we are the source and target pane.
if (firstPane == sourcePane)
{
return nullptr;
}
return firstPane;
}
// We are left with directional traversal now
// If the focus direction does not match the split direction, the source pane
// and its neighbor must necessarily be contained within the same child.
if (!DirectionMatchesSplit(direction, _splitState))
{
if (const auto p = _firstChild->NavigateDirection(sourcePane, direction, mruPanes))
if (auto p = _firstChild->NavigateDirection(sourcePane, direction))
{
return p;
}
return _secondChild->NavigateDirection(sourcePane, direction, mruPanes);
return _secondChild->NavigateDirection(sourcePane, direction);
}
// Since the direction is the same as our split, it is possible that we must
// move focus from from one child to another child.
// We now must keep track of state while we recurse.
// If we have it, get the size of this pane.
const auto scaleX = _root.ActualWidth() > 0 ? gsl::narrow_cast<float>(_root.ActualWidth()) : 1.f;
const auto scaleY = _root.ActualHeight() > 0 ? gsl::narrow_cast<float>(_root.ActualHeight()) : 1.f;
const auto paneNeighborPair = _FindPaneAndNeighbor(sourcePane, direction, { 0, 0, scaleX, scaleY });
const auto paneNeighborPair = _FindPaneAndNeighbor(sourcePane, direction, { 0, 0 });
if (paneNeighborPair.source && paneNeighborPair.neighbor)
{
@@ -553,15 +517,17 @@ bool Pane::SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second)
}
// Method Description:
// - Given two panes' offsets, test whether the `direction` side of first is adjacent to second.
// - Given two panes, test whether the `direction` side of first is adjacent to second.
// Arguments:
// - firstOffset: The offset for the reference pane
// - secondOffset: the offset to test adjacency with.
// - first: The reference pane.
// - second: the pane to test adjacency with.
// - direction: The direction to search in from the reference pane.
// Return Value:
// - true if the two panes are adjacent.
bool Pane::_IsAdjacent(const PanePoint firstOffset,
const PanePoint secondOffset,
bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
const Pane::PanePoint firstOffset,
const std::shared_ptr<Pane> second,
const Pane::PanePoint secondOffset,
const FocusDirection& direction) const
{
// Since float equality is tricky (arithmetic is non-associative, commutative),
@@ -570,22 +536,14 @@ bool Pane::_IsAdjacent(const PanePoint firstOffset,
return abs(left - right) < 1e-4F;
};
auto getXMax = [](PanePoint offset) {
return offset.x + offset.scaleX;
};
auto getYMax = [](PanePoint offset) {
return offset.y + offset.scaleY;
};
// When checking containment in a range, the range is half-closed, i.e. [x, x+w).
// If the direction is left test that the left side of the first element is
// next to the right side of the second element, and that the top left
// corner of the first element is within the second element's height
if (direction == FocusDirection::Left)
{
const auto sharesBorders = floatEqual(firstOffset.x, getXMax(secondOffset));
const auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < getYMax(secondOffset));
auto sharesBorders = floatEqual(firstOffset.x, secondOffset.x + gsl::narrow_cast<float>(second->GetRootElement().ActualWidth()));
auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < secondOffset.y + gsl::narrow_cast<float>(second->GetRootElement().ActualHeight()));
return sharesBorders && withinHeight;
}
@@ -594,8 +552,8 @@ bool Pane::_IsAdjacent(const PanePoint firstOffset,
// corner of the first element is within the second element's height
else if (direction == FocusDirection::Right)
{
const auto sharesBorders = floatEqual(getXMax(firstOffset), secondOffset.x);
const auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < getYMax(secondOffset));
auto sharesBorders = floatEqual(firstOffset.x + gsl::narrow_cast<float>(first->GetRootElement().ActualWidth()), secondOffset.x);
auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < secondOffset.y + gsl::narrow_cast<float>(second->GetRootElement().ActualHeight()));
return sharesBorders && withinHeight;
}
@@ -604,8 +562,8 @@ bool Pane::_IsAdjacent(const PanePoint firstOffset,
// corner of the first element is within the second element's width
else if (direction == FocusDirection::Up)
{
const auto sharesBorders = floatEqual(firstOffset.y, getYMax(secondOffset));
const auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < getXMax(secondOffset));
auto sharesBorders = floatEqual(firstOffset.y, secondOffset.y + gsl::narrow_cast<float>(second->GetRootElement().ActualHeight()));
auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < secondOffset.x + gsl::narrow_cast<float>(second->GetRootElement().ActualWidth()));
return sharesBorders && withinWidth;
}
@@ -614,52 +572,14 @@ bool Pane::_IsAdjacent(const PanePoint firstOffset,
// corner of the first element is within the second element's width
else if (direction == FocusDirection::Down)
{
const auto sharesBorders = floatEqual(getYMax(firstOffset), secondOffset.y);
const auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < getXMax(secondOffset));
auto sharesBorders = floatEqual(firstOffset.y + gsl::narrow_cast<float>(first->GetRootElement().ActualHeight()), secondOffset.y);
auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < secondOffset.x + gsl::narrow_cast<float>(second->GetRootElement().ActualWidth()));
return sharesBorders && withinWidth;
}
return false;
}
// Method Description:
// - Gets the offsets for the two children of this parent pane
// - If real dimensions are not available, simulated ones based on the split size
// will be used instead.
// Arguments:
// - parentOffset the location and scale information of this pane.
// Return Value:
// - the two location/scale points for the children panes.
std::pair<Pane::PanePoint, Pane::PanePoint> Pane::_GetOffsetsForPane(const PanePoint parentOffset) const
{
assert(!_IsLeaf());
auto firstOffset = parentOffset;
auto secondOffset = parentOffset;
// Make up fake dimensions using an exponential layout. This is useful
// since we might need to navigate when there are panes not attached to
// the ui tree, such as initialization, command running, and zoom.
// Basically create the tree layout on the fly by partitioning [0,1].
// This could run into issues if the tree depth is >127 (or other
// degenerate splits) as a float's mantissa only has so many bits of
// precision.
if (_splitState == SplitState::Horizontal)
{
secondOffset.y += (1 - _desiredSplitPosition) * parentOffset.scaleY;
firstOffset.scaleY *= _desiredSplitPosition;
secondOffset.scaleY *= (1 - _desiredSplitPosition);
}
else
{
secondOffset.x += (1 - _desiredSplitPosition) * parentOffset.scaleX;
firstOffset.scaleX *= _desiredSplitPosition;
secondOffset.scaleX *= (1 - _desiredSplitPosition);
}
return { firstOffset, secondOffset };
}
// Method Description:
// - Given the source pane, and its relative position in the tree, attempt to
// find its visual neighbor within the current pane's tree.
@@ -691,14 +611,24 @@ Pane::PaneNeighborSearch Pane::_FindNeighborForPane(const FocusDirection& direct
// If we are a leaf node test if we adjacent to the focus node
if (_IsLeaf())
{
if (_IsAdjacent(searchResult.sourceOffset, offset, direction))
if (_IsAdjacent(searchResult.source, searchResult.sourceOffset, shared_from_this(), offset, direction))
{
searchResult.neighbor = shared_from_this();
}
return searchResult;
}
auto [firstOffset, secondOffset] = _GetOffsetsForPane(offset);
auto firstOffset = offset;
auto secondOffset = offset;
// The second child has an offset depending on the split
if (_splitState == SplitState::Horizontal)
{
secondOffset.y += gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualHeight());
}
else
{
secondOffset.x += gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualWidth());
}
auto sourceNeighborSearch = _firstChild->_FindNeighborForPane(direction, searchResult, sourceIsSecondSide, firstOffset);
if (sourceNeighborSearch.neighbor)
{
@@ -731,7 +661,18 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr<Pane>
return { nullptr, nullptr, offset };
}
auto [firstOffset, secondOffset] = _GetOffsetsForPane(offset);
// Search the first child, which has no offset from the parent pane
auto firstOffset = offset;
auto secondOffset = offset;
// The second child has an offset depending on the split
if (_splitState == SplitState::Horizontal)
{
secondOffset.y += gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualHeight());
}
else
{
secondOffset.x += gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualWidth());
}
auto sourceNeighborSearch = _firstChild->_FindPaneAndNeighbor(sourcePane, direction, firstOffset);
// If we have both the focus element and its neighbor, we are done
@@ -1193,7 +1134,7 @@ std::shared_ptr<Pane> Pane::DetachPane(std::shared_ptr<Pane> pane)
auto detached = isFirstChild ? _firstChild : _secondChild;
// Remove the child from the tree, replace the current node with the
// other child.
_CloseChild(isFirstChild, true);
_CloseChild(isFirstChild);
detached->_borders = Borders::None;
detached->_UpdateBorders();
@@ -1221,12 +1162,9 @@ std::shared_ptr<Pane> Pane::DetachPane(std::shared_ptr<Pane> pane)
// Arguments:
// - closeFirst: if true, the first child should be closed, and the second
// should be preserved, and vice-versa for false.
// - isDetaching: if true, then the pane event handlers for the closed child
// should be kept, this way they don't have to be recreated when it is later
// reattached to a tree somewhere as the control moves with the pane.
// Return Value:
// - <none>
void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
void Pane::_CloseChild(const bool closeFirst)
{
// Lock the create/close lock so that another operation won't concurrently
// modify our tree
@@ -1244,8 +1182,6 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
auto closedChild = closeFirst ? _firstChild : _secondChild;
auto remainingChild = closeFirst ? _secondChild : _firstChild;
auto closedChildClosedToken = closeFirst ? _firstClosedToken : _secondClosedToken;
auto remainingChildClosedToken = closeFirst ? _secondClosedToken : _firstClosedToken;
// If the only child left is a leaf, that means we're a leaf now.
if (remainingChild->_IsLeaf())
@@ -1271,18 +1207,11 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
// themselves closing, and remove their handlers for their controls
// closing. At this point, if the remaining child's control is closed,
// they'll trigger only our event handler for the control's close.
// However, if we are detaching the pane we want to keep its control
// handlers since it is just getting moved.
if (!isDetaching)
{
closedChild->_control.ConnectionStateChanged(closedChild->_connectionStateChangedToken);
closedChild->_control.WarningBell(closedChild->_warningBellToken);
}
closedChild->Closed(closedChildClosedToken);
remainingChild->Closed(remainingChildClosedToken);
_firstChild->Closed(_firstClosedToken);
_secondChild->Closed(_secondClosedToken);
closedChild->_control.ConnectionStateChanged(closedChild->_connectionStateChangedToken);
remainingChild->_control.ConnectionStateChanged(remainingChild->_connectionStateChangedToken);
closedChild->_control.WarningBell(closedChild->_warningBellToken);
remainingChild->_control.WarningBell(remainingChild->_warningBellToken);
// If either of our children was focused, we want to take that focus from
@@ -1342,6 +1271,12 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
// Find what borders need to persist after we close the child
auto remainingBorders = _GetCommonBorders();
// First stash away references to the old panes and their tokens
const auto oldFirstToken = _firstClosedToken;
const auto oldSecondToken = _secondClosedToken;
const auto oldFirst = _firstChild;
const auto oldSecond = _secondChild;
// Steal all the state from our child
_splitState = remainingChild->_splitState;
_firstChild = remainingChild->_firstChild;
@@ -1354,14 +1289,11 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
_firstChild->Closed(remainingChild->_firstClosedToken);
_secondChild->Closed(remainingChild->_secondClosedToken);
// Remove the event handlers on the old children
remainingChild->Closed(remainingChildClosedToken);
closedChild->Closed(closedChildClosedToken);
if (!isDetaching)
{
closedChild->_control.ConnectionStateChanged(closedChild->_connectionStateChangedToken);
closedChild->_control.WarningBell(closedChild->_warningBellToken);
}
// Revoke event handlers on old panes and controls
oldFirst->Closed(oldFirstToken);
oldSecond->Closed(oldSecondToken);
closedChild->_control.ConnectionStateChanged(closedChild->_connectionStateChangedToken);
closedChild->_control.WarningBell(closedChild->_warningBellToken);
// Reset our UI:
_root.Children().Clear();
@@ -1432,7 +1364,7 @@ winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst)
// this one doesn't seem to.
if (!animationsEnabledInOS || !animationsEnabledInApp || eitherChildZoomed)
{
pane->_CloseChild(closeFirst, false);
pane->_CloseChild(closeFirst);
co_return;
}
@@ -1539,7 +1471,7 @@ winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst)
{
// We don't need to manually undo any of the above trickiness.
// We're going to re-parent the child's content into us anyways
pane->_CloseChild(closeFirst, false);
pane->_CloseChild(closeFirst);
}
});
}

View File

@@ -75,9 +75,7 @@ public:
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
void Relayout();
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane,
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
const std::vector<uint32_t>& mruPanes);
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second);
std::shared_ptr<Pane> NextPane(const std::shared_ptr<Pane> pane);
@@ -202,8 +200,7 @@ private:
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
std::shared_ptr<Pane> _FindParentOfPane(const std::shared_ptr<Pane> pane);
std::pair<PanePoint, PanePoint> _GetOffsetsForPane(const PanePoint parentOffset) const;
bool _IsAdjacent(const PanePoint firstOffset, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
bool _IsAdjacent(const std::shared_ptr<Pane> first, const PanePoint firstOffset, const std::shared_ptr<Pane> second, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
PaneNeighborSearch _FindNeighborForPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
PaneNeighborSearch searchResult,
const bool focusIsSecondSide,
@@ -212,7 +209,7 @@ private:
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
const PanePoint offset);
void _CloseChild(const bool closeFirst, const bool isDetaching);
void _CloseChild(const bool closeFirst);
winrt::fire_and_forget _CloseChildRoutine(const bool closeFirst);
void _FocusFirstChild();
@@ -228,6 +225,7 @@ private:
SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const;
SnapSizeResult _CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
void _AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& sizeNode) const;
winrt::Windows::Foundation::Size _GetMinSize() const;
LayoutSizeNode _CreateMinSizeTree(const bool widthOrHeight) const;
float _ClampSplitPosition(const bool widthOrHeight, const float requestedValue, const float totalSize) const;
@@ -276,8 +274,6 @@ private:
{
float x;
float y;
float scaleX;
float scaleY;
};
struct PaneNeighborSearch

View File

@@ -685,16 +685,4 @@
<data name="DropPathTabSplit.Text" xml:space="preserve">
<value>Split the window and start in given directory</value>
</data>
<data name="ExportTabText" xml:space="preserve">
<value>Export Text</value>
</data>
<data name="ExportFailure" xml:space="preserve">
<value>Failed to export terminal content</value>
</data>
<data name="ExportSuccess" xml:space="preserve">
<value>Successfully exported terminal content</value>
</data>
<data name="PlainText" xml:space="preserve">
<value>Plain Text</value>
</data>
</root>

View File

@@ -28,9 +28,6 @@ using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::System;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Pickers;
using namespace winrt::Windows::Storage::Provider;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
@@ -174,16 +171,6 @@ namespace winrt::TerminalApp::implementation
}
});
newTabImpl->ExportTabRequested([weakTab, weakThis{ get_weak() }]() {
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
if (page && tab)
{
page->_ExportTab(*tab);
}
});
auto tabViewItem = newTabImpl->TabViewItem();
_tabView.TabItems().Append(tabViewItem);
@@ -363,10 +350,8 @@ namespace winrt::TerminalApp::implementation
// current control's live settings (which will include changes
// made through VT).
if (auto profile = tab.GetFocusedProfile())
if (const auto profile = tab.GetFocusedProfile())
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
const auto settingsCreateResult{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
@@ -404,45 +389,6 @@ namespace winrt::TerminalApp::implementation
CATCH_LOG();
}
// Method Description:
// - Exports the content of the Terminal Buffer inside the tab
// Arguments:
// - tab: tab to export
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
{
try
{
if (const auto control{ tab.GetActiveTerminalControl() })
{
const FileSavePicker savePicker;
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd);
savePicker.SuggestedStartLocation(PickerLocationId::Downloads);
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" });
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices);
savePicker.SuggestedFileName(control.Title());
const StorageFile file = co_await savePicker.PickSaveFileAsync();
if (file != nullptr)
{
const auto buffer = control.ReadEntireBuffer();
CachedFileManager::DeferUpdates(file);
co_await FileIO::WriteTextAsync(file, buffer);
const auto status = co_await CachedFileManager::CompleteUpdatesAsync(file);
switch (status)
{
case FileUpdateStatus::Complete:
case FileUpdateStatus::CompleteAndRenamed:
_ShowControlNoticeDialog(RS_(L"NoticeInfo"), RS_(L"ExportSuccess"));
break;
default:
_ShowControlNoticeDialog(RS_(L"NoticeError"), RS_(L"ExportFailure"));
}
}
}
}
CATCH_LOG();
}
// Method Description:
// - Removes the tab (both TerminalControl and XAML) after prompting for approval
// Arguments:

View File

@@ -380,13 +380,13 @@
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View File

@@ -461,10 +461,7 @@ namespace winrt::TerminalApp::implementation
// GH#6586: now that we're done processing all startup commands,
// focus the active control. This will work as expected for both
// commandline invocations and for `wt` action invocations.
if (const auto control = _GetActiveControl())
{
control.Focus(FocusState::Programmatic);
}
_GetActiveControl().Focus(FocusState::Programmatic);
}
if (initial)
{
@@ -814,6 +811,12 @@ namespace winrt::TerminalApp::implementation
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(Profile profile,
TerminalSettings settings)
{
if (!profile)
{
// Use the default profile if we didn't get one as an argument.
profile = _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
}
TerminalConnection::ITerminalConnection connection{ nullptr };
winrt::guid connectionType = profile.ConnectionType();
@@ -1164,6 +1167,7 @@ namespace winrt::TerminalApp::implementation
{
if (const auto terminalTab{ _GetFocusedTabImpl() })
{
_UnZoomIfNeeded();
return terminalTab->NavigateFocus(direction);
}
return false;
@@ -1362,8 +1366,6 @@ namespace winrt::TerminalApp::implementation
profile = tab.GetFocusedProfile();
if (profile)
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
const auto workingDirectory = tab.GetActiveTerminalControl().WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
@@ -2529,28 +2531,13 @@ namespace winrt::TerminalApp::implementation
// and wait on it hence the locking mechanism.
if (Dispatcher().HasThreadAccess())
{
try
{
NewTerminalArgs newTerminalArgs{};
// TODO GH#10952: When we pass the actual commandline (or originating application), the
// settings model can choose the right settings based on command matching, or synthesize
// a profile from the registry/link settings (TODO GH#9458).
// TODO GH#9458: Get and pass the LNK/EXE filenames.
// Passing in a commandline forces GetProfileForArgs to use Base Layer instead of Default Profile;
// in the future, it can make a better decision based on the value we pull out of the process handle.
// TODO GH#5047: When we hang on to the N.T.A., try not to spawn "default... .exe" :)
newTerminalArgs.Commandline(L"default-terminal-invocation-placeholder");
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
// TODO: GH 9458 will give us more context so we can try to choose a better profile.
auto hr = _OpenNewTab(nullptr, connection);
_CreateNewTabWithProfileAndSettings(profile, settings, connection);
// Request a summon of this window to the foreground
_SummonWindowRequestedHandlers(*this, nullptr);
// Request a summon of this window to the foreground
_SummonWindowRequestedHandlers(*this, nullptr);
}
CATCH_RETURN();
return S_OK;
return hr;
}
else
{
@@ -2558,8 +2545,9 @@ namespace winrt::TerminalApp::implementation
HRESULT finalVal = S_OK;
Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() {
// Re-running ourselves under the dispatcher will cause us to take the first branch above.
finalVal = _OnNewConnection(connection);
finalVal = _OpenNewTab(nullptr, connection);
_SummonWindowRequestedHandlers(*this, nullptr);
latch.count_down();
});
@@ -3057,16 +3045,4 @@ namespace winrt::TerminalApp::implementation
{
return WindowName() == QuakeWindowName;
}
// Method Description:
// - This function stops people from duplicating the base profile, because
// it gets ~ ~ weird ~ ~ when they do. Remove when TODO GH#5047 is done.
Profile TerminalPage::GetClosestProfileForDuplicationOfProfile(const Profile& profile) const noexcept
{
if (profile == _settings.ProfileDefaults())
{
return _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
}
return profile;
}
}

View File

@@ -220,7 +220,6 @@ namespace winrt::TerminalApp::implementation
void _DuplicateTab(const TerminalTab& tab);
void _SplitTab(TerminalTab& tab);
winrt::fire_and_forget _ExportTab(const TerminalTab& tab);
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
void _CloseTabAtIndex(uint32_t index);
@@ -359,8 +358,6 @@ namespace winrt::TerminalApp::implementation
void _SetFocusMode(const bool inFocusMode);
winrt::Microsoft::Terminal::Settings::Model::Profile GetClosestProfileForDuplicationOfProfile(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile) const noexcept;
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);

View File

@@ -648,21 +648,26 @@ namespace winrt::TerminalApp::implementation
// to the terminal when no other panes are present (GH#6219)
bool TerminalTab::NavigateFocus(const FocusDirection& direction)
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
if (const auto newFocus = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
if (direction == FocusDirection::Previous)
{
const auto res = _rootPane->FocusPane(newFocus);
if (_zoomedPane)
if (_mruPanes.size() < 2)
{
UpdateZoom(newFocus);
return false;
}
// To get to the previous pane, get the id of the previous pane and focus to that
return _rootPane->FocusPane(_mruPanes.at(1));
}
else
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
if (auto newFocus = _rootPane->NavigateDirection(_activePane, direction))
{
return _rootPane->FocusPane(newFocus);
}
return res;
return false;
}
return false;
}
// Method Description:
@@ -675,11 +680,27 @@ namespace winrt::TerminalApp::implementation
// - true if two panes were swapped.
bool TerminalTab::SwapPane(const FocusDirection& direction)
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
if (direction == FocusDirection::Previous)
{
return _rootPane->SwapPanes(_activePane, neighbor);
if (_mruPanes.size() < 2)
{
return false;
}
if (auto lastPane = _rootPane->FindPane(_mruPanes.at(1)))
{
return _rootPane->SwapPanes(_activePane, lastPane);
}
}
else
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction))
{
return _rootPane->SwapPanes(_activePane, neighbor);
}
return false;
}
return false;
@@ -1190,23 +1211,6 @@ namespace winrt::TerminalApp::implementation
splitTabMenuItem.Icon(splitTabSymbol);
}
Controls::MenuFlyoutItem exportTabMenuItem;
{
// "Split Tab"
Controls::FontIcon exportTabSymbol;
exportTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
exportTabSymbol.Glyph(L"\xE74E"); // Save
exportTabMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_ExportTabRequestedHandlers();
}
});
exportTabMenuItem.Text(RS_(L"ExportTabText"));
exportTabMenuItem.Icon(exportTabSymbol);
}
// Build the menu
Controls::MenuFlyout contextMenuFlyout;
Controls::MenuFlyoutSeparator menuSeparator;
@@ -1214,7 +1218,6 @@ namespace winrt::TerminalApp::implementation
contextMenuFlyout.Items().Append(renameTabMenuItem);
contextMenuFlyout.Items().Append(duplicateTabMenuItem);
contextMenuFlyout.Items().Append(splitTabMenuItem);
contextMenuFlyout.Items().Append(exportTabMenuItem);
contextMenuFlyout.Items().Append(menuSeparator);
// GH#5750 - When the context menu is dismissed with ESC, toss the focus
@@ -1485,22 +1488,6 @@ namespace winrt::TerminalApp::implementation
return _rootPane->PreCalculateCanSplit(_activePane, splitType, splitSize, availableSpace).value_or(false);
}
// Method Description:
// - Updates the zoomed pane when the focus changes
// Arguments:
// - newFocus: the new pane to be zoomed
// Return Value:
// - <none>
void TerminalTab::UpdateZoom(std::shared_ptr<Pane> newFocus)
{
// clear the existing content so the old zoomed pane can be added back to the root tree
Content(nullptr);
_rootPane->Restore(_zoomedPane);
_zoomedPane = newFocus;
_rootPane->Maximize(_zoomedPane);
Content(_zoomedPane->GetRootElement());
}
// Method Description:
// - Toggle our zoom state.
// * If we're not zoomed, then zoom the active pane, making it take the
@@ -1605,5 +1592,4 @@ namespace winrt::TerminalApp::implementation
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
}

View File

@@ -79,7 +79,6 @@ namespace winrt::TerminalApp::implementation
void ResetRuntimeTabColor();
void ActivateColorPicker();
void UpdateZoom(std::shared_ptr<Pane> newFocus);
void ToggleZoom();
bool IsZoomed();
void EnterZoom();
@@ -104,7 +103,6 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
DECLARE_EVENT(DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
DECLARE_EVENT(SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
DECLARE_EVENT(ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
private:

View File

@@ -89,13 +89,13 @@
</ItemGroup>
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.1.3\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
</Target>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.6.2-prerelease.210818003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
</packages>

View File

@@ -56,9 +56,6 @@
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include <winrt/Microsoft.Terminal.Settings.Editor.h>
#include <winrt/Microsoft.Terminal.Settings.Model.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Provider.h>
#include <winrt/Windows.Storage.Pickers.h>
#include <windows.ui.xaml.media.dxinterop.h>

View File

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

View File

@@ -513,16 +513,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
}
void ConptyConnection::ClearBuffer()
{
// If we haven't connected yet, then we really don't need to do
// anything. The connection should already start clear!
if (_isConnected())
{
THROW_IF_FAILED(ConptyClearPseudoConsole(_hPC.get()));
}
}
void ConptyConnection::Close() noexcept
try
{

View File

@@ -35,7 +35,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void WriteInput(hstring const& data);
void Resize(uint32_t rows, uint32_t columns);
void Close() noexcept;
void ClearBuffer();
winrt::guid Guid() const noexcept;

View File

@@ -9,7 +9,6 @@ namespace Microsoft.Terminal.TerminalConnection
{
ConptyConnection();
Guid Guid { get; };
void ClearBuffer();
static event NewConnectionHandler NewConnection;
static void StartInboundListener();

View File

@@ -0,0 +1,101 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ContentProcess.h"
#include "ContentProcess.g.cpp"
namespace winrt::Microsoft::Terminal::Control::implementation
{
ContentProcess::ContentProcess() :
_ourPID{ GetCurrentProcessId() } {}
bool ContentProcess::Initialize(Control::IControlSettings settings,
TerminalConnection::ConnectionInformation connectionInfo)
{
auto conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectionInfo) };
if (conn == nullptr)
{
return false;
}
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, conn);
return true;
}
ContentProcess::~ContentProcess()
{
// DANGER - We're straight up going to EXIT THE ENTIRE PROCESS when we
// get destructed. This eliminates the need to do any sort of
// refcounting weirdness. This entire process exists to host one
// singular ContentProcess instance. When we're destructed, it's because
// every other window process was done with us. We can die now, knowing
// that our job is complete.
ExitProcess(0);
}
Control::ControlInteractivity ContentProcess::GetInteractivity()
{
return _interactivity;
}
uint64_t ContentProcess::GetPID()
{
return _ourPID;
}
// Method Description:
// - Duplicate the swap chain handle to the provided process.
// - If the provided PID is our pid, then great - we don't need to do anything.
// Arguments:
// - callersPid: the PID of the process calling this method.
// Return Value:
// - The value of the swapchain handle in the callers process
// Notes:
// - This is BODGY! We're basically asking to marshal a HANDLE here. WinRT
// has no good mechanism for doing this, so we're doing it by casting the
// value to a uint64_t. In all reality, we _should_ be using a COM
// interface for this, because it can set up the security on these handles
// more appropriately. Fortunately, all we're dealing with is swapchains,
// so the security doesn't matter all that much.
uint64_t ContentProcess::RequestSwapChainHandle(const uint64_t callersPid)
{
auto ourPid = GetCurrentProcessId();
HANDLE ourHandle = reinterpret_cast<HANDLE>(_interactivity.Core().SwapChainHandle());
if (callersPid == ourPid)
{
return reinterpret_cast<uint64_t>(ourHandle);
}
wil::unique_handle hWindowProcess{ OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
static_cast<DWORD>(callersPid)) };
if (hWindowProcess.get() == nullptr)
{
const auto gle = GetLastError();
gle;
// TODO! tracelog an error here
return 0;
}
HANDLE theirHandle{ nullptr };
BOOL success = DuplicateHandle(GetCurrentProcess(),
ourHandle,
hWindowProcess.get(),
&theirHandle,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (!success)
{
const auto gle = GetLastError();
gle;
// TODO! tracelog an error here
return 0;
}
// At this point, the handle is now in their process space, with value
// theirHandle
return reinterpret_cast<uint64_t>(theirHandle);
}
}

View File

@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "ContentProcess.g.h"
#include "ControlInteractivity.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct ContentProcess : ContentProcessT<ContentProcess>
{
ContentProcess();
~ContentProcess();
bool Initialize(Control::IControlSettings settings,
TerminalConnection::ConnectionInformation connectionInfo);
Control::ControlInteractivity GetInteractivity();
uint64_t GetPID();
uint64_t RequestSwapChainHandle(const uint64_t pid);
private:
Control::ControlInteractivity _interactivity{ nullptr };
uint64_t _ourPID;
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(ContentProcess);
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ControlInteractivity.idl";
namespace Microsoft.Terminal.Control
{
runtimeclass ContentProcess {
ContentProcess();
Boolean Initialize(IControlSettings settings,
Microsoft.Terminal.TerminalConnection.ConnectionInformation connectionInfo);
ControlInteractivity GetInteractivity();
UInt64 GetPID();
UInt64 RequestSwapChainHandle(UInt64 pid);
};
}

View File

@@ -414,8 +414,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// This is a scroll event that wasn't initiated by the terminal
// itself - it was initiated by the mouse wheel, or the scrollbar.
_terminal->UserScrollViewport(viewTop);
_updatePatternLocations->Run();
}
void ControlCore::AdjustOpacity(const double adjustment)
@@ -1394,18 +1392,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _terminal != nullptr && _terminal->IsTrackingMouseInput();
}
bool ControlCore::IsCursorOffScreen() const
{
// If we haven't been initialized yet, then just return true
if (!_initializedTerminal)
{
return true;
}
auto lock = _terminal->LockForReading();
return _terminal->IsCursorOffScreen();
}
til::point ControlCore::CursorPosition() const
{
// If we haven't been initialized yet, then fake it.
@@ -1511,67 +1497,4 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_updatePatternLocations->Run();
}
void ControlCore::TrackCursorMovement(bool track) noexcept
{
_terminal->TrackCursorMovement(track);
}
// Method Description:
// - Clear the contents of the buffer. The region cleared is given by
// clearType:
// * Screen: Clear only the contents of the visible viewport, leaving the
// cursor row at the top of the viewport.
// * Scrollback: Clear the contents of the scrollback.
// * All: Do both - clear the visible viewport and the scrollback, leaving
// only the cursor row at the top of the viewport.
// Arguments:
// - clearType: The type of clear to perform.
// Return Value:
// - <none>
void ControlCore::ClearBuffer(Control::ClearBufferType clearType)
{
if (clearType == Control::ClearBufferType::Scrollback || clearType == Control::ClearBufferType::All)
{
_terminal->EraseInDisplay(::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType::Scrollback);
}
if (clearType == Control::ClearBufferType::Screen || clearType == Control::ClearBufferType::All)
{
// Send a signal to conpty to clear the buffer.
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
{
// ConPTY will emit sequences to sync up our buffer with its new
// contents.
conpty.ClearBuffer();
}
}
}
hstring ControlCore::ReadEntireBuffer() const
{
auto terminalLock = _terminal->LockForWriting();
const auto& textBuffer = _terminal->GetTextBuffer();
std::wstringstream ss;
const auto lastRow = textBuffer.GetLastNonSpaceCharacter().Y;
for (auto rowIndex = 0; rowIndex <= lastRow; rowIndex++)
{
const auto& row = textBuffer.GetRowByOffset(rowIndex);
auto rowText = row.GetText();
const auto strEnd = rowText.find_last_not_of(UNICODE_SPACE);
if (strEnd != std::string::npos)
{
rowText.erase(strEnd + 1);
ss << rowText;
}
if (!row.WasWrapForced())
{
ss << UNICODE_CARRIAGERETURN << UNICODE_LINEFEED;
}
}
return hstring(ss.str());
}
}

View File

@@ -70,8 +70,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void AdjustOpacity(const double adjustment);
void ResumeRendering();
void TrackCursorMovement(bool track) noexcept;
void UpdatePatternLocations();
void SetHoveredCell(Core::Point terminalPosition);
void ClearHoveredCell();
@@ -114,9 +112,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const short wheelDelta,
const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);
void UserScrollViewport(const int viewTop);
void ClearBuffer(Control::ClearBufferType clearType);
#pragma endregion
void BlinkAttributeTick();
@@ -125,7 +120,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void CursorOn(const bool isCursorOn);
bool IsVtMouseModeEnabled() const;
bool IsCursorOffScreen() const;
til::point CursorPosition() const;
bool HasSelection() const;
@@ -150,8 +144,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool IsInReadOnlyMode() const;
void ToggleReadOnlyMode();
hstring ReadEntireBuffer() const;
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
@@ -255,14 +247,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
if (_dispatcher)
{
// _closing isn't atomic and may only be accessed from the main thread.
//
// Though, the unit tests don't actually run in TAEF's main
// thread, so we don't care when we're running in tests.
assert(_inUnitTests || _dispatcher.HasThreadAccess());
}
// // TODO! This may not be strictly true if the core is running out of
// // proc with XAML. I keep hitting this assertion every time it
// // exits, so we might need a better solution.
// if (_dispatcher)
// {
// // _closing isn't atomic and may only be accessed from the main thread.
// //
// // Though, the unit tests don't actually run in TAEF's main
// // thread, so we don't care when we're running in tests.
// assert(_inUnitTests || _dispatcher.HasThreadAccess());
// }
#endif
return _closing;
}

View File

@@ -22,14 +22,6 @@ namespace Microsoft.Terminal.Control
IsRightButtonDown = 0x4
};
enum ClearBufferType
{
Screen,
Scrollback,
All
};
[default_interface] runtimeclass ControlCore : ICoreState
{
ControlCore(IControlSettings settings,
@@ -57,7 +49,6 @@ namespace Microsoft.Terminal.Control
Microsoft.Terminal.Core.ControlKeyStates modifiers);
void SendInput(String text);
void PasteText(String text);
void ClearBuffer(ClearBufferType clearType);
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
void ClearHoveredCell();
@@ -78,8 +69,6 @@ namespace Microsoft.Terminal.Control
void SetBackgroundOpacity(Double opacity);
Microsoft.Terminal.Core.Color BackgroundColor { get; };
void TrackCursorMovement(Boolean track);
Boolean HasSelection { get; };
IVector<String> SelectedText(Boolean trimTrailingWhitespace);
@@ -91,9 +80,6 @@ namespace Microsoft.Terminal.Control
Boolean IsInReadOnlyMode { get; };
Boolean CursorOn;
void EnablePainting();
Boolean IsCursorOffScreen();
String ReadEntireBuffer();
event FontSizeChangedEventArgs FontSizeChanged;

View File

@@ -44,6 +44,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_controlPadding = padding;
}
void InteractivityAutomationPeer::SetParentProvider(Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider)
{
_parentProvider = parentProvider;
}
// Method Description:
// - Signals the ui automation client that the terminal's selection has
// changed and should be updated
@@ -111,7 +116,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr,
&returnVal));
const auto parentProvider = this->ProviderFromPeer(*this);
// const auto parentProvider = this->ProviderFromPeer(*this);
const auto parentProvider = _parentProvider;
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
}
@@ -121,7 +127,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
UIA::ITextRangeProvider* returnVal;
THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal));
const auto parentProvider = this->ProviderFromPeer(*this);
// const auto parentProvider = this->ProviderFromPeer(*this);
const auto parentProvider = _parentProvider;
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
}
@@ -131,7 +138,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
UIA::ITextRangeProvider* returnVal;
THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal));
const auto parentProvider = this->ProviderFromPeer(*this);
// const auto parentProvider = this->ProviderFromPeer(*this);
const auto parentProvider = _parentProvider;
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
return xutr.as<XamlAutomation::ITextRangeProvider>();
}
@@ -194,7 +202,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::vector<XamlAutomation::ITextRangeProvider> vec;
vec.reserve(count);
auto parentProvider = this->ProviderFromPeer(*this);
// auto parentProvider = this->ProviderFromPeer(*this);
const auto parentProvider = _parentProvider;
for (int i = 0; i < count; i++)
{
auto xutr = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);

View File

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

View File

@@ -3,13 +3,21 @@
namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass InteractivityAutomationPeer :
[default_interface] runtimeclass InteractivityAutomationPeer/* :
Windows.UI.Xaml.Automation.Peers.AutomationPeer,
Windows.UI.Xaml.Automation.Provider.ITextProvider
Windows.UI.Xaml.Automation.Provider.ITextProvider*/
{
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider[] GetSelection();
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider[] GetVisibleRanges();
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider RangeFromChild(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple childElement);
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider RangeFromPoint(Windows.Foundation.Point screenLocation);
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider DocumentRange();
Windows.UI.Xaml.Automation.SupportedTextSelection SupportedTextSelection();
void SetControlBounds(Windows.Foundation.Rect bounds);
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
void SetParentProvider(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple provider);
event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged;

View File

@@ -200,4 +200,10 @@ Please either install the missing font or choose another one.</value>
<data name="TermControlReadOnly" xml:space="preserve">
<value>Read-only mode is enabled.</value>
</data>
<data name="TermControl_ContentDiedTextBlock.Text" xml:space="preserve">
<value>The content of this terminal was closed unexpectedly.</value>
</data>
<data name="TermControl_ContentDiedButton.Content" xml:space="preserve">
<value>Close</value>
</data>
</root>

View File

@@ -1,5 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// The functions for handling content processes in the TermControl are largely
// in TermControlContentManageent.cpp
#include "pch.h"
#include "TermControl.h"
@@ -32,8 +35,10 @@ using namespace winrt::Windows::ApplicationModel::DataTransfer;
// The updates are throttled to limit power usage.
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
// The minimum delay between updating the TSF input control.
// This is already throttled primarily in the ControlCore, with a timeout of 100ms. We're adding another smaller one here, as the (potentially x-proc) call will come in off the UI thread
// The minimum delay between updating the TSF input control. This is already
// throttled primarily in the ControlCore, with a timeout of 100ms. We're adding
// another smaller one here, as the (potentially x-proc) call will come in off
// the UI thread
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(8);
// The minimum delay between updating the locations of regex patterns
@@ -50,7 +55,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
TermControl::TermControl(IControlSettings settings,
TerminalConnection::ITerminalConnection connection) :
TermControl(winrt::guid{}, settings, connection) {}
TermControl::TermControl(winrt::guid contentGuid,
IControlSettings settings,
TerminalConnection::ITerminalConnection connection) :
_initializedTerminal{ false },
_settings{ settings },
_closing{ false },
_isInternalScrollBarUpdate{ false },
_autoScrollVelocity{ 0 },
_autoScrollingPointerPoint{ std::nullopt },
@@ -62,7 +74,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
InitializeComponent();
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, connection);
if (contentGuid != winrt::guid{})
{
_contentProc = create_instance<Control::ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
if (_contentProc != nullptr)
{
_interactivity = _contentProc.GetInteractivity();
_contentWaitInterrupt.create();
_createContentWaitThread();
}
}
if (_interactivity == nullptr)
{
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, connection);
}
_core = _interactivity.Core();
// These events might all be triggered by the connection, but that
@@ -75,6 +101,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here.
_core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState });
_core.ConnectionStateChanged({ get_weak(), &TermControl::_coreConnectionStateChanged });
// These callbacks can only really be triggered by UI interactions. So
// they don't need weak refs - they can't be triggered unless we're
// alive.
@@ -118,16 +146,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
});
_moveCursorLight = std::make_shared<ThrottledFuncLeading>(
dispatcher,
ScrollBarUpdateInterval,
[weakThis = get_weak()]() {
if (auto control{ weakThis.get() }; !control->_IsClosing())
{
control->_MoveCursorLightHelper();
}
});
_updateScrollBar = std::make_shared<ThrottledFuncTrailing<ScrollBarUpdate>>(
dispatcher,
ScrollBarUpdateInterval,
@@ -361,10 +379,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_core.SendInput(wstr);
}
void TermControl::ClearBuffer(Control::ClearBufferType clearType)
{
_core.ClearBuffer(clearType);
}
void TermControl::ToggleShaderEffects()
{
@@ -514,6 +528,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::~TermControl()
{
if (_contentIsOutOfProc())
{
_contentWaitInterrupt.SetEvent();
_contentWaitThread.join();
}
Close();
}
@@ -540,6 +559,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
margins.Right,
margins.Bottom };
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, padding, interactivityAutoPeer);
interactivityAutoPeer.SetParentProvider(_automationPeer.GetParentProvider());
return _automationPeer;
}
}
@@ -560,7 +580,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ConnectionState TermControl::ConnectionState() const
{
return _core.ConnectionState();
try
{
return _core.ConnectionState();
}
CATCH_LOG();
return TerminalConnection::ConnectionState::Closed;
}
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/)
@@ -574,7 +599,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (auto control{ weakThis.get() })
{
const HANDLE chainHandle = reinterpret_cast<HANDLE>(control->_core.SwapChainHandle());
// TODO! very good chance we leak this handle
const HANDLE chainHandle = reinterpret_cast<HANDLE>(control->_contentIsOutOfProc() ?
control->_contentProc.RequestSwapChainHandle(GetCurrentProcessId()) :
control->_core.SwapChainHandle());
_AttachDxgiSwapChainToXaml(chainHandle);
}
}
@@ -662,7 +690,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
_interactivity.Initialize();
_AttachDxgiSwapChainToXaml(reinterpret_cast<HANDLE>(_core.SwapChainHandle()));
// TODO! very good chance we leak this handle
const HANDLE chainHandle = reinterpret_cast<HANDLE>(_contentIsOutOfProc() ?
_contentProc.RequestSwapChainHandle(GetCurrentProcessId()) :
_core.SwapChainHandle());
_AttachDxgiSwapChainToXaml(chainHandle);
// Tell the DX Engine to notify us when the swap chain changes. We do
// this after we initially set the swapchain so as to avoid unnecessary
@@ -914,24 +946,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
if (vkey == VK_SPACE && modifiers.IsAltPressed())
{
if (const auto bindings = _settings.KeyBindings())
{
if (!bindings.IsKeyChordExplicitlyUnbound({ modifiers.IsCtrlPressed(), modifiers.IsAltPressed(), modifiers.IsShiftPressed(), modifiers.IsWinPressed(), vkey, scanCode }))
{
// If we get here, it means that
// 1. we do not have a command bound to alt+space
// 2. alt+space was not explicitly unbound
// That means that XAML handled the alt+space to open up the context menu, and
// so we don't want to send anything to the terminal
// TODO GH#11018: Add a new "openSystemMenu" keybinding
e.Handled(true);
return;
}
}
}
if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown))
{
e.Handled(true);
@@ -1687,10 +1701,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
update.newValue = args.ViewTop();
_updateScrollBar->Run(update);
if (CursorLight::GetIsTarget(RootGrid()))
{
_moveCursorLight->Run();
}
}
// Method Description:
@@ -1711,10 +1721,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (auto control{ weakThis.get() }; !control->_IsClosing())
{
control->TSFInputControl().TryRedrawCanvas();
if (CursorLight::GetIsTarget(RootGrid()))
{
_MoveCursorLightHelper();
}
}
}
@@ -1774,8 +1780,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Disconnect the TSF input control so it doesn't receive EditContext events.
TSFInputControl().Close();
_autoScrollTimer.Stop();
_core.Close();
if (!_contentIsOutOfProc())
{
_core.Close();
}
}
}
@@ -2420,56 +2428,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core.TaskbarProgress();
}
// Method Description:
// - Toggles the spotlight on the cursor
// - If the cursor is currently off the screen, does nothing
void TermControl::HighlightCursor()
{
if (CursorLight::GetIsTarget(RootGrid()))
{
CursorLight::SetIsTarget(RootGrid(), false);
_core.TrackCursorMovement(false);
}
else if (_MoveCursorLightHelper())
{
CursorLight::SetIsTarget(RootGrid(), true);
_core.TrackCursorMovement(true);
}
}
// Method Description:
// - Computes the cursor position and moves the cursor light to it
// Return Value:
// - True if the cursor light was moved, false otherwise (this will
// happen if the cursor is off-screen)
bool TermControl::_MoveCursorLightHelper()
{
if (_core.IsCursorOffScreen())
{
// If the cursor is off screen, just switch off the light instead of moving it
CursorLight::SetIsTarget(RootGrid(), false);
return false;
}
else
{
// Compute the location of where to place the light
const auto charSizeInPixels = CharacterDimensions();
const auto htInDips = charSizeInPixels.Height / SwapChainPanel().CompositionScaleY();
const auto wtInDips = charSizeInPixels.Width / SwapChainPanel().CompositionScaleX();
const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top };
const til::size fontSize{ til::math::rounding, _core.FontSize() };
const til::point cursorPos = _core.CursorPosition();
const til::point cursorPosInPixels{ cursorPos * fontSize };
const til::point cursorPosInDIPs{ cursorPosInPixels / SwapChainPanel().CompositionScaleX() };
const til::point cursorLocationInDIPs{ cursorPosInDIPs + marginsInDips };
CursorLight().ChangeLocation(gsl::narrow_cast<float>(cursorLocationInDIPs.x()) + wtInDips / 2,
gsl::narrow_cast<float>(cursorLocationInDIPs.y()) + htInDips / 2);
return true;
}
}
void TermControl::BellLightOn()
{
// Initialize the animation if it does not exist
@@ -2658,9 +2616,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_playWarningBell->Run();
}
hstring TermControl::ReadEntireBuffer() const
void TermControl::_coreConnectionStateChanged(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
return _core.ReadEntireBuffer();
_ConnectionStateChangedHandlers(*this, nullptr);
}
}

View File

@@ -25,6 +25,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
struct TermControl : TermControlT<TermControl>
{
TermControl(winrt::guid contentGuid, IControlSettings settings, TerminalConnection::ITerminalConnection connection);
TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection);
winrt::fire_and_forget UpdateSettings();
@@ -63,8 +64,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::point GetFontSize() const;
void SendInput(const winrt::hstring& input);
void ClearBuffer(Control::ClearBufferType clearType);
void ToggleShaderEffects();
winrt::fire_and_forget RenderEngineSwapChainChanged(IInspectable sender, IInspectable args);
@@ -72,6 +71,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args);
void _RenderRetryButton_Click(IInspectable const& button, IInspectable const& args);
void _ContentDiedCloseButton_Click(IInspectable const& button, IInspectable const& args);
winrt::fire_and_forget _RendererWarning(IInspectable sender,
Control::RendererWarningArgs args);
@@ -102,8 +102,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void BellLightOn();
void HighlightCursor();
bool ReadOnly() const noexcept;
void ToggleReadOnly();
@@ -111,8 +109,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point);
static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
hstring ReadEntireBuffer() const;
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
@@ -121,7 +117,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged);
PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged);
PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged);
PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged);
TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
@@ -151,6 +147,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Control::TermControlAutomationPeer _automationPeer{ nullptr };
Control::ControlInteractivity _interactivity{ nullptr };
Control::ControlCore _core{ nullptr };
Control::ContentProcess _contentProc{ nullptr };
winrt::com_ptr<SearchBoxControl> _searchBox;
@@ -160,7 +157,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _initializedTerminal{ false };
std::shared_ptr<ThrottledFuncLeading> _playWarningBell;
std::shared_ptr<ThrottledFuncLeading> _moveCursorLight;
struct ScrollBarUpdate
{
@@ -190,6 +186,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
wil::unique_event _contentWaitInterrupt;
std::thread _contentWaitThread;
void _createContentWaitThread();
bool _contentIsOutOfProc() const;
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
@@ -264,8 +265,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive);
void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, Windows::UI::Xaml::RoutedEventArgs const& args);
bool _MoveCursorLightHelper();
// TSFInputControl Handlers
void _CompositionCompleted(winrt::hstring text);
void _CurrentCursorPositionHandler(const IInspectable& sender, const CursorPositionEventArgs& eventArgs);
@@ -279,6 +278,8 @@ 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);
winrt::fire_and_forget _raiseContentDied();
void _coreConnectionStateChanged(const IInspectable& sender, const IInspectable& args);
};
}

View File

@@ -6,7 +6,6 @@ import "IControlSettings.idl";
import "IDirectKeyListener.idl";
import "EventArgs.idl";
import "ICoreState.idl";
import "ControlCore.idl";
namespace Microsoft.Terminal.Control
{
@@ -16,6 +15,10 @@ namespace Microsoft.Terminal.Control
IMouseWheelListener,
ICoreState
{
TermControl(Guid contentGuid,
IControlSettings settings,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
TermControl(IControlSettings settings,
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
@@ -47,7 +50,6 @@ namespace Microsoft.Terminal.Control
Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
void PasteTextFromClipboard();
void ClearBuffer(ClearBufferType clearType);
void Close();
Windows.Foundation.Size CharacterDimensions { get; };
Windows.Foundation.Size MinimumSize { get; };
@@ -67,11 +69,7 @@ namespace Microsoft.Terminal.Control
void BellLightOn();
void HighlightCursor();
Boolean ReadOnly { get; };
void ToggleReadOnly();
String ReadEntireBuffer();
}
}

View File

@@ -35,7 +35,6 @@
<Grid x:Name="RootGrid">
<Grid.Lights>
<local:VisualBellLight x:Name="BellLight" />
<local:CursorLight x:Name="CursorLight" />
</Grid.Lights>
<Image x:Name="BackgroundImage"
AutomationProperties.AccessibilityView="Raw" />
@@ -131,6 +130,27 @@
</Border>
</Grid>
<Grid x:Name="ContentDiedNotice"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="False">
<Border Margin="8,8,8,8"
Padding="8,8,8,8"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
BorderBrush="{ThemeResource SystemAccentColor}"
BorderThickness="2,2,2,2"
CornerRadius="{ThemeResource OverlayCornerRadius}">
<StackPanel>
<TextBlock x:Uid="TermControl_ContentDiedTextBlock"
HorizontalAlignment="Center"
TextWrapping="WrapWholeWords" />
<Button x:Uid="TermControl_ContentDiedButton"
HorizontalAlignment="Right"
Click="_ContentDiedCloseButton_Click" />
</StackPanel>
</Border>
</Grid>
</Grid>
</UserControl>

View File

@@ -67,6 +67,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_contentAutomationPeer.SetControlPadding(padding);
}
XamlAutomation::IRawElementProviderSimple TermControlAutomationPeer::GetParentProvider()
{
const auto parentProvider = this->ProviderFromPeer(*this);
return parentProvider;
}
// Method Description:
// - Signals the ui automation client that the terminal's selection has changed and should be updated
// Arguments:
@@ -153,7 +159,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
{
return RS_(L"TerminalControl_ControlType");
// return RS_(L"TerminalControl_ControlType");
return L"foo";
}
Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const

View File

@@ -48,6 +48,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void UpdateControlBounds();
void SetControlPadding(const Core::Padding padding);
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple GetParentProvider();
#pragma region FrameworkElementAutomationPeer
hstring GetClassNameCore() const;

View File

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

View File

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

View File

@@ -28,6 +28,9 @@
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="ContentProcess.h">
<DependentUpon>ContentProcess.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ControlCore.h">
<DependentUpon>ControlCore.idl</DependentUpon>
</ClInclude>
@@ -58,13 +61,18 @@
<ClInclude Include="TSFInputControl.h">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="XamlUiaTextRange.h" />
<ClInclude Include="XamlUiaTextRange.h" >
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="ContentProcess.cpp">
<DependentUpon>ContentProcess.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ControlCore.cpp">
<DependentUpon>ControlCore.idl</DependentUpon>
</ClCompile>
@@ -87,6 +95,9 @@
<ClCompile Include="TermControl.cpp">
<DependentUpon>TermControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TermControlContentManagement.cpp">
<DependentUpon>TermControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TSFInputControl.cpp">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</ClCompile>
@@ -97,10 +108,13 @@
<ClCompile Include="InteractivityAutomationPeer.cpp">
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="XamlUiaTextRange.cpp" />
<ClCompile Include="XamlUiaTextRange.cpp" >
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
</ClCompile>
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
<Midl Include="ContentProcess.idl" />
<Midl Include="ControlCore.idl" />
<Midl Include="ControlInteractivity.idl" />
<Midl Include="ICoreState.idl" />
@@ -123,6 +137,7 @@
<Midl Include="TSFInputControl.idl">
<DependentUpon>TSFInputControl.xaml</DependentUpon>
</Midl>
<Midl Include="XamlUiaTextRange.idl" />
</ItemGroup>
<!-- ========================= XAML Files ======================== -->
<ItemGroup>

View File

@@ -5,7 +5,6 @@
#include "TermControl.h"
#include "XamlLights.h"
#include "VisualBellLight.g.cpp"
#include "CursorLight.g.cpp"
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Media;
@@ -13,7 +12,27 @@ using namespace winrt::Windows::UI::Xaml::Media;
namespace winrt::Microsoft::Terminal::Control::implementation
{
DependencyProperty VisualBellLight::_IsTargetProperty{ nullptr };
DependencyProperty CursorLight::_IsTargetProperty{ nullptr };
VisualBellLight::VisualBellLight()
{
_InitializeProperties();
}
void VisualBellLight::_InitializeProperties()
{
// Initialize any dependency properties here.
// This performs a lazy load on these properties, instead of
// initializing them when the DLL loads.
if (!_IsTargetProperty)
{
_IsTargetProperty =
DependencyProperty::RegisterAttached(
L"IsTarget",
winrt::xaml_typename<bool>(),
winrt::xaml_typename<Control::VisualBellLight>(),
PropertyMetadata{ winrt::box_value(false), PropertyChangedCallback{ &VisualBellLight::OnIsTargetChanged } });
}
}
// Method Description:
// - This function is called when the first target UIElement is shown on the screen,
@@ -30,50 +49,59 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void CursorLight::ChangeLocation(float xCoord, float yCoord)
// Method Description:
// - This function is called when there are no more target UIElements on the screen
// - Disposes of composition resources when no longer in use
// Arguments:
// - oldElement: unused
void VisualBellLight::OnDisconnected(UIElement const& /* oldElement */)
{
if (const auto light = CompositionLight())
if (CompositionLight())
{
if (const auto cursorLight = light.try_as<Windows::UI::Composition::SpotLight>())
CompositionLight(nullptr);
}
}
winrt::hstring VisualBellLight::GetId()
{
return VisualBellLight::GetIdStatic();
}
void VisualBellLight::OnIsTargetChanged(DependencyObject const& d, DependencyPropertyChangedEventArgs const& e)
{
const auto uielem{ d.try_as<UIElement>() };
const auto brush{ d.try_as<Brush>() };
if (!uielem && !brush)
{
// terminate early
return;
}
const auto isAdding = winrt::unbox_value<bool>(e.NewValue());
const auto id = GetIdStatic();
if (isAdding)
{
if (uielem)
{
cursorLight.Offset({ xCoord, yCoord, 100 });
XamlLight::AddTargetElement(id, uielem);
}
else
{
XamlLight::AddTargetBrush(id, brush);
}
}
else
{
_InitializeHelper(xCoord, yCoord);
}
}
// Method Description:
// - This function is called when the first target UIElement is shown on the screen,
// this enables delaying composition object creation until it's actually necessary.
// Arguments:
// - newElement: unused
void CursorLight::OnConnected(UIElement const& /* newElement */)
{
if (!CompositionLight())
{
_InitializeHelper(0, 0);
}
}
// Method Description:
// - Helper to initialize the properties of the spotlight such as the location and
// the angles of the inner and outer cones
// Arguments:
// - xCoord: the x-coordinate of where to put the light
// - yCoord: the y-coordinate of where to put the light
void CursorLight::_InitializeHelper(float xCoord, float yCoord)
{
if (!CompositionLight())
{
auto spotLight{ Window::Current().Compositor().CreateSpotLight() };
spotLight.InnerConeColor(Windows::UI::Colors::White());
spotLight.InnerConeAngleInDegrees(10);
spotLight.OuterConeAngleInDegrees(25);
spotLight.Offset({ xCoord, yCoord, 100 });
CompositionLight(spotLight);
if (uielem)
{
XamlLight::RemoveTargetElement(id, uielem);
}
else
{
XamlLight::RemoveTargetBrush(id, brush);
}
}
}
}

View File

@@ -5,123 +5,45 @@
#include "cppwinrt_utils.h"
#include "VisualBellLight.g.h"
#include "CursorLight.g.h"
#define CREATE_XAML_LIGHT(name) \
public: \
name() \
{ \
_InitializeProperties(); \
} \
\
winrt::hstring GetId() \
{ \
return name::GetIdStatic(); \
} \
\
static Windows::UI::Xaml::DependencyProperty IsTargetProperty() \
{ \
return _IsTargetProperty; \
} \
\
static bool GetIsTarget(Windows::UI::Xaml::DependencyObject const& target) \
{ \
return winrt::unbox_value<bool>(target.GetValue(_IsTargetProperty)); \
} \
\
static void SetIsTarget(Windows::UI::Xaml::DependencyObject const& target, bool value) \
{ \
target.SetValue(_IsTargetProperty, winrt::box_value(value)); \
} \
\
void OnDisconnected(Windows::UI::Xaml::UIElement const& /*oldElement*/) \
{ \
if (CompositionLight()) \
{ \
CompositionLight(nullptr); \
} \
} \
\
static void OnIsTargetChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e) \
{ \
const auto uielem{ d.try_as<Windows::UI::Xaml::UIElement>() }; \
const auto brush{ d.try_as<Windows::UI::Xaml::Media::Brush>() }; \
\
if (!uielem && !brush) \
{ \
/* terminate early*/ \
return; \
} \
\
const auto isAdding = winrt::unbox_value<bool>(e.NewValue()); \
const auto id = GetIdStatic(); \
\
if (isAdding) \
{ \
if (uielem) \
{ \
Windows::UI::Xaml::Media::XamlLight::AddTargetElement(id, uielem); \
} \
else \
{ \
Windows::UI::Xaml::Media::XamlLight::AddTargetBrush(id, brush); \
} \
} \
else \
{ \
if (uielem) \
{ \
Windows::UI::Xaml::Media::XamlLight::RemoveTargetElement(id, uielem); \
} \
else \
{ \
Windows::UI::Xaml::Media::XamlLight::RemoveTargetBrush(id, brush); \
} \
} \
} \
\
inline static winrt::hstring GetIdStatic() \
{ \
/* This specifies the unique name of the light. In most cases you should use the type's full name. */ \
return winrt::xaml_typename<winrt::Microsoft::Terminal::Control::name>().Name; \
} \
\
private: \
static Windows::UI::Xaml::DependencyProperty _IsTargetProperty; \
static void _InitializeProperties() \
{ \
if (!_IsTargetProperty) \
{ \
_IsTargetProperty = \
Windows::UI::Xaml::DependencyProperty::RegisterAttached( \
L"IsTarget", \
winrt::xaml_typename<bool>(), \
winrt::xaml_typename<Control::name>(), \
Windows::UI::Xaml::PropertyMetadata{ winrt::box_value(false), Windows::UI::Xaml::PropertyChangedCallback{ &name::OnIsTargetChanged } }); \
} \
}
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct VisualBellLight : VisualBellLightT<VisualBellLight>
{
void OnConnected(Windows::UI::Xaml::UIElement const& newElement);
CREATE_XAML_LIGHT(VisualBellLight);
};
VisualBellLight();
winrt::hstring GetId();
static Windows::UI::Xaml::DependencyProperty IsTargetProperty() { return _IsTargetProperty; }
static bool GetIsTarget(Windows::UI::Xaml::DependencyObject const& target)
{
return winrt::unbox_value<bool>(target.GetValue(_IsTargetProperty));
}
static void SetIsTarget(Windows::UI::Xaml::DependencyObject const& target, bool value)
{
target.SetValue(_IsTargetProperty, winrt::box_value(value));
}
struct CursorLight : CursorLightT<CursorLight>
{
void ChangeLocation(float xCoord, float yCoord);
void OnConnected(Windows::UI::Xaml::UIElement const& newElement);
CREATE_XAML_LIGHT(CursorLight);
void OnDisconnected(Windows::UI::Xaml::UIElement const& oldElement);
static void OnIsTargetChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e);
inline static winrt::hstring GetIdStatic()
{
// This specifies the unique name of the light. In most cases you should use the type's full name.
return winrt::xaml_typename<winrt::Microsoft::Terminal::Control::VisualBellLight>().Name;
}
private:
void _InitializeHelper(float xCoord, float yCoord);
static void _InitializeProperties();
static Windows::UI::Xaml::DependencyProperty _IsTargetProperty;
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(VisualBellLight);
BASIC_FACTORY(CursorLight);
}

View File

@@ -1,22 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#define XAML_LIGHT(Name) \
Name(); \
static Windows.UI.Xaml.DependencyProperty IsTargetProperty { get; }; \
static Boolean GetIsTarget(Windows.UI.Xaml.DependencyObject target); \
static void SetIsTarget(Windows.UI.Xaml.DependencyObject target, Boolean value)
namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass VisualBellLight : Windows.UI.Xaml.Media.XamlLight
{
XAML_LIGHT(VisualBellLight);
}
[default_interface] runtimeclass CursorLight : Windows.UI.Xaml.Media.XamlLight
{
XAML_LIGHT(CursorLight);
void ChangeLocation(Single xCoord, Single yCoord);
VisualBellLight();
static Windows.UI.Xaml.DependencyProperty IsTargetProperty { get; };
static Boolean GetIsTarget(Windows.UI.Xaml.DependencyObject target);
static void SetIsTarget(Windows.UI.Xaml.DependencyObject target, Boolean value);
}
}

View File

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

View File

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

View File

@@ -1078,17 +1078,12 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
// 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() || _trackingCursorMovement)
if (!cursor.IsDeferDrawing())
{
_NotifyTerminalCursorPositionChanged();
}
}
void Terminal::TrackCursorMovement(bool track) noexcept
{
_trackingCursorMovement = track;
}
void Terminal::UserScrollViewport(const int viewTop)
{
// we're going to modify state here that the renderer could be reading.
@@ -1225,17 +1220,6 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept
return cursor.IsBlinkingAllowed();
}
// Method Description:
// - Computes whether the cursor is currently off the screen
// Return value:
// - True if the cursor if off the screen, false otherwise
bool Terminal::IsCursorOffScreen() noexcept
{
const auto absoluteCursorPosY = _buffer->GetCursor().GetPosition().Y;
const auto scrollOffset = GetScrollOffset();
return absoluteCursorPosY < scrollOffset || absoluteCursorPosY >= (scrollOffset + _GetMutableViewport().Height());
}
// Method Description:
// - Update our internal knowledge about where regex patterns are on the screen
// - This is called by TerminalControl (through a throttled function) when the visible

View File

@@ -213,13 +213,10 @@ public:
void SetCursorOn(const bool isOn);
bool IsCursorBlinkingAllowed() const noexcept;
bool IsCursorOffScreen() noexcept;
void UpdatePatternsUnderLock() noexcept;
void ClearPatternTree() noexcept;
void TrackCursorMovement(bool track) noexcept;
const std::optional<til::color> GetTabColor() const noexcept;
til::color GetDefaultBackground() const noexcept;
@@ -292,8 +289,6 @@ private:
size_t _hyperlinkPatternId;
bool _trackingCursorMovement{ false };
std::wstring _workingDirectory;
// This default fake font value is only used to check if the font is a raster font.

View File

@@ -198,9 +198,4 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
globals.Language(currentLanguage);
}
}
bool GlobalAppearance::FeatureTrayIconEnabled() const noexcept
{
return Feature_TrayIcon::IsEnabled();
}
}

View File

@@ -25,8 +25,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
bool FeatureTrayIconEnabled() const noexcept;
WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr);
GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals, Theme);
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, State().Globals, TabWidthMode);

View File

@@ -25,7 +25,5 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentTabWidthMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };
Boolean FeatureTrayIconEnabled { get; };
}
}

View File

@@ -84,18 +84,6 @@
<local:SettingContainer x:Uid="Globals_DisableAnimationsReversed">
<ToggleSwitch IsOn="{x:Bind local:Converters.InvertBoolean(State.Globals.DisableAnimations), BindBack=State.Globals.SetInvertedDisableAnimationsValue, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Always Show Tray Icon -->
<local:SettingContainer x:Uid="Globals_AlwaysShowTrayIcon"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowTrayIcon, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Minimize To Tray -->
<local:SettingContainer x:Uid="Globals_MinimizeToTray"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.MinimizeToTray, Mode=TwoWay}" />
</local:SettingContainer>
</StackPanel>
</ScrollViewer>
</Page>

View File

@@ -312,12 +312,12 @@
</ItemDefinitionGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.6.2-prerelease.210818003\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>

View File

@@ -1090,14 +1090,6 @@
<value>Pane animations</value>
<comment>Header for a control to toggle animations on panes. "Enabled" value enables the animations.</comment>
</data>
<data name="Globals_AlwaysShowTrayIcon.Header" xml:space="preserve">
<value>Always display an icon in the notification area</value>
<comment>Header for a control to toggle whether the tray icon should always be shown.</comment>
</data>
<data name="Globals_MinimizeToTray.Header" xml:space="preserve">
<value>Hide Terminal in the notification area when it is minimized</value>
<comment>Header for a control to toggle whether the terminal should hide itself in the tray instead of the taskbar when minimized.</comment>
</data>
<data name="SettingContainer_OverrideMessageBaseLayer" xml:space="preserve">
<value>Reset to inherited value.</value>
<comment>This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button.</comment>

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