conflict
This commit is contained in:
commit
3beeafa427
1
.github/actions/spelling/allow/allow.txt
vendored
1
.github/actions/spelling/allow/allow.txt
vendored
|
@ -15,6 +15,7 @@ downsides
|
|||
dze
|
||||
dzhe
|
||||
Enum'd
|
||||
Fitt
|
||||
formattings
|
||||
ftp
|
||||
fvar
|
||||
|
|
7
.github/actions/spelling/expect/expect.txt
vendored
7
.github/actions/spelling/expect/expect.txt
vendored
|
@ -105,6 +105,7 @@ autoscrolling
|
|||
Autowrap
|
||||
AVerify
|
||||
AVI
|
||||
AVX
|
||||
awch
|
||||
azuredevopspodcast
|
||||
azzle
|
||||
|
@ -190,6 +191,7 @@ CARETBLINKINGENABLED
|
|||
CARRIAGERETURN
|
||||
cascadia
|
||||
cassert
|
||||
castsi
|
||||
catid
|
||||
cazamor
|
||||
CBash
|
||||
|
@ -270,6 +272,7 @@ Cmdlet
|
|||
cmdline
|
||||
CMOUSEBUTTONS
|
||||
cmp
|
||||
cmpeq
|
||||
cmt
|
||||
cmyk
|
||||
CNL
|
||||
|
@ -1021,6 +1024,7 @@ IAction
|
|||
IApi
|
||||
IApplication
|
||||
IBase
|
||||
ICache
|
||||
icacls
|
||||
iccex
|
||||
icch
|
||||
|
@ -1260,6 +1264,7 @@ lnkd
|
|||
lnkfile
|
||||
LNM
|
||||
LOADONCALL
|
||||
loadu
|
||||
LOBYTE
|
||||
localappdata
|
||||
localhost
|
||||
|
@ -1420,6 +1425,7 @@ MOUSEFIRST
|
|||
MOUSEHWHEEL
|
||||
MOUSEMOVE
|
||||
mousewheel
|
||||
movemask
|
||||
MOVESTART
|
||||
msb
|
||||
msbuild
|
||||
|
@ -2530,6 +2536,7 @@ vcvarsall
|
|||
vcxitems
|
||||
vcxproj
|
||||
vec
|
||||
vectorized
|
||||
VERCTRL
|
||||
versioning
|
||||
VERTBAR
|
||||
|
|
|
@ -1,48 +1,492 @@
|
|||
# This build should never run as CI or against a pull request.
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
name: Package ES Standard Build
|
||||
|
||||
parameters:
|
||||
- name: branding
|
||||
displayName: "Branding (Build Type)"
|
||||
type: string
|
||||
default: Release
|
||||
values:
|
||||
- Release
|
||||
- Preview
|
||||
- name: buildTerminal
|
||||
displayName: "Build Windows Terminal MSIX"
|
||||
type: boolean
|
||||
default: true
|
||||
- name: buildTerminalVPack
|
||||
displayName: "Build Windows Terminal VPack"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: buildWPF
|
||||
displayName: "Build Terminal WPF Control"
|
||||
type: boolean
|
||||
default: false
|
||||
- name: pgoBuildMode
|
||||
displayName: "PGO Build Mode"
|
||||
type: string
|
||||
default: Optimize
|
||||
values:
|
||||
- Optimize
|
||||
- Instrument
|
||||
- None
|
||||
|
||||
- name: buildConfigurations
|
||||
type: object
|
||||
default:
|
||||
- Release
|
||||
- name: buildPlatforms
|
||||
type: object
|
||||
default:
|
||||
- x64
|
||||
- x86
|
||||
- arm64
|
||||
|
||||
variables:
|
||||
baseYearForVersioning: 2019 # Used by build-console-int
|
||||
versionMajor: 0
|
||||
versionMinor: 1
|
||||
TerminalInternalPackageVersion: "0.0.7"
|
||||
|
||||
# When we move off PackageES for Versioning, we'll need to switch
|
||||
# name to this format. For now, though, we need to use DayOfYear.Rev
|
||||
# to unique our builds, as mandated by PackageES's Setup task.
|
||||
# name: '$(versionMajor).$(versionMinor).$(DayOfYear)$(Rev:r).0'
|
||||
#
|
||||
# Build name/version number above must end with .0 to make the
|
||||
# store publication machinery happy.
|
||||
name: 'Terminal_$(date:yyMM).$(date:dd)$(rev:rrr)'
|
||||
|
||||
# Build Arguments:
|
||||
# WindowsTerminalOfficialBuild=[true,false]
|
||||
# true - this is running on our build agent
|
||||
# false - running locally
|
||||
# WindowsTerminalBranding=[Dev,Preview,Release]
|
||||
# <none> - Development build resources (default)
|
||||
# Preview - Preview build resources
|
||||
# Release - regular build resources
|
||||
name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr)
|
||||
resources:
|
||||
repositories:
|
||||
- repository: self
|
||||
type: git
|
||||
ref: main
|
||||
jobs:
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
- job: Build
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each config in parameters.buildConfigurations }}:
|
||||
${{ each platform in parameters.buildPlatforms }}:
|
||||
${{ config }}_${{ platform }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
BuildPlatform: ${{ platform }}
|
||||
displayName: Build
|
||||
cancelTimeoutInMinutes: 1
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: OpenConsole
|
||||
disableOutputRedirect: true
|
||||
- task: PowerShell@2
|
||||
displayName: Rationalize Build Platform
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Arch = "$(BuildPlatform)"
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: x86
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 5.10
|
||||
inputs:
|
||||
versionSpec: 5.10
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet custom
|
||||
inputs:
|
||||
command: custom
|
||||
selectOrConfig: config
|
||||
nugetConfigPath: NuGet.Config
|
||||
arguments: restore OpenConsole.sln -SolutionDirectory $(Build.SourcesDirectory)
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
feedListDownload: 2b3f8893-a6e8-411f-b197-a9e05576da48
|
||||
packageListDownload: e82d490c-af86-4733-9dc4-07b772033204
|
||||
versionListDownload: $(TerminalInternalPackageVersion)
|
||||
- task: TouchdownBuildTask@1
|
||||
displayName: Download Localization Files
|
||||
inputs:
|
||||
teamId: 7105
|
||||
authId: $(TouchdownAppId)
|
||||
authKey: $(TouchdownAppKey)
|
||||
resourceFilePath: >-
|
||||
src\cascadia\TerminalApp\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/build-console-int.yml
|
||||
parameters:
|
||||
platform: arm64
|
||||
additionalBuildArguments: /p:WindowsTerminalOfficialBuild=true;WindowsTerminalBranding=Preview
|
||||
src\cascadia\TerminalControl\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/check-formatting.yml
|
||||
src\cascadia\TerminalConnection\Resources\en-US\Resources.resw
|
||||
|
||||
- template: ./templates/release-sign-and-bundle.yml
|
||||
src\cascadia\TerminalSettingsModel\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\TerminalSettingsEditor\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\WindowsTerminalUniversal\Resources\en-US\Resources.resw
|
||||
|
||||
src\cascadia\CascadiaPackage\Resources\en-US\Resources.resw
|
||||
appendRelativeDir: true
|
||||
localizationTarget: false
|
||||
pseudoSetting: Included
|
||||
- task: PowerShell@2
|
||||
displayName: Move Loc files one level up
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Files = Get-ChildItem . -R -Filter 'Resources.resw' | ? FullName -Like '*en-US\*\Resources.resw'
|
||||
|
||||
$Files | % { Move-Item -Verbose $_.Directory $_.Directory.Parent.Parent -EA:Ignore }
|
||||
pwsh: true
|
||||
- task: PowerShell@2
|
||||
displayName: Generate NOTICE.html from NOTICE.md
|
||||
inputs:
|
||||
filePath: .\build\scripts\Generate-ThirdPartyNotices.ps1
|
||||
arguments: -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
|
||||
pwsh: true
|
||||
- ${{ if eq(parameters.pgoBuildMode, 'Optimize') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Restore PGO Database
|
||||
inputs:
|
||||
filePath: tools/PGODatabase/restore-pgodb.ps1
|
||||
workingDirectory: $(Build.SourcesDirectory)\tools\PGODatabase
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
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
|
||||
maximumCpuCount: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: binlog'
|
||||
condition: failed()
|
||||
continueOnError: True
|
||||
inputs:
|
||||
PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog
|
||||
ArtifactName: binlog-$(BuildPlatform)
|
||||
- ${{ if eq(parameters.pgoBuildMode, 'Optimize') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Validate binaries are optimized
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Binaries = 'OpenConsole.exe', 'WindowsTerminal.exe', 'TerminalApp.dll', 'TerminalConnection.dll', 'Microsoft.Terminal.Control.dll', 'Microsoft.Terminal.Remoting.dll', 'Microsoft.Terminal.Settings.Editor.dll', 'Microsoft.Terminal.Settings.Model.dll'
|
||||
|
||||
foreach ($BinFile in $Binaries) {
|
||||
|
||||
& "$(Build.SourcesDirectory)\tools\PGODatabase\verify-pgo.ps1" "$(Build.SourcesDirectory)/src/cascadia/CascadiaPackage/bin/$(BuildPlatform)/$(BuildConfiguration)/$BinFile"
|
||||
|
||||
}
|
||||
- task: PowerShell@2
|
||||
displayName: Check MSIX for common regressions
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
$Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
|
||||
|
||||
.\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
|
||||
pwsh: true
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln for PublicTerminalCore
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalOfficialBuild=true /p:WindowsTerminalBranding=${{ parameters.branding }} /p:WindowsTerminalReleaseBuild=true /t:Terminal\wpf\PublicTerminalCore
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
- task: PowerShell@2
|
||||
displayName: Source Index PDBs
|
||||
inputs:
|
||||
filePath: build\scripts\Index-Pdbs.ps1
|
||||
arguments: -SearchDir '$(Build.SourcesDirectory)' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion)
|
||||
errorActionPreference: silentlyContinue
|
||||
- task: ComponentGovernanceComponentDetection@0
|
||||
displayName: Component Detection
|
||||
- task: PowerShell@2
|
||||
displayName: Run Unit Tests
|
||||
condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'x86')))
|
||||
enabled: False
|
||||
inputs:
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*unit.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
- task: PowerShell@2
|
||||
displayName: Run Feature Tests
|
||||
condition: and(succeeded(), eq(variables['BuildPlatform'], 'x64'))
|
||||
enabled: False
|
||||
inputs:
|
||||
filePath: build\scripts\Run-Tests.ps1
|
||||
arguments: -MatchPattern '*feature.test*.dll' -Platform '$(RationalizedBuildPlatform)' -Configuration '$(BuildConfiguration)'
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy *.appx/*.msix to Artifacts
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/*.appx
|
||||
|
||||
**/*.msix
|
||||
|
||||
**/*.appxsym
|
||||
|
||||
!**/Microsoft.VCLibs*.appx
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/appx
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (appx)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/appx
|
||||
ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy PublicTerminalCore.dll to Artifacts
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
Contents: >-
|
||||
**/PublicTerminalCore.dll
|
||||
|
||||
**/api-ms-win-core-synch-l1-2-0.dll
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/wpf
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (PublicTerminalCore)
|
||||
condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64'))
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf
|
||||
ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)
|
||||
- task: PublishSymbols@2
|
||||
displayName: Publish symbols path
|
||||
continueOnError: True
|
||||
inputs:
|
||||
SearchPattern: '**/*.pdb'
|
||||
IndexSources: false
|
||||
SymbolServerType: TeamServices
|
||||
|
||||
- ${{ if eq(parameters.buildTerminal, true) }}:
|
||||
- job: BundleAndSign
|
||||
displayName: Create and sign AppX/MSIX bundles
|
||||
dependsOn: Build
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: OpenConsole
|
||||
disableOutputRedirect: true
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Artifacts (*.appx, *.msix)
|
||||
inputs:
|
||||
downloadType: specific
|
||||
itemPattern: >-
|
||||
**/*.msix
|
||||
|
||||
**/*.appx
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Create WindowsTerminal*.msixbundle
|
||||
inputs:
|
||||
filePath: build\scripts\Create-AppxBundle.ps1
|
||||
arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
- task: PowerShell@2
|
||||
displayName: Create WindowsTerminalUniversal*.msixbundle
|
||||
inputs:
|
||||
filePath: build\scripts\Create-AppxBundle.ps1
|
||||
arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName WindowsTerminalUniversal -BundleVersion $(XES_APPXMANIFESTVERSION) -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminalUniversal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle"
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: Submit *.msixbundle to ESRP for code signing
|
||||
inputs:
|
||||
ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
|
||||
FolderPath: $(System.ArtifactsDirectory)
|
||||
Pattern: Microsoft.WindowsTerminal*.msixbundle
|
||||
UseMinimatch: true
|
||||
signConfigType: inlineSignParams
|
||||
inlineOperation: >-
|
||||
[
|
||||
{
|
||||
"KeyCode": "Dynamic",
|
||||
"CertTemplateName": "WINMSAPP1ST",
|
||||
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
|
||||
"OperationCode": "SigntoolSign",
|
||||
"Parameters": {
|
||||
"OpusName": "Microsoft",
|
||||
"OpusInfo": "http://www.microsoft.com",
|
||||
"FileDigest": "/fd \"SHA256\"",
|
||||
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
|
||||
},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"KeyCode": "Dynamic",
|
||||
"CertTemplateName": "WINMSAPP1ST",
|
||||
"CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US",
|
||||
"OperationCode": "SigntoolVerify",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Artifact: appxbundle-signed'
|
||||
inputs:
|
||||
PathtoPublish: $(System.ArtifactsDirectory)
|
||||
ArtifactName: appxbundle-signed
|
||||
|
||||
- ${{ if eq(parameters.buildWPF, true) }}:
|
||||
- job: PackageAndSignWPF
|
||||
strategy:
|
||||
matrix:
|
||||
${{ each config in parameters.buildConfigurations }}:
|
||||
${{ config }}:
|
||||
BuildConfiguration: ${{ config }}
|
||||
displayName: Create NuGet Package (WPF Terminal Control)
|
||||
dependsOn: Build
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
persistCredentials: True
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: Package ES - Setup Build
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: OpenConsole
|
||||
disableOutputRedirect: true
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download x86 PublicTerminalCore
|
||||
inputs:
|
||||
artifactName: wpf-dll-x86-$(BuildConfiguration)
|
||||
itemPattern: '**/*.dll'
|
||||
downloadPath: bin\Win32\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download x64 PublicTerminalCore
|
||||
inputs:
|
||||
artifactName: wpf-dll-x64-$(BuildConfiguration)
|
||||
itemPattern: '**/*.dll'
|
||||
downloadPath: bin\x64\$(BuildConfiguration)\
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Move downloaded artifacts up a level
|
||||
inputs:
|
||||
targetType: inline
|
||||
# Find all artifact files and move them up a directory. Ugh.
|
||||
script: >-
|
||||
Get-ChildItem bin -Recurse -Directory -Filter wpf-dll-* | % {
|
||||
$_ | Get-ChildItem -Recurse -File | % {
|
||||
Move-Item -Verbose $_.FullName $_.Directory.Parent.FullName
|
||||
}
|
||||
}
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: Use NuGet 5.10.0
|
||||
inputs:
|
||||
versionSpec: 5.10.0
|
||||
- task: NuGetCommand@2
|
||||
displayName: NuGet restore copy
|
||||
inputs:
|
||||
selectOrConfig: config
|
||||
nugetConfigPath: NuGet.Config
|
||||
- task: VSBuild@1
|
||||
displayName: Build solution **\OpenConsole.sln for WPF Control
|
||||
inputs:
|
||||
solution: '**\OpenConsole.sln'
|
||||
vsVersion: 16.0
|
||||
msbuildArgs: /p:WindowsTerminalReleaseBuild=$(UseReleaseBranding);Version=$(XES_PACKAGEVERSIONNUMBER) /t:Pack
|
||||
platform: Any CPU
|
||||
configuration: $(BuildConfiguration)
|
||||
maximumCpuCount: true
|
||||
- task: PublishSymbols@2
|
||||
displayName: Publish symbols path
|
||||
continueOnError: True
|
||||
inputs:
|
||||
SearchPattern: '**/*.pdb'
|
||||
IndexSources: false
|
||||
SymbolServerType: TeamServices
|
||||
SymbolsArtifactName: Symbols_WPF_$(BuildConfiguration)
|
||||
- task: CopyFiles@2
|
||||
displayName: Copy *.nupkg to Artifacts
|
||||
inputs:
|
||||
Contents: '**/*Wpf*.nupkg'
|
||||
TargetFolder: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
OverWrite: true
|
||||
flattenFolders: true
|
||||
- task: EsrpCodeSigning@1
|
||||
displayName: Submit *.nupkg to ESRP for code signing
|
||||
inputs:
|
||||
ConnectedServiceName: 9d6d2960-0793-4d59-943e-78dcb434840a
|
||||
FolderPath: $(Build.ArtifactStagingDirectory)/nupkg
|
||||
Pattern: '*.nupkg'
|
||||
UseMinimatch: true
|
||||
signConfigType: inlineSignParams
|
||||
inlineOperation: >-
|
||||
[
|
||||
{
|
||||
"KeyCode": "CP-401405",
|
||||
"OperationCode": "NuGetSign",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
},
|
||||
{
|
||||
"KeyCode": "CP-401405",
|
||||
"OperationCode": "NuGetVerify",
|
||||
"Parameters": {},
|
||||
"ToolName": "sign",
|
||||
"ToolVersion": "1.0"
|
||||
}
|
||||
]
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Artifact (nupkg)
|
||||
inputs:
|
||||
PathtoPublish: $(Build.ArtifactStagingDirectory)\nupkg
|
||||
ArtifactName: wpf-nupkg-$(BuildConfiguration)
|
||||
|
||||
- ${{ if eq(parameters.buildTerminalVPack, true) }}:
|
||||
- job: VPack
|
||||
displayName: Create Windows vPack
|
||||
dependsOn: BundleAndSign
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
submodules: true
|
||||
- task: PkgESSetupBuild@12
|
||||
displayName: Package ES - Setup Build
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download Build Artifacts
|
||||
inputs:
|
||||
artifactName: appxbundle-signed
|
||||
extractTars: false
|
||||
- task: PowerShell@2
|
||||
displayName: Rename and stage packages for vpack
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: >-
|
||||
# Rename to known/fixed name for Windows build system
|
||||
|
||||
Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' }
|
||||
|
||||
|
||||
# Create vpack directory and place item inside
|
||||
|
||||
mkdir WindowsTerminal.app
|
||||
|
||||
mv Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle .\WindowsTerminal.app\
|
||||
workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed
|
||||
- task: PkgESVPack@10
|
||||
displayName: 'Package ES - VPack'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
inputs:
|
||||
sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app
|
||||
description: Windows Terminal pre-install application
|
||||
pushPkgName: WindowsTerminal.app
|
||||
owner: condev
|
||||
...
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
parameters:
|
||||
configuration: 'Release'
|
||||
platform: ''
|
||||
additionalBuildArguments: ''
|
||||
|
||||
jobs:
|
||||
- job: Build${{ parameters.platform }}${{ parameters.configuration }}
|
||||
displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }}
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
BuildPlatform: ${{ parameters.platform }}
|
||||
PGOBuildMode: 'Optimize'
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
demands:
|
||||
- msbuild
|
||||
- visualstudio
|
||||
- vstest
|
||||
|
||||
steps:
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- template: build-console-steps.yml
|
||||
parameters:
|
||||
additionalBuildArguments: "/p:XesUseOneStoreVersioning=true;XesBaseYearForStoreVersion=$(baseYearForVersioning) ${{ parameters.additionalBuildArguments }}"
|
|
@ -1,74 +0,0 @@
|
|||
parameters:
|
||||
configuration: 'Release'
|
||||
|
||||
jobs:
|
||||
- job: SignDeploy${{ parameters.configuration }}
|
||||
displayName: Sign and Deploy for ${{ parameters.configuration }}
|
||||
|
||||
dependsOn:
|
||||
- Buildx64AuditMode
|
||||
- Buildx64Release
|
||||
- Buildx86Release
|
||||
- Buildarm64Release
|
||||
- CodeFormatCheck
|
||||
condition: |
|
||||
and
|
||||
(
|
||||
in(dependencies.Buildx64AuditMode.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildx86Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.Buildarm64Release.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'),
|
||||
in(dependencies.CodeFormatCheck.result, 'Succeeded', 'SucceededWithIssues', 'Skipped')
|
||||
)
|
||||
|
||||
variables:
|
||||
BuildConfiguration: ${{ parameters.configuration }}
|
||||
AppxProjectName: CascadiaPackage
|
||||
AppxBundleName: Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle
|
||||
|
||||
pool:
|
||||
name: Package ES Lab E
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
clean: true
|
||||
|
||||
- task: PkgESSetupBuild@10
|
||||
displayName: 'Package ES - Setup Build'
|
||||
inputs:
|
||||
useDfs: false
|
||||
productName: WindowsTerminal
|
||||
disableOutputRedirect: true
|
||||
|
||||
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
|
||||
displayName: 'Component Detection'
|
||||
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download AppX artifacts
|
||||
inputs:
|
||||
artifactName: 'appx-$(BuildConfiguration)'
|
||||
itemPattern: |
|
||||
**/*.appx
|
||||
**/*.msix
|
||||
downloadPath: '$(Build.ArtifactStagingDirectory)\appx'
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Create $(AppxBundleName)'
|
||||
inputs:
|
||||
targetType: filePath
|
||||
filePath: '.\build\scripts\Create-AppxBundle.ps1'
|
||||
arguments: |
|
||||
-InputPath "$(Build.ArtifactStagingDirectory)\appx" -ProjectName $(AppxProjectName) -BundleVersion 0.0.0.0 -OutputPath "$(Build.ArtifactStagingDirectory)\$(AppxBundleName)"
|
||||
|
||||
- task: PkgESCodeSign@10
|
||||
displayName: 'Package ES - SignConfig.WindowsTerminal.xml'
|
||||
inputs:
|
||||
signConfigXml: 'build\config\SignConfig.WindowsTerminal.xml'
|
||||
inPathRoot: '$(Build.ArtifactStagingDirectory)'
|
||||
outPathRoot: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish Signed AppX'
|
||||
inputs:
|
||||
PathtoPublish: '$(Build.ArtifactStagingDirectory)\signed'
|
||||
ArtifactName: 'appxbundle-signed-$(BuildConfiguration)'
|
|
@ -4,7 +4,7 @@
|
|||
"title": "Microsoft's Windows Terminal Settings Profile Schema",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
"pattern": "^(?:(?:ctrl|alt|shift|win)\\+)*(?:app|backspace|comma|delete|down|end|enter|esc|escape|home|insert|left|menu|minus|pagedown|pageup|period|pgdn|pgup|plus|right|space|tab|up|f(?:1\\d?|2[0-4]?|[3-9])|numpad\\d|numpad_(?:\\d|add|decimal|divide|minus|multiply|period|plus|subtract)|(?:vk|sc)\\((?:[1-9]|1?\\d{2}|2[0-4]\\d|25[0-5])\\)|[^\\s+])(?:\\+(?:ctrl|alt|shift|win))*$",
|
||||
"pattern": "^(?:(?:ctrl|alt|shift|win)\\+)*(?:app|backspace|browser_(?:back|forward|refresh|stop|search|favorites|home)|comma|delete|down|end|enter|esc|escape|home|insert|left|menu|minus|pagedown|pageup|period|pgdn|pgup|plus|right|space|tab|up|f(?:1\\d?|2[0-4]?|[3-9])|numpad\\d|numpad_(?:\\d|add|decimal|divide|minus|multiply|period|plus|subtract)|(?:vk|sc)\\((?:[1-9]|1?\\d{2}|2[0-4]\\d|25[0-5])\\)|[^\\s+])(?:\\+(?:ctrl|alt|shift|win))*$",
|
||||
"type": "string",
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+][win+]<KeyName>\", where each modifier is optional. KeyName is either any single key character, an explicit virtual key or scan code in the form vk(nnn) and sc(nnn) respectively, or one of the special names listed at https://docs.microsoft.com/en-us/windows/terminal/customize-settings/actions#accepted-modifiers-and-keys"
|
||||
},
|
||||
|
@ -271,6 +271,7 @@
|
|||
"toggleFocusMode",
|
||||
"toggleFullscreen",
|
||||
"togglePaneZoom",
|
||||
"toggleSplitOrientation",
|
||||
"toggleReadOnlyMode",
|
||||
"toggleShaderEffects",
|
||||
"wt",
|
||||
|
|
|
@ -6,7 +6,7 @@ Module Name:
|
|||
- OutputCellView.hpp
|
||||
|
||||
Abstract:
|
||||
- Read-only view into a single cell of data that someone is attempting to write into the output buffer.
|
||||
- Read view into a single cell of data that someone is attempting to write into the output buffer.
|
||||
- This is done for performance reasons (avoid heap allocs and copies).
|
||||
|
||||
Author:
|
||||
|
@ -36,6 +36,21 @@ public:
|
|||
TextAttribute TextAttr() const noexcept;
|
||||
TextAttributeBehavior TextAttrBehavior() const noexcept;
|
||||
|
||||
void UpdateText(const std::wstring_view& view) noexcept
|
||||
{
|
||||
_view = view;
|
||||
};
|
||||
|
||||
void UpdateDbcsAttribute(const DbcsAttribute& dbcsAttr) noexcept
|
||||
{
|
||||
_dbcsAttr = dbcsAttr;
|
||||
}
|
||||
|
||||
void UpdateTextAttribute(const TextAttribute& textAttr) noexcept
|
||||
{
|
||||
_textAttr = textAttr;
|
||||
}
|
||||
|
||||
bool operator==(const OutputCellView& view) const noexcept;
|
||||
bool operator!=(const OutputCellView& view) const noexcept;
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ bool TextAttribute::IsLegacy() const noexcept
|
|||
// - blinkingIsFaint: true if blinking should be interpreted as faint.
|
||||
// Return Value:
|
||||
// - the foreground and background colors that should be displayed.
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> TextAttribute::CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode,
|
||||
|
|
|
@ -64,7 +64,7 @@ public:
|
|||
static TextAttribute StripErroneousVT16VersionsOfLegacyDefaults(const TextAttribute& attribute) noexcept;
|
||||
WORD GetLegacyAttributes() const noexcept;
|
||||
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const gsl::span<const COLORREF> colorTable,
|
||||
std::pair<COLORREF, COLORREF> CalculateRgbColors(const std::array<COLORREF, 256>& colorTable,
|
||||
const COLORREF defaultFgColor,
|
||||
const COLORREF defaultBgColor,
|
||||
const bool reverseScreenMode = false,
|
||||
|
|
|
@ -50,6 +50,9 @@ constexpr std::array<BYTE, 256> Index256ToIndex16 = {
|
|||
|
||||
// clang-format on
|
||||
|
||||
// We should only need 4B for TextColor. Any more than that is just waste.
|
||||
static_assert(sizeof(TextColor) == 4);
|
||||
|
||||
bool TextColor::CanBeBrightened() const noexcept
|
||||
{
|
||||
return IsIndex16() || IsDefault();
|
||||
|
@ -138,15 +141,12 @@ void TextColor::SetDefault() noexcept
|
|||
// - brighten: if true, we'll brighten a dark color table index.
|
||||
// Return Value:
|
||||
// - a COLORREF containing the real value of this TextColor.
|
||||
COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
bool brighten) const noexcept
|
||||
COLORREF TextColor::GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten) const noexcept
|
||||
{
|
||||
if (IsDefault())
|
||||
{
|
||||
if (brighten)
|
||||
{
|
||||
FAIL_FAST_IF(colorTable.size() < 16);
|
||||
// See MSFT:20266024 for context on this fix.
|
||||
// Additionally todo MSFT:20271956 to fix this better for 19H2+
|
||||
// If we're a default color, check to see if the defaultColor exists
|
||||
|
@ -156,6 +156,58 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
|||
// (Settings::_DefaultForeground==INVALID_COLOR, and the index
|
||||
// from _wFillAttribute is being used instead.)
|
||||
// If we find a match, return instead the bright version of this color
|
||||
|
||||
static_assert(sizeof(COLORREF) * 8 == 32, "The vectorized code broke. If you can't fix COLORREF, just remove the vectorized code.");
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
|
||||
#pragma warning(disable : 26490) // Don't use reinterpret_cast (type.1).
|
||||
#ifdef __AVX2__
|
||||
// I wrote this vectorized code one day, because the sun was shining so nicely.
|
||||
// There's no other reason for this to exist here, except for being pretty.
|
||||
// This code implements the exact same for loop you can find below, but is ~3x faster.
|
||||
//
|
||||
// A brief explanation for people unfamiliar with vectorized instructions:
|
||||
// Vectorized instructions, like "SSE" or "AVX", allow you to run
|
||||
// common operations like additions, multiplications, comparisons,
|
||||
// or bitwise operations concurrently on multiple values at once.
|
||||
//
|
||||
// We want to find the given defaultColor in the first 8 values of colorTable.
|
||||
// Coincidentally a COLORREF is a DWORD and 8 of them are exactly 256 bits.
|
||||
// -- The size of a single AVX register.
|
||||
//
|
||||
// Thus, the code works like this:
|
||||
// 1. Load all 8 DWORDs at once into one register
|
||||
// 2. Set the same defaultColor 8 times in another register
|
||||
// 3. Compare all 8 values at once
|
||||
// The result is either 0xffffffff or 0x00000000.
|
||||
// 4. Extract the most significant bit of each DWORD
|
||||
// Assuming that no duplicate colors exist in colorTable,
|
||||
// the result will be something like 0b00100000.
|
||||
// 5. Use BitScanForward (bsf) to find the index of the most significant 1 bit.
|
||||
const auto haystack = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(colorTable.data())); // 1.
|
||||
const auto needle = _mm256_set1_epi32(__builtin_bit_cast(int, defaultColor)); // 2.
|
||||
const auto result = _mm256_cmpeq_epi32(haystack, needle); // 3.
|
||||
const auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(result)); // 4.
|
||||
unsigned long index;
|
||||
return _BitScanForward(&index, mask) ? til::at(colorTable, static_cast<size_t>(index) + 8) : defaultColor; // 5.
|
||||
#elif _M_AMD64
|
||||
// If you look closely this SSE2 algorithm is the exact same as the AVX one.
|
||||
// The two differences are that we need to:
|
||||
// * do everything twice, because SSE is limited to 128 bits and not 256.
|
||||
// * use _mm_packs_epi32 to merge two 128 bits vectors into one in step 3.5.
|
||||
// _mm_packs_epi32 takes two SSE registers and truncates all 8 DWORDs into 8 WORDs,
|
||||
// the latter of which fits into a single register (which is then used in the identical step 4).
|
||||
const auto haystack1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 0));
|
||||
const auto haystack2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(colorTable.data() + 4));
|
||||
const auto needle = _mm_set1_epi32(__builtin_bit_cast(int, defaultColor));
|
||||
const auto result1 = _mm_cmpeq_epi32(haystack1, needle);
|
||||
const auto result2 = _mm_cmpeq_epi32(haystack2, needle);
|
||||
const auto result = _mm_packs_epi32(result1, result2); // 3.5
|
||||
const auto mask = _mm_movemask_ps(_mm_castsi128_ps(result));
|
||||
unsigned long index;
|
||||
return _BitScanForward(&index, mask) ? til::at(colorTable, static_cast<size_t>(index) + 8) : defaultColor;
|
||||
#else
|
||||
for (size_t i = 0; i < 8; i++)
|
||||
{
|
||||
if (til::at(colorTable, i) == defaultColor)
|
||||
|
@ -163,6 +215,8 @@ COLORREF TextColor::GetColor(gsl::span<const COLORREF> colorTable,
|
|||
return til::at(colorTable, i + 8);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#pragma warning(pop)
|
||||
}
|
||||
|
||||
return defaultColor;
|
||||
|
@ -199,7 +253,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
|
|||
}
|
||||
else if (IsIndex256())
|
||||
{
|
||||
return Index256ToIndex16.at(GetIndex());
|
||||
return til::at(Index256ToIndex16, GetIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -208,7 +262,7 @@ BYTE TextColor::GetLegacyIndex(const BYTE defaultIndex) const noexcept
|
|||
const BYTE compressedRgb = (_red & 0b11100000) +
|
||||
((_green >> 3) & 0b00011100) +
|
||||
((_blue >> 6) & 0b00000011);
|
||||
return CompressedRgbToIndex16.at(compressedRgb);
|
||||
return til::at(CompressedRgbToIndex16, compressedRgb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,10 +86,7 @@ public:
|
|||
void SetIndex(const BYTE index, const bool isIndex256) noexcept;
|
||||
void SetDefault() noexcept;
|
||||
|
||||
COLORREF GetColor(gsl::span<const COLORREF> colorTable,
|
||||
const COLORREF defaultColor,
|
||||
const bool brighten = false) const noexcept;
|
||||
|
||||
COLORREF GetColor(const std::array<COLORREF, 256>& colorTable, const COLORREF defaultColor, bool brighten = false) const noexcept;
|
||||
BYTE GetLegacyIndex(const BYTE defaultIndex) const noexcept;
|
||||
|
||||
constexpr BYTE GetIndex() const noexcept
|
||||
|
@ -157,5 +154,3 @@ namespace WEX
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static_assert(sizeof(TextColor) <= 4 * sizeof(BYTE), "We should only need 4B for an entire TextColor. Any more than that is just waste");
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<ClInclude Include="..\Row.hpp" />
|
||||
<ClInclude Include="..\search.h" />
|
||||
<ClInclude Include="..\TextColor.h" />
|
||||
<ClInclude Include="..\TextAttribute.h" />
|
||||
<ClInclude Include="..\TextAttribute.hpp" />
|
||||
<ClInclude Include="..\textBuffer.hpp" />
|
||||
<ClInclude Include="..\textBufferCellIterator.hpp" />
|
||||
<ClInclude Include="..\textBufferTextIterator.hpp" />
|
||||
|
|
|
@ -94,20 +94,93 @@ bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const
|
|||
// - Reference to self after movement.
|
||||
TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
// Note that this method is called intensively when the terminal is under heavy load.
|
||||
// We need to aggressively optimize it, comparing to the -= operator.
|
||||
ptrdiff_t move = movement;
|
||||
auto newPos = _pos;
|
||||
while (move > 0 && !_exceeded)
|
||||
if (move < 0)
|
||||
{
|
||||
_exceeded = !_bounds.IncrementInBounds(newPos);
|
||||
// Early branching to leave the rare case to -= operator.
|
||||
// This helps reducing the instruction count within this method, which is good for instruction cache.
|
||||
return *this -= -move;
|
||||
}
|
||||
|
||||
// The remaining code in this function is functionally equivalent to:
|
||||
// auto newPos = _pos;
|
||||
// while (move > 0 && !_exceeded)
|
||||
// {
|
||||
// _exceeded = !_bounds.IncrementInBounds(newPos);
|
||||
// move--;
|
||||
// }
|
||||
// _SetPos(newPos);
|
||||
//
|
||||
// _SetPos() necessitates calling _GenerateView() and thus the construction
|
||||
// of a new OutputCellView(). This has a high performance impact (ICache spill?).
|
||||
// The code below inlines _bounds.IncrementInBounds as well as SetPos.
|
||||
// In the hot path (_pos.Y doesn't change) we modify the _view directly.
|
||||
|
||||
// Hoist these integers which will be used frequently later.
|
||||
const auto boundsRightInclusive = _bounds.RightInclusive();
|
||||
const auto boundsLeft = _bounds.Left();
|
||||
const auto boundsBottomInclusive = _bounds.BottomInclusive();
|
||||
const auto boundsTop = _bounds.Top();
|
||||
const auto oldX = _pos.X;
|
||||
const auto oldY = _pos.Y;
|
||||
|
||||
// Under MSVC writing the individual members of a COORD generates worse assembly
|
||||
// compared to having them be local variables. This causes a performance impact.
|
||||
auto newX = oldX;
|
||||
auto newY = oldY;
|
||||
|
||||
while (move > 0)
|
||||
{
|
||||
if (newX == boundsRightInclusive)
|
||||
{
|
||||
newX = boundsLeft;
|
||||
newY++;
|
||||
if (newY > boundsBottomInclusive)
|
||||
{
|
||||
newY = boundsTop;
|
||||
_exceeded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newX++;
|
||||
_exceeded = false;
|
||||
}
|
||||
move--;
|
||||
}
|
||||
while (move < 0 && !_exceeded)
|
||||
|
||||
if (_exceeded)
|
||||
{
|
||||
_exceeded = !_bounds.DecrementInBounds(newPos);
|
||||
move++;
|
||||
// Early return because nothing needs to be done here.
|
||||
return *this;
|
||||
}
|
||||
_SetPos(newPos);
|
||||
return (*this);
|
||||
|
||||
if (newY == oldY)
|
||||
{
|
||||
// hot path
|
||||
const auto diff = gsl::narrow_cast<ptrdiff_t>(newX) - gsl::narrow_cast<ptrdiff_t>(oldX);
|
||||
_attrIter += diff;
|
||||
_view.UpdateTextAttribute(*_attrIter);
|
||||
|
||||
const CharRow& charRow = _pRow->GetCharRow();
|
||||
_view.UpdateText(charRow.GlyphAt(newX));
|
||||
_view.UpdateDbcsAttribute(charRow.DbcsAttrAt(newX));
|
||||
_pos.X = newX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// cold path (_GenerateView is slow)
|
||||
_pRow = s_GetRow(_buffer, { newX, newY });
|
||||
_attrIter = _pRow->GetAttrRow().cbegin() + newX;
|
||||
_pos.X = newX;
|
||||
_pos.Y = newY;
|
||||
_GenerateView();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -118,7 +191,22 @@ TextBufferCellIterator& TextBufferCellIterator::operator+=(const ptrdiff_t& move
|
|||
// - Reference to self after movement.
|
||||
TextBufferCellIterator& TextBufferCellIterator::operator-=(const ptrdiff_t& movement)
|
||||
{
|
||||
return this->operator+=(-movement);
|
||||
ptrdiff_t move = movement;
|
||||
if (move < 0)
|
||||
{
|
||||
return (*this) += (-move);
|
||||
}
|
||||
|
||||
auto newPos = _pos;
|
||||
while (move > 0 && !_exceeded)
|
||||
{
|
||||
_exceeded = !_bounds.DecrementInBounds(newPos);
|
||||
move--;
|
||||
}
|
||||
_SetPos(newPos);
|
||||
|
||||
_GenerateView();
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
|
@ -23,11 +23,9 @@ class TextAttributeTests
|
|||
TEST_METHOD(TestReverseDefaultColors);
|
||||
TEST_METHOD(TestRoundtripDefaultColors);
|
||||
|
||||
static const int COLOR_TABLE_SIZE = 16;
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextAttributeTests::ClassSetup()
|
||||
|
@ -51,11 +49,6 @@ bool TextAttributeTests::ClassSetup()
|
|||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextAttributeTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripLegacy()
|
||||
{
|
||||
WORD expectedLegacy = FOREGROUND_BLUE | BACKGROUND_RED;
|
||||
|
@ -133,23 +126,22 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
const COLORREF faintRed = RGB(127, 0, 0);
|
||||
const COLORREF green = RGB(0, 255, 0);
|
||||
TextAttribute attr(red, green);
|
||||
auto view = _GetTableView();
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// reset the reverse video
|
||||
attr.SetReverseVideo(false);
|
||||
|
@ -158,17 +150,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
// while the background and getters stay the same
|
||||
attr.SetFaint(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(faintRed, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched, and the background fainter, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, faintRed), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// reset the reverse video and faint attributes
|
||||
attr.SetReverseVideo(false);
|
||||
|
@ -178,17 +170,17 @@ void TextAttributeTests::TestTextAttributeColorGetters()
|
|||
// background, while getters stay the same
|
||||
attr.SetInvisible(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(green, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, the calculated background value should match
|
||||
// the foreground, while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(red, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestReverseDefaultColors()
|
||||
|
@ -196,40 +188,39 @@ void TextAttributeTests::TestReverseDefaultColors()
|
|||
const COLORREF red = RGB(255, 0, 0);
|
||||
const COLORREF green = RGB(0, 255, 0);
|
||||
TextAttribute attr{};
|
||||
auto view = _GetTableView();
|
||||
|
||||
// verify that calculated foreground/background are the same as the direct
|
||||
// values when reverse video is not set
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, _defaultBg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
// with reverse video set, calculated foreground/background values should be
|
||||
// switched while getters stay the same
|
||||
attr.SetReverseVideo(true);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, _defaultFg), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
attr.SetForeground(red);
|
||||
VERIFY_IS_TRUE(attr.IsReverseVideo());
|
||||
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(red, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(_defaultBg, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultBg, red), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
|
||||
attr.Invert();
|
||||
VERIFY_IS_FALSE(attr.IsReverseVideo());
|
||||
attr.SetDefaultForeground();
|
||||
attr.SetBackground(green);
|
||||
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(view, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(view, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(view, _defaultFg, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(_defaultFg, attr.GetForeground().GetColor(_colorTable, _defaultFg));
|
||||
VERIFY_ARE_EQUAL(green, attr.GetBackground().GetColor(_colorTable, _defaultBg));
|
||||
VERIFY_ARE_EQUAL(std::make_pair(_defaultFg, green), attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg));
|
||||
}
|
||||
|
||||
void TextAttributeTests::TestRoundtripDefaultColors()
|
||||
|
|
|
@ -23,11 +23,9 @@ class TextColorTests
|
|||
TEST_METHOD(TestRgbColor);
|
||||
TEST_METHOD(TestChangeColor);
|
||||
|
||||
static const int COLOR_TABLE_SIZE = 16;
|
||||
COLORREF _colorTable[COLOR_TABLE_SIZE];
|
||||
std::array<COLORREF, 256> _colorTable;
|
||||
COLORREF _defaultFg = RGB(1, 2, 3);
|
||||
COLORREF _defaultBg = RGB(4, 5, 6);
|
||||
gsl::span<const COLORREF> _GetTableView();
|
||||
};
|
||||
|
||||
bool TextColorTests::ClassSetup()
|
||||
|
@ -51,11 +49,6 @@ bool TextColorTests::ClassSetup()
|
|||
return true;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> TextColorTests::_GetTableView()
|
||||
{
|
||||
return gsl::span<const COLORREF>(&_colorTable[0], COLOR_TABLE_SIZE);
|
||||
}
|
||||
|
||||
void TextColorTests::TestDefaultColor()
|
||||
{
|
||||
TextColor defaultColor;
|
||||
|
@ -64,18 +57,16 @@ void TextColorTests::TestDefaultColor()
|
|||
VERIFY_IS_FALSE(defaultColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(defaultColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = defaultColor.GetColor(view, _defaultFg, false);
|
||||
auto color = defaultColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultFg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultBg, false);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = defaultColor.GetColor(view, _defaultBg, true);
|
||||
color = defaultColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
}
|
||||
|
||||
|
@ -87,18 +78,16 @@ void TextColorTests::TestDarkIndexColor()
|
|||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = indexColor.GetColor(view, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
|
@ -110,18 +99,16 @@ void TextColorTests::TestBrightIndexColor()
|
|||
VERIFY_IS_TRUE(indexColor.IsLegacy());
|
||||
VERIFY_IS_FALSE(indexColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = indexColor.GetColor(view, _defaultFg, false);
|
||||
auto color = indexColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultFg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, false);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = indexColor.GetColor(view, _defaultBg, true);
|
||||
color = indexColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
||||
|
@ -134,18 +121,16 @@ void TextColorTests::TestRgbColor()
|
|||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
}
|
||||
|
||||
|
@ -158,57 +143,55 @@ void TextColorTests::TestChangeColor()
|
|||
VERIFY_IS_FALSE(rgbColor.IsLegacy());
|
||||
VERIFY_IS_TRUE(rgbColor.IsRgb());
|
||||
|
||||
auto view = _GetTableView();
|
||||
|
||||
auto color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
auto color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(myColor, color);
|
||||
|
||||
rgbColor.SetDefault();
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultFg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_defaultBg, color);
|
||||
|
||||
rgbColor.SetIndex(7, false);
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[7], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
rgbColor.SetIndex(15, false);
|
||||
color = rgbColor.GetColor(view, _defaultFg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultFg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultFg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, false);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, false);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
|
||||
color = rgbColor.GetColor(view, _defaultBg, true);
|
||||
color = rgbColor.GetColor(_colorTable, _defaultBg, true);
|
||||
VERIFY_ARE_EQUAL(_colorTable[15], color);
|
||||
}
|
||||
|
|
|
@ -397,6 +397,10 @@ namespace SettingsModelLocalTests
|
|||
"name":"action6",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\foo", "commandline": "bar.exe" }
|
||||
},
|
||||
{
|
||||
"name":"action7_startingDirectoryWithTrailingSlash",
|
||||
"command": { "action": "newWindow", "startingDirectory":"C:\\", "commandline": "bar.exe" }
|
||||
},
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
|
@ -405,7 +409,7 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(0u, commands.Size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(7u, commands.Size());
|
||||
VERIFY_ARE_EQUAL(8u, commands.Size());
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action0");
|
||||
|
@ -503,5 +507,20 @@ namespace SettingsModelLocalTests
|
|||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\foo\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
|
||||
{
|
||||
auto command = commands.Lookup(L"action7_startingDirectoryWithTrailingSlash");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.ActionAndArgs());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::NewWindow, command.ActionAndArgs().Action());
|
||||
const auto& realArgs = command.ActionAndArgs().Args().try_as<NewWindowArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
const auto& terminalArgs = realArgs.TerminalArgs();
|
||||
VERIFY_IS_NOT_NULL(terminalArgs);
|
||||
auto cmdline = terminalArgs.ToCommandline();
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"cmdline: \"%s\"", cmdline.c_str()));
|
||||
VERIFY_ARE_EQUAL(L"--startingDirectory \"C:\\\\\" -- \"bar.exe\"", terminalArgs.ToCommandline());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,6 +161,13 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleToggleSplitOrientation(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
_ToggleSplitOrientation();
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
|
@ -307,8 +314,11 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
else
|
||||
{
|
||||
_MoveFocus(realArgs.FocusDirection());
|
||||
args.Handled(true);
|
||||
// Mark as handled only when the move succeeded (e.g. when there
|
||||
// is a pane to move to), otherwise mark as unhandled so the
|
||||
// keychord can propagate to the terminal (GH#6129)
|
||||
const auto moveSucceeded = _MoveFocus(realArgs.FocusDirection());
|
||||
args.Handled(moveSucceeded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1449,8 +1449,26 @@ void Pane::_UpdateBorders()
|
|||
_border.BorderThickness(ThicknessHelper::FromLengths(left, top, right, bottom));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Find the borders for the leaf pane, or the shared borders for child panes.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
Borders Pane::_GetCommonBorders()
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
return _borders;
|
||||
}
|
||||
|
||||
return _firstChild->_GetCommonBorders() & _secondChild->_GetCommonBorders();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the row/column of our child UI elements, to match our current split type.
|
||||
// - In case the split definition or parent borders were changed, this recursively
|
||||
// updates the children as well.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
|
@ -1466,9 +1484,8 @@ void Pane::_ApplySplitDefinitions()
|
|||
_secondChild->_borders = _borders | Borders::Left;
|
||||
_borders = Borders::None;
|
||||
|
||||
_UpdateBorders();
|
||||
_firstChild->_UpdateBorders();
|
||||
_secondChild->_UpdateBorders();
|
||||
_firstChild->_ApplySplitDefinitions();
|
||||
_secondChild->_ApplySplitDefinitions();
|
||||
}
|
||||
else if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
|
@ -1479,10 +1496,10 @@ void Pane::_ApplySplitDefinitions()
|
|||
_secondChild->_borders = _borders | Borders::Top;
|
||||
_borders = Borders::None;
|
||||
|
||||
_UpdateBorders();
|
||||
_firstChild->_UpdateBorders();
|
||||
_secondChild->_UpdateBorders();
|
||||
_firstChild->_ApplySplitDefinitions();
|
||||
_secondChild->_ApplySplitDefinitions();
|
||||
}
|
||||
_UpdateBorders();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1743,6 +1760,43 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::Split(SplitState s
|
|||
return _Split(splitType, splitSize, profile, control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Toggle the split orientation of the currently focused pane
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - true if a split was changed
|
||||
bool Pane::ToggleSplitOrientation()
|
||||
{
|
||||
// If we are a leaf there is no split to toggle.
|
||||
if (_IsLeaf())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if either our first or second child is the currently focused leaf.
|
||||
// If they are then switch the split orientation on the current pane.
|
||||
const bool firstIsFocused = _firstChild->_IsLeaf() && _firstChild->_lastActive;
|
||||
const bool secondIsFocused = _secondChild->_IsLeaf() && _secondChild->_lastActive;
|
||||
if (firstIsFocused || secondIsFocused)
|
||||
{
|
||||
// Switch the split orientation
|
||||
_splitState = _splitState == SplitState::Horizontal ? SplitState::Vertical : SplitState::Horizontal;
|
||||
|
||||
// then update the borders and positioning on ourselves and our children.
|
||||
_borders = _GetCommonBorders();
|
||||
// Since we changed if we are using rows/columns, make sure we remove the old definitions
|
||||
_root.ColumnDefinitions().Clear();
|
||||
_root.RowDefinitions().Clear();
|
||||
_CreateRowColDefinitions();
|
||||
_ApplySplitDefinitions();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return _firstChild->ToggleSplitOrientation() || _secondChild->ToggleSplitOrientation();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Converts an "automatic" split type into either Vertical or Horizontal,
|
||||
// based upon the current dimensions of the Pane.
|
||||
|
|
|
@ -69,6 +69,7 @@ public:
|
|||
const float splitSize,
|
||||
const GUID& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
bool ToggleSplitOrientation();
|
||||
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitState> PreCalculateAutoSplit(const std::shared_ptr<Pane> target,
|
||||
const winrt::Windows::Foundation::Size parentSize) const;
|
||||
|
@ -146,6 +147,7 @@ private:
|
|||
void _ApplySplitDefinitions();
|
||||
void _SetupEntranceAnimation();
|
||||
void _UpdateBorders();
|
||||
Borders _GetCommonBorders();
|
||||
|
||||
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
|
||||
|
|
|
@ -662,4 +662,10 @@
|
|||
<data name="DropPathTabRun.Text" xml:space="preserve">
|
||||
<value>Open a new tab in given starting directory</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="DropPathTabNewWindow.Text" xml:space="preserve">
|
||||
<value>Open a new window with given starting directory</value>
|
||||
</data>
|
||||
<data name="DropPathTabSplit.Text" xml:space="preserve">
|
||||
<value>Split the window and start in given directory</value>
|
||||
</data>
|
||||
</root>
|
|
@ -105,6 +105,14 @@ namespace winrt::TerminalApp::implementation
|
|||
// Create a connection based on the values in our settings object if we weren't given one.
|
||||
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profileGuid, settings.DefaultSettings());
|
||||
|
||||
// If we had an `existingConnection`, then this is an inbound handoff from somewhere else.
|
||||
// We need to tell it about our size information so it can match the dimensions of what
|
||||
// we are about to present.
|
||||
if (existingConnection)
|
||||
{
|
||||
connection.Resize(settings.DefaultSettings().InitialRows(), settings.DefaultSettings().InitialCols());
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection debugConnection{ nullptr };
|
||||
if (_settings.GlobalSettings().DebugFeaturesEnabled())
|
||||
{
|
||||
|
|
|
@ -61,8 +61,19 @@ namespace winrt::TerminalApp::implementation
|
|||
// Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
|
||||
e.AcceptedOperation(DataPackageOperation::Copy);
|
||||
|
||||
// Sets custom UI text
|
||||
e.DragUIOverride().Caption(RS_(L"DropPathTabRun/Text"));
|
||||
const auto modifiers = static_cast<uint32_t>(e.Modifiers());
|
||||
if (WI_IsFlagSet(modifiers, static_cast<uint32_t>(DragDrop::DragDropModifiers::Alt)))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DropPathTabSplit/Text"));
|
||||
}
|
||||
else if (WI_IsFlagSet(modifiers, static_cast<uint32_t>(DragDrop::DragDropModifiers::Shift)))
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DropPathTabNewWindow/Text"));
|
||||
}
|
||||
else
|
||||
{
|
||||
e.DragUIOverride().Caption(RS_(L"DropPathTabRun/Text"));
|
||||
}
|
||||
|
||||
// Sets if the caption is visible
|
||||
e.DragUIOverride().IsCaptionVisible(true);
|
||||
|
|
|
@ -253,16 +253,8 @@ namespace winrt::TerminalApp::implementation
|
|||
path = path.parent_path();
|
||||
}
|
||||
|
||||
std::wstring pathText = path.wstring();
|
||||
|
||||
// Handle edge case of "C:\\", seems like the "StartingDirectory" doesn't like path which ends with '\'
|
||||
if (pathText.back() == L'\\')
|
||||
{
|
||||
pathText.erase(std::prev(pathText.end()));
|
||||
}
|
||||
|
||||
NewTerminalArgs args;
|
||||
args.StartingDirectory(winrt::hstring{ pathText });
|
||||
args.StartingDirectory(winrt::hstring{ path.wstring() });
|
||||
this->_OpenNewTerminal(args);
|
||||
|
||||
TraceLoggingWrite(
|
||||
|
@ -837,13 +829,19 @@ namespace winrt::TerminalApp::implementation
|
|||
// construction, because the connection might not spawn the child
|
||||
// process until later, on another thread, after we've already
|
||||
// restored the CWD to it's original value.
|
||||
std::wstring cwdString{ wil::GetCurrentDirectoryW<std::wstring>() };
|
||||
std::filesystem::path cwd{ cwdString };
|
||||
cwd /= settings.StartingDirectory().c_str();
|
||||
winrt::hstring newWorkingDirectory{ settings.StartingDirectory() };
|
||||
if (newWorkingDirectory.size() <= 1 ||
|
||||
!(newWorkingDirectory[0] == L'~' || newWorkingDirectory[0] == L'/'))
|
||||
{ // We only want to resolve the new WD against the CWD if it doesn't look like a Linux path (see GH#592)
|
||||
std::wstring cwdString{ wil::GetCurrentDirectoryW<std::wstring>() };
|
||||
std::filesystem::path cwd{ cwdString };
|
||||
cwd /= settings.StartingDirectory().c_str();
|
||||
newWorkingDirectory = winrt::hstring{ cwd.wstring() };
|
||||
}
|
||||
|
||||
auto conhostConn = TerminalConnection::ConptyConnection();
|
||||
conhostConn.Initialize(TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(),
|
||||
winrt::hstring{ cwd.wstring() },
|
||||
newWorkingDirectory,
|
||||
settings.StartingTitle(),
|
||||
envMap.GetView(),
|
||||
::base::saturated_cast<uint32_t>(settings.InitialRows()),
|
||||
|
@ -1123,14 +1121,16 @@ namespace winrt::TerminalApp::implementation
|
|||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_MoveFocus(const FocusDirection& direction)
|
||||
// - Whether changing the focus succeeded. This allows a keychord to propagate
|
||||
// to the terminal when no other panes are present (GH#6219)
|
||||
bool TerminalPage::_MoveFocus(const FocusDirection& direction)
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->NavigateFocus(direction);
|
||||
return terminalTab->NavigateFocus(direction);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1308,6 +1308,21 @@ namespace winrt::TerminalApp::implementation
|
|||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Switches the split orientation of the currently focused pane.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_ToggleSplitOrientation()
|
||||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ToggleSplitOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to move a separator between panes, as to resize each child on
|
||||
// either size of the separator. See Pane::ResizePane for details.
|
||||
|
|
|
@ -234,7 +234,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode);
|
||||
bool _SelectTab(uint32_t tabIndex);
|
||||
void _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
void _MovePane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl();
|
||||
|
@ -255,6 +255,7 @@ namespace winrt::TerminalApp::implementation
|
|||
const float splitSize = 0.5f,
|
||||
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
void _ToggleSplitOrientation();
|
||||
|
||||
void _ScrollPage(ScrollDirection scrollDirection);
|
||||
void _ScrollToBufferEdge(ScrollDirection scrollDirection);
|
||||
|
|
|
@ -440,6 +440,17 @@ namespace winrt::TerminalApp::implementation
|
|||
_UpdateActivePane(second);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Find the currently active pane, and then switch the split direction of
|
||||
// its parent. E.g. switch from Horizontal to Vertical.
|
||||
// Return Value:
|
||||
|
||||
// - <none>
|
||||
void TerminalTab::ToggleSplitOrientation()
|
||||
{
|
||||
_rootPane->ToggleSplitOrientation();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - See Pane::CalcSnappedDimension
|
||||
float TerminalTab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
|
||||
|
@ -481,23 +492,24 @@ namespace winrt::TerminalApp::implementation
|
|||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::NavigateFocus(const FocusDirection& direction)
|
||||
// - Whether changing the focus succeeded. This allows a keychord to propagate
|
||||
// to the terminal when no other panes are present (GH#6219)
|
||||
bool TerminalTab::NavigateFocus(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
{
|
||||
if (_mruPanes.size() < 2)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// To get to the previous pane, get the id of the previous pane and focus to that
|
||||
_rootPane->FocusPane(_mruPanes.at(1));
|
||||
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.
|
||||
_rootPane->NavigateFocus(direction);
|
||||
return _rootPane->NavigateFocus(direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1221,6 +1233,7 @@ namespace winrt::TerminalApp::implementation
|
|||
EnterZoom();
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalTab::EnterZoom()
|
||||
{
|
||||
_zoomedPane = _activePane;
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace winrt::TerminalApp::implementation
|
|||
const GUID& profile,
|
||||
winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
|
||||
void ToggleSplitOrientation();
|
||||
winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath);
|
||||
winrt::fire_and_forget HideIcon(const bool hide);
|
||||
|
||||
|
@ -52,7 +53,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
void NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
void MovePane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool FocusPane(const uint32_t id);
|
||||
|
||||
|
|
|
@ -57,6 +57,72 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Promotes a starting directory provided to a WSL invocation to a commandline argument.
|
||||
// This is necessary because WSL has some modicum of support for linux-side directories (!) which
|
||||
// CreateProcess never will.
|
||||
static std::tuple<std::wstring, std::wstring> _tryMangleStartingDirectoryForWSL(std::wstring_view commandLine, std::wstring_view startingDirectory)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (startingDirectory.size() > 0 && commandLine.size() >= 3)
|
||||
{ // "wsl" is three characters; this is a safe bet. no point in doing it if there's no starting directory though!
|
||||
// Find the first space, quote or the end of the string -- we'll look for wsl before that.
|
||||
const auto terminator{ commandLine.find_first_of(LR"(" )", 1) }; // look past the first character in case it starts with "
|
||||
const auto start{ til::at(commandLine, 0) == L'"' ? 1 : 0 };
|
||||
const std::filesystem::path executablePath{ commandLine.substr(start, terminator - start) };
|
||||
const auto executableFilename{ executablePath.filename().wstring() };
|
||||
if (executableFilename == L"wsl" || executableFilename == L"wsl.exe")
|
||||
{
|
||||
// We've got a WSL -- let's just make sure it's the right one.
|
||||
if (executablePath.has_parent_path())
|
||||
{
|
||||
std::wstring systemDirectory{};
|
||||
if (FAILED(wil::GetSystemDirectoryW(systemDirectory)))
|
||||
{
|
||||
break; // just bail out.
|
||||
}
|
||||
if (executablePath.parent_path().wstring() != systemDirectory)
|
||||
{
|
||||
break; // it wasn't in system32!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// assume that unqualified WSL is the one in system32 (minor danger)
|
||||
}
|
||||
|
||||
const auto arguments{ terminator == std::wstring_view::npos ? std::wstring_view{} : commandLine.substr(terminator + 1) };
|
||||
if (arguments.find(L"--cd") != std::wstring_view::npos)
|
||||
{
|
||||
break; // they've already got a --cd!
|
||||
}
|
||||
|
||||
const auto tilde{ arguments.find_first_of(L'~') };
|
||||
if (tilde != std::wstring_view::npos)
|
||||
{
|
||||
if (tilde + 1 == arguments.size() || til::at(arguments, tilde + 1) == L' ')
|
||||
{
|
||||
// We want to suppress --cd if they have added a bare ~ to their commandline (they conflict).
|
||||
break;
|
||||
}
|
||||
// Tilde followed by non-space should be okay (like, wsl -d Debian ~/blah.sh)
|
||||
}
|
||||
|
||||
return {
|
||||
fmt::format(LR"("{}" --cd "{}" {})", executablePath.wstring(), startingDirectory, arguments),
|
||||
std::wstring{}
|
||||
};
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return {
|
||||
std::wstring{ commandLine },
|
||||
std::wstring{ startingDirectory }
|
||||
};
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - launches the client application attached to the new pseudoconsole
|
||||
HRESULT ConptyConnection::_LaunchAttachedClient() noexcept
|
||||
|
@ -163,11 +229,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
siEx.StartupInfo.lpTitle = mutableTitle.data();
|
||||
}
|
||||
|
||||
const wchar_t* const startingDirectory = _startingDirectory.size() > 0 ? _startingDirectory.c_str() : nullptr;
|
||||
auto [newCommandLine, newStartingDirectory] = _tryMangleStartingDirectoryForWSL(cmdline, _startingDirectory);
|
||||
const wchar_t* const startingDirectory = newStartingDirectory.size() > 0 ? newStartingDirectory.c_str() : nullptr;
|
||||
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
|
||||
nullptr,
|
||||
cmdline.data(),
|
||||
newCommandLine.data(),
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
false, // bInheritHandles
|
||||
|
@ -289,12 +356,21 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
{
|
||||
_transitionToState(ConnectionState::Connecting);
|
||||
|
||||
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };
|
||||
|
||||
// If we do not have pipes already, then this is a fresh connection... not an inbound one that is a received
|
||||
// handoff from an already-started PTY process.
|
||||
if (!_inPipe)
|
||||
{
|
||||
const COORD dimensions{ gsl::narrow_cast<SHORT>(_initialCols), gsl::narrow_cast<SHORT>(_initialRows) };
|
||||
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, PSEUDOCONSOLE_RESIZE_QUIRK | PSEUDOCONSOLE_WIN32_INPUT_MODE, &_inPipe, &_outPipe, &_hPC));
|
||||
THROW_IF_FAILED(_LaunchAttachedClient());
|
||||
}
|
||||
// But if it was an inbound handoff... attempt to synchronize the size of it with what our connection
|
||||
// window is expecting it to be on the first layout.
|
||||
else
|
||||
{
|
||||
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), dimensions));
|
||||
}
|
||||
|
||||
_startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
@ -423,11 +499,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
|
||||
void ConptyConnection::Resize(uint32_t rows, uint32_t columns)
|
||||
{
|
||||
if (!_hPC)
|
||||
// If we haven't started connecting at all, it's still fair to update
|
||||
// the initial rows and columns before we set things up.
|
||||
if (!_isStateAtOrBeyond(ConnectionState::Connecting))
|
||||
{
|
||||
_initialRows = rows;
|
||||
_initialCols = columns;
|
||||
}
|
||||
// Otherwise, we can really only dispatch a resize if we're already connected.
|
||||
else if (_isConnected())
|
||||
{
|
||||
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), { Utils::ClampToShortMax(columns, 1), Utils::ClampToShortMax(rows, 1) }));
|
||||
|
|
|
@ -279,7 +279,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const bool focused,
|
||||
const til::point pixelPosition)
|
||||
const til::point pixelPosition,
|
||||
const bool pointerPressedInBounds)
|
||||
{
|
||||
const til::point terminalPosition = _getTerminalPosition(pixelPosition);
|
||||
|
||||
|
@ -288,7 +289,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
_core->SendMouseEvent(terminalPosition, pointerUpdateKind, modifiers, 0, toInternalMouseState(buttonState));
|
||||
}
|
||||
else if (focused && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
|
||||
// GH#4603 - don't modify the selection if the pointer press didn't
|
||||
// actually start _in_ the control bounds. Case in point - someone drags
|
||||
// a file into the bounds of the control. That shouldn't send the
|
||||
// selection into space.
|
||||
else if (focused && pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
|
||||
{
|
||||
if (_singleClickTouchdownPos)
|
||||
{
|
||||
|
|
|
@ -58,7 +58,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
const unsigned int pointerUpdateKind,
|
||||
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
|
||||
const bool focused,
|
||||
const til::point pixelPosition);
|
||||
const til::point pixelPosition,
|
||||
const bool pointerPressedInBounds);
|
||||
void TouchMoved(const til::point newTouchPoint,
|
||||
const bool focused);
|
||||
|
||||
|
|
|
@ -39,7 +39,9 @@ namespace Microsoft.Terminal.Control
|
|||
UInt32 pointerUpdateKind,
|
||||
Microsoft.Terminal.Core.ControlKeyStates modifiers,
|
||||
Boolean focused,
|
||||
Microsoft.Terminal.Core.Point pixelPosition);
|
||||
Microsoft.Terminal.Core.Point pixelPosition,
|
||||
Boolean pointerPressedInBounds);
|
||||
|
||||
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint,
|
||||
Boolean focused);
|
||||
|
||||
|
|
|
@ -1052,6 +1052,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
Focus(FocusState::Pointer);
|
||||
}
|
||||
|
||||
// Mark that this pointer event actually started within our bounds.
|
||||
// We'll need this later, for PointerMoved events.
|
||||
_pointerPressedInBounds = true;
|
||||
|
||||
if (type == Windows::Devices::Input::PointerDeviceType::Touch)
|
||||
{
|
||||
const auto contactRect = point.Properties().ContactRect();
|
||||
|
@ -1104,10 +1108,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
TermControl::GetPointerUpdateKind(point),
|
||||
ControlKeyStates(args.KeyModifiers()),
|
||||
_focused,
|
||||
pixelPosition);
|
||||
pixelPosition,
|
||||
_pointerPressedInBounds);
|
||||
|
||||
if (_focused && point.Properties().IsLeftButtonPressed())
|
||||
// GH#9109 - Only start an auto-scroll when the drag actually
|
||||
// started within our bounds. Otherwise, someone could start a drag
|
||||
// outside the terminal control, drag into the padding, and trick us
|
||||
// into starting to scroll.
|
||||
if (_focused && _pointerPressedInBounds && point.Properties().IsLeftButtonPressed())
|
||||
{
|
||||
// We want to find the distance relative to the bounds of the
|
||||
// SwapChainPanel, not the entire control. If they drag out of
|
||||
// the bounds of the text, into the padding, we still what that
|
||||
// to auto-scroll
|
||||
const double cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
|
||||
const double cursorAboveTopDist = -1 * cursorPosition.Y + SwapChainPanel().Margin().Top;
|
||||
|
||||
|
@ -1157,6 +1170,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
return;
|
||||
}
|
||||
|
||||
_pointerPressedInBounds = false;
|
||||
|
||||
const auto ptr = args.Pointer();
|
||||
const auto point = args.GetCurrentPoint(*this);
|
||||
const auto cursorPosition = point.Position();
|
||||
|
|
|
@ -169,11 +169,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
std::shared_ptr<ThrottledFuncTrailing<ScrollBarUpdate>> _updateScrollBar;
|
||||
bool _isInternalScrollBarUpdate;
|
||||
|
||||
// Auto scroll occurs when user, while selecting, drags cursor outside viewport. View is then scrolled to 'follow' the cursor.
|
||||
// Auto scroll occurs when user, while selecting, drags cursor outside
|
||||
// viewport. View is then scrolled to 'follow' the cursor.
|
||||
double _autoScrollVelocity;
|
||||
std::optional<Windows::UI::Input::PointerPoint> _autoScrollingPointerPoint;
|
||||
Windows::UI::Xaml::DispatcherTimer _autoScrollTimer;
|
||||
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
|
||||
bool _pointerPressedInBounds{ false };
|
||||
|
||||
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation{ nullptr };
|
||||
Windows::UI::Xaml::DispatcherTimer _bellLightTimer{ nullptr };
|
||||
|
|
|
@ -45,11 +45,12 @@ const TextAttribute Terminal::GetDefaultBrushColors() noexcept
|
|||
std::pair<COLORREF, COLORREF> Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept
|
||||
{
|
||||
_blinkingState.RecordBlinkingUsage(attr);
|
||||
auto colors = attr.CalculateRgbColors({ _colorTable.data(), _colorTable.size() },
|
||||
_defaultFg,
|
||||
_defaultBg,
|
||||
_screenReversed,
|
||||
_blinkingState.IsBlinkingFaint());
|
||||
auto colors = attr.CalculateRgbColors(
|
||||
_colorTable,
|
||||
_defaultFg,
|
||||
_defaultBg,
|
||||
_screenReversed,
|
||||
_blinkingState.IsBlinkingFaint());
|
||||
colors.first |= 0xff000000;
|
||||
// We only care about alpha for the default BG (which enables acrylic)
|
||||
// If the bg isn't the default bg color, or reverse video is enabled, make it fully opaque.
|
||||
|
|
|
@ -49,6 +49,7 @@ static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
|
|||
static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
|
||||
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
|
||||
static constexpr std::string_view TogglePaneZoomKey{ "togglePaneZoom" };
|
||||
static constexpr std::string_view ToggleSplitOrientationKey{ "toggleSplitOrientation" };
|
||||
static constexpr std::string_view LegacyToggleRetroEffectKey{ "toggleRetroEffect" };
|
||||
static constexpr std::string_view ToggleShaderEffectsKey{ "toggleShaderEffects" };
|
||||
static constexpr std::string_view MoveTabKey{ "moveTab" };
|
||||
|
@ -350,6 +351,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
{ ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
|
||||
{ ShortcutAction::ToggleFullscreen, RS_(L"ToggleFullscreenCommandKey") },
|
||||
{ ShortcutAction::TogglePaneZoom, RS_(L"TogglePaneZoomCommandKey") },
|
||||
{ ShortcutAction::ToggleSplitOrientation, RS_(L"ToggleSplitOrientationCommandKey") },
|
||||
{ ShortcutAction::ToggleShaderEffects, RS_(L"ToggleShaderEffectsCommandKey") },
|
||||
{ ShortcutAction::MoveTab, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::BreakIntoDebugger, RS_(L"BreakIntoDebuggerCommandKey") },
|
||||
|
|
|
@ -118,7 +118,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
if (!StartingDirectory().empty())
|
||||
{
|
||||
ss << fmt::format(L"--startingDirectory \"{}\" ", StartingDirectory());
|
||||
// If the directory ends in a '\', we need to add another one on so that the enclosing quote added
|
||||
// afterwards isn't escaped
|
||||
const auto trailingBackslashEscape = StartingDirectory().back() == L'\\' ? L"\\" : L"";
|
||||
ss << fmt::format(L"--startingDirectory \"{}{}\" ", StartingDirectory(), trailingBackslashEscape);
|
||||
}
|
||||
|
||||
if (!TabTitle().empty())
|
||||
|
|
|
@ -78,7 +78,14 @@ constexpr std::wstring_view WIN_KEY{ L"win" };
|
|||
XX(VK_OEM_PLUS, L"plus") /* '+' any country */ \
|
||||
XX(VK_OEM_COMMA, L"comma") /* ',' any country */ \
|
||||
XX(VK_OEM_MINUS, L"minus") /* '-' any country */ \
|
||||
XX(VK_OEM_PERIOD, L"period") /* '.' any country */
|
||||
XX(VK_OEM_PERIOD, L"period") /* '.' any country */ \
|
||||
XX(VK_BROWSER_BACK, L"browser_back") \
|
||||
XX(VK_BROWSER_FORWARD, L"browser_forward") \
|
||||
XX(VK_BROWSER_REFRESH, L"browser_refresh") \
|
||||
XX(VK_BROWSER_STOP, L"browser_stop") \
|
||||
XX(VK_BROWSER_SEARCH, L"browser_search") \
|
||||
XX(VK_BROWSER_FAVORITES, L"browser_favorites") \
|
||||
XX(VK_BROWSER_HOME, L"browser_home")
|
||||
|
||||
constexpr std::wstring_view vkeyPrefix{ L"vk(" };
|
||||
constexpr std::wstring_view scanCodePrefix{ L"sc(" };
|
||||
|
|
|
@ -448,11 +448,6 @@ winrt::Microsoft::Terminal::Settings::Model::FontConfig Profile::FontInfo()
|
|||
// - the function returns an evaluated version of %userprofile% to avoid blocking the session from starting.
|
||||
std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory)
|
||||
{
|
||||
// First expand path
|
||||
DWORD numCharsInput = ExpandEnvironmentStrings(directory.c_str(), nullptr, 0);
|
||||
std::unique_ptr<wchar_t[]> evaluatedPath = std::make_unique<wchar_t[]>(numCharsInput);
|
||||
THROW_LAST_ERROR_IF(0 == ExpandEnvironmentStrings(directory.c_str(), evaluatedPath.get(), numCharsInput));
|
||||
|
||||
// Prior to GH#9541, we'd validate that the user's startingDirectory existed
|
||||
// here. If it was invalid, we'd gracefully fall back to %USERPROFILE%.
|
||||
//
|
||||
|
@ -463,7 +458,7 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory)
|
|||
//
|
||||
// If the path is eventually invalid, we'll display warning in the
|
||||
// ConptyConnection when the process fails to launch.
|
||||
return std::wstring(evaluatedPath.get(), numCharsInput);
|
||||
return wil::ExpandEnvironmentStringsW<std::wstring>(directory.c_str());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
|
|
|
@ -390,6 +390,9 @@
|
|||
<data name="ToggleFullscreenCommandKey" xml:space="preserve">
|
||||
<value>Toggle fullscreen</value>
|
||||
</data>
|
||||
<data name="ToggleSplitOrientationCommandKey" xml:space="preserve">
|
||||
<value>Toggle pane split orientation</value>
|
||||
</data>
|
||||
<data name="TogglePaneZoomCommandKey" xml:space="preserve">
|
||||
<value>Toggle pane zoom</value>
|
||||
</data>
|
||||
|
|
|
@ -350,6 +350,7 @@
|
|||
{ "command": { "action": "movePane", "direction": "up" } },
|
||||
{ "command": { "action": "movePane", "direction": "previous"} },
|
||||
{ "command": "togglePaneZoom" },
|
||||
{ "command": "toggleSplitOrientation" },
|
||||
{ "command": "toggleReadOnlyMode" },
|
||||
|
||||
// Clipboard Integration
|
||||
|
|
|
@ -32,6 +32,9 @@ namespace ControlUnitTests
|
|||
TEST_METHOD(ScrollWithSelection);
|
||||
TEST_METHOD(TestScrollWithTrackpad);
|
||||
TEST_METHOD(TestQuickDragOnSelect);
|
||||
|
||||
TEST_METHOD(TestDragSelectOutsideBounds);
|
||||
|
||||
TEST_METHOD(PointerClickOutsideActiveRegion);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
|
@ -288,7 +291,8 @@ namespace ControlUnitTests
|
|||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1);
|
||||
cursorPosition1,
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
|
||||
|
@ -300,7 +304,8 @@ namespace ControlUnitTests
|
|||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition2);
|
||||
cursorPosition2,
|
||||
true);
|
||||
Log::Comment(L"Verify that there's now two selections (one on each row)");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
VERIFY_ARE_EQUAL(2u, core->_terminal->GetSelectionRects().size());
|
||||
|
@ -333,7 +338,8 @@ namespace ControlUnitTests
|
|||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition4);
|
||||
cursorPosition4,
|
||||
true);
|
||||
Log::Comment(L"Verify that there's now one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
|
||||
|
@ -388,7 +394,8 @@ namespace ControlUnitTests
|
|||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1);
|
||||
cursorPosition1,
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
|
||||
|
@ -536,7 +543,8 @@ namespace ControlUnitTests
|
|||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1);
|
||||
cursorPosition1,
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
|
||||
|
@ -546,6 +554,74 @@ namespace ControlUnitTests
|
|||
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
|
||||
}
|
||||
|
||||
void ControlInteractivityTests::TestDragSelectOutsideBounds()
|
||||
{
|
||||
// This is a test for GH#4603
|
||||
|
||||
auto [settings, conn] = _createSettingsAndConnection();
|
||||
auto [core, interactivity] = _createCoreAndInteractivity(*settings, *conn);
|
||||
_standardInit(core, interactivity);
|
||||
|
||||
// For this test, don't use any modifiers
|
||||
const auto modifiers = ControlKeyStates();
|
||||
const Control::MouseButtonState leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
|
||||
const Control::MouseButtonState noMouseDown{};
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
Log::Comment(L"Click on the terminal");
|
||||
const til::point cursorPosition0{ 6, 0 };
|
||||
interactivity->PointerPressed(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
0, // timestamp
|
||||
modifiers,
|
||||
cursorPosition0);
|
||||
|
||||
Log::Comment(L"Verify that there's not yet a selection");
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
|
||||
VERIFY_IS_TRUE(interactivity->_singleClickTouchdownPos.has_value());
|
||||
VERIFY_ARE_EQUAL(cursorPosition0, interactivity->_singleClickTouchdownPos.value());
|
||||
|
||||
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
|
||||
const til::point cursorPosition1{ 6 + fontSize.width<int>() * 2, 0 };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1,
|
||||
true);
|
||||
Log::Comment(L"Verify that there's one selection");
|
||||
VERIFY_IS_TRUE(core->HasSelection());
|
||||
VERIFY_ARE_EQUAL(1u, core->_terminal->GetSelectionRects().size());
|
||||
|
||||
Log::Comment(L"Verify that it started on the first cell we clicked on, not the one we dragged to");
|
||||
COORD expectedAnchor{ 0, 0 };
|
||||
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
|
||||
COORD expectedEnd{ 2, 0 };
|
||||
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
|
||||
|
||||
interactivity->PointerReleased(noMouseDown,
|
||||
WM_LBUTTONUP,
|
||||
modifiers,
|
||||
cursorPosition1);
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
|
||||
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
|
||||
|
||||
Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
|
||||
const til::point cursorPosition2{ fontSize.width<int>() * 10, 0 };
|
||||
interactivity->PointerMoved(leftMouseDown,
|
||||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition2,
|
||||
false);
|
||||
|
||||
Log::Comment(L"The selection should be unchanged.");
|
||||
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
|
||||
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
|
||||
}
|
||||
|
||||
void ControlInteractivityTests::PointerClickOutsideActiveRegion()
|
||||
{
|
||||
// This is a test for GH#10642
|
||||
|
@ -561,7 +637,6 @@ namespace ControlUnitTests
|
|||
const Control::MouseButtonState noMouseDown{};
|
||||
|
||||
const til::size fontSize{ 9, 21 };
|
||||
|
||||
interactivity->_rowsToScroll = 1;
|
||||
int expectedTop = 0;
|
||||
int expectedViewHeight = 20;
|
||||
|
@ -630,7 +705,8 @@ namespace ControlUnitTests
|
|||
WM_LBUTTONDOWN, //pointerUpdateKind
|
||||
modifiers,
|
||||
true, // focused,
|
||||
cursorPosition1);
|
||||
cursorPosition1,
|
||||
true);
|
||||
Log::Comment(L"Verify that there's still no selection");
|
||||
VERIFY_IS_FALSE(core->HasSelection());
|
||||
}
|
||||
|
|
|
@ -274,7 +274,9 @@ LRESULT IslandWindow::_OnSizing(const WPARAM wParam, const LPARAM lParam)
|
|||
LRESULT IslandWindow::_OnMoving(const WPARAM /*wParam*/, const LPARAM lParam)
|
||||
{
|
||||
LPRECT winRect = reinterpret_cast<LPRECT>(lParam);
|
||||
// If we're the quake window, prevent moving the window
|
||||
|
||||
// If we're the quake window, prevent moving the window. If we don't do
|
||||
// this, then Alt+Space...Move will still be able to move the window.
|
||||
if (IsQuakeWindow())
|
||||
{
|
||||
// Stuff our current window into the lParam, and return true. This
|
||||
|
@ -506,6 +508,61 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
|
|||
case WM_THEMECHANGED:
|
||||
UpdateWindowIconForActiveMetrics(_window.get());
|
||||
return 0;
|
||||
case WM_WINDOWPOSCHANGING:
|
||||
{
|
||||
// GH#10274 - if the quake window gets moved to another monitor via aero
|
||||
// snap (win+shift+arrows), then re-adjust the size for the new monitor.
|
||||
if (IsQuakeWindow())
|
||||
{
|
||||
// Retrieve the suggested dimensions and make a rect and size.
|
||||
LPWINDOWPOS lpwpos = (LPWINDOWPOS)lparam;
|
||||
|
||||
// We only need to apply restrictions if the position is changing.
|
||||
// The SWP_ flags are confusing to read. This is
|
||||
// "if we're not moving the window, do nothing."
|
||||
if (WI_IsFlagSet(lpwpos->flags, SWP_NOMOVE))
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Figure out the suggested dimensions and position.
|
||||
RECT rcSuggested;
|
||||
rcSuggested.left = lpwpos->x;
|
||||
rcSuggested.top = lpwpos->y;
|
||||
rcSuggested.right = rcSuggested.left + lpwpos->cx;
|
||||
rcSuggested.bottom = rcSuggested.top + lpwpos->cy;
|
||||
|
||||
// Find the bounds of the current monitor, and the monitor that
|
||||
// we're suggested to be on.
|
||||
|
||||
HMONITOR current = MonitorFromWindow(_window.get(), MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO currentInfo;
|
||||
currentInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(current, ¤tInfo);
|
||||
|
||||
HMONITOR proposed = MonitorFromRect(&rcSuggested, MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO proposedInfo;
|
||||
proposedInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(proposed, &proposedInfo);
|
||||
|
||||
// If the monitor changed...
|
||||
if (til::rectangle{ proposedInfo.rcMonitor } !=
|
||||
til::rectangle{ currentInfo.rcMonitor })
|
||||
{
|
||||
const auto newWindowRect{ _getQuakeModeSize(proposed) };
|
||||
|
||||
// Inform User32 that we want to be placed at the position
|
||||
// and dimensions that _getQuakeModeSize returned. When we
|
||||
// snap across monitor boundaries, this will re-evaluate our
|
||||
// size for the new monitor.
|
||||
lpwpos->x = newWindowRect.left<int>();
|
||||
lpwpos->y = newWindowRect.top<int>();
|
||||
lpwpos->cx = newWindowRect.width<int>();
|
||||
lpwpos->cy = newWindowRect.height<int>();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
case CM_NOTIFY_FROM_TRAY:
|
||||
{
|
||||
switch (LOWORD(lparam))
|
||||
|
@ -884,23 +941,39 @@ void IslandWindow::_RestoreFullscreenPosition(const RECT rcWork)
|
|||
rcWork.left - _rcWorkBeforeFullscreen.left,
|
||||
rcWork.top - _rcWorkBeforeFullscreen.top);
|
||||
|
||||
const til::size ncSize{ GetTotalNonClientExclusiveSize(dpiWindow) };
|
||||
|
||||
RECT rcWorkAdjusted = rcWork;
|
||||
|
||||
// GH#10199 - adjust the size of the "work" rect by the size of our borders.
|
||||
// We want to make sure the window is restored within the bounds of the
|
||||
// monitor we're on, but it's totally fine if the invisible borders are
|
||||
// outside the monitor.
|
||||
const auto halfWidth{ ncSize.width<long>() / 2 };
|
||||
const auto halfHeight{ ncSize.height<long>() / 2 };
|
||||
|
||||
rcWorkAdjusted.left -= halfWidth;
|
||||
rcWorkAdjusted.right += halfWidth;
|
||||
rcWorkAdjusted.top -= halfHeight;
|
||||
rcWorkAdjusted.bottom += halfHeight;
|
||||
|
||||
// Enforce that our position is entirely within the bounds of our work area.
|
||||
// Prefer the top-left be on-screen rather than bottom-right (right before left, bottom before top).
|
||||
if (rcRestore.right > rcWork.right)
|
||||
if (rcRestore.right > rcWorkAdjusted.right)
|
||||
{
|
||||
OffsetRect(&rcRestore, rcWork.right - rcRestore.right, 0);
|
||||
OffsetRect(&rcRestore, rcWorkAdjusted.right - rcRestore.right, 0);
|
||||
}
|
||||
if (rcRestore.left < rcWork.left)
|
||||
if (rcRestore.left < rcWorkAdjusted.left)
|
||||
{
|
||||
OffsetRect(&rcRestore, rcWork.left - rcRestore.left, 0);
|
||||
OffsetRect(&rcRestore, rcWorkAdjusted.left - rcRestore.left, 0);
|
||||
}
|
||||
if (rcRestore.bottom > rcWork.bottom)
|
||||
if (rcRestore.bottom > rcWorkAdjusted.bottom)
|
||||
{
|
||||
OffsetRect(&rcRestore, 0, rcWork.bottom - rcRestore.bottom);
|
||||
OffsetRect(&rcRestore, 0, rcWorkAdjusted.bottom - rcRestore.bottom);
|
||||
}
|
||||
if (rcRestore.top < rcWork.top)
|
||||
if (rcRestore.top < rcWorkAdjusted.top)
|
||||
{
|
||||
OffsetRect(&rcRestore, 0, rcWork.top - rcRestore.top);
|
||||
OffsetRect(&rcRestore, 0, rcWorkAdjusted.top - rcRestore.top);
|
||||
}
|
||||
|
||||
// Show the window at the computed position.
|
||||
|
@ -1432,6 +1505,13 @@ void IslandWindow::IsQuakeWindow(bool isQuakeWindow) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enter quake mode for the monitor this window is currently on. This involves
|
||||
// resizing it to the top half of the monitor.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::_enterQuakeMode()
|
||||
{
|
||||
if (!_window)
|
||||
|
@ -1441,6 +1521,29 @@ void IslandWindow::_enterQuakeMode()
|
|||
|
||||
RECT windowRect = GetWindowRect();
|
||||
HMONITOR hmon = MonitorFromRect(&windowRect, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
// Get the size and position of the window that we should occupy
|
||||
const auto newRect{ _getQuakeModeSize(hmon) };
|
||||
|
||||
SetWindowPos(GetHandle(),
|
||||
HWND_TOP,
|
||||
newRect.left<int>(),
|
||||
newRect.top<int>(),
|
||||
newRect.width<int>(),
|
||||
newRect.height<int>(),
|
||||
SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the size and position of the window that a "quake mode" should occupy
|
||||
// on the given monitor.
|
||||
// - The window will occupy the top half of the monitor.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
til::rectangle IslandWindow::_getQuakeModeSize(HMONITOR hmon)
|
||||
{
|
||||
MONITORINFO nearestMonitorInfo;
|
||||
|
||||
UINT dpix = USER_DEFAULT_SCREEN_DPI;
|
||||
|
@ -1472,14 +1575,7 @@ void IslandWindow::_enterQuakeMode()
|
|||
availableSpace.height() / 2
|
||||
};
|
||||
|
||||
const til::rectangle newRect{ origin, dimensions };
|
||||
SetWindowPos(GetHandle(),
|
||||
HWND_TOP,
|
||||
newRect.left<int>(),
|
||||
newRect.top<int>(),
|
||||
newRect.width<int>(),
|
||||
newRect.height<int>(),
|
||||
SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE);
|
||||
return til::rectangle{ origin, dimensions };
|
||||
}
|
||||
|
||||
DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
|
||||
|
|
|
@ -109,7 +109,9 @@ protected:
|
|||
void _moveToMonitor(const MONITORINFO activeMonitor);
|
||||
|
||||
bool _isQuakeWindow{ false };
|
||||
|
||||
void _enterQuakeMode();
|
||||
til::rectangle _getQuakeModeSize(HMONITOR hmon);
|
||||
|
||||
void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);
|
||||
|
||||
|
|
|
@ -360,7 +360,21 @@ void NonClientIslandWindow::_OnMaximizeChange() noexcept
|
|||
// sizes of our child XAML Islands to match our new sizing.
|
||||
void NonClientIslandWindow::_UpdateIslandPosition(const UINT windowWidth, const UINT windowHeight)
|
||||
{
|
||||
const auto topBorderHeight = Utils::ClampToShortMax(_GetTopBorderHeight(), 0);
|
||||
const auto originalTopHeight = _GetTopBorderHeight();
|
||||
// GH#7422
|
||||
// !! BODGY !!
|
||||
//
|
||||
// For inexplicable reasons, the top row of pixels on our tabs, new tab
|
||||
// button, and caption buttons is totally un-clickable. The mouse simply
|
||||
// refuses to interact with them. So when we're maximized, on certain
|
||||
// monitor configurations, this results in the top row of pixels not
|
||||
// reacting to clicks at all. To obey Fitt's Law, we're gonna shift
|
||||
// the entire island up one pixel. That will result in the top row of pixels
|
||||
// in the window actually being the _second_ row of pixels for those
|
||||
// buttons, which will make them clickable. It's perhaps not the right fix,
|
||||
// but it works.
|
||||
// _GetTopBorderHeight() returns 0 when we're maximized.
|
||||
const short topBorderHeight = ::base::saturated_cast<short>((originalTopHeight == 0) ? -1 : originalTopHeight);
|
||||
|
||||
const COORD newIslandPos = { 0, topBorderHeight };
|
||||
|
||||
|
|
|
@ -259,11 +259,12 @@ COLORREF CONSOLE_INFORMATION::GetDefaultBackground() const noexcept
|
|||
std::pair<COLORREF, COLORREF> CONSOLE_INFORMATION::LookupAttributeColors(const TextAttribute& attr) const noexcept
|
||||
{
|
||||
_blinkingState.RecordBlinkingUsage(attr);
|
||||
return attr.CalculateRgbColors(Get256ColorTable(),
|
||||
GetDefaultForeground(),
|
||||
GetDefaultBackground(),
|
||||
IsScreenReversed(),
|
||||
_blinkingState.IsBlinkingFaint());
|
||||
return attr.CalculateRgbColors(
|
||||
GetColorTable(),
|
||||
GetDefaultForeground(),
|
||||
GetDefaultBackground(),
|
||||
IsScreenReversed(),
|
||||
_blinkingState.IsBlinkingFaint());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -1931,15 +1931,11 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
|
|||
// to embed control characters in that string.
|
||||
if (gci.IsInVtIoMode())
|
||||
{
|
||||
std::wstring sanitized;
|
||||
sanitized.reserve(title.size());
|
||||
for (size_t i = 0; i < title.size(); i++)
|
||||
{
|
||||
if (title.at(i) >= UNICODE_SPACE)
|
||||
{
|
||||
sanitized.push_back(title.at(i));
|
||||
}
|
||||
}
|
||||
std::wstring sanitized{ title };
|
||||
sanitized.erase(std::remove_if(sanitized.begin(), sanitized.end(), [](auto ch) {
|
||||
return ch < UNICODE_SPACE || (ch > UNICODE_DEL && ch < UNICODE_NBSP);
|
||||
}),
|
||||
sanitized.end());
|
||||
|
||||
gci.SetTitle({ sanitized });
|
||||
}
|
||||
|
|
|
@ -1904,10 +1904,17 @@ const SCREEN_INFORMATION& SCREEN_INFORMATION::GetMainBuffer() const
|
|||
ppsiNewScreenBuffer);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
// Update the alt buffer's cursor style to match our own.
|
||||
// Update the alt buffer's cursor style, visibility, and position to match our own.
|
||||
auto& myCursor = GetTextBuffer().GetCursor();
|
||||
auto* const createdBuffer = *ppsiNewScreenBuffer;
|
||||
createdBuffer->GetTextBuffer().GetCursor().SetStyle(myCursor.GetSize(), myCursor.GetColor(), myCursor.GetType());
|
||||
auto& altCursor = createdBuffer->GetTextBuffer().GetCursor();
|
||||
altCursor.SetStyle(myCursor.GetSize(), myCursor.GetColor(), myCursor.GetType());
|
||||
altCursor.SetIsVisible(myCursor.IsVisible());
|
||||
altCursor.SetBlinkingAllowed(myCursor.IsBlinkingAllowed());
|
||||
// The new position should match the viewport-relative position of the main buffer.
|
||||
auto altCursorPos = myCursor.GetPosition();
|
||||
altCursorPos.Y -= GetVirtualViewport().Top();
|
||||
altCursor.SetPosition(altCursorPos);
|
||||
|
||||
s_InsertScreenBuffer(createdBuffer);
|
||||
|
||||
|
@ -1998,6 +2005,13 @@ void SCREEN_INFORMATION::UseMainScreenBuffer()
|
|||
s_RemoveScreenBuffer(psiAlt); // this will also delete the alt buffer
|
||||
// deleting the alt buffer will give the GetSet back to its main
|
||||
|
||||
// Copy the alt buffer's cursor style and visibility back to the main buffer.
|
||||
const auto& altCursor = psiAlt->GetTextBuffer().GetCursor();
|
||||
auto& mainCursor = psiMain->GetTextBuffer().GetCursor();
|
||||
mainCursor.SetStyle(altCursor.GetSize(), altCursor.GetColor(), altCursor.GetType());
|
||||
mainCursor.SetIsVisible(altCursor.IsVisible());
|
||||
mainCursor.SetBlinkingAllowed(altCursor.IsBlinkingAllowed());
|
||||
|
||||
// Tell the VT MouseInput handler that we're in the main buffer now
|
||||
gci.GetActiveInputBuffer()->GetTerminalInput().UseMainScreenBuffer();
|
||||
}
|
||||
|
|
|
@ -726,16 +726,6 @@ void Settings::SetHistoryNoDup(const bool bHistoryNoDup)
|
|||
_bHistoryNoDup = bHistoryNoDup;
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> Settings::Get16ColorTable() const
|
||||
{
|
||||
return Get256ColorTable().subspan(0, 16);
|
||||
}
|
||||
|
||||
gsl::span<const COLORREF> Settings::Get256ColorTable() const
|
||||
{
|
||||
return { _colorTable.data(), _colorTable.size() };
|
||||
}
|
||||
|
||||
void Settings::SetColorTableEntry(const size_t index, const COLORREF ColorValue)
|
||||
{
|
||||
_colorTable.at(index) = ColorValue;
|
||||
|
|
|
@ -159,8 +159,12 @@ public:
|
|||
bool GetHistoryNoDup() const;
|
||||
void SetHistoryNoDup(const bool fHistoryNoDup);
|
||||
|
||||
gsl::span<const COLORREF> Get16ColorTable() const;
|
||||
gsl::span<const COLORREF> Get256ColorTable() const;
|
||||
// The first 16 items of the color table are the same as the 16-color palette.
|
||||
inline const std::array<COLORREF, XTERM_COLOR_TABLE_SIZE>& GetColorTable() const noexcept
|
||||
{
|
||||
return _colorTable;
|
||||
}
|
||||
|
||||
void SetColorTableEntry(const size_t index, const COLORREF ColorValue);
|
||||
COLORREF GetColorTableEntry(const size_t index) const;
|
||||
|
||||
|
|
|
@ -419,7 +419,7 @@ void Telemetry::WriteFinalTraceLog()
|
|||
TraceLoggingBool(gci.GetQuickEdit(), "QuickEdit"),
|
||||
TraceLoggingValue(gci.GetWindowAlpha(), "WindowAlpha"),
|
||||
TraceLoggingBool(gci.GetWrapText(), "WrapText"),
|
||||
TraceLoggingUInt32Array((UINT32 const*)gci.Get16ColorTable().data(), (UINT16)gci.Get16ColorTable().size(), "ColorTable"),
|
||||
TraceLoggingUInt32Array((UINT32 const*)gci.GetColorTable().data(), 16, "ColorTable"),
|
||||
TraceLoggingValue(gci.CP, "CodePageInput"),
|
||||
TraceLoggingValue(gci.OutputCP, "CodePageOutput"),
|
||||
TraceLoggingValue(gci.GetFontSize().X, "FontSizeX"),
|
||||
|
@ -453,7 +453,7 @@ void Telemetry::WriteFinalTraceLog()
|
|||
TraceLoggingValue(gci.GetShowWindow(), "ShowWindow"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
static_assert(sizeof(UINT32) == sizeof(gci.Get16ColorTable()[0]), "gci.Get16ColorTable()");
|
||||
static_assert(sizeof(UINT32) == sizeof(gci.GetColorTable()[0]), "gci.Get16ColorTable()");
|
||||
|
||||
// I could use the TraceLoggingUIntArray, but then we would have to know the order of the enums on the backend.
|
||||
// So just log each enum count separately with its string representation which makes it more human readable.
|
||||
|
|
|
@ -119,6 +119,7 @@ class ConptyOutputTests
|
|||
TEST_METHOD(WriteTwoLinesUsesNewline);
|
||||
TEST_METHOD(WriteAFewSimpleLines);
|
||||
TEST_METHOD(InvalidateUntilOneBeforeEnd);
|
||||
TEST_METHOD(SetConsoleTitleWithControlChars);
|
||||
|
||||
private:
|
||||
bool _writeCallback(const char* const pch, size_t const cch);
|
||||
|
@ -364,3 +365,37 @@ void ConptyOutputTests::InvalidateUntilOneBeforeEnd()
|
|||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
void ConptyOutputTests::SetConsoleTitleWithControlChars()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:control", L"{0x00, 0x0A, 0x1B, 0x80, 0x9B, 0x9C}")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
int control;
|
||||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"control", control));
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"SetConsoleTitle with a control character (0x%02X) embedded in the text", control));
|
||||
|
||||
std::wstringstream titleText;
|
||||
titleText << L"Hello " << wchar_t(control) << L"World!";
|
||||
VERIFY_SUCCEEDED(DoSrvSetConsoleTitleW(titleText.str()));
|
||||
|
||||
// This is the standard init sequences for the first frame.
|
||||
expectedOutput.push_back("\x1b[2J");
|
||||
expectedOutput.push_back("\x1b[m");
|
||||
expectedOutput.push_back("\x1b[H");
|
||||
|
||||
// The title change is propagated as an OSC 0 sequence.
|
||||
// Control characters are stripped, so it's always "Hello World".
|
||||
expectedOutput.push_back("\x1b]0;Hello World!\a");
|
||||
|
||||
// This is also part of the standard init sequence.
|
||||
expectedOutput.push_back("\x1b[?25h");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
|
|
@ -89,6 +89,8 @@ class ScreenBufferTests
|
|||
|
||||
TEST_METHOD(MultipleAlternateBuffersFromMainCreationTest);
|
||||
|
||||
TEST_METHOD(AlternateBufferCursorInheritanceTest);
|
||||
|
||||
TEST_METHOD(TestReverseLineFeed);
|
||||
|
||||
TEST_METHOD(TestResetClearTabStops);
|
||||
|
@ -344,6 +346,71 @@ void ScreenBufferTests::MultipleAlternateBuffersFromMainCreationTest()
|
|||
}
|
||||
}
|
||||
|
||||
void ScreenBufferTests::AlternateBufferCursorInheritanceTest()
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
gci.LockConsole(); // Lock must be taken to manipulate buffer.
|
||||
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
|
||||
|
||||
auto& mainBuffer = gci.GetActiveOutputBuffer();
|
||||
auto& mainCursor = mainBuffer.GetTextBuffer().GetCursor();
|
||||
|
||||
Log::Comment(L"Set the cursor attributes in the main buffer.");
|
||||
auto mainCursorPos = COORD{ 3, 5 };
|
||||
auto mainCursorVisible = false;
|
||||
auto mainCursorSize = 33u;
|
||||
auto mainCursorColor = RGB(1, 2, 3);
|
||||
auto mainCursorType = CursorType::DoubleUnderscore;
|
||||
auto mainCursorBlinking = false;
|
||||
mainCursor.SetPosition(mainCursorPos);
|
||||
mainCursor.SetIsVisible(mainCursorVisible);
|
||||
mainCursor.SetStyle(mainCursorSize, mainCursorColor, mainCursorType);
|
||||
mainCursor.SetBlinkingAllowed(mainCursorBlinking);
|
||||
|
||||
Log::Comment(L"Switch to the alternate buffer.");
|
||||
VERIFY_SUCCEEDED(mainBuffer.UseAlternateScreenBuffer());
|
||||
auto& altBuffer = gci.GetActiveOutputBuffer();
|
||||
auto& altCursor = altBuffer.GetTextBuffer().GetCursor();
|
||||
auto useMain = wil::scope_exit([&] { altBuffer.UseMainScreenBuffer(); });
|
||||
|
||||
Log::Comment(L"Confirm the cursor position is inherited from the main buffer.");
|
||||
VERIFY_ARE_EQUAL(mainCursorPos, altCursor.GetPosition());
|
||||
Log::Comment(L"Confirm the cursor visibility is inherited from the main buffer.");
|
||||
VERIFY_ARE_EQUAL(mainCursorVisible, altCursor.IsVisible());
|
||||
Log::Comment(L"Confirm the cursor style is inherited from the main buffer.");
|
||||
VERIFY_ARE_EQUAL(mainCursorSize, altCursor.GetSize());
|
||||
VERIFY_ARE_EQUAL(mainCursorColor, altCursor.GetColor());
|
||||
VERIFY_ARE_EQUAL(mainCursorType, altCursor.GetType());
|
||||
VERIFY_ARE_EQUAL(mainCursorBlinking, altCursor.IsBlinkingAllowed());
|
||||
|
||||
Log::Comment(L"Set the cursor attributes in the alt buffer.");
|
||||
auto altCursorPos = COORD{ 5, 3 };
|
||||
auto altCursorVisible = true;
|
||||
auto altCursorSize = 66u;
|
||||
auto altCursorColor = RGB(3, 2, 1);
|
||||
auto altCursorType = CursorType::EmptyBox;
|
||||
auto altCursorBlinking = true;
|
||||
altCursor.SetPosition(altCursorPos);
|
||||
altCursor.SetIsVisible(altCursorVisible);
|
||||
altCursor.SetStyle(altCursorSize, altCursorColor, altCursorType);
|
||||
altCursor.SetBlinkingAllowed(altCursorBlinking);
|
||||
|
||||
Log::Comment(L"Switch back to the main buffer.");
|
||||
useMain.release();
|
||||
altBuffer.UseMainScreenBuffer();
|
||||
VERIFY_ARE_EQUAL(&mainBuffer, &gci.GetActiveOutputBuffer());
|
||||
|
||||
Log::Comment(L"Confirm the cursor position is restored to what it was.");
|
||||
VERIFY_ARE_EQUAL(mainCursorPos, mainCursor.GetPosition());
|
||||
Log::Comment(L"Confirm the cursor visibility is inherited from the alt buffer.");
|
||||
VERIFY_ARE_EQUAL(altCursorVisible, mainCursor.IsVisible());
|
||||
Log::Comment(L"Confirm the cursor style is inherited from the alt buffer.");
|
||||
VERIFY_ARE_EQUAL(altCursorSize, mainCursor.GetSize());
|
||||
VERIFY_ARE_EQUAL(altCursorColor, mainCursor.GetColor());
|
||||
VERIFY_ARE_EQUAL(altCursorType, mainCursor.GetType());
|
||||
VERIFY_ARE_EQUAL(altCursorBlinking, mainCursor.IsBlinkingAllowed());
|
||||
}
|
||||
|
||||
void ScreenBufferTests::TestReverseLineFeed()
|
||||
{
|
||||
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
|
@ -4948,6 +5015,9 @@ void ScreenBufferTests::ClearAlternateBuffer()
|
|||
|
||||
auto useMain = wil::scope_exit([&] { altBuffer.UseMainScreenBuffer(); });
|
||||
|
||||
// Set the position to home, otherwise it's inherited from the main buffer.
|
||||
VERIFY_SUCCEEDED(altBuffer.SetCursorPosition({ 0, 0 }, true));
|
||||
|
||||
WriteText(altBuffer.GetTextBuffer());
|
||||
VerifyText(altBuffer.GetTextBuffer());
|
||||
|
||||
|
|
|
@ -467,17 +467,10 @@ void DxFontRenderData::_SetFeatures(const std::unordered_map<std::wstring_view,
|
|||
{
|
||||
// Populate the feature map with the standard list first
|
||||
std::unordered_map<DWRITE_FONT_FEATURE_TAG, uint32_t> featureMap{
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('r', 'l', 'i', 'g'), 1 }, // Required Ligatures
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('r', 'c', 'l', 't'), 1 }, // Required Contextual Alternates
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('l', 'o', 'c', 'l'), 1 }, // Localized Forms
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'c', 'm', 'p'), 1 }, // Glyph Composition / Decomposition
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'a', 'l', 't'), 1 }, // Contextual Alternates
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('l', 'i', 'g', 'a'), 1 }, // Standard Ligatures
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('c', 'l', 'i', 'g'), 1 }, // Contextual Ligatures
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('k', 'e', 'r', 'n'), 1 }, // Kerning
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('m', 'a', 'r', 'k'), 1 }, // Mark Positioning
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('m', 'k', 'm', 'k'), 1 }, // Mark to Mark Positioning
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('d', 'i', 's', 't'), 1 } // Distances
|
||||
{ DWRITE_MAKE_FONT_FEATURE_TAG('k', 'e', 'r', 'n'), 1 } // Kerning
|
||||
};
|
||||
|
||||
// Update our feature map with the provided features
|
||||
|
|
|
@ -582,9 +582,6 @@ try
|
|||
|
||||
_displaySizePixels = _GetClientSize();
|
||||
|
||||
_invalidMap.resize(_displaySizePixels / _fontRenderData->GlyphCell());
|
||||
RETURN_IF_FAILED(InvalidateAll());
|
||||
|
||||
// Get the other device types so we have deeper access to more functionality
|
||||
// in our pipeline than by just walking straight from the D3D device.
|
||||
|
||||
|
@ -1288,6 +1285,7 @@ try
|
|||
if (_isEnabled)
|
||||
{
|
||||
const auto clientSize = _GetClientSize();
|
||||
const auto glyphCellSize = _fontRenderData->GlyphCell();
|
||||
|
||||
// If we don't have device resources or if someone has requested that we
|
||||
// recreate the device... then make new resources. (Create will dump the old ones.)
|
||||
|
@ -1317,8 +1315,11 @@ try
|
|||
|
||||
// And persist the new size.
|
||||
_displaySizePixels = clientSize;
|
||||
}
|
||||
|
||||
_invalidMap.resize(clientSize / _fontRenderData->GlyphCell());
|
||||
if (const auto size = clientSize / glyphCellSize; size != _invalidMap.size())
|
||||
{
|
||||
_invalidMap.resize(size);
|
||||
RETURN_IF_FAILED(InvalidateAll());
|
||||
}
|
||||
|
||||
|
@ -1337,7 +1338,7 @@ try
|
|||
_ShouldForceGrayscaleAA(),
|
||||
_dwriteFactory.Get(),
|
||||
spacing,
|
||||
_fontRenderData->GlyphCell(),
|
||||
glyphCellSize,
|
||||
_d2dDeviceContext->GetSize(),
|
||||
std::nullopt,
|
||||
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
|
||||
|
|
Loading…
Reference in a new issue