Compare commits

...

22 Commits

Author SHA1 Message Date
Mike Griese
757399650d Fix the cmdpal moving the infobar down (#11670)
Just read the code, it's immediately obvious what I messed up

Closes #11645

@DHowett turns out I was wrong, I could get this one done before 5 😜

(cherry picked from commit 3667678df1)
2021-12-13 20:50:46 -06:00
Comzyh
1ce8424ab7 Parse UTF-16 surrogates pairs for calculating pattern's position (#11915)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request

Properly handle UTF-16 surrogates when calculating the position of matched pattern.

Fix #8709

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References
b88ffb21b0/src/buffer/out/search.cpp (L335-L339)

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [ ] Closes #8709
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
use `Utf16Parser::Parse` to handle code points from U+010000 to U+10FFFF in UTF-16.

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

![image](https://user-images.githubusercontent.com/1068203/145421736-c842c7d4-0136-42d0-ad72-f004f58d9e3b.png)

also the case by @mas90  https://github.com/microsoft/terminal/issues/8709#issuecomment-884915485:

![image](https://user-images.githubusercontent.com/1068203/145420264-3fe220b4-42c5-44ac-aa94-4e604b164ed3.png)

(cherry picked from commit a2d96d6b1f)
2021-12-13 16:56:00 -06:00
Leonard Hecker
e0c4277777 Fix length calculation of GetConsoleCommandHistoryLengthA (#11897)
This is a primitive bug fix for GetConsoleCommandHistoryLengthA.

## PR Checklist
* [x] I work here
* [x] Tests added/passed

(cherry picked from commit ca20bbde1e)
2021-12-13 16:56:00 -06:00
Schuyler Rosefield
3a51afc375 Make sure we dont access an invalid optional on close (#11857)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Clean up an invalid access that I introduced in #11440

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? -->
## References

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [x] Closes #11684
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

(cherry picked from commit 29e6235151)
2021-12-13 16:56:00 -06:00
Michael Niksa
a303c63b07 Tell PublishSymbols task about binaries as well as the PDB files (#11852)
We have been advised to give not only the PDB paths, but paths to the EXE and DLL files we produce, to the PublishSymbols build task. We are assured by our engineering systems teams that enlightening the task to all of this information helps it hook things up better somewhere between our build machine and the symbol server such that debugging is more robust, especially around thrown exception stacks.

## PR Checklist
* [x] Closes #11737 - main fix for feeding EXEs and DLLs into the symbol publisher
* [x] Closes #11860 - bonus fix because I noticed the PDB source linking wasn't working
* [x] I work here.
* [x] If it fits, it sits.

(cherry picked from commit 52235b0fb6)
2021-12-13 16:56:00 -06:00
Mike Griese
ccc74686a2 Add snap-layouts support to the Terminal (#11680)
Adds snap layout support to the Terminal's maximize button. This PR is
full of BODGY, so brace yourselves.

Big thanks to Chris Swan in #11134 for building the prototype.
I don't believe this solves #8795, because XAML islands can't get
nchittest messages

- The window procedure for the drag bar forwards clicks on its client
  area to its parent as non-client clicks.
- BODGY: It also _manually_ handles the caption buttons. They exist in
  the titlebar, and work reasonably well with just XAML, if the drag bar
  isn't covering them.
- However, to get snap layout support, we need to actually return
  `HTMAXBUTTON` where the maximize button is. If the drag bar doesn't
  cover the caption buttons, then the core input site (which takes up
  the entirety of the XAML island) will steal the `WM_NCHITTEST` before
  we get a chance to handle it.
- So, the drag bar covers the caption buttons, and manually handles
  hovering and pressing them when needed. This gives the impression that
  they're getting input as they normally would, even if they're not
  _really_ getting input via XAML.
- We also need to manually display the button tooltips now, because XAML
  doesn't know when they've been hovered for long enough. Hence, the
  `_displayToolTip` `ThrottledFuncTrailing`

## Validation
Minimized, maximized, restored down, hovered the buttons slowly, moved
the mouse over them quickly, they feel the same as before. But now with
snap layouts appearing.

## TODO!
* [x] I'm working on getting the ToolTips on the caption buttons back. Alas, I needed a demo of this _today_, so I'll fix that tomorrow morning.
* [x] mild concern: I should probably test Win 10 to make sure there wasn't weird changes to the message loop in win11 that means this is broken on win10.
* [x] I think I used the wrong issue number for tons of my comments throughout this PR. Double check that. Should be #9443, not #9447.

Closes #9443
I thought this took care of #8587 ~as a bonus, because I was here, and the fix is _now_ trivial~, but looking at the latest commit that regressed.

Co-authored-by: Chris Swan <chswan@microsoft.com>
(cherry picked from commit f2ebb21bd1)
2021-12-13 16:56:00 -06:00
Mike Griese
0c022811e6 Don't crash if we fail to BeginBufferedPaint (#11674)
Fixes MSFT:34673647, at least I'm pretty sure. That's only ever hit a few
times externally, and internally it's hitting a lot on 1.9.1942 builds, which
doesn't really make any sense.

(cherry picked from commit a74c37bbcd)
2021-12-13 16:56:00 -06:00
Sergey
83b482a05f Fix missing window border when use "win+arrow down" in fullscreen mode in Terminal (#11653)
Window sends an event that requests exit from fullscreen then SC_RESTORE messages is sent and it is in fullscreen mode.
Closes #10607

## Validation Steps Performed
Border and tabbar now appear after exiting fullscreen via "win+arrow down".

(cherry picked from commit 7aae2e9100)
2021-12-13 16:55:26 -06:00
Mike Griese
217196e592 Make sure to format the error message with an UNSIGNED int (#11667)
Closes #11556
![image](https://user-images.githubusercontent.com/18356694/139715591-b18ef7c1-2967-42a5-9528-2522220aa177.png)

(cherry picked from commit 8826cc028b)
2021-12-13 16:55:26 -06:00
James Holderness
ab683c00cc Default all G-sets to ASCII unless ISO-2022 is requested (#11658)
## Summary of the Pull Request

There is a non-zero subset of applications that randomly output _Locking Shift_ escape sequences which will invoke a character set from G2 or G3 into the left half of the code table. If those G-sets are mapped to Latin1, that can result in the terminal producing output that appears to be broken. This PR now defaults all G-sets to ASCII, to prevent an unintentional _Locking Shift_ from having any effect.

## PR Checklist
* [x] Closes #10408
* [x] CLA signed.
* [ ] Tests added/passed
* [ ] Documentation updated.
* [ ] Schema updated.
* [x] I've discussed this with core contributors already. Issue number where discussion took place: #10408

## Detailed Description of the Pull Request / Additional comments

Most other modern terminals also default to ASCII in all G-sets, so this shouldn't break any modern applications. Legacy 8-bit applications may still expect the G2 and G3 sets mapped to Latin1, but they would also need to have the ISO-2022 encoding enabled, so we can keep them happy by setting G2 and G3 correctly when the ISO-2022 encoding is requested.

## Validation Steps Performed

I've manually confirmed that `echo -e "\en"` and `echo -e "\eo"` no longer have any visible effect on the output (at least without first invoking another character set into G2 or G3). I've also confirmed that they do still work as expected (i.e. selecting Latin1) after enabling the ISO-2022 encoding.

(cherry picked from commit 27e042b784)
2021-12-13 16:55:26 -06:00
PankajBhojwani
4ad3156f42 Check that the control exists before we try to focus it (#11635)
## Summary of the Pull Request
When we are on a settings UI tab, `_GetActiveControl` returns a `nullptr`, make sure not to try and focus it in that case

## PR Checklist
* [x] Closes #11633
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here

## Validation Steps Performed
No longer crashes

(cherry picked from commit a7ce93a357)
2021-12-13 16:55:26 -06:00
Mike Griese
bf27a79ace Fix the wt action in defterm windows (#11646)
This is a pretty obvious typo in retrospect. Never hit it before, because in all non-defterm windows, the `_startupActions` always has one action.

* [x] Closes #11463

(cherry picked from commit b90f3605a2)
2021-12-13 16:55:26 -06:00
Ian O'Neill
3a615149da Ensure the background image path is displayed in the settings UI (#11580)
## Summary of the Pull Request
Ensures that the background image path is displayed in the settings UI.

## References
One of the items on #11353

## PR Checklist
* [x] Closes #11541
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA

## Validation Steps Performed
Set the background image path and saw that it was displayed in the settings UI.

(cherry picked from commit 9662bc6910)
2021-12-13 16:55:26 -06:00
Dustin L. Howett
dadee5bdaf Update Cascadia Code to 2111.01 (#11937)
This update fixes the bracket ligatures in italic.

See microsoft/cascadia-code#595 for more details.

(cherry picked from commit 246e57f1b2)
2021-12-13 16:41:24 -06:00
Leonard Hecker
4c364e9342 Use nearby fonts for font fallback (#11764)
This commit is a minimal fix in order to pass the
`IDWriteFontCollection` we create out of .ttf files residing next to our
binaries to the `IDWriteFontFallback::MapCharacters` call. The
`IDWriteTextFormat` is used in order to carry the font collection over
into `CustomTextLayout`.

## Validation
* Put `JetBrainsMono-Regular.ttf` into the binary output directory
* Modify `HKCU:\Console\*\FaceName`  to `JetBrains Mono`
* Launch OpenConsole.exe
* OpenConsole uses JetBrains Mono ✔️

Closes #11032
Closes #11648

(cherry picked from commit 131f5d2b32)
2021-12-13 14:19:52 -06:00
Mike Griese
aaabce77c7 Fix the opacity slider (#11643)
I can't even write a description for this. Just read the code change, you'll see what I goofed.

Regressed in #11372

Closes #11555

(cherry picked from commit 1cedac6a33)
2021-12-13 14:19:52 -06:00
Leonard Hecker
c9cde49716 Fix loading of fragments that update multiple profiles (#11598)
The "updates" key is an alternative "guid" key for fragment profiles.
But SettingsLoader::_appendProfile stores and deduplicates profiles according
to their "guid" only. We need to modify the function to optionally store
profiles by their "updates" key as well, otherwise multiple fragment
profiles without "guid" might collide as they produce the same default GUID.

## PR Checklist
* [x] Closes #11597
* [x] I work here
* [ ] Tests added/passed
* [ ] Schema updated.
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

## Validation Steps Performed
* Unit tests pass ✔️
* Issue #11597 doesn't reproduce anymore ✔️

(cherry picked from commit fe26a6efc0)
2021-12-13 14:19:52 -06:00
Mike Griese
5198c8e2e4 Make sure the infobar is inserted before the tab content, not on top of (#11609)
Fixes #11606

This is weird, but the infobars would appear totally on top of the
TerminalPage when `showTabsInTitlebar:false`. This would result in the infobar
obscuring the tabs.

Now, the infobars are strictly inserted after the tabs, before the content. So
when they appear, they will reduce the amount of space usable for the control.
That is a little annoying, but preferable to the tabs totally not existing.

Relevant conversation notes from #10798:

> > If the info bar is not local to the tab, then its location between the tab
> > bar (when the title bar is hidden) and the terminal panes feels
> > misleading. Should it instead be above the tab bar or below the terminal
> > panes?
>
> You're... not wrong here. It's maybe not the best place for it, but _on top_
> of the tabs would look insane, and probably wouldn't even work easily, given
> the way we reparent the tab row into the titlebar.
>
> In the pane itself would make more sense, but that runs abreast of all sorts
> of things like #9024, #4998, which might make more sense.

I'm just gonna go with this now, because it's _better_ than before, while we
work out what's _best_.

![gh-11606-fix](https://user-images.githubusercontent.com/18356694/138729178-b96b7003-0dd2-4521-8fff-0fd2a5989f22.gif)

(cherry picked from commit a916a5d9de)
2021-12-13 14:19:52 -06:00
Leonard Hecker
2aa2458b22 Compile OpenConsoleProxy without CRT (#11610)
After this commit OpenConsoleProxy will be built without a CRT.
This cuts down its binary size and DLL dependency bloat.
We hope that this fixes a COM server activation bug if the
user doesn't have a CRT installed globally on their system.

Fixes #11529

(cherry picked from commit def1bdd693)
2021-12-13 14:19:52 -06:00
Leonard Hecker
aacfc2a424 Fix AltGr not working in the settings UI (#11808) (#11814)
Since the settings UI's input fields behave similarly to the terminal's input,
`TerminalPage::_KeyDownHandler` also needs to behave similarly to
`TermControl::_KeyHandler`. This commit copies all relevant code
over from the latter into the former, including the suppression
of AltGr keys for keychord/action handling.

## PR Checklist
* [x] Closes #11788
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed
* Use a German keyboard layout
* Open 2 regular tabs and 1 settings tab and focus an input field
* AltGr+2 produces the character ² ✔️
* Ctrl+Alt+2 opens the second tab ✔️

(cherry picked from commit 80f8383860)
2021-12-01 05:38:26 -06:00
Leonard Hecker
39b72f78c3 Fixed VsDevCmd command line quoting (#11554)
(cherry picked from commit 5cd9663269)
2021-10-20 21:59:10 +02:00
PankajBhojwani
89674ac4fb Updating PGO branch 2021-10-19 17:29:16 -07:00
62 changed files with 1399 additions and 263 deletions

View File

@@ -30,6 +30,7 @@ hyperlink
hyperlinking
hyperlinks
img
inlined
It'd
kje
liga

View File

@@ -38,12 +38,14 @@ fullkbd
futex
GETDESKWALLPAPER
GETHIGHCONTRAST
GETMOUSEHOVERTIME
Hashtable
HIGHCONTRASTON
HIGHCONTRASTW
hotkeys
href
hrgn
HTCLOSE
IActivation
IApp
IAppearance
@@ -87,11 +89,14 @@ MENUDATA
MENUINFO
memicmp
mptt
MOUSELEAVE
mov
msappx
MULTIPLEUSE
NCHITTEST
NCLBUTTONDBLCLK
NCMOUSELEAVE
NCMOUSEMOVE
NCRBUTTONDBLCLK
NIF
NIN
@@ -153,9 +158,11 @@ TASKBARCREATED
TBPF
THEMECHANGED
tlg
TME
tmp
tolower
toupper
TRACKMOUSEEVENT
TTask
TVal
UChar

View File

@@ -385,12 +385,14 @@ CPINFOEX
CPLINFO
cplusplus
cpp
CPPARM
CPPCORECHECK
cppcorecheckrules
cppm
cpprest
cpprestsdk
cppwinrt
CPPx
CProc
cpx
crbegin
@@ -938,6 +940,7 @@ GTP
guc
gui
guidatom
guiddef
GValue
GWL
GWLP
@@ -1535,6 +1538,7 @@ NOCOLOR
NOCOMM
NOCONTEXTHELP
NOCOPYBITS
nodefaultlib
nodiscard
NODUP
noexcept
@@ -1574,6 +1578,7 @@ nothrow
NOTICKS
NOTIMPL
notin
notmatch
NOTNULL
NOTOPMOST
NOTRACK
@@ -2232,6 +2237,7 @@ STARTWPARMSW
Statusline
stdafx
STDAPI
stdc
stdcall
stdcpp
stderr
@@ -2475,6 +2481,7 @@ uint
uintptr
ulcch
ulong
umd
Unadvise
unattend
uncomment
@@ -2645,6 +2652,7 @@ wddmcon
wddmconrenderer
WDDMCONSOLECONTEXT
wdm
webclient
webpage
website
websocket
@@ -2719,6 +2727,7 @@ Winperf
WInplace
winres
winrt
winsdk
wintelnet
winternl
winuser

View File

@@ -96,6 +96,8 @@ jobs:
selectOrConfig: config
nugetConfigPath: NuGet.Config
arguments: restore OpenConsole.sln -SolutionDirectory $(Build.SourcesDirectory)
# Pull the Windows SDK for the developer tools like the debuggers so we can index sources later
- template: .\templates\install-winsdk-steps.yml
- task: UniversalPackages@0
displayName: Download terminal-internal Universal Package
inputs:
@@ -261,7 +263,10 @@ jobs:
displayName: Publish symbols path
continueOnError: True
inputs:
SearchPattern: '**/*.pdb'
SearchPattern: |
$(Build.SourcesDirectory)/bin/**/*.pdb
$(Build.SourcesDirectory)/bin/**/*.exe
$(Build.SourcesDirectory)/bin/**/*.dll
IndexSources: false
SymbolServerType: TeamServices
@@ -402,7 +407,10 @@ jobs:
displayName: Publish symbols path
continueOnError: True
inputs:
SearchPattern: '**/*.pdb'
SearchPattern: |
$(Build.SourcesDirectory)/bin/**/*.pdb
$(Build.SourcesDirectory)/bin/**/*.exe
$(Build.SourcesDirectory)/bin/**/*.dll
IndexSources: false
SymbolServerType: TeamServices
SymbolsArtifactName: Symbols_WPF_$(BuildConfiguration)

View File

@@ -0,0 +1,9 @@
parameters:
sdkVersion: 18362
steps:
- task: powershell@2
inputs:
targetType: filePath
filePath: build\scripts\Install-WindowsSdkISO.ps1
arguments: ${{ parameters.sdkVersion }}
displayName: 'Install Windows SDK (${{ parameters.sdkVersion }})'

View File

@@ -23,6 +23,7 @@ $mappedFiles = New-Object System.Collections.ArrayList
foreach ($file in (Get-ChildItem -r:$recursive "$SearchDir\*.pdb"))
{
$mappedFiles = New-Object System.Collections.ArrayList
Write-Verbose "Found $file"
$ErrorActionPreference = "Continue" # Azure Pipelines defaults to "Stop", continue past errors in this script.
@@ -50,7 +51,7 @@ foreach ($file in (Get-ChildItem -r:$recursive "$SearchDir\*.pdb"))
if ($relative)
{
$mapping = $allFiles[$i] + "*$relative"
$mappedFiles.Add($mapping)
$ignore = $mappedFiles.Add($mapping)
Write-Verbose "Mapped path $($i): $mapping"
}
@@ -78,7 +79,26 @@ $($mappedFiles -join "`r`n")
SRCSRV: end ------------------------------------------------
"@ | Set-Content $pdbstrFile
Write-Host
Write-Host
Write-Host (Get-Content $pdbstrFile)
Write-Host
Write-Host
Write-Host "$pdbstrExe -p:""$file"" -w -s:srcsrv -i:$pdbstrFile"
& $pdbstrExe -p:"$file" -w -s:srcsrv -i:$pdbstrFile
Write-Host
Write-Host
Write-Host "$pdbstrExe -p:""$file"" -r -s:srcsrv"
& $pdbstrExe -p:"$file" -r -s:srcsrv
Write-Host
Write-Host
Write-Host "$srctoolExe $file"
& $srctoolExe "$file"
Write-Host
Write-Host
}
# Return with exit 0 to override any weird error code from other tools

View File

@@ -0,0 +1,346 @@
[CmdletBinding()]
param([Parameter(Mandatory=$true, Position=0)]
[string]$buildNumber)
# Ensure the error action preference is set to the default for PowerShell3, 'Stop'
$ErrorActionPreference = 'Stop'
# Constants
$WindowsSDKOptions = @("OptionId.UWPCpp", "OptionId.DesktopCPPx64", "OptionId.DesktopCPPx86", "OptionId.DesktopCPPARM64", "OptionId.DesktopCPPARM", "OptionId.WindowsDesktopDebuggers")
$WindowsSDKRegPath = "HKLM:\Software\WOW6432Node\Microsoft\Windows Kits\Installed Roots"
$WindowsSDKRegRootKey = "KitsRoot10"
$WindowsSDKVersion = "10.0.$buildNumber.0"
$WindowsSDKInstalledRegPath = "$WindowsSDKRegPath\$WindowsSDKVersion\Installed Options"
$StrongNameRegPath = "HKLM:\SOFTWARE\Microsoft\StrongName\Verification"
$PublicKeyTokens = @("31bf3856ad364e35")
if ($buildNumber -notmatch "^\d{5,}$")
{
Write-Host "ERROR: '$buildNumber' doesn't look like a windows build number"
Write-Host
Exit 1
}
function Download-File
{
param ([string] $outDir,
[string] $downloadUrl,
[string] $downloadName)
$downloadPath = Join-Path $outDir "$downloadName.download"
$downloadDest = Join-Path $outDir $downloadName
$downloadDestTemp = Join-Path $outDir "$downloadName.tmp"
Write-Host -NoNewline "Downloading $downloadName..."
$retries = 10
$downloaded = $false
while (-not $downloaded)
{
try
{
$webclient = new-object System.Net.WebClient
$webclient.DownloadFile($downloadUrl, $downloadPath)
$downloaded = $true
}
catch [System.Net.WebException]
{
Write-Host
Write-Warning "Failed to fetch updated file from $downloadUrl : $($error[0])"
if (!(Test-Path $downloadDest))
{
if ($retries -gt 0)
{
Write-Host "$retries retries left, trying download again"
$retries--
start-sleep -Seconds 10
}
else
{
throw "$downloadName was not found at $downloadDest"
}
}
else
{
Write-Warning "$downloadName may be out of date"
}
}
}
Unblock-File $downloadPath
$downloadDestTemp = $downloadPath;
# Delete and rename to final dest
Write-Host "testing $downloadDest"
if (Test-Path $downloadDest)
{
Write-Host "Deleting: $downloadDest"
Remove-Item $downloadDest -Force
}
Move-Item -Force $downloadDestTemp $downloadDest
Write-Host "Done"
return $downloadDest
}
function Get-ISODriveLetter
{
param ([string] $isoPath)
$diskImage = Get-DiskImage -ImagePath $isoPath
if ($diskImage)
{
$volume = Get-Volume -DiskImage $diskImage
if ($volume)
{
$driveLetter = $volume.DriveLetter
if ($driveLetter)
{
$driveLetter += ":"
return $driveLetter
}
}
}
return $null
}
function Mount-ISO
{
param ([string] $isoPath)
# Check if image is already mounted
$isoDrive = Get-ISODriveLetter $isoPath
if (!$isoDrive)
{
Mount-DiskImage -ImagePath $isoPath -StorageType ISO | Out-Null
}
$isoDrive = Get-ISODriveLetter $isoPath
Write-Verbose "$isoPath mounted to ${isoDrive}:"
}
function Dismount-ISO
{
param ([string] $isoPath)
$isoDrive = (Get-DiskImage -ImagePath $isoPath | Get-Volume).DriveLetter
if ($isoDrive)
{
Write-Verbose "$isoPath dismounted"
Dismount-DiskImage -ImagePath $isoPath | Out-Null
}
}
function Disable-StrongName
{
param ([string] $publicKeyToken = "*")
reg ADD "HKLM\SOFTWARE\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
{
reg ADD "HKLM\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,$publicKeyToken" /f | Out-Null
}
}
function Test-Admin
{
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal $identity
$principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}
function Test-RegistryPathAndValue
{
param (
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $path,
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string] $value)
try
{
if (Test-Path $path)
{
Get-ItemProperty -Path $path | Select-Object -ExpandProperty $value -ErrorAction Stop | Out-Null
return $true
}
}
catch
{
}
return $false
}
function Test-InstallWindowsSDK
{
$retval = $true
if (Test-RegistryPathAndValue -Path $WindowsSDKRegPath -Value $WindowsSDKRegRootKey)
{
# A Windows SDK is installed
# Is an SDK of our version installed with the options we need?
$allRequiredSdkOptionsInstalled = $true
foreach($sdkOption in $WindowsSDKOptions)
{
if (!(Test-RegistryPathAndValue -Path $WindowsSDKInstalledRegPath -Value $sdkOption))
{
$allRequiredSdkOptionsInstalled = $false
}
}
if($allRequiredSdkOptionsInstalled)
{
# It appears we have what we need. Double check the disk
$sdkRoot = Get-ItemProperty -Path $WindowsSDKRegPath | Select-Object -ExpandProperty $WindowsSDKRegRootKey
if ($sdkRoot)
{
if (Test-Path $sdkRoot)
{
$refPath = Join-Path $sdkRoot "References\$WindowsSDKVersion"
if (Test-Path $refPath)
{
$umdPath = Join-Path $sdkRoot "UnionMetadata\$WindowsSDKVersion"
if (Test-Path $umdPath)
{
# Pretty sure we have what we need
$retval = $false
}
}
}
}
}
}
return $retval
}
function Test-InstallStrongNameHijack
{
foreach($publicKeyToken in $PublicKeyTokens)
{
$key = "$StrongNameRegPath\*,$publicKeyToken"
if (!(Test-Path $key))
{
return $true
}
}
return $false
}
Write-Host -NoNewline "Checking for installed Windows SDK $WindowsSDKVersion..."
$InstallWindowsSDK = Test-InstallWindowsSDK
if ($InstallWindowsSDK)
{
Write-Host "Installation required"
}
else
{
Write-Host "INSTALLED"
}
$StrongNameHijack = Test-InstallStrongNameHijack
Write-Host -NoNewline "Checking if StrongName bypass required..."
if ($StrongNameHijack)
{
Write-Host "REQUIRED"
}
else
{
Write-Host "Done"
}
if ($StrongNameHijack -or $InstallWindowsSDK)
{
if (!(Test-Admin))
{
Write-Host
throw "ERROR: Elevation required"
}
}
if ($InstallWindowsSDK)
{
# Static(ish) link for Windows SDK
# Note: there is a delay from Windows SDK announcements to availability via the static link
$uri = "https://software-download.microsoft.com/download/sg/Windows_InsiderPreview_SDK_en-us_$($buildNumber)_1.iso";
if ($env:TEMP -eq $null)
{
$env:TEMP = Join-Path $env:SystemDrive 'temp'
}
$winsdkTempDir = Join-Path (Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName())) "WindowsSDK"
if (![System.IO.Directory]::Exists($winsdkTempDir))
{
[void][System.IO.Directory]::CreateDirectory($winsdkTempDir)
}
$file = "winsdk_$buildNumber.iso"
Write-Verbose "Getting WinSDK from $uri"
$downloadFile = Download-File $winsdkTempDir $uri $file
Write-Verbose "File is at $downloadFile"
$downloadFileItem = Get-Item $downloadFile
# Check to make sure the file is at least 10 MB.
if ($downloadFileItem.Length -lt 10*1024*1024)
{
Write-Host
Write-Host "ERROR: Downloaded file doesn't look large enough to be an ISO. The requested version may not be on microsoft.com yet."
Write-Host
Exit 1
}
# TODO Check if zip, exe, iso, etc.
try
{
Write-Host -NoNewline "Mounting ISO $file..."
Mount-ISO $downloadFile
Write-Host "Done"
$isoDrive = Get-ISODriveLetter $downloadFile
if (Test-Path $isoDrive)
{
Write-Host -NoNewLine "Installing WinSDK..."
$setupPath = Join-Path "$isoDrive" "WinSDKSetup.exe"
Start-Process -Wait $setupPath "/features $WindowsSDKOptions /q"
Write-Host "Done"
}
else
{
throw "Could not find mounted ISO at ${isoDrive}"
}
}
finally
{
Write-Host -NoNewline "Dismounting ISO $file..."
Dismount-ISO $downloadFile
Write-Host "Done"
}
}
if ($StrongNameHijack)
{
Write-Host -NoNewline "Disabling StrongName for Windows SDK..."
foreach($key in $PublicKeyTokens)
{
Disable-StrongName $key
}
Write-Host "Done"
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -8,6 +8,7 @@
#include "../types/inc/utils.hpp"
#include "../types/inc/convert.hpp"
#include "../../types/inc/Utf16Parser.hpp"
#include "../../types/inc/GlyphWidth.hpp"
#pragma hdrstop
@@ -2585,16 +2586,17 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
// match and the previous match, so we use the size of the prefix
// along with the size of the match to determine the locations
size_t prefixSize = 0;
for (const auto ch : i->prefix().str())
for (const std::vector<wchar_t> parsedGlyph : Utf16Parser::Parse(i->prefix().str()))
{
prefixSize += IsGlyphFullWidth(ch) ? 2 : 1;
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
prefixSize += IsGlyphFullWidth(glyph) ? 2 : 1;
}
const auto start = lenUpToThis + prefixSize;
size_t matchSize = 0;
for (const auto ch : i->str())
for (const std::vector<wchar_t> parsedGlyph : Utf16Parser::Parse(i->str()))
{
matchSize += IsGlyphFullWidth(ch) ? 2 : 1;
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
matchSize += IsGlyphFullWidth(glyph) ? 2 : 1;
}
const auto end = start + matchSize;
lenUpToThis = end;

View File

@@ -7,7 +7,9 @@
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "JsonTestClass.h"
#include "TestUtils.h"
#include <defaults.h>
#include <userDefaults.h>
using namespace Microsoft::Console;
using namespace WEX::Logging;
@@ -70,6 +72,7 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestCloneInheritanceTree);
TEST_METHOD(TestValidDefaults);
TEST_METHOD(TestInheritedCommand);
TEST_METHOD(LoadFragmentsWithMultipleUpdates);
private:
static winrt::com_ptr<implementation::CascadiaSettings> createSettings(const std::string_view& userJSON)
@@ -1979,4 +1982,34 @@ namespace SettingsModelLocalTests
VERIFY_IS_NULL(actualKeyChord);
}
}
// This test ensures GH#11597 doesn't regress.
void DeserializationTests::LoadFragmentsWithMultipleUpdates()
{
static constexpr std::wstring_view fragmentSource{ L"fragment" };
static constexpr std::string_view fragmentJson{ R"({
"profiles": [
{
"updates": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"cursorShape": "filledBox"
},
{
"updates": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"cursorShape": "filledBox"
},
{
"guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
"commandline": "cmd.exe"
}
]
})" };
implementation::SettingsLoader loader{ std::string_view{}, DefaultJson };
loader.MergeInboxIntoUserSettings();
loader.MergeFragmentIntoUserSettings(winrt::hstring{ fragmentSource }, fragmentJson);
loader.FinalizeLayering();
VERIFY_IS_FALSE(loader.duplicateProfile);
VERIFY_ARE_EQUAL(3u, loader.userSettings.profiles.size());
}
}

View File

@@ -556,7 +556,7 @@ namespace winrt::TerminalApp::implementation
auto actions = winrt::single_threaded_vector<ActionAndArgs>(std::move(
TerminalPage::ConvertExecuteCommandlineToActions(realArgs)));
if (_startupActions.Size() != 0)
if (actions.Size() != 0)
{
actionArgs.Handled(true);
ProcessStartupActions(actions, false);

View File

@@ -1557,6 +1557,11 @@ namespace winrt::TerminalApp::implementation
return _root->IsQuakeWindow();
}
void AppLogic::RequestExitFullscreen()
{
_root->SetFullscreen(false);
}
bool AppLogic::GetMinimizeToNotificationArea()
{
if constexpr (Feature_NotificationIcon::IsEnabled())

View File

@@ -79,6 +79,7 @@ namespace winrt::TerminalApp::implementation
void SetPersistedLayoutIdx(const uint32_t idx);
void SetNumberOfOpenWindows(const uint64_t num);
bool IsQuakeWindow() const noexcept;
void RequestExitFullscreen();
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
bool CenterOnLaunch();

View File

@@ -60,6 +60,7 @@ namespace TerminalApp
void SetPersistedLayoutIdx(UInt32 idx);
void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed();
void RequestExitFullscreen();
Boolean IsQuakeWindow();
Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi);

View File

@@ -1,25 +1,67 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// MinMaxCloseControl.xaml.cpp
// Implementation of the MinMaxCloseControl class
//
#include "pch.h"
#include "MinMaxCloseControl.h"
#include "MinMaxCloseControl.g.cpp"
#include <LibraryResources.h>
using namespace winrt::Windows::UI::Xaml;
namespace winrt::TerminalApp::implementation
{
static void closeToolTipForButton(const Controls::Button& button)
{
if (auto tt{ Controls::ToolTipService::GetToolTip(button) })
{
if (auto tooltip{ tt.try_as<Controls::ToolTip>() })
{
tooltip.IsOpen(false);
}
}
}
MinMaxCloseControl::MinMaxCloseControl()
{
// Get our dispatcher. This will get us the same dispatcher as
// Dispatcher(), but it's a DispatcherQueue, so we can use it with
// ThrottledFunc
auto dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
InitializeComponent();
// Get the tooltip hover time from the system, or default to 400ms
// (which should be the default, see:
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-trackmouseevent#remarks)
unsigned int hoverTimeoutMillis{ 400 };
LOG_IF_WIN32_BOOL_FALSE(SystemParametersInfoW(SPI_GETMOUSEHOVERTIME, 0, &hoverTimeoutMillis, 0));
const auto toolTipInterval = std::chrono::milliseconds(hoverTimeoutMillis);
// Create a ThrottledFunc for opening the tooltip after the hover
// timeout. If we hover another button, we should make sure to call
// Run() with the new button. Calling `_displayToolTip.Run(nullptr)`,
// which will cause us to not display a tooltip, which is used when we
// leave the control entirely.
_displayToolTip = std::make_shared<ThrottledFuncTrailing<Controls::Button>>(
dispatcher,
toolTipInterval,
[weakThis = get_weak()](Controls::Button button) {
// If we provide a button, then open the tooltip on that button.
// We can "dismiss" this throttled func by calling it with null,
// which will cause us to do nothing at the end of the timeout
// instead.
if (button)
{
if (auto tt{ Controls::ToolTipService::GetToolTip(button) })
{
if (auto tooltip{ tt.try_as<Controls::ToolTip>() })
{
tooltip.IsOpen(true);
}
}
}
});
}
// These event handlers simply forward each buttons click events up to the
@@ -95,4 +137,104 @@ namespace winrt::TerminalApp::implementation
break;
}
}
// Method Description:
// - Called when the mouse hovers a button.
// - Transition that button to `PointerOver`
// - run the throttled func with this button, to display the tooltip after
// a timeout
// - dismiss any open tooltips on other buttons.
// Arguments:
// - button: the button that was hovered
void MinMaxCloseControl::HoverButton(CaptionButton button)
{
// Keep track of the button that's been pressed. we get a mouse move
// message when we open the tooltip. If we move the mouse on top of this
// button, that we've already pressed, then no need to move to the
// "hovered" state, we should stay in the pressed state.
if (_lastPressedButton && _lastPressedButton.value() == button)
{
return;
}
switch (button)
{
// Make sure to use true for the useTransitions parameter, to
// animate the fade in/out transition between colors.
case CaptionButton::Minimize:
VisualStateManager::GoToState(MinimizeButton(), L"PointerOver", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
_displayToolTip->Run(MinimizeButton());
closeToolTipForButton(MaximizeButton());
closeToolTipForButton(CloseButton());
break;
case CaptionButton::Maximize:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"PointerOver", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
closeToolTipForButton(MinimizeButton());
_displayToolTip->Run(MaximizeButton());
closeToolTipForButton(CloseButton());
break;
case CaptionButton::Close:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"PointerOver", true);
closeToolTipForButton(MinimizeButton());
closeToolTipForButton(MaximizeButton());
_displayToolTip->Run(CloseButton());
break;
}
}
// Method Description:
// - Called when the mouse presses down on a button. NOT when it is
// released. That's handled one level above, in
// TitleBarControl::ReleaseButtons
// - Transition that button to `Pressed`
// Arguments:
// - button: the button that was pressed
void MinMaxCloseControl::PressButton(CaptionButton button)
{
switch (button)
{
case CaptionButton::Minimize:
VisualStateManager::GoToState(MinimizeButton(), L"Pressed", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
break;
case CaptionButton::Maximize:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Pressed", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
break;
case CaptionButton::Close:
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Pressed", true);
break;
}
_lastPressedButton = button;
}
// Method Description:
// - Called when buttons are no longer hovered or pressed. Return them all
// to the normal state, and dismiss the tooltips.
void MinMaxCloseControl::ReleaseButtons()
{
_displayToolTip->Run(nullptr);
VisualStateManager::GoToState(MinimizeButton(), L"Normal", true);
VisualStateManager::GoToState(MaximizeButton(), L"Normal", true);
VisualStateManager::GoToState(CloseButton(), L"Normal", true);
closeToolTipForButton(MinimizeButton());
closeToolTipForButton(MaximizeButton());
closeToolTipForButton(CloseButton());
_lastPressedButton = std::nullopt;
}
}

View File

@@ -6,11 +6,9 @@
#pragma once
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "MinMaxCloseControl.g.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
#include <ThrottledFunc.h>
namespace winrt::TerminalApp::implementation
{
@@ -20,6 +18,10 @@ namespace winrt::TerminalApp::implementation
void SetWindowVisualState(WindowVisualState visualState);
void HoverButton(CaptionButton button);
void PressButton(CaptionButton button);
void ReleaseButtons();
void _MinimizeClick(winrt::Windows::Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
void _MaximizeClick(winrt::Windows::Foundation::IInspectable const& sender,
@@ -30,6 +32,9 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(MinimizeClick, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs);
TYPED_EVENT(MaximizeClick, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs);
TYPED_EVENT(CloseClick, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs);
std::shared_ptr<ThrottledFuncTrailing<winrt::Windows::UI::Xaml::Controls::Button>> _displayToolTip{ nullptr };
std::optional<CaptionButton> _lastPressedButton{ std::nullopt };
};
}

View File

@@ -11,6 +11,10 @@ namespace TerminalApp
void SetWindowVisualState(WindowVisualState visualState);
void HoverButton(CaptionButton button);
void PressButton(CaptionButton button);
void ReleaseButtons();
event Windows.Foundation.TypedEventHandler<MinMaxCloseControl, Windows.UI.Xaml.RoutedEventArgs> MinimizeClick;
event Windows.Foundation.TypedEventHandler<MinMaxCloseControl, Windows.UI.Xaml.RoutedEventArgs> MaximizeClick;
event Windows.Foundation.TypedEventHandler<MinMaxCloseControl, Windows.UI.Xaml.RoutedEventArgs> CloseClick;

View File

@@ -220,7 +220,7 @@
</StackPanel.Resources>
<Button x:Name="MinimizeButton"
x:Uid="WindowMinimizeButton"
x:Uid="MinimizeButton"
Width="46.0"
Height="{StaticResource CaptionButtonHeightWindowed}"
MinWidth="46.0"
@@ -232,9 +232,14 @@
<x:String x:Key="CaptionButtonPath">M 0 0 H 10</x:String>
</ResourceDictionary>
</Button.Resources>
<ToolTipService.ToolTip>
<ToolTip>
<TextBlock x:Uid="WindowMinimizeButtonToolTip" />
</ToolTip>
</ToolTipService.ToolTip>
</Button>
<Button x:Name="MaximizeButton"
x:Uid="WindowMaximizeButton"
x:Uid="MaximizeButton"
Width="46.0"
Height="{StaticResource CaptionButtonHeightWindowed}"
MinWidth="46.0"
@@ -256,7 +261,7 @@
</ToolTipService.ToolTip>
</Button>
<Button x:Name="CloseButton"
x:Uid="WindowCloseButton"
x:Uid="CloseButton"
Width="46.0"
Height="{StaticResource CaptionButtonHeightWindowed}"
MinWidth="46.0"
@@ -309,5 +314,10 @@
<x:String x:Key="CaptionButtonPath">M 0 0 L 10 10 M 10 0 L 0 10</x:String>
</ResourceDictionary>
</Button.Resources>
<ToolTipService.ToolTip>
<ToolTip>
<TextBlock x:Uid="WindowCloseButtonToolTip" />
</ToolTip>
</ToolTipService.ToolTip>
</Button>
</StackPanel>

View File

@@ -411,6 +411,9 @@
<data name="WindowCloseButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Close</value>
</data>
<data name="WindowCloseButtonToolTip.Text" xml:space="preserve">
<value>Close</value>
</data>
<data name="WindowMaximizeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Maximize</value>
</data>
@@ -420,6 +423,9 @@
<data name="WindowMinimizeButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Minimize</value>
</data>
<data name="WindowMinimizeButtonToolTip.Text" xml:space="preserve">
<value>Minimize</value>
</data>
<data name="AboutDialog.Title" xml:space="preserve">
<value>About</value>
</data>

View File

@@ -20,6 +20,7 @@
#include "../inc/WindowingBehavior.h"
#include <til/latch.h>
#include <TerminalCore/ControlKeyStates.hpp>
using namespace winrt;
using namespace winrt::Windows::Foundation::Collections;
@@ -35,6 +36,7 @@ using namespace winrt::Microsoft::Terminal::TerminalConnection;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace ::TerminalApp;
using namespace ::Microsoft::Console;
using namespace ::Microsoft::Terminal::Core;
using namespace std::chrono_literals;
#define HOOKUP_ACTION(action) _actionDispatch->action({ this, &TerminalPage::_Handle##action });
@@ -1038,29 +1040,153 @@ namespace winrt::TerminalApp::implementation
}
// Method Description:
// Called when the users pressed keyBindings while CommandPalette is open.
// - Called when the users pressed keyBindings while CommandPalette is open.
// - This method is effectively an extract of TermControl::_KeyHandler and TermControl::_TryHandleKeyBinding.
// Arguments:
// - e: the KeyRoutedEventArgs containing info about the keystroke.
// Return Value:
// - <none>
void TerminalPage::_KeyDownHandler(Windows::Foundation::IInspectable const& /*sender*/, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
{
const auto key = e.OriginalKey();
const auto scanCode = e.KeyStatus().ScanCode;
const auto coreWindow = CoreWindow::GetForCurrentThread();
const auto ctrlDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Control), CoreVirtualKeyStates::Down);
const auto altDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Menu), CoreVirtualKeyStates::Down);
const auto shiftDown = WI_IsFlagSet(coreWindow.GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down);
const auto keyStatus = e.KeyStatus();
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
const auto scanCode = gsl::narrow_cast<WORD>(keyStatus.ScanCode);
const auto modifiers = _GetPressedModifierKeys();
winrt::Microsoft::Terminal::Control::KeyChord kc{ ctrlDown, altDown, shiftDown, false, static_cast<int32_t>(key), static_cast<int32_t>(scanCode) };
if (const auto cmd{ _settings.ActionMap().GetActionByKeyChord(kc) })
// GH#11076:
// For some weird reason we sometimes receive a WM_KEYDOWN
// message without vkey or scanCode if a user drags a tab.
// The KeyChord constructor has a debug assertion ensuring that all KeyChord
// either have a valid vkey/scanCode. This is important, because this prevents
// accidential insertion of invalid KeyChords into classes like ActionMap.
if (!vkey && !scanCode)
{
if (CommandPalette().Visibility() == Visibility::Visible && cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
return;
}
// Alt-Numpad# input will send us a character once the user releases
// Alt, so we should be ignoring the individual keydowns. The character
// will be sent through the TSFInputControl. See GH#1401 for more
// details
if (modifiers.IsAltPressed() && (vkey >= VK_NUMPAD0 && vkey <= VK_NUMPAD9))
{
return;
}
// GH#2235: Terminal::Settings hasn't been modified to differentiate
// between AltGr and Ctrl+Alt yet.
// -> Don't check for key bindings if this is an AltGr key combination.
if (modifiers.IsAltGrPressed())
{
return;
}
const auto actionMap = _settings.ActionMap();
if (!actionMap)
{
return;
}
const auto cmd = actionMap.GetActionByKeyChord({
modifiers.IsCtrlPressed(),
modifiers.IsAltPressed(),
modifiers.IsShiftPressed(),
modifiers.IsWinPressed(),
vkey,
scanCode,
});
if (!cmd)
{
return;
}
if (!_actionDispatch->DoAction(cmd.ActionAndArgs()))
{
return;
}
if (const auto p = CommandPalette(); p.Visibility() == Visibility::Visible && cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
{
p.Visibility(Visibility::Collapsed);
}
// Let's assume the user has bound the dead key "^" to a sendInput command that sends "b".
// If the user presses the two keys "^a" it'll produce "bâ", despite us marking the key event as handled.
// The following is used to manually "consume" such dead keys and clear them from the keyboard state.
_ClearKeyboardState(vkey, scanCode);
e.Handled(true);
}
// Method Description:
// - Get the modifier keys that are currently pressed. This can be used to
// find out which modifiers (ctrl, alt, shift) are pressed in events that
// don't necessarily include that state.
// - This is a copy of TermControl::_GetPressedModifierKeys.
// Return Value:
// - The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
ControlKeyStates TerminalPage::_GetPressedModifierKeys() noexcept
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
// DONT USE
// != CoreVirtualKeyStates::None
// OR
// == CoreVirtualKeyStates::Down
// Sometimes with the key down, the state is Down | Locked.
// Sometimes with the key up, the state is Locked.
// IsFlagSet(Down) is the only correct solution.
struct KeyModifier
{
VirtualKey vkey;
ControlKeyStates flags;
};
constexpr std::array<KeyModifier, 7> modifiers{ {
{ VirtualKey::RightMenu, ControlKeyStates::RightAltPressed },
{ VirtualKey::LeftMenu, ControlKeyStates::LeftAltPressed },
{ VirtualKey::RightControl, ControlKeyStates::RightCtrlPressed },
{ VirtualKey::LeftControl, ControlKeyStates::LeftCtrlPressed },
{ VirtualKey::Shift, ControlKeyStates::ShiftPressed },
{ VirtualKey::RightWindows, ControlKeyStates::RightWinPressed },
{ VirtualKey::LeftWindows, ControlKeyStates::LeftWinPressed },
} };
ControlKeyStates flags;
for (const auto& mod : modifiers)
{
const auto state = window.GetKeyState(mod.vkey);
const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
if (isDown)
{
CommandPalette().Visibility(Visibility::Collapsed);
flags |= mod.flags;
}
_actionDispatch->DoAction(cmd.ActionAndArgs());
e.Handled(true);
}
return flags;
}
// Method Description:
// - Discards currently pressed dead keys.
// - This is a copy of TermControl::_ClearKeyboardState.
// Arguments:
// - vkey: The vkey of the key pressed.
// - scanCode: The scan code of the key pressed.
void TerminalPage::_ClearKeyboardState(const WORD vkey, const WORD scanCode) noexcept
{
std::array<BYTE, 256> keyState;
if (!GetKeyboardState(keyState.data()))
{
return;
}
// As described in "Sometimes you *want* to interfere with the keyboard's state buffer":
// http://archives.miloush.net/michkap/archive/2006/09/10/748775.html
// > "The key here is to keep trying to pass stuff to ToUnicode until -1 is not returned."
std::array<wchar_t, 16> buffer;
while (ToUnicodeEx(vkey, scanCode, keyState.data(), buffer.data(), gsl::narrow_cast<int>(buffer.size()), 0b1, nullptr) < 0)
{
}
}
@@ -1597,7 +1723,10 @@ namespace winrt::TerminalApp::implementation
// the control here instead.
if (_startupState == StartupState::Initialized)
{
_GetActiveControl().Focus(FocusState::Programmatic);
if (const auto control = _GetActiveControl())
{
control.Focus(FocusState::Programmatic);
}
}
}
CATCH_LOG();
@@ -2478,9 +2607,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::ToggleFullscreen()
{
_isFullscreen = !_isFullscreen;
_UpdateTabView();
_FullscreenChangedHandlers(*this, nullptr);
SetFullscreen(!_isFullscreen);
}
// Method Description:
@@ -2697,6 +2824,17 @@ namespace winrt::TerminalApp::implementation
return _isAlwaysOnTop;
}
void TerminalPage::SetFullscreen(bool newFullscreen)
{
if (_isFullscreen == newFullscreen)
{
return;
}
_isFullscreen = newFullscreen;
_UpdateTabView();
_FullscreenChangedHandlers(*this, nullptr);
}
HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection)
{
// We need to be on the UI thread in order for _OpenNewTab to run successfully.

View File

@@ -14,12 +14,17 @@
static constexpr uint32_t DefaultRowsToScroll{ 3 };
static constexpr std::wstring_view TabletInputServiceKey{ L"TabletInputService" };
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
class TabTests;
class SettingsTests;
};
}
namespace Microsoft::Terminal::Core
{
class ControlKeyStates;
}
namespace winrt::TerminalApp::implementation
{
@@ -83,6 +88,7 @@ namespace winrt::TerminalApp::implementation
bool FocusMode() const;
bool Fullscreen() const;
bool AlwaysOnTop() const;
void SetFullscreen(bool);
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
@@ -223,6 +229,8 @@ namespace winrt::TerminalApp::implementation
void _ThirdPartyNoticesOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _KeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;
static void _ClearKeyboardState(const WORD vkey, const WORD scanCode) noexcept;
void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap) noexcept;
void _RegisterActionCallbacks();

View File

@@ -15,6 +15,7 @@
<Grid x:Name="Root"
Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
@@ -22,8 +23,50 @@
<local:TabRowControl x:Name="TabRow"
Grid.Row="0" />
<StackPanel Grid.Row="1">
<mux:InfoBar x:Name="KeyboardServiceWarningInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"
Severity="Warning">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_KeyboardServiceWarningInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
<mux:InfoBar x:Name="CloseOnExitInfoBar"
x:Uid="CloseOnExitInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Severity="Informational">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_CloseOnExitInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
<mux:InfoBar x:Name="SetAsDefaultInfoBar"
x:Uid="SetAsDefaultInfoBar"
x:Load="False"
CloseButtonClick="_SetAsDefaultDismissHandler"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Severity="Informational">
<mux:InfoBar.ActionButton>
<HyperlinkButton x:Uid="SetAsDefaultTip_OpenSettingsLink"
Click="_SetAsDefaultOpenSettingsHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
</StackPanel>
<Grid x:Name="TabContent"
Grid.Row="1"
Grid.Row="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
@@ -110,53 +153,11 @@
</ContentDialog>
<local:CommandPalette x:Name="CommandPalette"
Grid.Row="1"
Grid.Row="2"
VerticalAlignment="Stretch"
PreviewKeyDown="_KeyDownHandler"
Visibility="Collapsed" />
<StackPanel>
<mux:InfoBar x:Name="KeyboardServiceWarningInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"
Severity="Warning">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_KeyboardServiceWarningInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
<mux:InfoBar x:Name="CloseOnExitInfoBar"
x:Uid="CloseOnExitInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Severity="Informational">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_CloseOnExitInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
<mux:InfoBar x:Name="SetAsDefaultInfoBar"
x:Uid="SetAsDefaultInfoBar"
x:Load="False"
CloseButtonClick="_SetAsDefaultDismissHandler"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Severity="Informational">
<mux:InfoBar.ActionButton>
<HyperlinkButton x:Uid="SetAsDefaultTip_OpenSettingsLink"
Click="_SetAsDefaultOpenSettingsHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
</StackPanel>
<!--
A TeachingTip with IsLightDismissEnabled="True" will immediately
dismiss itself if the window is unfocused (In Xaml Islands). This is

View File

@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// TitlebarControl.xaml.cpp
// Implementation of the TitlebarControl class
//
@@ -24,6 +23,14 @@ namespace winrt::TerminalApp::implementation
MinMaxCloseControl().CloseClick({ this, &TitlebarControl::Close_Click });
}
double TitlebarControl::CaptionButtonWidth()
{
// Divide by three, since we know there are only three buttons. When
// Windows 12 comes along and adds another, we can update this /s
static double width{ MinMaxCloseControl().ActualWidth() / 3.0 };
return width;
}
IInspectable TitlebarControl::Content()
{
return ContentRoot().Content();
@@ -93,4 +100,48 @@ namespace winrt::TerminalApp::implementation
{
MinMaxCloseControl().SetWindowVisualState(visualState);
}
// GH#9443: HoverButton, PressButton, ClickButton and ReleaseButtons are all
// used to manually interact with the buttons, in the same way that XAML
// would normally send events.
void TitlebarControl::HoverButton(CaptionButton button)
{
MinMaxCloseControl().HoverButton(button);
}
void TitlebarControl::PressButton(CaptionButton button)
{
MinMaxCloseControl().PressButton(button);
}
winrt::fire_and_forget TitlebarControl::ClickButton(CaptionButton button)
{
// GH#8587: Handle this on the _next_ pass of the UI thread. If we
// handle this immediately, then we'll accidentally leave the button in
// the "Hovered" state when we minimize. This will leave the button
// visibly hovered in the taskbar preview for our window.
auto weakThis{ get_weak() };
co_await MinMaxCloseControl().Dispatcher();
if (auto self{ weakThis.get() })
{
// Just handle this in the same way we would if the button were
// clicked normally.
switch (button)
{
case CaptionButton::Minimize:
Minimize_Click(nullptr, nullptr);
break;
case CaptionButton::Maximize:
Maximize_Click(nullptr, nullptr);
break;
case CaptionButton::Close:
Close_Click(nullptr, nullptr);
break;
}
}
}
void TitlebarControl::ReleaseButtons()
{
MinMaxCloseControl().ReleaseButtons();
}
}

View File

@@ -1,14 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// Declaration of the MainUserControl class.
//
#pragma once
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "TitlebarControl.g.h"
namespace winrt::TerminalApp::implementation
@@ -17,6 +11,12 @@ namespace winrt::TerminalApp::implementation
{
TitlebarControl(uint64_t handle);
void HoverButton(CaptionButton button);
void PressButton(CaptionButton button);
winrt::fire_and_forget ClickButton(CaptionButton button);
void ReleaseButtons();
double CaptionButtonWidth();
IInspectable Content();
void Content(IInspectable content);

View File

@@ -10,11 +10,25 @@ namespace TerminalApp
WindowVisualStateIconified
};
// For simplicity, make sure that these are the same values as the ones used
// by messages like WM_NCHITTEST
enum CaptionButton {
Minimize = 8, // HTMINBUTTON
Maximize = 9, // HTMAXBUTTON
Close = 20 // HTCLOSE
};
[default_interface] runtimeclass TitlebarControl : Windows.UI.Xaml.Controls.Grid
{
TitlebarControl(UInt64 parentWindowHandle);
void SetWindowVisualState(WindowVisualState visualState);
void HoverButton(CaptionButton button);
void PressButton(CaptionButton button);
void ClickButton(CaptionButton button);
void ReleaseButtons();
Double CaptionButtonWidth { get; };
IInspectable Content;
Windows.UI.Xaml.Controls.Border DragBar { get; };
}

View File

@@ -429,8 +429,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// EXIT POINT
const auto hr = wil::ResultFromCaughtException();
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int
winrt::hstring failureText{ fmt::format(std::wstring_view{ RS_(L"ProcessFailedToLaunch") },
fmt::format(_errorFormat, hr),
fmt::format(_errorFormat, static_cast<unsigned int>(hr)),
_commandline) };
_TerminalOutputHandlers(failureText);
@@ -457,6 +458,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
try
{
// GH#11556 - make sure to format the error code to this string as an UNSIGNED int
winrt::hstring exitText{ fmt::format(std::wstring_view{ RS_(L"ProcessExited") }, fmt::format(_errorFormat, status)) };
_TerminalOutputHandlers(L"\r\n");
_TerminalOutputHandlers(exitText);

View File

@@ -753,12 +753,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_HidePointerCursorHandlers(*this, nullptr);
const auto ch = e.Character();
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
const auto keyStatus = e.KeyStatus();
const auto scanCode = gsl::narrow_cast<WORD>(keyStatus.ScanCode);
auto modifiers = _GetPressedModifierKeys();
if (e.KeyStatus().IsExtendedKey)
if (keyStatus.IsExtendedKey)
{
modifiers |= ControlKeyStates::EnhancedKey;
}
const bool handled = _core.SendCharEvent(ch, scanCode, modifiers);
e.Handled(handled);
}
@@ -853,6 +856,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto scanCode = gsl::narrow_cast<WORD>(keyStatus.ScanCode);
auto modifiers = _GetPressedModifierKeys();
if (keyStatus.IsExtendedKey)
{
modifiers |= ControlKeyStates::EnhancedKey;
}
// GH#11076:
// For some weird reason we sometimes receive a WM_KEYDOWN
// message without vkey or scanCode if a user drags a tab.
@@ -886,11 +894,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
if (keyStatus.IsExtendedKey)
{
modifiers |= ControlKeyStates::EnhancedKey;
}
// Alt-Numpad# input will send us a character once the user releases
// Alt, so we should be ignoring the individual keydowns. The character
// will be sent through the TSFInputControl. See GH#1401 for more
@@ -968,7 +971,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Arguments:
// - vkey: The vkey of the key pressed.
// - scanCode: The scan code of the key pressed.
void TermControl::_ClearKeyboardState(const WORD vkey, const WORD scanCode) const noexcept
void TermControl::_ClearKeyboardState(const WORD vkey, const WORD scanCode) noexcept
{
std::array<BYTE, 256> keyState;
if (!GetKeyboardState(keyState.data()))
@@ -2042,7 +2045,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// don't necessarily include that state.
// Return Value:
// - The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
ControlKeyStates TermControl::_GetPressedModifierKeys() const
ControlKeyStates TermControl::_GetPressedModifierKeys() noexcept
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
// DONT USE

View File

@@ -250,9 +250,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _UpdateAutoScroll(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
void _KeyHandler(Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e, const bool keyDown);
::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() const;
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;
bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const;
void _ClearKeyboardState(const WORD vkey, const WORD scanCode) const noexcept;
static void _ClearKeyboardState(const WORD vkey, const WORD scanCode) noexcept;
bool _TrySendKeyEvent(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
const til::point _toTerminalOrigin(winrt::Windows::Foundation::Point cursorPosition);

View File

@@ -220,7 +220,7 @@
<TextBox IsEnabled="{x:Bind local:Converters.StringsAreNotEqual('desktopWallpaper', Appearance.BackgroundImagePath), Mode=OneWay}"
IsSpellCheckEnabled="False"
Style="{StaticResource TextBoxSettingStyle}"
Text="{x:Bind local:Converters.StringFallBackToEmptyString('desktopWallpaper', Appearance.BackgroundImagePath), Mode=TwoWay, BindBack=Appearance.SetBackgroundImagePath}" />
Text="{x:Bind local:Converters.StringOrEmptyIfPlaceholder('desktopWallpaper', Appearance.BackgroundImagePath), Mode=TwoWay, BindBack=Appearance.SetBackgroundImagePath}" />
<Button x:Uid="Profile_BackgroundImageBrowse"
Click="BackgroundImage_Click"
IsEnabled="{x:Bind local:Converters.StringsAreNotEqual('desktopWallpaper', Appearance.BackgroundImagePath), Mode=OneWay}"

View File

@@ -1,4 +1,4 @@
#include "pch.h"
#include "pch.h"
#include "Converters.h"
#if __has_include("Converters.g.cpp")
#include "Converters.g.cpp"
@@ -99,8 +99,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
return value.empty() ? winrt::Windows::UI::Xaml::Visibility::Collapsed : winrt::Windows::UI::Xaml::Visibility::Visible;
}
winrt::hstring Converters::StringFallBackToEmptyString(winrt::hstring expected, winrt::hstring actual)
// Method Description:
// - Returns the value string, unless it matches the placeholder in which case the empty string.
// Arguments:
// - placeholder - the placeholder string.
// - value - the value string.
// Return Value:
// - The value string, unless it matches the placeholder in which case the empty string.
winrt::hstring Converters::StringOrEmptyIfPlaceholder(winrt::hstring placeholder, winrt::hstring value)
{
return expected == actual ? expected : L"";
return placeholder == value ? L"" : value;
}
}

View File

@@ -21,7 +21,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static double PercentageValueToPercentage(double value);
static bool StringsAreNotEqual(winrt::hstring expected, winrt::hstring actual);
static winrt::Windows::UI::Xaml::Visibility StringNotEmptyToVisibility(winrt::hstring value);
static winrt::hstring StringFallBackToEmptyString(winrt::hstring expected, winrt::hstring actual);
static winrt::hstring StringOrEmptyIfPlaceholder(winrt::hstring placeholder, winrt::hstring value);
};
}

View File

@@ -20,6 +20,6 @@ namespace Microsoft.Terminal.Settings.Editor
static Double PercentageValueToPercentage(Double value);
static Boolean StringsAreNotEqual(String expected, String actual);
static Windows.UI.Xaml.Visibility StringNotEmptyToVisibility(String value);
static String StringFallBackToEmptyString(String expected, String actual);
static String StringOrEmptyIfPlaceholder(String placeholder, String value);
}
}

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "Profiles.h"
#include "PreviewConnection.h"
#include "Profiles.g.cpp"
#include "EnumEntry.h"
@@ -10,6 +11,54 @@
#include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"
// This function is a copy of DxFontInfo::_NearbyCollection() with
// * the call to DxFontInfo::s_GetNearbyFonts() inlined
// * checkForUpdates for GetSystemFontCollection() set to true
static wil::com_ptr<IDWriteFontCollection1> NearbyCollection(IDWriteFactory* dwriteFactory)
{
// The convenience interfaces for loading fonts from files
// are only available on Windows 10+.
wil::com_ptr<IDWriteFactory6> factory6;
// wil's query() facilities don't work inside WinRT land at the moment.
// They produce a compilation error due to IUnknown and winrt::Windows::Foundation::IUnknown being ambiguous.
if (!SUCCEEDED(dwriteFactory->QueryInterface(__uuidof(IDWriteFactory6), factory6.put_void())))
{
return nullptr;
}
wil::com_ptr<IDWriteFontCollection1> systemFontCollection;
THROW_IF_FAILED(factory6->GetSystemFontCollection(false, systemFontCollection.addressof(), true));
wil::com_ptr<IDWriteFontSet> systemFontSet;
THROW_IF_FAILED(systemFontCollection->GetFontSet(systemFontSet.addressof()));
wil::com_ptr<IDWriteFontSetBuilder2> fontSetBuilder2;
THROW_IF_FAILED(factory6->CreateFontSetBuilder(fontSetBuilder2.addressof()));
THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.get()));
{
const std::filesystem::path module{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
const auto folder{ module.parent_path() };
for (const auto& p : std::filesystem::directory_iterator(folder))
{
if (til::ends_with(p.path().native(), L".ttf"))
{
fontSetBuilder2->AddFontFile(p.path().c_str());
}
}
}
wil::com_ptr<IDWriteFontSet> fontSet;
THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(fontSet.addressof()));
wil::com_ptr<IDWriteFontCollection1> fontCollection;
THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.get(), &fontCollection));
return fontCollection;
}
using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
@@ -107,8 +156,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
reinterpret_cast<::IUnknown**>(factory.put())));
// get the font collection; subscribe to updates
com_ptr<IDWriteFontCollection> fontCollection;
THROW_IF_FAILED(factory->GetSystemFontCollection(fontCollection.put(), TRUE));
const auto fontCollection = NearbyCollection(factory.get());
for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i)
{

View File

@@ -33,7 +33,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// only works on Win11. So we'll use that.
//
// Remove when we can remove the rest of GH#11285
if (value < 100.0 && winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings::IsDefaultTerminalAvailable())
if (value < 100.0 &&
!winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings::IsDefaultTerminalAvailable())
{
UseAcrylic(true);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// pch.h
@@ -51,8 +51,7 @@
#include <shlobj.h>
#include <shobjidl_core.h>
#include <dwrite.h>
#include <dwrite_1.h>
#include <dwrite_3.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"

View File

@@ -57,6 +57,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void ApplyRuntimeInitialSettings();
void MergeInboxIntoUserSettings();
void FindFragmentsAndMergeIntoUserSettings();
void MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content);
void FinalizeLayering();
bool DisableDeletedProfiles();
@@ -82,7 +83,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void _parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
static JsonSettings _parseJson(const std::string_view& content);
static winrt::com_ptr<implementation::Profile> _parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson);
void _appendProfile(winrt::com_ptr<implementation::Profile>&& profile, ParsedSettings& settings);
void _appendProfile(winrt::com_ptr<Profile>&& profile, const winrt::guid& guid, ParsedSettings& settings);
static void _addParentProfile(const winrt::com_ptr<implementation::Profile>& profile, ParsedSettings& settings);
void _executeGenerator(const IDynamicProfileGenerator& generator);

View File

@@ -207,26 +207,6 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
{
const auto content = ReadUTF8File(fragmentExt.path());
_parseFragment(source, content, fragmentSettings);
for (const auto& fragmentProfile : fragmentSettings.profiles)
{
if (const auto updates = fragmentProfile->Updates(); updates != winrt::guid{})
{
if (const auto it = userSettings.profilesByGuid.find(updates); it != userSettings.profilesByGuid.end())
{
it->second->InsertParent(0, fragmentProfile);
}
}
else
{
_addParentProfile(fragmentProfile, userSettings);
}
}
for (const auto& kv : fragmentSettings.globals->ColorSchemes())
{
userSettings.globals->AddColorScheme(kv.Value());
}
}
CATCH_LOG();
}
@@ -289,6 +269,15 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
}
}
// See FindFragmentsAndMergeIntoUserSettings.
// This function does the same, but for a single given JSON blob and source
// and at the time of writing is used for unit tests only.
void SettingsLoader::MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content)
{
ParsedSettings fragmentSettings;
_parseFragment(source, content, fragmentSettings);
}
// Call this method before passing SettingsLoader to the CascadiaSettings constructor.
// It layers all remaining objects onto each other (those that aren't covered
// by MergeInboxIntoUserSettings/FindFragmentsAndMergeIntoUserSettings).
@@ -475,7 +464,7 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source
// GH#9962: Discard Guid-less, Name-less profiles.
if (profile->HasGuid())
{
_appendProfile(std::move(profile), settings);
_appendProfile(std::move(profile), profile->Guid(), settings);
}
}
}
@@ -517,14 +506,37 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str
auto profile = _parseProfile(OriginTag::Fragment, source, profileJson);
// GH#9962: Discard Guid-less, Name-less profiles, but...
// allow ones with an Updates field, as those are special for fragments.
if (profile->HasGuid() || profile->Updates() != winrt::guid{})
// We need to make sure to only call Guid() if HasGuid() is true,
// as Guid() will dynamically generate a return value otherwise.
const auto guid = profile->HasGuid() ? profile->Guid() : profile->Updates();
if (guid != winrt::guid{})
{
_appendProfile(std::move(profile), settings);
_appendProfile(std::move(profile), guid, settings);
}
}
CATCH_LOG()
}
}
for (const auto& fragmentProfile : settings.profiles)
{
if (const auto updates = fragmentProfile->Updates(); updates != winrt::guid{})
{
if (const auto it = userSettings.profilesByGuid.find(updates); it != userSettings.profilesByGuid.end())
{
it->second->InsertParent(0, fragmentProfile);
}
}
else
{
_addParentProfile(fragmentProfile, userSettings);
}
}
for (const auto& kv : settings.globals->ColorSchemes())
{
userSettings.globals->AddColorScheme(kv.Value());
}
}
SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view& content)
@@ -564,11 +576,11 @@ winrt::com_ptr<Profile> SettingsLoader::_parseProfile(const OriginTag origin, co
// Adds a profile to the ParsedSettings instance. Takes ownership of the profile.
// It ensures no duplicate GUIDs are added to the ParsedSettings instance.
void SettingsLoader::_appendProfile(winrt::com_ptr<Profile>&& profile, ParsedSettings& settings)
void SettingsLoader::_appendProfile(winrt::com_ptr<Profile>&& profile, const winrt::guid& guid, ParsedSettings& settings)
{
// FYI: The static_cast ensures we don't move the profile into
// `profilesByGuid`, even though we still need it later for `profiles`.
if (settings.profilesByGuid.emplace(profile->Guid(), static_cast<const winrt::com_ptr<Profile>&>(profile)).second)
if (settings.profilesByGuid.emplace(guid, static_cast<const winrt::com_ptr<Profile>&>(profile)).second)
{
settings.profiles.emplace_back(profile);
}

View File

@@ -40,7 +40,7 @@ std::wstring VsDevCmdGenerator::GetProfileCommandLine(const VsSetupConfiguration
{
std::wstring commandLine;
commandLine.reserve(256);
commandLine.append(LR"("cmd.exe /k ")");
commandLine.append(LR"(cmd.exe /k ")");
commandLine.append(GetDevCmdScriptPath(instance));
#if defined(_M_ARM64)
commandLine.append(LR"(" -arch=arm64 -host_arch=x64)");

View File

@@ -84,6 +84,7 @@ AppHost::AppHost() noexcept :
_window->WindowMoved({ this, &AppHost::_WindowMoved });
_window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed });
_window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop());
_window->ShouldExitFullscreen({ &_logic, &winrt::TerminalApp::AppLogic::RequestExitFullscreen });
_window->MakeWindow();
_GetWindowLayoutRequestedToken = _windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& args) {
@@ -401,9 +402,14 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se
_HideNotificationIconRequested();
}
// We don't want to try to save layouts if we are about to close
// We don't want to try to save layouts if we are about to close.
_getWindowLayoutThrottler.reset();
_windowManager.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken);
// We also don't need to update any of our bookkeeping on how many
// windows are open.
_windowManager.WindowCreated(_WindowCreatedToken);
_windowManager.WindowClosed(_WindowClosedToken);
// Remove ourself from the list of peasants so that we aren't included in
// any future requests. This will also mean we block until any existing
// event handler finishes.
@@ -811,11 +817,17 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s
// and subscribe for updates if there are any changes to that number.
_logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants());
_windowManager.WindowCreated([this](auto&&, auto&&) {
_getWindowLayoutThrottler.value()();
_WindowCreatedToken = _windowManager.WindowCreated([this](auto&&, auto&&) {
if (_getWindowLayoutThrottler) {
_getWindowLayoutThrottler.value()();
}
_logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); });
_windowManager.WindowClosed([this](auto&&, auto&&) {
_getWindowLayoutThrottler.value()();
_WindowClosedToken = _windowManager.WindowClosed([this](auto&&, auto&&) {
if (_getWindowLayoutThrottler)
{
_getWindowLayoutThrottler.value()();
}
_logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants());
});

View File

@@ -115,4 +115,6 @@ private:
winrt::event_token _ShowNotificationIconContextMenuToken;
winrt::event_token _NotificationIconMenuItemSelectedToken;
winrt::event_token _GetWindowLayoutRequestedToken;
winrt::event_token _WindowCreatedToken;
winrt::event_token _WindowClosedToken;
};

View File

@@ -603,6 +603,15 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
}
break;
}
case WM_SYSCOMMAND:
{
if (wparam == SC_RESTORE && _fullscreen)
{
_ShouldExitFullscreenHandlers();
return 0;
}
break;
}
case WM_MENUCOMMAND:
{
_NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam);

View File

@@ -65,6 +65,7 @@ public:
WINRT_CALLBACK(NotifyShowNotificationIconContextMenu, winrt::delegate<void(til::point)>);
WINRT_CALLBACK(NotifyNotificationIconMenuItemSelected, winrt::delegate<void(HMENU, UINT)>);
WINRT_CALLBACK(NotifyReAddNotificationIcon, winrt::delegate<void()>);
WINRT_CALLBACK(ShouldExitFullscreen, winrt::delegate<void()>);
WINRT_CALLBACK(WindowMoved, winrt::delegate<void()>);

View File

@@ -87,51 +87,203 @@ void NonClientIslandWindow::MakeWindow() noexcept
THROW_HR_IF_NULL(E_UNEXPECTED, _dragBarWindow);
}
// Function Description:
// - The window procedure for the drag bar forwards clicks on its client area to its parent as non-client clicks.
LRESULT NonClientIslandWindow::_InputSinkMessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
LRESULT NonClientIslandWindow::_dragBarNcHitTest(const til::point& pointer)
{
std::optional<UINT> nonClientMessage{ std::nullopt };
RECT rcParent = GetWindowRect();
// The size of the buttons doesn't change over the life of the application.
const auto buttonWidthInDips{ _titlebar.CaptionButtonWidth() };
// translate WM_ messages on the window to WM_NC* on the top level window
// However, the DPI scaling might, so get the updated size of the buttons in pixels
const auto buttonWidthInPixels{ buttonWidthInDips * GetCurrentDpiScale() };
// make sure to account for the width of the window frame!
const til::rectangle nonClientFrame{ GetNonClientFrame(_currentDpi) };
const auto rightBorder{ rcParent.right - nonClientFrame.right<int>() };
// From the right to the left,
// * are we in the close button?
// * the maximize button?
// * the minimize button?
// If we're not, then we're in either the top resize border, or just
// generally in the titlebar.
if ((rightBorder - pointer.x()) < (buttonWidthInPixels))
{
return HTCLOSE;
}
else if ((rightBorder - pointer.x()) < (buttonWidthInPixels * 2))
{
return HTMAXBUTTON;
}
else if ((rightBorder - pointer.x()) < (buttonWidthInPixels * 3))
{
return HTMINBUTTON;
}
else
{
// If we're not on a caption button, then check if we're on the top
// border. If we're not on the top border, then we're just generally in
// the caption area.
const auto resizeBorderHeight = _GetResizeHandleHeight();
const auto isOnResizeBorder = pointer.y() < rcParent.top + resizeBorderHeight;
return isOnResizeBorder ? HTTOP : HTCAPTION;
}
}
// Function Description:
// - The window procedure for the drag bar forwards clicks on its client area to
// its parent as non-client clicks.
// - BODGY: It also _manually_ handles the caption buttons. They exist in the
// titlebar, and work reasonably well with just XAML, if the drag bar isn't
// covering them.
// - However, to get snap layout support (GH#9443), we need to actually return
// HTMAXBUTTON where the maximize button is. If the drag bar doesn't cover the
// caption buttons, then the core input site (which takes up the entirety of
// the XAML island) will steal the WM_NCHITTEST before we get a chance to
// handle it.
// - So, the drag bar covers the caption buttons, and manually handles hovering
// and pressing them when needed. This gives the impression that they're
// getting input as they normally would, even if they're not _really_ getting
// input via XAML.
LRESULT NonClientIslandWindow::_InputSinkMessageHandler(UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept
{
switch (message)
{
case WM_LBUTTONDOWN:
nonClientMessage = WM_NCLBUTTONDOWN;
break;
case WM_LBUTTONDBLCLK:
nonClientMessage = WM_NCLBUTTONDBLCLK;
break;
case WM_LBUTTONUP:
nonClientMessage = WM_NCLBUTTONUP;
break;
case WM_RBUTTONDOWN:
nonClientMessage = WM_NCRBUTTONDOWN;
break;
case WM_RBUTTONDBLCLK:
nonClientMessage = WM_NCRBUTTONDBLCLK;
break;
case WM_RBUTTONUP:
nonClientMessage = WM_NCRBUTTONUP;
break;
}
if (nonClientMessage.has_value())
case WM_NCHITTEST:
{
const POINT clientPt{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
POINT screenPt{ clientPt };
if (ClientToScreen(_dragBarWindow.get(), &screenPt))
// Try to determine what part of the window is being hovered here. This
// is absolutely critical to making sure Snap Layouts (GH#9443) works!
return _dragBarNcHitTest(til::point{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) });
}
break;
case WM_NCMOUSEMOVE:
// When we get this message, it's because the mouse moved when it was
// over somewhere we said was the non-client area.
//
// We'll use this to communicate state to the title bar control, so that
// it can update its visuals.
// - If we're over a button, hover it.
// - If we're over _anything else_, stop hovering the buttons.
switch (wparam)
{
case HTTOP:
case HTCAPTION:
{
_titlebar.ReleaseButtons();
// Pass caption-related nonclient messages to the parent window.
// Make sure to do this for the HTTOP, which is the top resize
// border, so we can resize the window on the top.
auto parentWindow{ GetHandle() };
const LPARAM newLparam = MAKELPARAM(screenPt.x, screenPt.y);
// Hit test the parent window at the screen coordinates the user clicked in the drag input sink window,
// then pass that click through as an NC click in that location.
const LRESULT hitTest{ SendMessage(parentWindow, WM_NCHITTEST, 0, newLparam) };
SendMessage(parentWindow, nonClientMessage.value(), hitTest, newLparam);
return 0;
return SendMessage(parentWindow, message, wparam, lparam);
}
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
_titlebar.HoverButton(static_cast<winrt::TerminalApp::CaptionButton>(wparam));
break;
default:
_titlebar.ReleaseButtons();
}
// If we haven't previously asked for mouse tracking, request mouse
// tracking. We need to do this so we can get the WM_NCMOUSELEAVE
// message when the mouse leave the titlebar. Otherwise, we won't always
// get that message (especially if the user moves the mouse _real
// fast_).
if (!_trackingMouse &&
(wparam == HTMINBUTTON || wparam == HTMAXBUTTON || wparam == HTCLOSE))
{
TRACKMOUSEEVENT ev{};
ev.cbSize = sizeof(TRACKMOUSEEVENT);
// TME_NONCLIENT is absolutely critical here. In my experimentation,
// we'd get WM_MOUSELEAVE messages after just a HOVER_DEFAULT
// timeout even though we're not requesting TME_HOVER, which kinda
// ruined the whole point of this.
ev.dwFlags = TME_LEAVE | TME_NONCLIENT;
ev.hwndTrack = _dragBarWindow.get();
ev.dwHoverTime = HOVER_DEFAULT; // we don't _really_ care about this.
LOG_IF_WIN32_BOOL_FALSE(TrackMouseEvent(&ev));
_trackingMouse = true;
}
break;
case WM_NCMOUSELEAVE:
case WM_MOUSELEAVE:
// When the mouse leaves the drag rect, make sure to dismiss any hover.
_titlebar.ReleaseButtons();
_trackingMouse = false;
break;
// NB: *Shouldn't be forwarding these* when they're not over the caption
// because they can inadvertently take action using the system's default
// metrics instead of our own.
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONDBLCLK:
// Manual handling for mouse clicks in the drag bar. If it's in a
// caption button, then tell the titlebar to "press" the button, which
// should change its visual state.
//
// If it's not in a caption button, then just forward the message along
// to the root HWND. Make sure to do this for the HTTOP, which is the
// top resize border.
switch (wparam)
{
case HTTOP:
case HTCAPTION:
{
// Pass caption-related nonclient messages to the parent window.
auto parentWindow{ GetHandle() };
return SendMessage(parentWindow, message, wparam, lparam);
}
// The buttons won't work as you'd expect; we need to handle those
// ourselves.
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
_titlebar.PressButton(static_cast<winrt::TerminalApp::CaptionButton>(wparam));
break;
}
return 0;
case WM_NCLBUTTONUP:
// Manual handling for mouse RELEASES in the drag bar. If it's in a
// caption button, then manually handle what we'd expect for that button.
//
// If it's not in a caption button, then just forward the message along
// to the root HWND.
switch (wparam)
{
case HTTOP:
case HTCAPTION:
{
// Pass caption-related nonclient messages to the parent window.
// The buttons won't work as you'd expect; we need to handle those ourselves.
auto parentWindow{ GetHandle() };
return SendMessage(parentWindow, message, wparam, lparam);
}
break;
// If we do find a button, then tell the titlebar to raise the same
// event that would be raised if it were "tapped"
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
_titlebar.ReleaseButtons();
_titlebar.ClickButton(static_cast<winrt::TerminalApp::CaptionButton>(wparam));
break;
}
return 0;
// Make sure to pass along right-clicks in this region to our parent window
// - we don't need to handle these.
case WM_NCRBUTTONDOWN:
case WM_NCRBUTTONDBLCLK:
case WM_NCRBUTTONUP:
auto parentWindow{ GetHandle() };
return SendMessage(parentWindow, message, wparam, lparam);
}
return DefWindowProc(_dragBarWindow.get(), message, wparam, lparam);
@@ -279,10 +431,17 @@ RECT NonClientIslandWindow::_GetDragAreaRect() const noexcept
{
const auto scale = GetCurrentDpiScale();
const auto transform = _dragBar.TransformToVisual(_rootGrid);
// GH#9443: Previously, we'd only extend the drag bar from the left of
// the tabs to the right of the caption buttons. Now, we're extending it
// all the way to the right side of the window, covering the caption
// buttons. We'll manually handle input to those buttons, to make it
// seem like they're still getting XAML input. We do this so we can get
// snap layout support for the maximize button.
const auto logicalDragBarRect = winrt::Windows::Foundation::Rect{
0.0f,
0.0f,
static_cast<float>(_dragBar.ActualWidth()),
static_cast<float>(_rootGrid.ActualWidth()),
static_cast<float>(_dragBar.ActualHeight())
};
const auto clientDragBarRect = transform.TransformBounds(logicalDragBarRect);
@@ -550,6 +709,7 @@ int NonClientIslandWindow::_GetResizeHandleHeight() const noexcept
// we didn't change them.
LPARAM lParam = MAKELONG(ptMouse.x, ptMouse.y);
const auto originalRet = DefWindowProc(_window.get(), WM_NCHITTEST, 0, lParam);
if (originalRet != HTCLIENT)
{
// If we're the quake window, suppress resizing on any side except the
@@ -843,7 +1003,11 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept
HPAINTBUFFER buf = BeginBufferedPaint(hdc.get(), &rcRest, BPBF_TOPDOWNDIB, &params, &opaqueDc);
if (!buf || !opaqueDc)
{
winrt::throw_last_error();
// MSFT:34673647 - BeginBufferedPaint can fail, but it probably
// shouldn't bring the whole Terminal down with it. So don't
// throw_last_error here.
LOG_LAST_ERROR();
return 0;
}
::FillRect(opaqueDc, &rcRest, _backgroundBrush.get());

View File

@@ -62,6 +62,7 @@ private:
winrt::Windows::UI::Xaml::ElementTheme _theme;
bool _isMaximized;
bool _trackingMouse{ false };
[[nodiscard]] static LRESULT __stdcall _StaticInputSinkWndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;
[[nodiscard]] LRESULT _InputSinkMessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;
@@ -71,6 +72,7 @@ private:
int _GetResizeHandleHeight() const noexcept;
RECT _GetDragAreaRect() const noexcept;
int _GetTopBorderHeight() const noexcept;
LRESULT _dragBarNcHitTest(const til::point& pointer);
[[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept override;
[[nodiscard]] LRESULT _OnNcCalcSize(const WPARAM wParam, const LPARAM lParam) noexcept;

View File

@@ -107,7 +107,7 @@
C4467: usage of ATL attributes is deprecated
Conhost code still uses ATL.
-->
<DisableSpecificWarnings>4103;4201;4312;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings>4103;4201;4312;4467;5105;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<PreprocessorDefinitions>_WINDOWS;EXTERNAL_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
@@ -117,6 +117,7 @@
<MinimalRebuild>false</MinimalRebuild>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<ControlFlowGuard>Guard</ControlFlowGuard>
<FloatingPointModel>Fast</FloatingPointModel>

View File

@@ -75,7 +75,7 @@
<ConformanceMode>true</ConformanceMode>
<UseStandardPreprocessor>true</UseStandardPreprocessor>
<AdditionalOptions>%(AdditionalOptions) /bigobj /Zc:twoPhase-</AdditionalOptions>
<DisableSpecificWarnings>5104;5105;28204;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings>5104;28204;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
</ClCompile>

View File

@@ -693,16 +693,16 @@ HRESULT GetConsoleCommandHistoryLengthImplHelper(const std::wstring_view exeName
const auto command = pCommandHistory->GetNth(i);
size_t cchCommand = command.size();
// This is the proposed length of the whole string.
size_t cchProposed;
RETURN_IF_FAILED(SizeTAdd(cchCommand, cchNull, &cchProposed));
// If we're counting how much multibyte space will be needed, trial convert the command string before we add.
if (!countInUnicode)
{
cchCommand = GetALengthFromW(codepage, command);
}
// This is the proposed length of the whole string.
size_t cchProposed;
RETURN_IF_FAILED(SizeTAdd(cchCommand, cchNull, &cchProposed));
// Accumulate the result
RETURN_IF_FAILED(SizeTAdd(cchNeeded, cchProposed, &cchNeeded));
}

View File

@@ -11,7 +11,7 @@
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<Midl Include="IConsoleHandoff.idl">
<!--
<!--
In Razzle, IDL files generate %FileName%.h
In Visual Studio, IDL files generate %FileName%_h.h
Visual Studio is easier to override than Razzle.
@@ -31,6 +31,7 @@
<ItemGroup>
<ClInclude Include="$(IntDir)\IConsoleHandoff.h" />
<ClInclude Include="$(IntDir)\ITerminalHandoff.h" />
<ClInclude Include="nodefaultlib_shim.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(IntDir)\dlldata.c" />
@@ -56,16 +57,26 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<CallingConvention>StdCall</CallingConvention>
<AdditionalIncludeDirectories>.;..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<CallingConvention Condition="'$(Platform)'!='ARM64'">StdCall</CallingConvention>
<!-- Must be Stdcall on all platforms to resolve _ObjectStublessClient3 -->
<PreprocessorDefinitions>REGISTER_PROXY_DLL;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<BufferSecurityCheck>false</BufferSecurityCheck>
<SDLCheck>false</SDLCheck>
<ForcedIncludeFiles>nodefaultlib_shim.h;%(ForcedIncludeFiles)</ForcedIncludeFiles>
</ClCompile>
<Link>
<ModuleDefinitionFile>OpenConsoleProxy.def</ModuleDefinitionFile>
<!--
Not depending on the CRT cuts binary size by half and prevents issues if this DLL
is copied elsewhere and executed outside of our app package without our bundled CRT present.
-->
<AdditionalDependencies />
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<EntryPointSymbol>DllMain</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />
</Project>
</Project>

View File

@@ -41,6 +41,9 @@
<ClInclude Include="$(IntDir)\ITerminalHandoff.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="nodefaultlib_shim.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="IConsoleHandoff.idl">

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include <guiddef.h>
#if !defined(_M_IX86) && !defined(_M_X64)
// ARM64 doesn't define a (__builtin_)memcmp function without CRT,
// but we need one to compile IID_GENERIC_CHECK_IID.
// Luckily we only ever use memcmp for IIDs.
#pragma function(memcmp)
inline int memcmp(const IID* a, const IID* b, size_t count)
{
(void)(count);
return 1 - InlineIsEqualGUID(a, b);
}
#endif

View File

@@ -96,6 +96,11 @@ bool DxFontInfo::GetFallback() const noexcept
return _didFallback;
}
IDWriteFontCollection* DxFontInfo::GetNearbyCollection() const noexcept
{
return _nearbyCollection.Get();
}
void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
const DWRITE_FONT_WEIGHT weight,
const DWRITE_FONT_STYLE style,
@@ -223,13 +228,11 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
// If the system collection missed, try the files sitting next to our binary.
if (withNearbyLookup && !familyExists)
{
auto&& nearbyCollection = _NearbyCollection(dwriteFactory);
// May be null on OS below Windows 10. If null, just skip the attempt.
if (nearbyCollection)
if (const auto nearbyCollection = _NearbyCollection(dwriteFactory))
{
nearbyCollection.As(&fontCollection);
THROW_IF_FAILED(fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists));
THROW_IF_FAILED(nearbyCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists));
fontCollection = nearbyCollection;
}
}
@@ -332,42 +335,48 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
// - dwriteFactory - The DWrite factory to use
// Return Value:
// - DirectWrite font collection. May be null if one cannot be created.
[[nodiscard]] const Microsoft::WRL::ComPtr<IDWriteFontCollection1>& DxFontInfo::_NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory) const
[[nodiscard]] IDWriteFontCollection* DxFontInfo::_NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory)
{
// Magic static so we only attempt to grovel the hard disk once no matter how many instances
// of the font collection itself we require.
static const auto knownPaths = s_GetNearbyFonts();
if (_nearbyCollection)
{
return _nearbyCollection.Get();
}
// The convenience interfaces for loading fonts from files
// are only available on Windows 10+.
// Don't try to look up if below that OS version.
static const bool s_isWindows10OrGreater = IsWindows10OrGreater();
if (s_isWindows10OrGreater && !_nearbyCollection)
::Microsoft::WRL::ComPtr<IDWriteFactory6> factory6;
if (FAILED(dwriteFactory->QueryInterface<IDWriteFactory6>(&factory6)))
{
// Factory3 has a convenience to get us a font set builder.
::Microsoft::WRL::ComPtr<IDWriteFactory3> factory3;
THROW_IF_FAILED(dwriteFactory->QueryInterface<IDWriteFactory3>(&factory3));
::Microsoft::WRL::ComPtr<IDWriteFontSetBuilder> fontSetBuilder;
THROW_IF_FAILED(factory3->CreateFontSetBuilder(&fontSetBuilder));
// Builder2 has a convenience to just feed in paths to font files.
::Microsoft::WRL::ComPtr<IDWriteFontSetBuilder2> fontSetBuilder2;
THROW_IF_FAILED(fontSetBuilder.As(&fontSetBuilder2));
for (auto& p : knownPaths)
{
fontSetBuilder2->AddFontFile(p.c_str());
}
::Microsoft::WRL::ComPtr<IDWriteFontSet> fontSet;
THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet));
THROW_IF_FAILED(factory3->CreateFontCollectionFromFontSet(fontSet.Get(), &_nearbyCollection));
return nullptr;
}
return _nearbyCollection;
::Microsoft::WRL::ComPtr<IDWriteFontCollection1> systemFontCollection;
THROW_IF_FAILED(factory6->GetSystemFontCollection(false, &systemFontCollection, 0));
::Microsoft::WRL::ComPtr<IDWriteFontSet> systemFontSet;
THROW_IF_FAILED(systemFontCollection->GetFontSet(&systemFontSet));
::Microsoft::WRL::ComPtr<IDWriteFontSetBuilder2> fontSetBuilder2;
THROW_IF_FAILED(factory6->CreateFontSetBuilder(&fontSetBuilder2));
THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.Get()));
// Magic static so we only attempt to grovel the hard disk once no matter how many instances
// of the font collection itself we require.
static const auto knownPaths = s_GetNearbyFonts();
for (auto& p : knownPaths)
{
fontSetBuilder2->AddFontFile(p.c_str());
}
::Microsoft::WRL::ComPtr<IDWriteFontSet> fontSet;
THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet));
::Microsoft::WRL::ComPtr<IDWriteFontCollection1> fontCollection;
THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.Get(), &fontCollection));
_nearbyCollection = fontCollection;
return _nearbyCollection.Get();
}
// Routine Description:
@@ -386,18 +395,11 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName,
const std::filesystem::path module{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
const auto folder{ module.parent_path() };
for (auto& p : std::filesystem::directory_iterator(folder))
for (const auto& p : std::filesystem::directory_iterator(folder))
{
if (p.is_regular_file())
if (til::ends_with(p.path().native(), L".ttf"))
{
auto extension = p.path().extension().wstring();
std::transform(extension.begin(), extension.end(), extension.begin(), std::towlower);
static constexpr std::wstring_view ttfExtension{ L".ttf" };
if (ttfExtension == extension)
{
paths.push_back(p);
}
paths.push_back(p.path());
}
}

View File

@@ -41,6 +41,8 @@ namespace Microsoft::Console::Render
bool GetFallback() const noexcept;
IDWriteFontCollection* GetNearbyCollection() const noexcept;
void SetFromEngine(const std::wstring_view familyName,
const DWRITE_FONT_WEIGHT weight,
const DWRITE_FONT_STYLE style,
@@ -57,11 +59,11 @@ namespace Microsoft::Console::Render
[[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null<IDWriteFontFamily*> const fontFamily,
std::wstring& localeName);
[[nodiscard]] const Microsoft::WRL::ComPtr<IDWriteFontCollection1>& _NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory) const;
[[nodiscard]] IDWriteFontCollection* _NearbyCollection(gsl::not_null<IDWriteFactory1*> dwriteFactory);
[[nodiscard]] static std::vector<std::filesystem::path> s_GetNearbyFonts();
mutable ::Microsoft::WRL::ComPtr<IDWriteFontCollection1> _nearbyCollection;
::Microsoft::WRL::ComPtr<IDWriteFontCollection> _nearbyCollection;
// The font name we should be looking for
std::wstring _familyName;

View File

@@ -894,11 +894,11 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
_glyphCell = actual.GetSize();
}
Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::_BuildTextFormat(const DxFontInfo fontInfo, const std::wstring_view localeName)
Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::_BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName)
{
Microsoft::WRL::ComPtr<IDWriteTextFormat> format;
THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontInfo.GetFamilyName().data(),
nullptr,
fontInfo.GetNearbyCollection(),
fontInfo.GetWeight(),
fontInfo.GetStyle(),
fontInfo.GetStretch(),

View File

@@ -122,7 +122,7 @@ namespace Microsoft::Console::Render
float _FontStretchToWidthAxisValue(DWRITE_FONT_STRETCH fontStretch) noexcept;
float _FontStyleToSlantFixedAxisValue(DWRITE_FONT_STYLE fontStyle) noexcept;
void _BuildFontRenderData(const FontInfoDesired& desired, FontInfo& actual, const int dpi);
Microsoft::WRL::ComPtr<IDWriteTextFormat> _BuildTextFormat(const DxFontInfo fontInfo, const std::wstring_view localeName);
Microsoft::WRL::ComPtr<IDWriteTextFormat> _BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName);
std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteTextFormat>> _textFormatMap;
std::unordered_map<FontAttributeMapKey, ::Microsoft::WRL::ComPtr<IDWriteFontFace1>> _fontFaceMap;

View File

@@ -18,21 +18,21 @@
<ClCompile Include="..\BoxDrawingEffect.cpp" />
<ClCompile Include="..\CustomTextLayout.cpp" />
<ClCompile Include="..\CustomTextRenderer.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\DxFontInfo.cpp" />
<ClCompile Include="..\DxFontRenderData.cpp" />
<ClCompile Include="..\DxRenderer.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\BoxDrawingEffect.h" />
<ClInclude Include="..\CustomTextLayout.h" />
<ClInclude Include="..\CustomTextRenderer.h" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\DxRenderer.hpp" />
<ClInclude Include="..\DxFontInfo.h" />
<ClInclude Include="..\DxFontRenderData.h" />
<ClInclude Include="..\DxRenderer.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\ScreenPixelShader.h" />
<ClInclude Include="..\ScreenVertexShader.h" />
</ItemGroup>

View File

@@ -4,24 +4,26 @@
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\BoxDrawingEffect.cpp" />
<ClCompile Include="..\CustomTextLayout.cpp" />
<ClCompile Include="..\CustomTextRenderer.cpp" />
<ClCompile Include="..\precomp.cpp" />
<ClCompile Include="..\DxFontInfo.cpp" />
<ClCompile Include="..\DxFontRenderData.cpp" />
<ClCompile Include="..\DxRenderer.cpp" />
<ClCompile Include="..\BoxDrawingEffect.cpp" />
<ClCompile Include="..\precomp.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\BoxDrawingEffect.h" />
<ClInclude Include="..\CustomTextLayout.h" />
<ClInclude Include="..\CustomTextRenderer.h" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\DxFontRenderData.h"/>
<ClInclude Include="..\DxFontInfo.h" />
<ClInclude Include="..\DxFontRenderData.h" />
<ClInclude Include="..\DxRenderer.hpp" />
<ClInclude Include="..\precomp.h" />
<ClInclude Include="..\ScreenPixelShader.h" />
<ClInclude Include="..\ScreenVertexShader.h" />
<ClInclude Include="..\BoxDrawingEffect.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="..\IBoxDrawingEffect.idl" />
</ItemGroup>
</Project>
</Project>

View File

@@ -10,10 +10,16 @@ using namespace Microsoft::Console::VirtualTerminal;
TerminalOutput::TerminalOutput() noexcept
{
// By default we set all of the G-sets to ASCII, so if someone accidentally
// triggers a locking shift, they won't end up with Latin1 in the GL table,
// making their system unreadable. If ISO-2022 encoding is selected, though,
// we'll reset the G2 and G3 tables to Latin1, so that 8-bit apps will get a
// more meaningful character mapping by default. This is triggered by a DOCS
// sequence, which will call the EnableGrTranslation method below.
_gsetTranslationTables.at(0) = Ascii;
_gsetTranslationTables.at(1) = Ascii;
_gsetTranslationTables.at(2) = Latin1;
_gsetTranslationTables.at(3) = Latin1;
_gsetTranslationTables.at(2) = Ascii;
_gsetTranslationTables.at(3) = Ascii;
}
bool TerminalOutput::Designate94Charset(size_t gsetNumber, const VTID charset)
@@ -91,7 +97,13 @@ bool TerminalOutput::NeedToTranslate() const noexcept
void TerminalOutput::EnableGrTranslation(boolean enabled)
{
_grTranslationEnabled = enabled;
// We need to reapply the right locking shift to (de)activate the translation table.
// The default table for G2 and G3 is Latin1 when GR translation is enabled,
// and ASCII when disabled. The reason for this is explained in the constructor.
const auto defaultTranslationTable = enabled ? std::wstring_view{ Latin1 } : std::wstring_view{ Ascii };
_gsetTranslationTables.at(2) = defaultTranslationTable;
_gsetTranslationTables.at(3) = defaultTranslationTable;
// We need to reapply the locking shifts in case the underlying G-sets have changed.
LockingShift(_glSetNumber);
LockingShiftRight(_grSetNumber);
}

View File

@@ -1,7 +1,7 @@
$pgoBranch = "main"
$pgoBranch = "release-1.12"
$packageId = "Microsoft.Internal.Windows.Terminal.PGODatabase"
# Get release version
[xml] $customProps = ( Get-Content "..\..\custom.props" )
$releaseVersionMajor = ( [int]::Parse( $customProps.GetElementsByTagName("VersionMajor").'#text' ) )
$releaseVersionMinor = ( [int]::Parse( $customProps.GetElementsByTagName("VersionMinor").'#text' ) )
$releaseVersionMinor = ( [int]::Parse( $customProps.GetElementsByTagName("VersionMinor").'#text' ) )