mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-08 23:31:03 +00:00
Compare commits
4 Commits
v0.3.2171.
...
dev/miniks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c41151aba7 | ||
|
|
9dfbdec349 | ||
|
|
9e8f891460 | ||
|
|
0bf347f428 |
2
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
2
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
@@ -20,7 +20,7 @@ I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
|
||||
All good? Then proceed!
|
||||
-->
|
||||
|
||||
# Description of the new feature/enhancement
|
||||
# Summary of the new feature/enhancement
|
||||
|
||||
<!--
|
||||
A clear and concise description of what the problem is that the new feature would solve.
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
|
||||
"Microsoft.VisualStudio.Component.VC.v142.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.v142.ARM64",
|
||||
"Microsoft.VisualStudio.Component.VC.v141.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.v141.ARM64",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v142",
|
||||
"Microsoft.VisualStudio.ComponentGroup.UWP.VC.v141",
|
||||
"Microsoft.VisualStudio.Component.UWP.VC.ARM64"
|
||||
]
|
||||
}
|
||||
|
||||
26
README.md
26
README.md
@@ -1,7 +1,7 @@
|
||||
# Welcome\!
|
||||
#### This repository contains the source code for:
|
||||
|
||||
* [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701)
|
||||
* Windows Terminal
|
||||
* The Windows console host (`conhost.exe`)
|
||||
* Components shared between the two projects
|
||||
* [ColorTool](https://github.com/Microsoft/Terminal/tree/master/src/tools/ColorTool)
|
||||
@@ -10,30 +10,6 @@
|
||||
#### Other related repositories include:
|
||||
* [Console API Documentation](https://github.com/MicrosoftDocs/Console-Docs)
|
||||
|
||||
## Installation
|
||||
|
||||
### Microsoft Store
|
||||
|
||||
Download the Microsoft Terminal free from the Microsoft Store and it'll be continuously updated. Or, feel free to side-load [releases](https://github.com/microsoft/terminal/releases) from GitHub, but note they won't auto-update.
|
||||
|
||||
<a href='//www.microsoft.com/store/apps/9n0dx20hk701?cid=storebadge&ocid=badge'><img src='https://assets.windowsphone.com/85864462-9c82-451e-9355-a3d5f874397a/English_get-it-from-MS_InvariantCulture_Default.png' alt='English badge' width="284" height="104" style='width: 284px; height: 104px;'/></a>
|
||||
|
||||
### Chocolatey (Unofficial)
|
||||
|
||||
Download and upgrade the Windows Terminal from [Chocolatey](https://chocolatey.org).
|
||||
|
||||
To install Windows Terminal, run the following command from the command line or from PowerShell:
|
||||
```powershell
|
||||
choco install microsoft-windows-terminal
|
||||
```
|
||||
|
||||
To upgrade Windows Terminal, run the following command from the command line or from PowerShell:
|
||||
```powershell
|
||||
choco upgrade microsoft-windows-terminal
|
||||
```
|
||||
|
||||
If you have any issues when installing/upgrading the package please go to the [package page](https://chocolatey.org/packages/microsoft-windows-terminal) and follow the [Chocolatey triage process](https://chocolatey.org/docs/package-triage-process)
|
||||
|
||||
### Build Status
|
||||
|
||||
Project|Build Status
|
||||
|
||||
@@ -19,11 +19,9 @@ pr:
|
||||
name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr)
|
||||
|
||||
jobs:
|
||||
# This is disabled because the build agents were running out of disk space.
|
||||
# We're pursuing that in the background, but the spice must flow in the meantime.
|
||||
# - template: ./templates/build-console-audit-job.yml
|
||||
# parameters:
|
||||
# platform: x64
|
||||
- template: ./templates/build-console-audit-job.yml
|
||||
parameters:
|
||||
platform: x64
|
||||
|
||||
- template: ./templates/build-console-ci.yml
|
||||
parameters:
|
||||
|
||||
@@ -46,14 +46,6 @@ steps:
|
||||
clean: true
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Check MSIX for common regressions'
|
||||
inputs:
|
||||
targetType: inline
|
||||
script: |
|
||||
$Package = Get-ChildItem -Recurse -Filter "CascadiaPackage_*.msix"
|
||||
.\build\scripts\Test-WindowsTerminalPackage.ps1 -Verbose -Path $Package.FullName
|
||||
|
||||
- task: VSTest@2
|
||||
displayName: 'Run Unit Tests'
|
||||
inputs:
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<BeforeLinkTargets Condition="'$(WindowsTargetPlatformVersion)' >= '10.0.18362.0'">
|
||||
$(BeforeLinkTargets);
|
||||
_ConsoleGenerateAdditionalWinmdManifests;
|
||||
</BeforeLinkTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="_ConsoleMapWinmdsToManifestFiles" DependsOnTargets="ResolveAssemblyReferences">
|
||||
<ItemGroup>
|
||||
<!-- For each non-system .winmd file in References, generate a .manifest in IntDir for it. -->
|
||||
<_ConsoleWinmdManifest Include="@(ReferencePath->'$(IntDir)\%(FileName).manifest')" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference'">
|
||||
<WinMDPath>%(ReferencePath.FullPath)</WinMDPath>
|
||||
<Implementation>%(ReferencePath.Implementation)</Implementation>
|
||||
</_ConsoleWinmdManifest>
|
||||
<!-- For each referenced project that _produces_ a winmd, generate a temporary item that maps to
|
||||
the winmd, and use that temporary item to generate a .manifest in IntDir for it.
|
||||
We don't set Implementation here because it's inherited from the _ResolvedNativeProjectReferencePaths. -->
|
||||
<_ConsoleWinmdProjectReference Condition="'%(_ResolvedNativeProjectReferencePaths.ProjectType)' != 'StaticLibrary'" Include="@(_ResolvedNativeProjectReferencePaths->WithMetadataValue('FileType','winmd')->'%(RootDir)%(Directory)%(TargetPath)')" />
|
||||
<_ConsoleWinmdManifest Include="@(_ConsoleWinmdProjectReference->'$(IntDir)\%(FileName).manifest')">
|
||||
<WinMDPath>%(Identity)</WinMDPath>
|
||||
</_ConsoleWinmdManifest>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_ConsoleGenerateAdditionalWinmdManifests"
|
||||
Inputs="@(_ConsoleWinmdManifest.WinMDPath)"
|
||||
Outputs="@(_ConsoleWinmdManifest)"
|
||||
DependsOnTargets="_ConsoleMapWinmdsToManifestFiles">
|
||||
|
||||
<!-- This target is batched and a new Exec is spawned for each entry in _ConsoleWinmdManifest. -->
|
||||
<Exec Command="mt.exe -winmd:%(_ConsoleWinmdManifest.WinMDPath) -dll:%(_ConsoleWinmdManifest.Implementation) -out:%(_ConsoleWinmdManifest.Identity)" />
|
||||
|
||||
<ItemGroup>
|
||||
<!-- Emit the generated manifest into the Link inputs. -->
|
||||
<Manifest Include="@(_ConsoleWinmdManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,79 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory=$true, ValueFromPipeline=$true,
|
||||
HelpMessage="Path to the .appx/.msix to validate")]
|
||||
[string]
|
||||
$Path,
|
||||
|
||||
[Parameter(HelpMessage="Path to Windows Kit")]
|
||||
[ValidateScript({Test-Path $_ -Type Leaf})]
|
||||
[string]
|
||||
$WindowsKitPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
If ($null -Eq (Get-Item $WindowsKitPath -EA:SilentlyContinue)) {
|
||||
Write-Error "Could not find a windows SDK at at `"$WindowsKitPath`".`nMake sure that WindowsKitPath points to a valid SDK."
|
||||
Exit 1
|
||||
}
|
||||
|
||||
$makeAppx = "$WindowsKitPath\x86\MakeAppx.exe"
|
||||
$makePri = "$WindowsKitPath\x86\MakePri.exe"
|
||||
|
||||
Function Expand-ApplicationPackage {
|
||||
Param(
|
||||
[Parameter(Mandatory, ValueFromPipeline)]
|
||||
[string]
|
||||
$Path
|
||||
)
|
||||
|
||||
$sentinelFile = New-TemporaryFile
|
||||
$directory = New-Item -Type Directory "$($sentinelFile.FullName)_Package"
|
||||
Remove-Item $sentinelFile -Force -EA:Ignore
|
||||
|
||||
& $makeAppx unpack /p $Path /d $directory /nv /o
|
||||
|
||||
If ($LastExitCode -Ne 0) {
|
||||
Throw "Failed to expand AppX"
|
||||
}
|
||||
|
||||
$directory
|
||||
}
|
||||
|
||||
Write-Verbose "Expanding $Path"
|
||||
$AppxPackageRoot = Expand-ApplicationPackage $Path
|
||||
$AppxPackageRootPath = $AppxPackageRoot.FullName
|
||||
|
||||
Write-Verbose "Expanded to $AppxPackageRootPath"
|
||||
|
||||
Try {
|
||||
& $makePri dump /if "$AppxPackageRootPath\resources.pri" /of "$AppxPackageRootPath\resources.pri.xml" /o
|
||||
If ($LastExitCode -Ne 0) {
|
||||
Throw "Failed to dump PRI"
|
||||
}
|
||||
|
||||
$Manifest = [xml](Get-Content "$AppxPackageRootPath\AppxManifest.xml")
|
||||
$PRIFile = [xml](Get-Content "$AppxPackageRootPath\resources.pri.xml")
|
||||
|
||||
### Check the activatable class entries for a few DLLs we need.
|
||||
$inProcServers = $Manifest.Package.Extensions.Extension.InProcessServer.Path
|
||||
$RequiredInProcServers = ("TerminalApp.dll", "TerminalControl.dll", "TerminalConnection.dll")
|
||||
|
||||
Write-Verbose "InProc Servers: $inProcServers"
|
||||
|
||||
ForEach ($req in $RequiredInProcServers) {
|
||||
If ($req -NotIn $inProcServers) {
|
||||
Throw "Failed to find $req in InProcServer list $inProcServers"
|
||||
}
|
||||
}
|
||||
|
||||
### Check that we have an App.xbf (which is a proxy for our resources having been merged)
|
||||
$resourceXpath = '/PriInfo/ResourceMap/ResourceMapSubtree[@name="Files"]/NamedResource[@name="App.xbf"]'
|
||||
$AppXbf = $PRIFile.SelectSingleNode($resourceXpath)
|
||||
If ($null -eq $AppXbf) {
|
||||
Throw "Failed to find App.xbf (TerminalApp project) in resources.pri"
|
||||
}
|
||||
} Finally {
|
||||
Remove-Item -Recurse -Force $AppxPackageRootPath
|
||||
}
|
||||
147
doc/Niksa.md
147
doc/Niksa.md
@@ -1,147 +0,0 @@
|
||||
# Niksa's explanations
|
||||
|
||||
Sometimes @miniksa will write a big, long explanatory comment in an issue thread that turns out to be a decent bit of reference material.
|
||||
This document serves as a storage point for those posts.
|
||||
|
||||
- [Why do we avoid changing CMD.exe?](#cmd)
|
||||
- [Why is typing-to-screen performance better than every other app?](#screenPerf)
|
||||
- [How are the Windows graphics/messaging stack assembled?](#gfxMsgStack)
|
||||
- [Output Processing between "Far East" and "Western"](#fesb)
|
||||
- [Why do we not backport things?](#backport)
|
||||
|
||||
## <a name="cmd"></a>Why do we avoid changing CMD.exe?
|
||||
`setlocal` doesn't behave the same way as an environment variable. It's a thing that would have to be put in at the top of the batch script that is `somefile.cmd` as one of its first commands to adjust the way that one specific batch file is processed by the `cmd.exe` engine. That's probably not suitable for your needs, but that's the way we have to go.
|
||||
|
||||
I don't think anyone is disagreeing with you, @mikemaccana, that this would be a five minute development change to read that environment variable and change the behavior of `cmd.exe`. It absolutely would be a tiny development time.
|
||||
|
||||
It's just that from our experience, we know there's going to be a 3-24 month bug tail here where we get massive investigation callbacks by some billion dollar enterprise customer who for whatever reason was already using the environment variable we pick for another purpose. Their script that they give their rank-and-file folks will tell them to press Ctrl+C at some point in the batch script to do whatever happens, it will do something different, those people will notice the script doesn't match the computer anymore. They will then halt the production line and tell their supervisor. The supervisor tells some director. Their director comes screaming at their Microsoft enterprise support contract person that we've introduced a change to the OS that is costing them millions if not billions of dollars in shipments per month. Our directors at Microsoft then come bashing down our doors angry with us and make us fix it ASAP or revert it, we don't get to go home at 5pm to our families or friends because we're fixing it, we get stressed the heck out, we have to spin up servicing potentially for already shipped operating systems which is expensive and headache-causing...etc.
|
||||
|
||||
We can see this story coming a million miles away because it has happened before with other 'tiny' change we've been asked to make to `cmd.exe` in the past few years.
|
||||
|
||||
I would just ask you to understand that `cmd.exe` is very, very much in a maintenance mode and I just want to set expectations here. We maintain it, yes. We have a renewed interest in command-line development, yes. But our focuses are revolving around improving the terminal and platform itself and bringing modern, supported shells to be the best they can be on Windows. Paul will put this on the backlog of things that people want in `cmd.exe`, yes. But it will sink to the bottom of the backlog because changing `cmd.exe` is our worst nightmare as its compatibility story is among the heaviest of any piece of the operating system.
|
||||
|
||||
I would highly recommend that Gulp convert to using PowerShell scripts and that if such an issue exists with PowerShell, that we get their modern, supported, and better-engineered platform to support the scenario. I don't want you to sit around waiting for `cmd.exe` to change this because it's really not going to happen faster than that script could be converted to `ps1` and it fixed in PowerShell Core (if that's even a problem in that world.)
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/217#issuecomment-404240443
|
||||
|
||||
## <a name="screenPerf"></a>Why is typing-to-screen performance better than every other app?
|
||||
|
||||
I really do not mind when someone comes by and decides to tell us that we're doing a good job at something. We hear so many complaints every day that a post like this is a breath of fresh air. Thanks for your thanks!
|
||||
|
||||
Also, I'm happy to discuss this with you until you're utterly sick of reading it. Please ask any follow-ons you want. I thrive on blathering about my work. :P
|
||||
|
||||
If I had to take an educated guess as to what is making us faster than pretty much any other application on Windows at putting your text on the screen... I would say it is because that is literally our only job! Also probably because we are using darn near the oldest and lowest level APIs that Windows has to accomplish this work.
|
||||
|
||||
Pretty much everything else you've listed has some sort of layer or framework involved, or many, many layers and frameworks, when you start talking about Electron and Javascript. We don't.
|
||||
|
||||
We have one bare, super un-special window with no additional controls attached to it. We get our keys fed into us from just barely above the kernel given that we're processing them from window messages and not from some sort of eventing framework common to pretty much any other more complicated UI framework than ours (WPF, WinForms, UWP, Electron). And we dump our text straight onto the window surface using GDI's [PolyTextOut](https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-polytextoutw) with no frills.
|
||||
|
||||
Even `notepad.exe` has multiple controls on its window at the very least and is probably (I haven't looked) using some sort of library framework in the edit control to figure out its text layout (which probably is using another library framework for internationalization support...)
|
||||
|
||||
Of course this also means that we have trade offs. We don't support fully international text like pretty much every other application will. RTL? No go zone right now. Surrogate pairs and emoji? We're getting there but not there yet. Indic scripts? Nope.
|
||||
|
||||
Why are we like this? For one, `conhost.exe` is old as dirt. It has to use the bare metal bottom layer of everything because it was created before most of those other frameworks were created. And also it maintains as low/bottom level as possible because it is pretty much the first thing that one needs to bring up when bringing up a new operating system edition or device before you have all the nice things like frameworks or what those frameworks require to operate. Also it's written in C/C++ which is about as low and bare metal as we can get.
|
||||
|
||||
Will this UI enhancement come to other apps on Windows? Almost certainly not. They have too much going on which is both a good and a bad thing. I'm jealous of their ability to just call one method and layout text in an uncomplicated manner in any language without manually calculating pixels or caring about what styles apply to their font. But my manual pixel calculations, dirty region math, scroll region madness, and more makes it so we go faster than them. I'm also jealous that when someone says "hey can you add a status bar to the bottom of your window" that they can pretty much click and drag that into place with their UI Framework and it will just work where as for us, it's been a backlog item forever and gives me heartburn to think about implementing.
|
||||
|
||||
Will we try to keep it from regressing? Yes! Right now it's sort of a manual process. We identify that something is getting slow and then we go haul out [WPR](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder) and start taking traces. We stare down the hot paths and try to reason out what is going on and then improve them. For instance, in the last cycle or two, we focused on heap allocations as a major area where we could improve our end-to-end performance, changing a ton of our code to use stack-constructed iterator-like facades over the underlying request buffer instead of translating and allocating it into a new heap space for each level of processing.
|
||||
|
||||
As an aside, @bitcrazed wants us to automate performance tests in some conhost specific way, but I haven't quite figured out a controlled environment to do this in yet. The Windows Engineering System runs performance tests each night that give us a coarse grained way of knowing if we messed something up for the whole operating system, and they technically offer a fine grained way for us to insert our own performance tests... but I just haven't got around to that yet. If you have an idea for a way for us to do this in an automated fashion, I'm all ears.
|
||||
|
||||
If there's anything else you'd like to know, let me know. I could go on all day. I deleted like 15 tangents from this reply before posting it....
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/327#issuecomment-447391705
|
||||
|
||||
## <a name="gfxMsgStack"></a>How are the Windows graphics/messaging stack assembled?
|
||||
|
||||
@stakx, I am referring to USER32 and GDI32.
|
||||
|
||||
I'll give you a cursory overview of what I know off the top of my head without spending hours confirming the details. As such, some of this is subject to handwaving and could be mildly incorrect but is probably in the right direction. Consider every statement to be my personal knowledge on how the world works and subject to opinion or error.
|
||||
|
||||
For the graphics part of the pipeline (GDI32), the user-mode portions of GDI are pretty far down. The app calls GDI32, some work is done in that DLL on the user-mode side, then a kernel call jumps over to the kernel and drawing occurs.
|
||||
|
||||
The portion that you're thinking of regarding "silently converted to sit on top of other stuff" is probably that once we hit the kernel calls, a bunch of the kernel GDI stuff tends to be re-platformed on top of the same stuff as DirectX when it is actually handled by the NVIDIA/AMD/Intel/etc. graphics driver and the GPU at the bottom of the stack. I think this happened with the graphics driver re-architecture that came as a part of WDDM for Windows Vista. There's a document out there somewhere about what calls are still really fast in GDI and which are slower as a result of the re-platforming. Last time I found that document and checked, we were using the fast ones.
|
||||
|
||||
On top of GDI, I believe there are things like Common Controls or comctl32.dll which provided folks reusable sets of buttons and elements to make their UIs before we had nicer declarative frameworks. We don't use those in the console really (except in the property sheet off the right click menu).
|
||||
|
||||
As for DirectWrite and D2D and D3D and DXGI themselves, they're a separate set of commands and paths that are completely off to the side from GDI at all both in user and kernel mode. They're not really related other than that there's some interoperability provisions between the two. Most of our other UI frameworks tend to be built on top of the DirectX stack though. XAML is for sure. I think WPF is. Not sure about WinForms. And I believe the composition stack and the window manager are using DirectX as well.
|
||||
|
||||
As for the input/interaction part of the pipeline (USER32), I tend to find most other newer things (at least for desktop PCs) are built on top of what is already there. USER32's major concept is windows and window handles and everything is sent to a window handle. As long as you're on a desktop machine (or a laptop or whatever... I mean a classic-style Windows-powered machine), there's a window handle involved and messages floating around and that means we're talking USER32.
|
||||
|
||||
The window message queue is just a straight up FIFO (more or less) of whatever input has occurred relevant to that window while it's in the foreground + whatever has been sent to the window by other components in the system.
|
||||
|
||||
The newer technologies and the frameworks like XAML and WPF and WinForms tend to receive the messages from the window message queue one way or another and process them and turn them into event callbacks to various objects that they've provisioned within their world.
|
||||
|
||||
However, the newer technologies that also work on other non-desktop platforms like XAML tend to have the ability to process stuff off of a completely different non-USER32 stack as well. There's a separate parallel stack to USER32 with all of our new innovations and realizations on how input and interaction should occur that doesn't exactly deal with classic messaging queues and window handles the same way. This is the whole Core* family of things like CoreWindow and CoreMessaging. They also have a different concept of "what is a user" that isn't so centric around your butt in rolling chair in front of a screen with a keyboard and mouse on the desk.
|
||||
|
||||
Now, if you're on XAML or one of the other Frameworks... all this intricacy is handled for you. XAML figures out how to draw on DirectX for you and negotiates with the compositor and window manager for cool effects on your behalf. It figures out whether to get your input events from USER32 or Core* or whatever transparently depending on your platform and the input stacks can handle pen, touch, keyboard, mouse, and so on in a unified manner. It has provisions inside it embedded to do all the sorts of globalization, accessibility, input interaction, etc. stuff that make your life easy. But you could choose to go directly to the low-level and handle it yourself or skip handling what you don't care about.
|
||||
|
||||
The trick is that GDI32 and USER32 were designed for a limited world with a limited set of commands. Desktop PCs were the only thing that existed, single user at the keyboard and mouse, simple graphics output to a VGA monitor. So using them directly at the "low level" like conhost does is pretty easy. The new platforms could be used at the "low level" but they're orders of magnitude more complicated because they now account for everything that has happened with personal computing in 20+ years like different form factors, multiple active users, multiple graphics adapters, and on and on and on and on. So you tend to use a framework when using the new stuff so your head doesn't explode. They handle it for you, but they handle more than they ever did before so they're slower to some degree.
|
||||
|
||||
So are GDI32 and USER32 "lower" than the new stuff? Sort of.
|
||||
Can you get that low with the newer stuff? Mostly yes, but you probably shouldn't and don't want to.
|
||||
Does new live on top of old or is old replatformed on the new? Sometimes and/or partially.
|
||||
Basically... it's like the answer to anything software... "it's an unmitigated disaster and if we all stepped back a moment, we should be astounded that it works at all." :P
|
||||
|
||||
Anyway, that's enough ramble for one morning. Hopefully that somewhat answered your questions and gave you a bit more insight.
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/327#issuecomment-447926388
|
||||
|
||||
## <a name="fesb"></a>Output Processing between "Far East" and "Western"
|
||||
|
||||
>
|
||||
> ```
|
||||
> if (WI_IsFlagSet(CharType, C1_CNTRL))
|
||||
> ```
|
||||
|
||||
In short, this is probably fine to fix.
|
||||
|
||||
However, I would personally feed a few characters through `WriteCharsLegacy` under the debugger and assert that your theory is correct first (that multiple flags coming back are what the problem is) before making the change.
|
||||
|
||||
I am mildly terrified, less than Dustin, because it is freaking `WriteCharsLegacy` which is the spawn of hell and I fear some sort of regression in it.
|
||||
|
||||
In long, why is it fine to fix?
|
||||
|
||||
For reference, this particular segment of code https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/host/_stream.cpp#L514-L539 appears to only be used when the codepoint is < 0x20 or == 0x7F https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/host/_stream.cpp#L408 and ENABLE_PROCESSED_OUTPUT is off. https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/host/_stream.cpp#L320
|
||||
|
||||
I looked back at the console v1 code and this particular section had a divergence for "Western" countries and "Far East" countries (a geopolitically-charged term, but what it was, nonetheless.)
|
||||
|
||||
For "Western" countries, we would unconditionally run all the characters through `MultiByteToWideChar` with `MB_USEGLYPHCHARS` without the `C1_CNTRL` test and move the result into the buffer.
|
||||
|
||||
For "Eastern" countries, we did the `C1_CNTRL` test and then if true, we would run through `MultiByteToWideChar` with `MB_USEGLYPHCHARS`. Otherwise, we would just move the original character into the buffer and call it a day.
|
||||
|
||||
Note in both of these, there is a little bit of indirection before `MultiByteToWideChar` is called through some other helper methods like `ConvertOutputToUnicode`, but that's the effective conversion point, as far as I can tell. And that's where the control characters would turn into acceptable low ASCII symbols.
|
||||
|
||||
When we took over the console codebase, this variation between "Western" and "Eastern" countries was especially painful because `conhost.exe` would choose which one it was in based on the `Codepage for Non-Unicode Applications` set in the Control Panel's Regional > Administrative panel and it could only be changed with a reboot. It wouldn't even change properly when you `chcp` to a different codepage. Heck, `chcp` would deny you from switching into many codepages. There was a block in place to prevent going to an "Eastern" codepage if you booted up in a "Western" codepage. There was also a block preventing you from going between "Eastern" codepages, if I recall correctly.
|
||||
|
||||
In modernizing, I decided a few things:
|
||||
1. What's good for the "Far East" should be good for the rest of the world. CJK languages that encompassed the "Far East" code have to be able to handle "Western" text as well even if the reverse wasn't true.
|
||||
2. We need to scrub all usages of "Far East" from the code. Someone already started that and replaced them with "East Asia" except then they left behind the shorthand of "FE" prefixing dozens of functions which made it hard to follow the code. It took us months to realize "FE" and "East Asia" were the same thing.
|
||||
3. It's obnoxious that the way this was handled was to literally double-define every output function in the code base to have two definitions, compile them both into the conhost, then choose to run down the SB_ versions or the FE_ versions depending on the startup Non-Unicode codepage. It was a massive pile of complex pre-compilation `#ifdef` and `#else`s that would sometimes surround individual lines in the function bodies. Gross.
|
||||
4. The fact that the FE_ versions of the functions were way slower than the SB_ ones was unacceptable even for the same output of Latin-character text.
|
||||
5. Anyone should be free to switch between any codepage they want at any time and restricting it based on a value from OS startup or region/locale is not acceptable in the modern world.
|
||||
6. I concluded by all of the above that I was going to tank/delete/remove the SB_ versions of everything and force the entire world to use the FE_ versions as truth. I would fix the FE_ versions to handle everything correctly, I would fix the performance characteristics of the FE_ versions so they were only slower when things were legitimately more complicated and never otherwise, I would banish all usage of "Far East", "East Asia", "FE_", and "SB_" from the codebase, and codepages would be freely switchable.
|
||||
7. Oh. Also, the conhost used to rewrite its entire backing buffer into whatever your current codepage was whenever you switched codepages. I changed that to always hold it as UTF-16.
|
||||
|
||||
Now, after that backstory. This is where the problem comes in. It looks like the code you're pointing to that didn't check flags and instead checked direct equality... is the way that it was ALWAYS done for the "Eastern" copy of the code. So it was ALWAYS broken for the "Eastern" codepages and country variants of the OS.
|
||||
|
||||
I don't know why the "Eastern" copy was checking `C1_CNTRL` at all in the first place. There is no documentation. I presume it has to do with Shift-JIS or GB-2312 or Unified Hangul or something having a conflict < 0x20 || == 0x7F. Or alternatively, it's because someone wrote the code naively thinking it was a good idea in a hurry and never tested it. Very possible and even probable.
|
||||
|
||||
Presuming CJK codepages have no conflict in this range for their DBCS codepages... we could probably remove the check with `GetStringTypeW` entirely and always run everything through `ConvertOutputToUnicode`. More risky than just the flag test change... but theoretically an option as well.
|
||||
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/166#issuecomment-510953359
|
||||
|
||||
## <a name="backport"></a>Why do we not backport things?
|
||||
|
||||
Someone has to prove that this is costing millions to billions of dollars of lost productivity or revenue to outweigh the risks of shipping the fix to hundreds of millions of Windows machines and potentially breaking something.
|
||||
|
||||
Our team generally finds it pretty hard to prove that against the developer audience given that they're only a small portion of the total installed market of Windows machines.
|
||||
|
||||
Our only backport successes really come from corporations with massive addressable market (like OEMs shipping PCs) who complain that this is fouling up their manufacturing line (or something of that ilk). Otherwise, our management typically says that the risks don't outweigh the benefits.
|
||||
|
||||
It's also costly in terms of time, effort, and testing for us to validate a modification to a released OS. We have a mindbogglingly massive amount of automated machinery dedicated to processing and validating the things that we check in while developing the current OS builds. But it's a special costly ask to spin up some to all of those activities to validate backported fixes. We do it all the time for Patch Tuesday, but in those patches, they only pass through the minimum number of fixes required to maximize the restoration of productivity/security/revenue/etc. because every additional fix adds additional complexity and additional risk.
|
||||
|
||||
So from our little team working hard to make developers happy, we virtually never make the cut for servicing. We're sorry, but we hope you can understand. It's just the reality of the situation to say "nope" when people ask for a backport. In our team's ideal world, you would all be running the latest console bits everywhere everytime we make a change. But that's just not how it is today.
|
||||
|
||||
Original Source: https://github.com/microsoft/terminal/issues/279#issuecomment-439179675
|
||||
12
doc/bot.md
12
doc/bot.md
@@ -13,7 +13,7 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
|
||||
### Tagging/Process Details
|
||||
1. When new issues arrive, or when issues are not properly tagged... we'll mark them as `Needs-Triage` automatically.
|
||||
- The core contributor team will then come through and mark them up as appropriate. The goal is to have a tag that fits the `Product`, `Area`, and `Issue` category.
|
||||
- The core contributor team will then come through and mark them up as appropriate. The goal is to have a tag that fits the `Product`, `Area`, and `Issue` category.
|
||||
- The `Needs-Triage` tag will be removed manually by the core contributor team during a triage meeting. (Exception, triage may also be done offline by senior team members during high-volume times.)
|
||||
- An issue may or may not be assigned to a contributor during triage. It is not necessary to assign someone to complete it.
|
||||
- We're not focusing on Projects yet.
|
||||
@@ -22,7 +22,7 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
- When this tag drops off, the bot will apply the `Needs-Attention` tag to get the core contribution team's attention again. If an author cares enough to be active, we will attempt to prioritize engaging with that author.
|
||||
- If the author doesn't come back around in a while, this will become a `No-Recent-Activity` tag.
|
||||
- If there's activity on an issue, the `No-Recent-Activity` tag will automatically drop.
|
||||
- If the `No-Recent-Activity` stays, the issue will be closed as stale.
|
||||
- If the `No-Recent-Activity` stays, the issue will be closed as stale.
|
||||
1. PRs will automatically get a `Needs-Author-Feedback` tag when reviewers wait on the author
|
||||
- This follows a similar decay strategy to issues.
|
||||
- If the author responds, the `Needs-Author-Feedback` tag will drop.
|
||||
@@ -37,7 +37,7 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
|
||||
#### Mark as Triage Needed
|
||||
- When an issue doesn't meet triage criteria, applies `Needs-Triage` tag. Right now, this is just when it's opened.
|
||||
|
||||
|
||||
#### Author Has Responded
|
||||
- When an issue with `Needs-Author-Feedback` gets an author response, drops that tag in favor of `Needs-Attention` to flag core contributors to drop by.
|
||||
|
||||
@@ -87,16 +87,16 @@ We'll be using tags, primarily, to help us understand what needs attention, what
|
||||
#### Auto-Merge pull requests
|
||||
- When a pull request has the `AutoMerge` label...
|
||||
- If it has been at least 480 minutes and all the statuses pass, merge it in.
|
||||
- Will use Squash merge strategy
|
||||
- Will use Squash merge stratgy
|
||||
- Will attempt to delete branch after merge, if possible
|
||||
- Will automatically remove the `AutoMerge` label if changes are pushed by someone *without* Write Access.
|
||||
- More information on bot-logic that can be controlled with comments is [here](https://github.com/OfficeDev/office-ui-fabric-react/wiki/Advanced-auto-merge)
|
||||
|
||||
|
||||
#### Mark issues with an active PR
|
||||
- If there is an active PR for an issue, label that issue with the `In-PR` label
|
||||
|
||||
#### Add committed fix tag for completed PRs
|
||||
- When a PR is finished and there's no outstanding work left on a linked issue, add the `Resolution-Fix-Committed` label
|
||||
|
||||
|
||||
## Admin Panel
|
||||
[Here](https://fabric-cp.azurewebsites.net/bot/)
|
||||
|
||||
@@ -12,7 +12,7 @@ Properties listed below affect the entire window, regardless of the profile sett
|
||||
| `requestedTheme` | _Required_ | String | `system` | Sets the theme of the application. Possible values: `"light"`, `"dark"`, `"system"` |
|
||||
| `showTerminalTitleInTitlebar` | _Required_ | Boolean | `true` | When set to `true`, titlebar displays the title of the selected tab. When set to `false`, titlebar displays "Windows Terminal". |
|
||||
| `showTabsInTitlebar` | Optional | Boolean | `true` | When set to `true`, the tabs are moved into the titlebar and the titlebar disappears. When set to `false`, the titlebar sits above the tabs. |
|
||||
| `wordDelimiters` | Optional | String | <code> /\()"'-:,.;<>~!@#$%^&*|+=[]{}~?│</code><br>_(`│` is `U+2502 BOX DRAWINGS LIGHT VERTICAL`)_ | Determines the delimiters used in a double click selection. |
|
||||
| `wordDelimiters` | Optional | String | ` ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502` | Determines the delimiters used in a double click selection. |
|
||||
|
||||
## Profiles
|
||||
Properties listed below are specific to each unique profile.
|
||||
@@ -25,7 +25,7 @@ Properties listed below are specific to each unique profile.
|
||||
| `commandline` | _Required_ | String | `powershell.exe` | Executable used in the profile. |
|
||||
| `cursorColor` | _Required_ | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. |
|
||||
| `cursorShape` | _Required_ | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) |
|
||||
| `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. |
|
||||
| `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. |
|
||||
| `fontSize` | _Required_ | Integer | `10` | Sets the font size. |
|
||||
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
|
||||
| `historySize` | _Required_ | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. |
|
||||
@@ -35,10 +35,6 @@ Properties listed below are specific to each unique profile.
|
||||
| `startingDirectory` | _Required_ | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. |
|
||||
| `useAcrylic` | _Required_ | Boolean | `false` | When set to `true`, the window will have an acrylic background. When set to `false`, the window will have a plain, untextured background. |
|
||||
| `background` | Optional | String | | Sets the background color of the profile. Overrides `background` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
| `backgroundImage` | Optional | String | | Sets the file location of the Image to draw over the window background. |
|
||||
| `backgroundImageAlignment` | Optional | String | `center` | Sets how the background image aligns to the boundaries of the window. Possible values: `"center"`, `"left"`, `"top"`, `"right"`, `"bottom"`, `"topLeft"`, `"topRight"`, `"bottomLeft"`, `"bottomRight"` |
|
||||
| `backgroundImageOpacity` | Optional | Number | `1.0` | Sets the transparency of the background image. Accepts floating point values from 0-1. |
|
||||
| `backgroundImageStretchMode` | Optional | String | `uniformToFill` | Sets how the background image is resized to fill the window. Possible values: `"none"`, `"fill"`, `"uniform"`, `"uniformToFill"` |
|
||||
| `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Colors use hex color format: `"#rrggbb"`. Ordering is as follows: `[black, red, green, yellow, blue, magenta, cyan, white, bright black, bright red, bright green, bright yellow, bright blue, bright magenta, bright cyan, bright white]` |
|
||||
| `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. |
|
||||
| `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. |
|
||||
|
||||
@@ -110,7 +110,7 @@ The schema name can then be referenced in one or more profiles.
|
||||
```json
|
||||
"backgroundImage": "ms-appdata:///Roaming/openlogo.jpg",
|
||||
"backgroundImageOpacity": 0.3,
|
||||
"backgroundImageStretchMode": "fill",
|
||||
"backgroundImageStretchMode": "Fill",
|
||||
```
|
||||
5. Make sure that `useAcrylic` is `false`.
|
||||
6. Save the file.
|
||||
|
||||
@@ -558,37 +558,22 @@ bool TextBuffer::IncrementCircularBuffer()
|
||||
|
||||
//Routine Description:
|
||||
// - Retrieves the position of the last non-space character on the final line of the text buffer.
|
||||
// - By default, we search the entire buffer to find the last non-space character
|
||||
//Arguments:
|
||||
// - <none>
|
||||
//Return Value:
|
||||
// - Coordinate position in screen coordinates (offset coordinates, not array index coordinates).
|
||||
COORD TextBuffer::GetLastNonSpaceCharacter() const
|
||||
{
|
||||
return GetLastNonSpaceCharacter(GetSize());
|
||||
}
|
||||
|
||||
//Routine Description:
|
||||
// - Retrieves the position of the last non-space character in the given viewport
|
||||
// - This is basically an optimized version of GetLastNonSpaceCharacter(), and can be called when
|
||||
// - we know the last character is within the given viewport (so we don't need to check the entire buffer)
|
||||
//Arguments:
|
||||
// - The viewport
|
||||
//Return value:
|
||||
// - Coordinate position (relative to the text buffer)
|
||||
COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::Viewport viewport) const
|
||||
{
|
||||
COORD coordEndOfText = { 0 };
|
||||
// Search the given viewport by starting at the bottom.
|
||||
coordEndOfText.Y = viewport.BottomInclusive();
|
||||
COORD coordEndOfText;
|
||||
// Always search the whole buffer, by starting at the bottom.
|
||||
coordEndOfText.Y = GetSize().BottomInclusive();
|
||||
|
||||
const ROW* pCurrRow = &GetRowByOffset(coordEndOfText.Y);
|
||||
// The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character)
|
||||
coordEndOfText.X = static_cast<short>(pCurrRow->GetCharRow().MeasureRight()) - 1;
|
||||
|
||||
// If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text.
|
||||
const auto viewportTop = viewport.Top();
|
||||
bool fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop); // this row is empty, and we're not at the top
|
||||
bool fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > 0); // this row is empty, and we're not at the top
|
||||
while (fDoBackUp)
|
||||
{
|
||||
coordEndOfText.Y--;
|
||||
@@ -596,7 +581,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View
|
||||
// We need to back up to the previous row if this line is empty, AND there are more rows
|
||||
|
||||
coordEndOfText.X = static_cast<short>(pCurrRow->GetCharRow().MeasureRight()) - 1;
|
||||
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop);
|
||||
fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > 0);
|
||||
}
|
||||
|
||||
// don't allow negative results
|
||||
|
||||
@@ -105,7 +105,6 @@ public:
|
||||
bool IncrementCircularBuffer();
|
||||
|
||||
COORD GetLastNonSpaceCharacter() const;
|
||||
COORD GetLastNonSpaceCharacter(const Microsoft::Console::Types::Viewport viewport) const;
|
||||
|
||||
Cursor& GetCursor();
|
||||
const Cursor& GetCursor() const;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.pre.props" />
|
||||
<PropertyGroup Label="Version">
|
||||
<!-- These fields are picked up by PackageES -->
|
||||
<VersionMajor>0</VersionMajor>
|
||||
<VersionMinor>3</VersionMinor>
|
||||
<VersionMinor>2</VersionMinor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
|
||||
@@ -257,11 +257,27 @@
|
||||
<Content Include="ProfileIcons\{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{9acb9455-ca41-5af7-950f-6bca1bc9722f}.scale-200.png" />
|
||||
<Content Include="ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-100.png" />
|
||||
<Content Include="ProfileIcons\{b453ae62-4e3d-5e58-b989-0a998ec441b8}.scale-200.png" />
|
||||
<PRIResource Include="Resources\en-US\Resources.resw" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.post.props" />
|
||||
<!--
|
||||
Microsoft.UI.Xaml contains some <Content> resource files that need to be included in our package.
|
||||
For some reason, they're not rolled up through dependent projects; if they were, their paths would
|
||||
be wrong.
|
||||
|
||||
WAP Packaging projects don't actually support nuget package references, so we added one manually.
|
||||
|
||||
This does mean that version changes to Microsoft.UI.Xaml must be manually reflected
|
||||
here.
|
||||
-->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
<!-- End workaround -->
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
|
||||
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
|
||||
@@ -279,8 +295,7 @@
|
||||
<!-- Override the filename for OpenConsole.exe (only) -->
|
||||
<TargetPath Condition="'%(Filename)' == 'OpenConsole' and '%(Extension)' == '.exe'">conhost.exe</TargetPath>
|
||||
<!-- Blank the SourceProject here to vend all files into the root of the package. -->
|
||||
<SourceProject>
|
||||
</SourceProject>
|
||||
<SourceProject></SourceProject>
|
||||
</_FilteredNonWapProjProjectOutput>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.18362.0" MaxVersionTested="10.0.18362.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 314 B |
Binary file not shown.
|
Before Width: | Height: | Size: 573 B |
@@ -123,16 +123,61 @@
|
||||
<data name="AppName" xml:space="preserve">
|
||||
<value>Windows Terminal (Preview)</value>
|
||||
</data>
|
||||
<data name="InitialJsonParseErrorText" xml:space="preserve">
|
||||
<value>Settings could not be loaded from file - temporarily using the default settings. Check for syntax errors, including trailing commas.</value>
|
||||
</data>
|
||||
<data name="InitialJsonParseErrorTitle" xml:space="preserve">
|
||||
<value>Failed to load settings</value>
|
||||
</data>
|
||||
<data name="Ok" xml:space="preserve">
|
||||
<value>Ok</value>
|
||||
</data>
|
||||
<data name="ReloadJsonParseErrorText" xml:space="preserve">
|
||||
<value>Settings could not be reloaded from file. Check for syntax errors, including trailing commas.</value>
|
||||
</data>
|
||||
<data name="ReloadJsonParseErrorTitle" xml:space="preserve">
|
||||
<value>Failed to reload settings</value>
|
||||
</data>
|
||||
<data name="AppDescriptionDev" xml:space="preserve">
|
||||
<value>The Windows Terminal, but Unofficial</value>
|
||||
</data>
|
||||
<data name="AppNameDev" xml:space="preserve">
|
||||
<value>Windows Terminal (Dev Build)</value>
|
||||
</data>
|
||||
<data name="AboutTitleText" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="VersionLabelText" xml:space="preserve">
|
||||
<value>Version:</value>
|
||||
</data>
|
||||
<data name="AppShortName" xml:space="preserve">
|
||||
<value>Terminal</value>
|
||||
</data>
|
||||
<data name="AppShortNameDev" xml:space="preserve">
|
||||
<value>Terminal (Dev)</value>
|
||||
</data>
|
||||
<data name="DocumentationLabelText" xml:space="preserve">
|
||||
<value>Documentation
|
||||
</value>
|
||||
</data>
|
||||
<data name="GettingStartedLabelText" xml:space="preserve">
|
||||
<value>Getting Started
|
||||
</value>
|
||||
</data>
|
||||
<data name="ReleaseNotesLabelText" xml:space="preserve">
|
||||
<value>Release Notes
|
||||
</value>
|
||||
</data>
|
||||
<data name="DocumentationUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-documentation</value>
|
||||
</data>
|
||||
<data name="GettingStartedUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-getting-started</value>
|
||||
</data>
|
||||
<data name="ReleaseNotesUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-release-notes</value>
|
||||
</data>
|
||||
<data name="FeedbackUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-feedback</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -19,6 +19,14 @@ using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
// Note: Generate GUID using TlgGuid.exe tool
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hTerminalAppProvider,
|
||||
"Microsoft.Windows.Terminal.App",
|
||||
// {24a1622f-7da7-5c77-3303-d850bd1ab2ed}
|
||||
(0x24a1622f, 0x7da7, 0x5c77, 0x33, 0x03, 0xd8, 0x50, 0xbd, 0x1a, 0xb2, 0xed),
|
||||
TraceLoggingOptionMicrosoftTelemetry());
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
@@ -32,8 +40,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_tabs{},
|
||||
_loadedInitialSettings{ false },
|
||||
_settingsLoadedResult{ S_OK },
|
||||
_dialogLock{},
|
||||
_resourceLoader{ L"TerminalApp/Resources" }
|
||||
_dialogLock{}
|
||||
{
|
||||
// For your own sanity, it's better to do setup outside the ctor.
|
||||
// If you do any setup in the ctor that ends up throwing an exception,
|
||||
@@ -54,12 +61,26 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void App::Create()
|
||||
void App::Create(uint64_t hWnd)
|
||||
{
|
||||
// Assert that we've already loaded our settings. We have to do
|
||||
// this as a MTA, before the app is Create()'d
|
||||
WINRT_ASSERT(_loadedInitialSettings);
|
||||
TraceLoggingRegister(g_hTerminalAppProvider);
|
||||
_Create(hWnd);
|
||||
}
|
||||
|
||||
App::~App()
|
||||
{
|
||||
TraceLoggingUnregister(g_hTerminalAppProvider);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create all of the initial UI elements of the Terminal app.
|
||||
// * Initializes the first terminal control, using the default profile,
|
||||
// and adds it to our list of tabs.
|
||||
void App::_Create(uint64_t parentHwnd)
|
||||
{
|
||||
/* !!! TODO
|
||||
This is not the correct way to host a XAML page. This exists today because we valued
|
||||
getting a .xaml over tearing out all of the terminal logic and splitting it across App
|
||||
@@ -71,21 +92,15 @@ namespace winrt::TerminalApp::implementation
|
||||
_root = terminalPage.as<winrt::Windows::UI::Xaml::Controls::Control>();
|
||||
_tabContent = terminalPage->TabContent();
|
||||
_tabRow = terminalPage->TabRow();
|
||||
_tabView = _tabRow.TabView();
|
||||
_newTabButton = _tabRow.NewTabButton();
|
||||
_tabView = terminalPage->TabView();
|
||||
_newTabButton = terminalPage->NewTabButton();
|
||||
|
||||
if (_settings->GlobalSettings().GetShowTabsInTitlebar())
|
||||
_minMaxCloseControl = terminalPage->MinMaxCloseControl();
|
||||
_minMaxCloseControl.ParentWindowHandle(parentHwnd);
|
||||
|
||||
if (!_settings->GlobalSettings().GetShowTabsInTitlebar())
|
||||
{
|
||||
// Remove the TabView from the page. We'll hang on to it, we need to
|
||||
// put it in the titlebar.
|
||||
uint32_t index = 0;
|
||||
if (terminalPage->Root().Children().IndexOf(_tabRow, index))
|
||||
{
|
||||
terminalPage->Root().Children().RemoveAt(index);
|
||||
}
|
||||
|
||||
// Inform the host that our titlebar content has changed.
|
||||
_setTitleBarContentHandlers(*this, _tabRow);
|
||||
_minMaxCloseControl.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
|
||||
// Event Bindings (Early)
|
||||
@@ -101,16 +116,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_OpenNewTab(std::nullopt);
|
||||
|
||||
_tabContent.SizeChanged({ this, &App::_OnContentSizeChanged });
|
||||
|
||||
_ApplyTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"AppCreated",
|
||||
TraceLoggingDescription("Event emitted when the application is started"),
|
||||
TraceLoggingBool(_settings->GlobalSettings().GetShowTabsInTitlebar(), "TabsInTitlebar"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -146,11 +151,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// xaml tree somehow.
|
||||
dialog.XamlRoot(_root.XamlRoot());
|
||||
|
||||
// IMPORTANT: Set the requested theme of the dialog, because the
|
||||
// PopupRoot isn't directly in the Xaml tree of our root. So the dialog
|
||||
// won't inherit our RequestedTheme automagically.
|
||||
dialog.RequestedTheme(_settings->GlobalSettings().GetRequestedTheme());
|
||||
|
||||
// Display the dialog.
|
||||
Controls::ContentDialogResult result = co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup);
|
||||
|
||||
@@ -169,9 +169,10 @@ namespace winrt::TerminalApp::implementation
|
||||
void App::_ShowOkDialog(const winrt::hstring& titleKey,
|
||||
const winrt::hstring& contentKey)
|
||||
{
|
||||
auto title = _resourceLoader.GetLocalizedString(titleKey);
|
||||
auto message = _resourceLoader.GetLocalizedString(contentKey);
|
||||
auto buttonText = _resourceLoader.GetLocalizedString(L"Ok");
|
||||
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView();
|
||||
auto title = resourceLoader.GetString(titleKey);
|
||||
auto message = resourceLoader.GetString(contentKey);
|
||||
auto buttonText = resourceLoader.GetString(L"Ok");
|
||||
|
||||
_ShowDialog(winrt::box_value(title), winrt::box_value(message), buttonText);
|
||||
}
|
||||
@@ -182,17 +183,19 @@ namespace winrt::TerminalApp::implementation
|
||||
// Notes link.
|
||||
void App::_ShowAboutDialog()
|
||||
{
|
||||
const auto title = _resourceLoader.GetLocalizedString(L"AboutTitleText");
|
||||
const auto versionLabel = _resourceLoader.GetLocalizedString(L"VersionLabelText");
|
||||
const auto gettingStartedLabel = _resourceLoader.GetLocalizedString(L"GettingStartedLabelText");
|
||||
const auto documentationLabel = _resourceLoader.GetLocalizedString(L"DocumentationLabelText");
|
||||
const auto releaseNotesLabel = _resourceLoader.GetLocalizedString(L"ReleaseNotesLabelText");
|
||||
const auto gettingStartedUriValue = _resourceLoader.GetLocalizedString(L"GettingStartedUriValue");
|
||||
const auto documentationUriValue = _resourceLoader.GetLocalizedString(L"DocumentationUriValue");
|
||||
const auto releaseNotesUriValue = _resourceLoader.GetLocalizedString(L"ReleaseNotesUriValue");
|
||||
auto resourceLoader = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView();
|
||||
const auto title = resourceLoader.GetString(L"AboutTitleText");
|
||||
const auto versionLabel = resourceLoader.GetString(L"VersionLabelText");
|
||||
const auto gettingStartedLabel = resourceLoader.GetString(L"GettingStartedLabelText");
|
||||
const auto documentationLabel = resourceLoader.GetString(L"DocumentationLabelText");
|
||||
const auto releaseNotesLabel = resourceLoader.GetString(L"ReleaseNotesLabelText");
|
||||
const auto gettingStartedUriValue = resourceLoader.GetString(L"GettingStartedUriValue");
|
||||
const auto documentationUriValue = resourceLoader.GetString(L"DocumentationUriValue");
|
||||
const auto releaseNotesUriValue = resourceLoader.GetString(L"ReleaseNotesUriValue");
|
||||
const auto package = winrt::Windows::ApplicationModel::Package::Current();
|
||||
const auto packageName = package.DisplayName();
|
||||
const auto version = package.Id().Version();
|
||||
Windows::UI::Xaml::Media::SolidColorBrush blueBrush{ Windows::UI::ColorHelper::FromArgb(255, 0, 115, 207) };
|
||||
winrt::Windows::UI::Xaml::Documents::Run about;
|
||||
winrt::Windows::UI::Xaml::Documents::Run gettingStarted;
|
||||
winrt::Windows::UI::Xaml::Documents::Run documentation;
|
||||
@@ -233,7 +236,11 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::hstring aboutText{ aboutTextStream.str() };
|
||||
about.Text(aboutText);
|
||||
|
||||
const auto buttonText = _resourceLoader.GetLocalizedString(L"Ok");
|
||||
const auto buttonText = resourceLoader.GetString(L"Ok");
|
||||
|
||||
gettingStartedLink.Foreground(blueBrush);
|
||||
documentationLink.Foreground(blueBrush);
|
||||
releaseNotesLink.Foreground(blueBrush);
|
||||
|
||||
Controls::TextBlock aboutTextBlock;
|
||||
aboutTextBlock.Inlines().Append(about);
|
||||
@@ -362,7 +369,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Create the settings button.
|
||||
auto settingsItem = Controls::MenuFlyoutItem{};
|
||||
settingsItem.Text(_resourceLoader.GetLocalizedString(L"SettingsMenuItem"));
|
||||
settingsItem.Text(L"Settings");
|
||||
|
||||
Controls::SymbolIcon ico{};
|
||||
ico.Symbol(Controls::Symbol::Setting);
|
||||
@@ -379,7 +386,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Create the feedback button.
|
||||
auto feedbackFlyout = Controls::MenuFlyoutItem{};
|
||||
feedbackFlyout.Text(_resourceLoader.GetLocalizedString(L"FeedbackMenuItem"));
|
||||
feedbackFlyout.Text(L"Feedback");
|
||||
|
||||
Controls::FontIcon feedbackIco{};
|
||||
feedbackIco.Glyph(L"\xE939");
|
||||
@@ -391,7 +398,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Create the about button.
|
||||
auto aboutFlyout = Controls::MenuFlyoutItem{};
|
||||
aboutFlyout.Text(_resourceLoader.GetLocalizedString(L"AboutMenuItem"));
|
||||
aboutFlyout.Text(L"About");
|
||||
|
||||
Controls::SymbolIcon aboutIco{};
|
||||
aboutIco.Symbol(Controls::Symbol::Help);
|
||||
@@ -444,11 +451,21 @@ namespace winrt::TerminalApp::implementation
|
||||
void App::_FeedbackButtonOnClick(const IInspectable&,
|
||||
const RoutedEventArgs&)
|
||||
{
|
||||
const auto feedbackUriValue = _resourceLoader.GetLocalizedString(L"FeedbackUriValue");
|
||||
const auto feedbackUriValue = Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView().GetString(L"FeedbackUriValue");
|
||||
|
||||
winrt::Windows::System::Launcher::LaunchUriAsync({ feedbackUriValue });
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Controls::Border App::GetDragBar() noexcept
|
||||
{
|
||||
if (_minMaxCloseControl)
|
||||
{
|
||||
return _minMaxCloseControl.DragBar();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the about button is clicked. See _ShowAboutDialog for more info.
|
||||
// Arguments:
|
||||
@@ -476,7 +493,6 @@ namespace winrt::TerminalApp::implementation
|
||||
bindings.NewTab([this]() { _OpenNewTab(std::nullopt); });
|
||||
bindings.DuplicateTab([this]() { _DuplicateTabViewItem(); });
|
||||
bindings.CloseTab([this]() { _CloseFocusedTab(); });
|
||||
bindings.ClosePane([this]() { _CloseFocusedPane(); });
|
||||
bindings.NewTabWithProfile([this](const auto index) { _OpenNewTab({ index }); });
|
||||
bindings.ScrollUp([this]() { _Scroll(-1); });
|
||||
bindings.ScrollDown([this]() { _Scroll(1); });
|
||||
@@ -489,7 +505,6 @@ namespace winrt::TerminalApp::implementation
|
||||
bindings.SwitchToTab([this](const auto index) { _SelectTab({ index }); });
|
||||
bindings.OpenSettings([this]() { _OpenSettings(); });
|
||||
bindings.ResizePane([this](const auto direction) { _ResizePane(direction); });
|
||||
bindings.MoveFocus([this](const auto direction) { _MoveFocus(direction); });
|
||||
bindings.CopyText([this](const auto trimWhitespace) { _CopyText(trimWhitespace); });
|
||||
bindings.PasteText([this]() { _PasteText(); });
|
||||
}
|
||||
@@ -707,38 +722,32 @@ namespace winrt::TerminalApp::implementation
|
||||
void App::_UpdateTitle(std::shared_ptr<Tab> tab)
|
||||
{
|
||||
auto newTabTitle = tab->GetFocusedTitle();
|
||||
const auto lastFocusedProfileOpt = tab->GetFocusedProfile();
|
||||
if (lastFocusedProfileOpt.has_value())
|
||||
const auto lastFocusedProfile = tab->GetFocusedProfile().value();
|
||||
const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile);
|
||||
|
||||
const auto tabTitle = matchingProfile->GetTabTitle();
|
||||
|
||||
// Checks if tab title has been set in the profile settings and
|
||||
// updates accordingly.
|
||||
|
||||
const auto newActualTitle = tabTitle.empty() ? newTabTitle : tabTitle;
|
||||
|
||||
tab->SetTabText(winrt::to_hstring(newActualTitle.data()));
|
||||
if (_settings->GlobalSettings().GetShowTitleInTitlebar() &&
|
||||
tab->IsFocused())
|
||||
{
|
||||
const auto lastFocusedProfile = lastFocusedProfileOpt.value();
|
||||
const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile);
|
||||
|
||||
const auto tabTitle = matchingProfile->GetTabTitle();
|
||||
|
||||
// Checks if tab title has been set in the profile settings and
|
||||
// updates accordingly.
|
||||
|
||||
const auto newActualTitle = tabTitle.empty() ? newTabTitle : tabTitle;
|
||||
|
||||
tab->SetTabText(winrt::to_hstring(newActualTitle.data()));
|
||||
if (_settings->GlobalSettings().GetShowTitleInTitlebar() &&
|
||||
tab->IsFocused())
|
||||
{
|
||||
_titleChangeHandlers(newActualTitle);
|
||||
}
|
||||
_titleChangeHandlers(newActualTitle);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Update the current theme of the application. This will trigger our
|
||||
// RequestedThemeChanged event, to have our host change the theme of the
|
||||
// root of the application.
|
||||
// - Update the current theme of the application. This will manually update
|
||||
// all of the elements in our UI to match the given theme.
|
||||
// Arguments:
|
||||
// - newTheme: The ElementTheme to apply to our elements.
|
||||
void App::_ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme)
|
||||
{
|
||||
// Propagate the event to the host layer, so it can update its own UI
|
||||
_requestedThemeChangedHandlers(*this, newTheme);
|
||||
_root.RequestedTheme(newTheme);
|
||||
}
|
||||
|
||||
UIElement App::GetRoot() noexcept
|
||||
@@ -818,8 +827,6 @@ namespace winrt::TerminalApp::implementation
|
||||
"TabInformation",
|
||||
TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
|
||||
TraceLoggingInt32(tabCount, "TabCount", "Count of tabs curently opened in TerminalApp"),
|
||||
TraceLoggingBool(profileIndex.has_value(), "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
|
||||
TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
@@ -881,7 +888,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_UpdateTitle(tab);
|
||||
});
|
||||
|
||||
term.GotFocus([this, weakTabPtr](auto&&, auto&&) {
|
||||
term.GetControl().GotFocus([this, weakTabPtr](auto&&, auto&&) {
|
||||
auto tab = weakTabPtr.lock();
|
||||
if (!tab)
|
||||
{
|
||||
@@ -904,12 +911,20 @@ namespace winrt::TerminalApp::implementation
|
||||
// currently displayed, it will be shown.
|
||||
// Arguments:
|
||||
// - settings: the TerminalSettings object to use to create the TerminalControl with.
|
||||
void App::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings)
|
||||
void App::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings, std::optional<uint64_t> serverHandle)
|
||||
{
|
||||
// Initialize the new tab
|
||||
|
||||
// Create a connection based on the values in our settings object.
|
||||
const auto connection = _CreateConnectionFromSettings(profileGuid, settings);
|
||||
TerminalConnection::ITerminalConnection connection = nullptr;
|
||||
// Create a Conhost connection based on the values in our settings object.
|
||||
if (!serverHandle)
|
||||
{
|
||||
connection = TerminalConnection::ConhostConnection(settings.Commandline(), settings.StartingDirectory(), 30, 80, winrt::guid());
|
||||
}
|
||||
else
|
||||
{
|
||||
connection = TerminalConnection::ConhostConnection(serverHandle.value(), 30, 80, winrt::guid());
|
||||
}
|
||||
|
||||
TermControl term{ settings, connection };
|
||||
|
||||
@@ -978,17 +993,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_RemoveTabViewItem(focusedTab->GetTabViewItem());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Close the currently focused pane. If the pane is the last pane in the
|
||||
// tab, the tab will also be closed. This will happen when we handle the
|
||||
// tab's Closed event.
|
||||
void App::_CloseFocusedPane()
|
||||
{
|
||||
int focusedTabIndex = _GetFocusedTabIndex();
|
||||
std::shared_ptr<Tab> focusedTab{ _tabs[focusedTabIndex] };
|
||||
focusedTab->ClosePane();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Move the viewport of the terminal of the currently focused tab up or
|
||||
// down a number of lines. Negative values of `delta` will move the
|
||||
@@ -1032,20 +1036,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_tabs[focusedTabIndex]->ResizePane(direction);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to move focus between panes, as to focus the child on
|
||||
// the other side of the separator. See Pane::NavigateFocus for details.
|
||||
// - Moves the focus of the currently focused tab.
|
||||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void App::_MoveFocus(const Direction& direction)
|
||||
{
|
||||
const auto focusedTabIndex = _GetFocusedTabIndex();
|
||||
_tabs[focusedTabIndex]->NavigateFocus(direction);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Copy text from the focused terminal to the Windows Clipboard
|
||||
// Arguments:
|
||||
@@ -1176,6 +1166,33 @@ namespace winrt::TerminalApp::implementation
|
||||
return { L"Windows Terminal" };
|
||||
}
|
||||
|
||||
void App::IncomingConnection(uint64_t serverHandle)
|
||||
{
|
||||
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, serverHandle]() {
|
||||
// Getting Guid for default profile
|
||||
const auto globalSettings = _settings->GlobalSettings();
|
||||
auto profileGuid = globalSettings.GetDefaultProfile();
|
||||
TerminalSettings settings = _settings->MakeSettings(profileGuid);
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid, settings, serverHandle);
|
||||
});
|
||||
}
|
||||
|
||||
void App::IncomingConnection(hstring cmdline, hstring workingDir)
|
||||
{
|
||||
_root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, cmdline, workingDir]() {
|
||||
// Getting Guid for default profile
|
||||
const auto globalSettings = _settings->GlobalSettings();
|
||||
auto profileGuid = globalSettings.GetDefaultProfile();
|
||||
TerminalSettings settings = _settings->MakeSettings(profileGuid);
|
||||
|
||||
settings.Commandline(cmdline);
|
||||
settings.StartingDirectory(workingDir);
|
||||
|
||||
_CreateNewTabFromSettings(profileGuid, settings);
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Additional responses to clicking on a TabView's item. Currently, just remove tab with middle click
|
||||
// Arguments:
|
||||
@@ -1226,7 +1243,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (focusedTabIndex >= _tabs.size())
|
||||
{
|
||||
focusedTabIndex = static_cast<int>(_tabs.size()) - 1;
|
||||
focusedTabIndex = _tabs.size() - 1;
|
||||
}
|
||||
|
||||
if (focusedTabIndex < 0)
|
||||
@@ -1251,9 +1268,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (profile.HasIcon())
|
||||
{
|
||||
std::wstring path{ profile.GetIconPath() };
|
||||
const auto envExpandedPath{ wil::ExpandEnvironmentStringsW<std::wstring>(path.data()) };
|
||||
winrt::hstring iconPath{ envExpandedPath };
|
||||
auto path = profile.GetIconPath();
|
||||
winrt::hstring iconPath{ path };
|
||||
winrt::Windows::Foundation::Uri iconUri{ iconPath };
|
||||
Controls::BitmapIconSource iconSource;
|
||||
// Make sure to set this to false, so we keep the RGB data of the
|
||||
@@ -1321,7 +1337,8 @@ namespace winrt::TerminalApp::implementation
|
||||
_settings->GlobalSettings().GetDefaultProfile();
|
||||
const auto controlSettings = _settings->MakeSettings(realGuid);
|
||||
|
||||
const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings);
|
||||
// Create a Conhost connection based on the values in our settings object.
|
||||
TerminalConnection::ITerminalConnection controlConnection = TerminalConnection::ConhostConnection(controlSettings.Commandline(), controlSettings.StartingDirectory(), 30, 80, winrt::guid());
|
||||
|
||||
TermControl newControl{ controlSettings, controlConnection };
|
||||
|
||||
@@ -1384,33 +1401,6 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handles the special case of providing a text override for the UI shortcut due to VK_OEM issue.
|
||||
// Looks at the flags from the KeyChord modifiers and provides a concatenated string value of all
|
||||
// in the same order that XAML would put them as well.
|
||||
// Return Value:
|
||||
// - a string representation of the key modifiers for the shortcut
|
||||
//NOTE: This needs to be localized with https://github.com/microsoft/terminal/issues/794 if XAML framework issue not resolved before then
|
||||
static std::wstring _FormatOverrideShortcutText(Settings::KeyModifiers modifiers)
|
||||
{
|
||||
std::wstring buffer{ L"" };
|
||||
|
||||
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl))
|
||||
{
|
||||
buffer += L"Ctrl+";
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift))
|
||||
{
|
||||
buffer += L"Shift+";
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt))
|
||||
{
|
||||
buffer += L"Alt+";
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Takes a MenuFlyoutItem and a corresponding KeyChord value and creates the accelerator for UI display.
|
||||
// Takes into account a special case for an error condition for a comma
|
||||
@@ -1418,7 +1408,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// - MenuFlyoutItem that will be displayed, and a KeyChord to map an accelerator
|
||||
void App::_SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord)
|
||||
{
|
||||
#ifdef DEP_MICROSOFT_UI_XAML_708_FIXED
|
||||
// work around https://github.com/microsoft/microsoft-ui-xaml/issues/708 in case of VK_OEM_COMMA
|
||||
if (keyChord.Vkey() != VK_OEM_COMMA)
|
||||
{
|
||||
@@ -1438,63 +1427,16 @@ namespace winrt::TerminalApp::implementation
|
||||
menuItem.KeyboardAccelerators().Append(menuShortcut);
|
||||
}
|
||||
else // we've got a comma, so need to just use the alternate method
|
||||
#endif
|
||||
{
|
||||
// extract the modifier and key to a nice format
|
||||
auto overrideString = _FormatOverrideShortcutText(keyChord.Modifiers());
|
||||
auto mappedCh = MapVirtualKeyW(keyChord.Vkey(), MAPVK_VK_TO_CHAR);
|
||||
if (mappedCh != 0)
|
||||
{
|
||||
menuItem.KeyboardAcceleratorTextOverride(overrideString + gsl::narrow_cast<wchar_t>(mappedCh));
|
||||
}
|
||||
auto overrideString = AppKeyBindings::FormatOverrideShortcutText(keyChord.Modifiers());
|
||||
menuItem.KeyboardAcceleratorTextOverride(overrideString + L" ,");
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new connection based on the profile settings
|
||||
// Arguments:
|
||||
// - the profile GUID we want the settings from
|
||||
// - the terminal settings
|
||||
// Return value:
|
||||
// - the desired connection
|
||||
TerminalConnection::ITerminalConnection App::_CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings)
|
||||
{
|
||||
const auto* const profile = _settings->FindProfile(profileGuid);
|
||||
TerminalConnection::ITerminalConnection connection{ nullptr };
|
||||
// The Azure connection has a boost dependency, and boost does not support ARM64
|
||||
// so we make sure that we do not try to compile the Azure connection code if we are in ARM64 (we would get build errors otherwise)
|
||||
GUID connectionType{ 0 };
|
||||
if (profile->HasConnectionType())
|
||||
{
|
||||
connectionType = profile->GetConnectionType();
|
||||
}
|
||||
#ifndef _M_ARM64
|
||||
if (connectionType == AzureConnectionType)
|
||||
{
|
||||
connection = TerminalConnection::AzureConnection(settings.InitialRows(), settings.InitialCols());
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
connection = TerminalConnection::ConhostConnection(settings.Commandline(), settings.StartingDirectory(), settings.InitialRows(), settings.InitialCols(), winrt::guid());
|
||||
}
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"ConnectionCreated",
|
||||
TraceLoggingDescription("Event emitted upon the creation of a connection"),
|
||||
TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// Winrt events need a method for adding a callback to the event and removing the callback.
|
||||
// These macros will define them both for you.
|
||||
DEFINE_EVENT(App, TitleChanged, _titleChangeHandlers, TerminalControl::TitleChangedEventArgs);
|
||||
DEFINE_EVENT(App, LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::UIElement);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "CascadiaSettings.h"
|
||||
#include "App.g.h"
|
||||
#include "App.base.h"
|
||||
#include "ScopedResourceLoader.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.TerminalControl.h>
|
||||
@@ -28,21 +27,25 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
Windows::UI::Xaml::UIElement GetRoot() noexcept;
|
||||
|
||||
void Create();
|
||||
// Gets the current dragglable area in the non client region of the top level window
|
||||
Windows::UI::Xaml::Controls::Border GetDragBar() noexcept;
|
||||
|
||||
void Create(uint64_t hParentWnd);
|
||||
void LoadSettings();
|
||||
|
||||
Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi);
|
||||
bool GetShowTabsInTitlebar();
|
||||
|
||||
~App() = default;
|
||||
~App();
|
||||
|
||||
hstring GetTitle();
|
||||
|
||||
void IncomingConnection(uint64_t serverHandle);
|
||||
void IncomingConnection(hstring cmdline, hstring workingDir);
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
DECLARE_EVENT(TitleChanged, _titleChangeHandlers, winrt::Microsoft::Terminal::TerminalControl::TitleChangedEventArgs);
|
||||
DECLARE_EVENT(LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::UIElement);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme);
|
||||
|
||||
private:
|
||||
// If you add controls here, but forget to null them either here or in
|
||||
@@ -53,9 +56,10 @@ namespace winrt::TerminalApp::implementation
|
||||
// (which is a root when the tabs are in the titlebar.)
|
||||
Windows::UI::Xaml::Controls::Control _root{ nullptr };
|
||||
Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr };
|
||||
TerminalApp::TabRowControl _tabRow{ nullptr };
|
||||
Windows::UI::Xaml::Controls::Grid _tabRow{ nullptr };
|
||||
Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr };
|
||||
Windows::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr };
|
||||
winrt::TerminalApp::MinMaxCloseControl _minMaxCloseControl{ nullptr };
|
||||
|
||||
std::vector<std::shared_ptr<Tab>> _tabs;
|
||||
|
||||
@@ -66,12 +70,11 @@ namespace winrt::TerminalApp::implementation
|
||||
bool _loadedInitialSettings;
|
||||
std::shared_mutex _dialogLock;
|
||||
|
||||
ScopedResourceLoader _resourceLoader;
|
||||
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
std::atomic<bool> _settingsReloadQueued{ false };
|
||||
|
||||
void _Create(uint64_t parentHWnd);
|
||||
void _CreateNewTabFlyout();
|
||||
|
||||
fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& titleElement,
|
||||
@@ -100,13 +103,11 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
|
||||
|
||||
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings);
|
||||
void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings, std::optional<uint64_t> serverHandle = std::nullopt);
|
||||
|
||||
void _OpenNewTab(std::optional<int> profileIndex);
|
||||
void _DuplicateTabViewItem();
|
||||
void _CloseFocusedTab();
|
||||
void _CloseFocusedPane();
|
||||
void _SelectNextTab(const bool bMoveRight);
|
||||
void _SelectTab(const int tabIndex);
|
||||
|
||||
@@ -124,7 +125,6 @@ namespace winrt::TerminalApp::implementation
|
||||
// MSFT:20641986: Add keybindings for New Window
|
||||
void _ScrollPage(int delta);
|
||||
void _ResizePane(const Direction& direction);
|
||||
void _MoveFocus(const Direction& direction);
|
||||
|
||||
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs);
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
namespace TerminalApp
|
||||
{
|
||||
delegate void LastTabClosedEventArgs();
|
||||
[default_interface] runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
|
||||
[default_interface]
|
||||
runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
|
||||
{
|
||||
App();
|
||||
|
||||
@@ -15,20 +16,22 @@ namespace TerminalApp
|
||||
// then it might look like TermApp just failed to activate, which will
|
||||
// cause you to chase down the rabbit hole of "why is TermApp not
|
||||
// registered?" when it definitely is.
|
||||
void Create();
|
||||
void Create(UInt64 hParentWnd);
|
||||
|
||||
void LoadSettings();
|
||||
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
Windows.UI.Xaml.Controls.Border GetDragBar{ get; };
|
||||
|
||||
Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi);
|
||||
Boolean GetShowTabsInTitlebar();
|
||||
|
||||
event Microsoft.Terminal.TerminalControl.TitleChangedEventArgs TitleChanged;
|
||||
event LastTabClosedEventArgs LastTabClosed;
|
||||
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.UIElement> SetTitleBarContent;
|
||||
event Windows.Foundation.TypedEventHandler<App, Windows.UI.Xaml.ElementTheme> RequestedThemeChanged;
|
||||
|
||||
String GetTitle();
|
||||
|
||||
void IncomingConnection(UInt64 serverHandle);
|
||||
void IncomingConnection(String cmdline, String workingDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,9 +102,6 @@ namespace winrt::TerminalApp::implementation
|
||||
case ShortcutAction::CloseTab:
|
||||
_CloseTabHandlers();
|
||||
return true;
|
||||
case ShortcutAction::ClosePane:
|
||||
_ClosePaneHandlers();
|
||||
return true;
|
||||
|
||||
case ShortcutAction::ScrollUp:
|
||||
_ScrollUpHandlers();
|
||||
@@ -172,18 +169,7 @@ namespace winrt::TerminalApp::implementation
|
||||
case ShortcutAction::ResizePaneDown:
|
||||
_ResizePaneHandlers(Direction::Down);
|
||||
return true;
|
||||
case ShortcutAction::MoveFocusLeft:
|
||||
_MoveFocusHandlers(Direction::Left);
|
||||
return true;
|
||||
case ShortcutAction::MoveFocusRight:
|
||||
_MoveFocusHandlers(Direction::Right);
|
||||
return true;
|
||||
case ShortcutAction::MoveFocusUp:
|
||||
_MoveFocusHandlers(Direction::Up);
|
||||
return true;
|
||||
case ShortcutAction::MoveFocusDown:
|
||||
_MoveFocusHandlers(Direction::Down);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -215,6 +201,33 @@ namespace winrt::TerminalApp::implementation
|
||||
return keyModifiers;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handles the special case of providing a text override for the UI shortcut due to VK_OEM_COMMA issue.
|
||||
// Looks at the flags from the KeyChord modifiers and provides a concatenated string value of all
|
||||
// in the same order that XAML would put them as well.
|
||||
// Return Value:
|
||||
// - a WinRT hstring representation of the key modifiers for the shortcut
|
||||
//NOTE: This needs to be localized with https://github.com/microsoft/terminal/issues/794 if XAML framework issue not resolved before then
|
||||
winrt::hstring AppKeyBindings::FormatOverrideShortcutText(Settings::KeyModifiers modifiers)
|
||||
{
|
||||
std::wstring buffer{ L"" };
|
||||
|
||||
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl))
|
||||
{
|
||||
buffer += L"Ctrl+";
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift))
|
||||
{
|
||||
buffer += L"Shift+";
|
||||
}
|
||||
if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt))
|
||||
{
|
||||
buffer += L"Alt+";
|
||||
}
|
||||
|
||||
return winrt::hstring{ buffer };
|
||||
}
|
||||
|
||||
// -------------------------------- Events ---------------------------------
|
||||
// clang-format off
|
||||
DEFINE_EVENT(AppKeyBindings, CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs);
|
||||
@@ -225,7 +238,6 @@ namespace winrt::TerminalApp::implementation
|
||||
DEFINE_EVENT(AppKeyBindings, NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ClosePane, _ClosePaneHandlers, TerminalApp::ClosePaneEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
|
||||
@@ -239,6 +251,5 @@ namespace winrt::TerminalApp::implementation
|
||||
DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, ResizePane, _ResizePaneHandlers, TerminalApp::ResizePaneEventArgs);
|
||||
DEFINE_EVENT(AppKeyBindings, MoveFocus, _MoveFocusHandlers, TerminalApp::MoveFocusEventArgs);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace winrt::TerminalApp::implementation
|
||||
Microsoft::Terminal::Settings::KeyChord GetKeyBinding(TerminalApp::ShortcutAction const& action);
|
||||
|
||||
static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
|
||||
static winrt::hstring FormatOverrideShortcutText(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers);
|
||||
|
||||
// clang-format off
|
||||
DECLARE_EVENT(CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs);
|
||||
@@ -47,7 +48,6 @@ namespace winrt::TerminalApp::implementation
|
||||
DECLARE_EVENT(NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs);
|
||||
DECLARE_EVENT(CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs);
|
||||
DECLARE_EVENT(CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs);
|
||||
DECLARE_EVENT(ClosePane, _ClosePaneHandlers, TerminalApp::ClosePaneEventArgs);
|
||||
DECLARE_EVENT(SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs);
|
||||
DECLARE_EVENT(NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs);
|
||||
DECLARE_EVENT(PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs);
|
||||
@@ -61,7 +61,6 @@ namespace winrt::TerminalApp::implementation
|
||||
DECLARE_EVENT(ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs);
|
||||
DECLARE_EVENT(OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs);
|
||||
DECLARE_EVENT(ResizePane, _ResizePaneHandlers, TerminalApp::ResizePaneEventArgs);
|
||||
DECLARE_EVENT(MoveFocus, _MoveFocusHandlers, TerminalApp::MoveFocusEventArgs);
|
||||
// clang-format on
|
||||
|
||||
private:
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace TerminalApp
|
||||
NewWindow,
|
||||
CloseWindow,
|
||||
CloseTab,
|
||||
ClosePane,
|
||||
NextTab,
|
||||
PrevTab,
|
||||
SplitVertical,
|
||||
@@ -54,10 +53,6 @@ namespace TerminalApp
|
||||
ResizePaneRight,
|
||||
ResizePaneUp,
|
||||
ResizePaneDown,
|
||||
MoveFocusLeft,
|
||||
MoveFocusRight,
|
||||
MoveFocusUp,
|
||||
MoveFocusDown,
|
||||
OpenSettings
|
||||
};
|
||||
|
||||
@@ -69,7 +64,6 @@ namespace TerminalApp
|
||||
delegate void NewWindowEventArgs();
|
||||
delegate void CloseWindowEventArgs();
|
||||
delegate void CloseTabEventArgs();
|
||||
delegate void ClosePaneEventArgs();
|
||||
delegate void NextTabEventArgs();
|
||||
delegate void PrevTabEventArgs();
|
||||
delegate void SplitVerticalEventArgs();
|
||||
@@ -83,9 +77,9 @@ namespace TerminalApp
|
||||
delegate void ScrollDownPageEventArgs();
|
||||
delegate void OpenSettingsEventArgs();
|
||||
delegate void ResizePaneEventArgs(Direction direction);
|
||||
delegate void MoveFocusEventArgs(Direction direction);
|
||||
|
||||
[default_interface] runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
|
||||
[default_interface]
|
||||
runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings
|
||||
{
|
||||
AppKeyBindings();
|
||||
|
||||
@@ -100,7 +94,6 @@ namespace TerminalApp
|
||||
event NewWindowEventArgs NewWindow;
|
||||
event CloseWindowEventArgs CloseWindow;
|
||||
event CloseTabEventArgs CloseTab;
|
||||
event ClosePaneEventArgs ClosePane;
|
||||
event SwitchToTabEventArgs SwitchToTab;
|
||||
event NextTabEventArgs NextTab;
|
||||
event PrevTabEventArgs PrevTab;
|
||||
@@ -114,6 +107,5 @@ namespace TerminalApp
|
||||
event ScrollDownPageEventArgs ScrollDownPage;
|
||||
event OpenSettingsEventArgs OpenSettings;
|
||||
event ResizePaneEventArgs ResizePane;
|
||||
event MoveFocusEventArgs MoveFocus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ static constexpr std::string_view NewTabWithProfile8Key{ "newTabProfile8" };
|
||||
static constexpr std::string_view NewWindowKey{ "newWindow" };
|
||||
static constexpr std::string_view CloseWindowKey{ "closeWindow" };
|
||||
static constexpr std::string_view CloseTabKey{ "closeTab" };
|
||||
static constexpr std::string_view ClosePaneKey{ "closePane" };
|
||||
static constexpr std::string_view SwitchtoTabKey{ "switchToTab" };
|
||||
static constexpr std::string_view NextTabKey{ "nextTab" };
|
||||
static constexpr std::string_view PrevTabKey{ "prevTab" };
|
||||
@@ -56,10 +55,6 @@ static constexpr std::string_view ResizePaneLeftKey{ "resizePaneLeft" };
|
||||
static constexpr std::string_view ResizePaneRightKey{ "resizePaneRight" };
|
||||
static constexpr std::string_view ResizePaneUpKey{ "resizePaneUp" };
|
||||
static constexpr std::string_view ResizePaneDownKey{ "resizePaneDown" };
|
||||
static constexpr std::string_view MoveFocusLeftKey{ "moveFocusLeft" };
|
||||
static constexpr std::string_view MoveFocusRightKey{ "moveFocusRight" };
|
||||
static constexpr std::string_view MoveFocusUpKey{ "moveFocusUp" };
|
||||
static constexpr std::string_view MoveFocusDownKey{ "moveFocusDown" };
|
||||
|
||||
// Specifically use a map here over an unordered_map. We want to be able to
|
||||
// iterate over these entries in-order when we're serializing the keybindings.
|
||||
@@ -87,7 +82,6 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
|
||||
{ NewWindowKey, ShortcutAction::NewWindow },
|
||||
{ CloseWindowKey, ShortcutAction::CloseWindow },
|
||||
{ CloseTabKey, ShortcutAction::CloseTab },
|
||||
{ ClosePaneKey, ShortcutAction::ClosePane },
|
||||
{ NextTabKey, ShortcutAction::NextTab },
|
||||
{ PrevTabKey, ShortcutAction::PrevTab },
|
||||
{ IncreaseFontSizeKey, ShortcutAction::IncreaseFontSize },
|
||||
@@ -111,10 +105,6 @@ static const std::map<std::string_view, ShortcutAction, std::less<>> commandName
|
||||
{ ResizePaneRightKey, ShortcutAction::ResizePaneRight },
|
||||
{ ResizePaneUpKey, ShortcutAction::ResizePaneUp },
|
||||
{ ResizePaneDownKey, ShortcutAction::ResizePaneDown },
|
||||
{ MoveFocusLeftKey, ShortcutAction::MoveFocusLeft },
|
||||
{ MoveFocusRightKey, ShortcutAction::MoveFocusRight },
|
||||
{ MoveFocusUpKey, ShortcutAction::MoveFocusUp },
|
||||
{ MoveFocusDownKey, ShortcutAction::MoveFocusDown },
|
||||
{ OpenSettingsKey, ShortcutAction::OpenSettings },
|
||||
};
|
||||
|
||||
|
||||
@@ -239,19 +239,6 @@ void CascadiaSettings::_CreateDefaultProfiles()
|
||||
powershellProfile.SetDefaultBackground(POWERSHELL_BLUE);
|
||||
powershellProfile.SetUseAcrylic(false);
|
||||
|
||||
// The Azure connection has a boost dependency, and boost does not support ARM64
|
||||
// so we don't create a default profile for the Azure cloud shell if we're in ARM64
|
||||
#ifndef _M_ARM64
|
||||
auto azureCloudShellProfile{ _CreateDefaultProfile(L"Azure Cloud Shell") };
|
||||
azureCloudShellProfile.SetCommandline(L"Azure");
|
||||
azureCloudShellProfile.SetStartingDirectory(DEFAULT_STARTING_DIRECTORY);
|
||||
azureCloudShellProfile.SetColorScheme({ L"Solarized Dark" });
|
||||
azureCloudShellProfile.SetAcrylicOpacity(0.85);
|
||||
azureCloudShellProfile.SetUseAcrylic(true);
|
||||
azureCloudShellProfile.SetCloseOnExit(false);
|
||||
azureCloudShellProfile.SetConnectionType(AzureConnectionType);
|
||||
#endif
|
||||
|
||||
// If the user has installed PowerShell Core, we add PowerShell Core as a default.
|
||||
// PowerShell Core default folder is "%PROGRAMFILES%\PowerShell\[Version]\".
|
||||
std::filesystem::path psCoreCmdline{};
|
||||
@@ -274,9 +261,6 @@ void CascadiaSettings::_CreateDefaultProfiles()
|
||||
|
||||
_profiles.emplace_back(powershellProfile);
|
||||
_profiles.emplace_back(cmdProfile);
|
||||
#ifndef _M_ARM64
|
||||
_profiles.emplace_back(azureCloudShellProfile);
|
||||
#endif
|
||||
try
|
||||
{
|
||||
_AppendWslProfiles(_profiles);
|
||||
@@ -293,16 +277,18 @@ void CascadiaSettings::_CreateDefaultProfiles()
|
||||
void CascadiaSettings::_CreateDefaultKeybindings()
|
||||
{
|
||||
AppKeyBindings keyBindings = _globals.GetKeybindings();
|
||||
// Set up some basic default keybindings
|
||||
// Set up spme basic default keybindings
|
||||
// TODO:MSFT:20700157 read our settings from some source, and configure
|
||||
// keychord,action pairings from that file
|
||||
keyBindings.SetKeyBinding(ShortcutAction::NewTab,
|
||||
KeyChord{ KeyModifiers::Ctrl | KeyModifiers::Shift,
|
||||
KeyChord{ KeyModifiers::Ctrl,
|
||||
static_cast<int>('T') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::DuplicateTab,
|
||||
KeyChord{ KeyModifiers::Ctrl | KeyModifiers::Shift,
|
||||
static_cast<int>('D') });
|
||||
|
||||
keyBindings.SetKeyBinding(ShortcutAction::ClosePane,
|
||||
KeyChord{ KeyModifiers::Ctrl | KeyModifiers::Shift,
|
||||
keyBindings.SetKeyBinding(ShortcutAction::CloseTab,
|
||||
KeyChord{ KeyModifiers::Ctrl,
|
||||
static_cast<int>('W') });
|
||||
|
||||
keyBindings.SetKeyBinding(ShortcutAction::CopyText,
|
||||
@@ -368,31 +354,31 @@ void CascadiaSettings::_CreateDefaultKeybindings()
|
||||
KeyChord{ KeyModifiers::Ctrl | KeyModifiers::Shift,
|
||||
VK_PRIOR });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab0,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('1') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab1,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('2') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab2,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('3') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab3,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('4') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab4,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('5') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab5,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('6') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab6,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('7') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab7,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('8') });
|
||||
keyBindings.SetKeyBinding(ShortcutAction::SwitchToTab8,
|
||||
KeyChord{ KeyModifiers::Alt | KeyModifiers::Ctrl,
|
||||
KeyChord{ KeyModifiers::Alt,
|
||||
static_cast<int>('9') });
|
||||
}
|
||||
|
||||
@@ -514,8 +500,7 @@ bool CascadiaSettings::_isPowerShellCoreInstalled(std::filesystem::path& cmdline
|
||||
// - true iff powershell core (pwsh.exe) is present in the given path
|
||||
bool CascadiaSettings::_isPowerShellCoreInstalledInPath(const std::wstring_view programFileEnv, std::filesystem::path& cmdline)
|
||||
{
|
||||
std::wstring programFileEnvNulTerm{ programFileEnv };
|
||||
std::filesystem::path psCorePath{ wil::ExpandEnvironmentStringsW<std::wstring>(programFileEnvNulTerm.data()) };
|
||||
std::filesystem::path psCorePath = ExpandEnvironmentVariableString(programFileEnv.data());
|
||||
psCorePath /= L"PowerShell";
|
||||
if (std::filesystem::exists(psCorePath))
|
||||
{
|
||||
@@ -612,7 +597,6 @@ void CascadiaSettings::_AppendWslProfiles(std::vector<TerminalApp::Profile>& pro
|
||||
auto WSLDistro{ _CreateDefaultProfile(distName) };
|
||||
WSLDistro.SetCommandline(L"wsl.exe -d " + distName);
|
||||
WSLDistro.SetColorScheme({ L"Campbell" });
|
||||
WSLDistro.SetStartingDirectory(DEFAULT_STARTING_DIRECTORY);
|
||||
std::wstring iconPath{ PACKAGED_PROFILE_ICON_PATH };
|
||||
iconPath.append(DEFAULT_LINUX_ICON_GUID);
|
||||
iconPath.append(PACKAGED_PROFILE_ICON_EXTENSION);
|
||||
@@ -622,6 +606,27 @@ void CascadiaSettings::_AppendWslProfiles(std::vector<TerminalApp::Profile>& pro
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Get a environment variable string.
|
||||
// Arguments:
|
||||
// - A string that contains an environment-variable string in the form: %variableName%.
|
||||
// Return Value:
|
||||
// - a string of the expending environment-variable string.
|
||||
std::wstring CascadiaSettings::ExpandEnvironmentVariableString(std::wstring_view source)
|
||||
{
|
||||
std::wstring result{};
|
||||
DWORD requiredSize = 0;
|
||||
do
|
||||
{
|
||||
result.resize(requiredSize);
|
||||
requiredSize = ::ExpandEnvironmentStringsW(source.data(), result.data(), gsl::narrow<DWORD>(result.size()));
|
||||
} while (requiredSize != result.size());
|
||||
|
||||
// Trim the terminating null character
|
||||
result.resize(requiredSize - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for creating a skeleton default profile with a pre-populated
|
||||
// guid and name.
|
||||
|
||||
@@ -20,8 +20,6 @@ Author(s):
|
||||
#include "GlobalAppSettings.h"
|
||||
#include "Profile.h"
|
||||
|
||||
static constexpr GUID AzureConnectionType = { 0xd9fcfdfa, 0xa479, 0x412c, { 0x83, 0xb7, 0xc5, 0x64, 0xe, 0x61, 0xcd, 0x62 } };
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
class CascadiaSettings;
|
||||
@@ -68,5 +66,6 @@ private:
|
||||
static bool _isPowerShellCoreInstalledInPath(const std::wstring_view programFileEnv, std::filesystem::path& cmdline);
|
||||
static bool _isPowerShellCoreInstalled(std::filesystem::path& cmdline);
|
||||
static void _AppendWslProfiles(std::vector<TerminalApp::Profile>& profileStorage);
|
||||
static std::wstring ExpandEnvironmentVariableString(std::wstring_view source);
|
||||
static Profile _CreateDefaultProfile(const std::wstring_view name);
|
||||
};
|
||||
|
||||
@@ -68,7 +68,21 @@ static const std::unordered_map<int32_t, std::wstring_view> vkeyNamePairs {
|
||||
{ VK_F22 , L"f22" },
|
||||
{ VK_F23 , L"f23" },
|
||||
{ VK_F24 , L"f24" },
|
||||
{ VK_OEM_PLUS , L"plus" }
|
||||
{ VK_OEM_PLUS , L"plus" },
|
||||
{ VK_OEM_COMMA , L"," },
|
||||
{ VK_OEM_MINUS , L"-" },
|
||||
{ VK_OEM_PERIOD , L"." }
|
||||
// TODO:
|
||||
// These all look like they'd be good keybindings, but change based on keyboard
|
||||
// layout. How do we deal with that?
|
||||
// #define VK_OEM_NEC_EQUAL 0x92 // '=' key on numpad
|
||||
// #define VK_OEM_1 0xBA // ';:' for US
|
||||
// #define VK_OEM_2 0xBF // '/?' for US
|
||||
// #define VK_OEM_3 0xC0 // '`~' for US
|
||||
// #define VK_OEM_4 0xDB // '[{' for US
|
||||
// #define VK_OEM_5 0xDC // '\|' for US
|
||||
// #define VK_OEM_6 0xDD // ']}' for US
|
||||
// #define VK_OEM_7 0xDE // ''"' for US
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -164,25 +178,6 @@ winrt::Microsoft::Terminal::Settings::KeyChord KeyChordSerialization::FromString
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't found a key, attempt a keyboard mapping
|
||||
if (!foundKey && part.size() == 1)
|
||||
{
|
||||
auto oemVk = VkKeyScanW(part[0]);
|
||||
if (oemVk != -1)
|
||||
{
|
||||
vkey = oemVk & 0xFF;
|
||||
auto oemModifiers = (oemVk & 0xFF00) >> 8;
|
||||
// We're using WI_SetFlagIf instead of WI_UpdateFlag because we want to be strictly additive
|
||||
// to the user's specified modifiers. ctrl+| should be the same as ctrl+shift+\,
|
||||
// but if we used WI_UpdateFlag, ctrl+shift+\ would turn _off_ Shift because \ doesn't
|
||||
// require it.
|
||||
WI_SetFlagIf(modifiers, KeyModifiers::Shift, WI_IsFlagSet(oemModifiers, 1U));
|
||||
WI_SetFlagIf(modifiers, KeyModifiers::Ctrl, WI_IsFlagSet(oemModifiers, 2U));
|
||||
WI_SetFlagIf(modifiers, KeyModifiers::Alt, WI_IsFlagSet(oemModifiers, 4U));
|
||||
foundKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we weren't able to find a match, throw an exception.
|
||||
if (!foundKey)
|
||||
{
|
||||
@@ -245,16 +240,6 @@ winrt::hstring KeyChordSerialization::ToString(const KeyChord& chord)
|
||||
buffer += vkeyNamePairs.at(vkey);
|
||||
serializedSuccessfully = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto mappedChar = MapVirtualKeyW(vkey, MAPVK_VK_TO_CHAR);
|
||||
if (mappedChar != 0)
|
||||
{
|
||||
wchar_t mappedWch = gsl::narrow_cast<wchar_t>(mappedChar);
|
||||
buffer += std::wstring_view{ &mappedWch, 1 };
|
||||
serializedSuccessfully = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!serializedSuccessfully)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
//
|
||||
// MinMaxCloseControl.xaml.cpp
|
||||
// Implementation of the MinMaxCloseControl class
|
||||
//
|
||||
@@ -10,7 +8,6 @@
|
||||
#include "MinMaxCloseControl.h"
|
||||
|
||||
#include "MinMaxCloseControl.g.cpp"
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
@@ -19,37 +16,59 @@ namespace winrt::TerminalApp::implementation
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
void MinMaxCloseControl::Maximize()
|
||||
uint64_t MinMaxCloseControl::ParentWindowHandle() const
|
||||
{
|
||||
VisualStateManager::GoToState(MaximizeButton(), L"WindowStateMaximized", false);
|
||||
return reinterpret_cast<uint64_t>(_window);
|
||||
}
|
||||
|
||||
void MinMaxCloseControl::RestoreDown()
|
||||
void MinMaxCloseControl::ParentWindowHandle(uint64_t handle)
|
||||
{
|
||||
VisualStateManager::GoToState(MaximizeButton(), L"WindowStateNormal", false);
|
||||
_window = reinterpret_cast<HWND>(handle);
|
||||
}
|
||||
|
||||
// These event handlers simply forward each buttons click events up to the
|
||||
// events we've exposed.
|
||||
void MinMaxCloseControl::_MinimizeClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
RoutedEventArgs const& e)
|
||||
void MinMaxCloseControl::_OnMaximize(byte flag)
|
||||
{
|
||||
_minimizeClickHandlers(*this, e);
|
||||
if (_window)
|
||||
{
|
||||
POINT point1 = {};
|
||||
::GetCursorPos(&point1);
|
||||
const LPARAM lParam = MAKELPARAM(point1.x, point1.y);
|
||||
WINDOWPLACEMENT placement = { sizeof(placement) };
|
||||
::GetWindowPlacement(_window, &placement);
|
||||
if (placement.showCmd == SW_SHOWNORMAL)
|
||||
{
|
||||
winrt::Windows::UI::Xaml::VisualStateManager::GoToState(this->Maximize(), L"WindowStateMaximized", false);
|
||||
::PostMessage(_window, WM_SYSCOMMAND, SC_MAXIMIZE | flag, lParam);
|
||||
}
|
||||
else if (placement.showCmd == SW_SHOWMAXIMIZED)
|
||||
{
|
||||
winrt::Windows::UI::Xaml::VisualStateManager::GoToState(this->Maximize(), L"WindowStateNormal", false);
|
||||
::PostMessage(_window, WM_SYSCOMMAND, SC_RESTORE | flag, lParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MinMaxCloseControl::_MaximizeClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
RoutedEventArgs const& e)
|
||||
void MinMaxCloseControl::Maximize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
|
||||
{
|
||||
_maximizeClickHandlers(*this, e);
|
||||
}
|
||||
void MinMaxCloseControl::_CloseClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
RoutedEventArgs const& e)
|
||||
{
|
||||
_closeClickHandlers(*this, e);
|
||||
_OnMaximize(HTMAXBUTTON);
|
||||
}
|
||||
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(MinMaxCloseControl, MinimizeClick, _minimizeClickHandlers, TerminalApp::MinMaxCloseControl, RoutedEventArgs);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(MinMaxCloseControl, MaximizeClick, _maximizeClickHandlers, TerminalApp::MinMaxCloseControl, RoutedEventArgs);
|
||||
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(MinMaxCloseControl, CloseClick, _closeClickHandlers, TerminalApp::MinMaxCloseControl, RoutedEventArgs);
|
||||
void MinMaxCloseControl::DragBar_DoubleTapped(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs const& e)
|
||||
{
|
||||
_OnMaximize(HTCAPTION);
|
||||
}
|
||||
|
||||
void MinMaxCloseControl::Minimize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
|
||||
{
|
||||
if (_window)
|
||||
{
|
||||
::PostMessage(_window, WM_SYSCOMMAND, SC_MINIMIZE | HTMINBUTTON, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MinMaxCloseControl::Close_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
|
||||
{
|
||||
::PostQuitMessage(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
//
|
||||
// Declaration of the MainUserControl class.
|
||||
//
|
||||
|
||||
@@ -10,7 +8,6 @@
|
||||
#include "winrt/Windows.UI.Xaml.Markup.h"
|
||||
#include "winrt/Windows.UI.Xaml.Interop.h"
|
||||
#include "MinMaxCloseControl.g.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
@@ -18,19 +15,17 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
MinMaxCloseControl();
|
||||
|
||||
void Maximize();
|
||||
void RestoreDown();
|
||||
void Minimize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void Maximize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void Close_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void DragBar_DoubleTapped(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs const& e);
|
||||
|
||||
void _MinimizeClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void _MaximizeClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void _CloseClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
uint64_t ParentWindowHandle() const;
|
||||
void ParentWindowHandle(uint64_t handle);
|
||||
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(MinimizeClick, _minimizeClickHandlers, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(MaximizeClick, _maximizeClickHandlers, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs);
|
||||
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(CloseClick, _closeClickHandlers, TerminalApp::MinMaxCloseControl, winrt::Windows::UI::Xaml::RoutedEventArgs);
|
||||
private:
|
||||
void _OnMaximize(byte flag);
|
||||
HWND _window{ nullptr }; // non-owning handle; should not be freed in the dtor.
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass MinMaxCloseControl : Windows.UI.Xaml.Controls.StackPanel
|
||||
[default_interface]
|
||||
runtimeclass MinMaxCloseControl : Windows.UI.Xaml.Controls.StackPanel
|
||||
{
|
||||
MinMaxCloseControl();
|
||||
|
||||
void Maximize();
|
||||
void RestoreDown();
|
||||
Windows.UI.Xaml.Controls.Grid Content{ get; };
|
||||
Windows.UI.Xaml.Controls.Border DragBar{ get; };
|
||||
|
||||
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;
|
||||
UInt64 ParentWindowHandle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information. -->
|
||||
<StackPanel
|
||||
<StackPanel
|
||||
x:Class="TerminalApp.MinMaxCloseControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
@@ -131,7 +129,13 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</ResourceDictionary>
|
||||
</StackPanel.Resources>
|
||||
|
||||
<Button Height="36.0" MinWidth="45.0" Width="45.0" x:Name="MinimizeButton" Style="{StaticResource CaptionButton}" Click="_MinimizeClick"
|
||||
<Grid x:Name="Content"></Grid>
|
||||
<Border Height="36.0"
|
||||
MinWidth="160.0"
|
||||
x:Name="DragBar"
|
||||
Background="{ThemeResource SystemChromeLowColor}"
|
||||
DoubleTapped="DragBar_DoubleTapped"/>
|
||||
<Button Height="36.0" Width="45.0" x:Name="Minimize" Style="{StaticResource CaptionButton}" Click="Minimize_Click"
|
||||
AutomationProperties.Name="Minimize">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
@@ -139,7 +143,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<Button Height="36.0" MinWidth="45.0" Width="45.0" x:Name="MaximizeButton" Style="{StaticResource CaptionButton}" Click="_MaximizeClick"
|
||||
<Button Height="36.0" Width="45.0" x:Name="Maximize" Style="{StaticResource CaptionButton}" Click="Maximize_Click"
|
||||
AutomationProperties.Name="Maximize">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
@@ -148,7 +152,7 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
</ResourceDictionary>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
<Button Height="36.0" MinWidth="45.0" Width="45.0" x:Name="CloseButton" Style="{StaticResource CaptionButton}" Click="_CloseClick"
|
||||
<Button Height="36.0" Width="45.0" x:Name="Close" Style="{StaticResource CaptionButton}" Click="Close_Click"
|
||||
AutomationProperties.Name="Close">
|
||||
<Button.Resources>
|
||||
<ResourceDictionary>
|
||||
|
||||
@@ -19,7 +19,7 @@ Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocus
|
||||
_lastFocused{ lastFocused },
|
||||
_profile{ profile }
|
||||
{
|
||||
_root.Children().Append(_control);
|
||||
_root.Children().Append(_control.GetControl());
|
||||
_connectionClosedToken = _control.ConnectionClosed({ this, &Pane::_ControlClosedHandler });
|
||||
|
||||
// Set the background of the pane to match that of the theme's default grid
|
||||
@@ -187,95 +187,6 @@ bool Pane::ResizePane(const Direction& direction)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to handle moving focus to one of our children. If our split
|
||||
// direction isn't appropriate for the move direction, then we'll return
|
||||
// false, to try and let our parent handle the move. If our child we'd move
|
||||
// focus to is already focused, we'll also return false, to again let our
|
||||
// parent try and handle the focus movement.
|
||||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - true if we handled this focus move request.
|
||||
bool Pane::_NavigateFocus(const Direction& direction)
|
||||
{
|
||||
if (!DirectionMatchesSplit(direction, _splitState))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool focusSecond = (direction == Direction::Right) || (direction == Direction::Down);
|
||||
|
||||
const auto newlyFocusedChild = focusSecond ? _secondChild : _firstChild;
|
||||
|
||||
// If the child we want to move focus to is _already_ focused, return false,
|
||||
// to try and let our parent figure it out.
|
||||
if (newlyFocusedChild->WasLastFocused())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Transfer focus to our child, and update the focus of our tree.
|
||||
newlyFocusedChild->_FocusFirstChild();
|
||||
UpdateFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempts to move focus to one of our children. If we have a focused child,
|
||||
// we'll try to move the focus in the direction requested.
|
||||
// - If there isn't a pane that exists as a child of this pane in the correct
|
||||
// direction, we'll return false. This will indicate to our parent that they
|
||||
// should try and move the focus themselves. In this way, the focus can move
|
||||
// up and down the tree to the correct pane.
|
||||
// - This method is _very_ similar to ResizePane. Both are trying to find the
|
||||
// right separator to move (focus) in a direction.
|
||||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - true if we or a child handled this focus move request.
|
||||
bool Pane::NavigateFocus(const Direction& direction)
|
||||
{
|
||||
// If we're a leaf, do nothing. We can't possibly have a descendant with a
|
||||
// separator the correct direction.
|
||||
if (_IsLeaf())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if either our first or second child is the currently focused leaf.
|
||||
// If it is, and the requested move direction matches our separator, then
|
||||
// we're the pane that needs to handle this focus move.
|
||||
const bool firstIsFocused = _firstChild->_IsLeaf() && _firstChild->_lastFocused;
|
||||
const bool secondIsFocused = _secondChild->_IsLeaf() && _secondChild->_lastFocused;
|
||||
if (firstIsFocused || secondIsFocused)
|
||||
{
|
||||
return _NavigateFocus(direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If neither of our children were the focused leaf, then recurse into
|
||||
// our children and see if they can handle the focus move.
|
||||
// For each child, if it has a focused descendant, try having that child
|
||||
// handle the focus move.
|
||||
// If the child wasn't able to handle the focus move, it's possible that
|
||||
// there were no descendants with a separator the correct direction. If
|
||||
// our separator _is_ the correct direction, then we should be the pane
|
||||
// to move focus into our other child. Otherwise, just return false, as
|
||||
// we couldn't handle it either.
|
||||
if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild())
|
||||
{
|
||||
return _firstChild->NavigateFocus(direction) || _NavigateFocus(direction);
|
||||
}
|
||||
else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild())
|
||||
{
|
||||
return _secondChild->NavigateFocus(direction) || _NavigateFocus(direction);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when our attached control is closed. Triggers listeners to our close
|
||||
// event, if we're a leaf pane.
|
||||
@@ -308,18 +219,6 @@ void Pane::_ControlClosedHandler()
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Fire our Closed event to tell our parent that we should be removed.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::Close()
|
||||
{
|
||||
// Fire our Closed event to tell our parent that we should be removed.
|
||||
_closedHandlers();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the root UIElement of this pane. There may be a single TermControl as a
|
||||
// child, or an entire tree of grids and panes as children of this element.
|
||||
@@ -426,7 +325,7 @@ bool Pane::_HasFocusedChild() const noexcept
|
||||
// We're intentionally making this one giant expression, so the compiler
|
||||
// will skip the following lookups if one of the lookups before it returns
|
||||
// true
|
||||
return (_control && _control.FocusState() != FocusState::Unfocused) ||
|
||||
return (_control && _control.GetControl().FocusState() != FocusState::Unfocused) ||
|
||||
(_firstChild && _firstChild->_HasFocusedChild()) ||
|
||||
(_secondChild && _secondChild->_HasFocusedChild());
|
||||
}
|
||||
@@ -445,7 +344,7 @@ void Pane::UpdateFocus()
|
||||
if (_IsLeaf())
|
||||
{
|
||||
const auto controlFocused = _control &&
|
||||
_control.FocusState() != FocusState::Unfocused;
|
||||
_control.GetControl().FocusState() != FocusState::Unfocused;
|
||||
|
||||
_lastFocused = controlFocused;
|
||||
}
|
||||
@@ -468,7 +367,7 @@ void Pane::_FocusFirstChild()
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
_control.Focus(FocusState::Programmatic);
|
||||
_control.GetControl().Focus(FocusState::Programmatic);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -564,11 +463,11 @@ void Pane::_CloseChild(const bool closeFirst)
|
||||
_separatorRoot = { nullptr };
|
||||
|
||||
// Reattach the TermControl to our grid.
|
||||
_root.Children().Append(_control);
|
||||
_root.Children().Append(_control.GetControl());
|
||||
|
||||
if (_lastFocused)
|
||||
{
|
||||
_control.Focus(FocusState::Programmatic);
|
||||
_control.GetControl().Focus(FocusState::Programmatic);
|
||||
}
|
||||
|
||||
_splitState = SplitState::None;
|
||||
|
||||
@@ -47,13 +47,10 @@ public:
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
bool ResizePane(const winrt::TerminalApp::Direction& direction);
|
||||
bool NavigateFocus(const winrt::TerminalApp::Direction& direction);
|
||||
|
||||
void SplitHorizontal(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
void SplitVertical(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
void Close();
|
||||
|
||||
DECLARE_EVENT(Closed, _closedHandlers, winrt::Microsoft::Terminal::TerminalControl::ConnectionClosedEventArgs);
|
||||
|
||||
private:
|
||||
@@ -83,9 +80,7 @@ private:
|
||||
void _CreateRowColDefinitions(const winrt::Windows::Foundation::Size& rootSize);
|
||||
void _CreateSplitContent();
|
||||
void _ApplySplitDefinitions();
|
||||
|
||||
bool _Resize(const winrt::TerminalApp::Direction& direction);
|
||||
bool _NavigateFocus(const winrt::TerminalApp::Direction& direction);
|
||||
|
||||
void _CloseChild(const bool closeFirst);
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ static constexpr std::string_view CursorColorKey{ "cursorColor" };
|
||||
static constexpr std::string_view CursorShapeKey{ "cursorShape" };
|
||||
static constexpr std::string_view CursorHeightKey{ "cursorHeight" };
|
||||
|
||||
static constexpr std::string_view ConnectionTypeKey{ "connectionType" };
|
||||
static constexpr std::string_view CommandlineKey{ "commandline" };
|
||||
static constexpr std::string_view FontFaceKey{ "fontFace" };
|
||||
static constexpr std::string_view FontSizeKey{ "fontSize" };
|
||||
@@ -39,8 +38,7 @@ static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" };
|
||||
static constexpr std::string_view IconKey{ "icon" };
|
||||
static constexpr std::string_view BackgroundImageKey{ "backgroundImage" };
|
||||
static constexpr std::string_view BackgroundImageOpacityKey{ "backgroundImageOpacity" };
|
||||
static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImageStretchMode" };
|
||||
static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" };
|
||||
static constexpr std::string_view BackgroundimageStretchModeKey{ "backgroundImageStretchMode" };
|
||||
|
||||
// Possible values for Scrollbar state
|
||||
static constexpr std::wstring_view AlwaysVisible{ L"visible" };
|
||||
@@ -59,17 +57,6 @@ static constexpr std::string_view ImageStretchModeFill{ "fill" };
|
||||
static constexpr std::string_view ImageStretchModeUniform{ "uniform" };
|
||||
static constexpr std::string_view ImageStretchModeUniformTofill{ "uniformToFill" };
|
||||
|
||||
// Possible values for Image Alignment
|
||||
static constexpr std::string_view ImageAlignmentCenter{ "center" };
|
||||
static constexpr std::string_view ImageAlignmentLeft{ "left" };
|
||||
static constexpr std::string_view ImageAlignmentTop{ "top" };
|
||||
static constexpr std::string_view ImageAlignmentRight{ "right" };
|
||||
static constexpr std::string_view ImageAlignmentBottom{ "bottom" };
|
||||
static constexpr std::string_view ImageAlignmentTopLeft{ "topLeft" };
|
||||
static constexpr std::string_view ImageAlignmentTopRight{ "topRight" };
|
||||
static constexpr std::string_view ImageAlignmentBottomLeft{ "bottomLeft" };
|
||||
static constexpr std::string_view ImageAlignmentBottomRight{ "bottomRight" };
|
||||
|
||||
Profile::Profile() :
|
||||
Profile(Utils::CreateGuid())
|
||||
{
|
||||
@@ -90,7 +77,6 @@ Profile::Profile(const winrt::guid& guid) :
|
||||
_cursorShape{ CursorStyle::Bar },
|
||||
_cursorHeight{ DEFAULT_CURSOR_HEIGHT },
|
||||
|
||||
_connectionType{},
|
||||
_commandline{ L"cmd.exe" },
|
||||
_startingDirectory{},
|
||||
_fontFace{ DEFAULT_FONT_FACE },
|
||||
@@ -103,8 +89,7 @@ Profile::Profile(const winrt::guid& guid) :
|
||||
_icon{},
|
||||
_backgroundImage{},
|
||||
_backgroundImageOpacity{},
|
||||
_backgroundImageStretchMode{},
|
||||
_backgroundImageAlignment{}
|
||||
_backgroundImageStretchMode{}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -215,14 +200,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::vector<ColorScheme>&
|
||||
terminalSettings.BackgroundImageStretchMode(_backgroundImageStretchMode.value());
|
||||
}
|
||||
|
||||
if (_backgroundImageAlignment)
|
||||
{
|
||||
const auto imageHorizontalAlignment = std::get<winrt::Windows::UI::Xaml::HorizontalAlignment>(_backgroundImageAlignment.value());
|
||||
const auto imageVerticalAlignment = std::get<winrt::Windows::UI::Xaml::VerticalAlignment>(_backgroundImageAlignment.value());
|
||||
terminalSettings.BackgroundImageHorizontalAlignment(imageHorizontalAlignment);
|
||||
terminalSettings.BackgroundImageVerticalAlignment(imageVerticalAlignment);
|
||||
}
|
||||
|
||||
return terminalSettings;
|
||||
}
|
||||
|
||||
@@ -282,10 +259,6 @@ Json::Value Profile::ToJson() const
|
||||
root[JsonKey(CloseOnExitKey)] = _closeOnExit;
|
||||
root[JsonKey(PaddingKey)] = winrt::to_string(_padding);
|
||||
|
||||
if (_connectionType)
|
||||
{
|
||||
root[JsonKey(ConnectionTypeKey)] = winrt::to_string(Utils::GuidToString(_connectionType.value()));
|
||||
}
|
||||
if (_scrollbarState)
|
||||
{
|
||||
const auto scrollbarState = winrt::to_string(_scrollbarState.value());
|
||||
@@ -320,12 +293,7 @@ Json::Value Profile::ToJson() const
|
||||
|
||||
if (_backgroundImageStretchMode)
|
||||
{
|
||||
root[JsonKey(BackgroundImageStretchModeKey)] = SerializeImageStretchMode(_backgroundImageStretchMode.value()).data();
|
||||
}
|
||||
|
||||
if (_backgroundImageAlignment)
|
||||
{
|
||||
root[JsonKey(BackgroundImageAlignmentKey)] = SerializeImageAlignment(_backgroundImageAlignment.value()).data();
|
||||
root[JsonKey(BackgroundimageStretchModeKey)] = SerializeImageStretchMode(_backgroundImageStretchMode.value()).data();
|
||||
}
|
||||
|
||||
return root;
|
||||
@@ -350,17 +318,6 @@ Profile Profile::FromJson(const Json::Value& json)
|
||||
{
|
||||
result._guid = Utils::GuidFromString(GetWstringFromJson(guid));
|
||||
}
|
||||
else
|
||||
{
|
||||
result._guid = Utils::CreateGuid();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"SynthesizedGuidForProfile",
|
||||
TraceLoggingDescription("Event emitted when a profile is deserialized without a GUID"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
|
||||
// Core Settings
|
||||
if (auto foreground{ json[JsonKey(ForegroundKey)] })
|
||||
@@ -423,10 +380,6 @@ Profile Profile::FromJson(const Json::Value& json)
|
||||
}
|
||||
|
||||
// Control Settings
|
||||
if (auto connectionType{ json[JsonKey(ConnectionTypeKey)] })
|
||||
{
|
||||
result._connectionType = Utils::GuidFromString(GetWstringFromJson(connectionType));
|
||||
}
|
||||
if (auto commandline{ json[JsonKey(CommandlineKey)] })
|
||||
{
|
||||
result._commandline = GetWstringFromJson(commandline);
|
||||
@@ -475,14 +428,10 @@ Profile Profile::FromJson(const Json::Value& json)
|
||||
{
|
||||
result._backgroundImageOpacity = backgroundImageOpacity.asFloat();
|
||||
}
|
||||
if (auto backgroundImageStretchMode{ json[JsonKey(BackgroundImageStretchModeKey)] })
|
||||
if (auto backgroundImageStretchMode{ json[JsonKey(BackgroundimageStretchModeKey)] })
|
||||
{
|
||||
result._backgroundImageStretchMode = ParseImageStretchMode(backgroundImageStretchMode.asString());
|
||||
}
|
||||
if (auto backgroundImageAlignment{ json[JsonKey(BackgroundImageAlignmentKey)] })
|
||||
{
|
||||
result._backgroundImageAlignment = ParseImageAlignment(backgroundImageAlignment.asString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -532,19 +481,9 @@ void Profile::SetDefaultBackground(COLORREF defaultBackground) noexcept
|
||||
_defaultBackground = defaultBackground;
|
||||
}
|
||||
|
||||
void Profile::SetCloseOnExit(bool defaultClose) noexcept
|
||||
{
|
||||
_closeOnExit = defaultClose;
|
||||
}
|
||||
|
||||
void Profile::SetConnectionType(GUID connectionType) noexcept
|
||||
{
|
||||
_connectionType = connectionType;
|
||||
}
|
||||
|
||||
bool Profile::HasIcon() const noexcept
|
||||
{
|
||||
return _icon.has_value() && !_icon.value().empty();
|
||||
return _icon.has_value();
|
||||
}
|
||||
|
||||
// Method Description
|
||||
@@ -607,18 +546,6 @@ std::wstring_view Profile::GetTabTitle() const noexcept
|
||||
std::wstring_view{ L"", 0 };
|
||||
}
|
||||
|
||||
bool Profile::HasConnectionType() const noexcept
|
||||
{
|
||||
return _connectionType.has_value();
|
||||
}
|
||||
|
||||
GUID Profile::GetConnectionType() const noexcept
|
||||
{
|
||||
return HasConnectionType() ?
|
||||
_connectionType.value() :
|
||||
_GUID{};
|
||||
}
|
||||
|
||||
bool Profile::GetCloseOnExit() const noexcept
|
||||
{
|
||||
return _closeOnExit;
|
||||
@@ -727,114 +654,6 @@ std::string_view Profile::SerializeImageStretchMode(const winrt::Windows::UI::Xa
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified image horizontal and vertical
|
||||
// alignment to the appropriate enum values tuple
|
||||
// Arguments:
|
||||
// - The value from the profiles.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum values tuple which maps to the string provided by the user
|
||||
std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> Profile::ParseImageAlignment(const std::string_view imageAlignment)
|
||||
{
|
||||
if (imageAlignment == ImageAlignmentTopLeft)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Left,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottomLeft)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Left,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Bottom);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentLeft)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Left,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Center);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentTopRight)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Right,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottomRight)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Right,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Bottom);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentRight)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Right,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Center);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentTop)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Center,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottom)
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Center,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Bottom);
|
||||
}
|
||||
else // Fall through to default alignment
|
||||
{
|
||||
return std::make_tuple(winrt::Windows::UI::Xaml::HorizontalAlignment::Center,
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment::Center);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the HorizontalAlignment+VerticalAlignment tuple
|
||||
// to the correct string value.
|
||||
// Arguments:
|
||||
// - imageAlignment: The enum values tuple to convert to a string.
|
||||
// Return Value:
|
||||
// - The string value for the given ImageAlignment
|
||||
std::string_view Profile::SerializeImageAlignment(const std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> imageAlignment)
|
||||
{
|
||||
const auto imageHorizontalAlignment = std::get<winrt::Windows::UI::Xaml::HorizontalAlignment>(imageAlignment);
|
||||
const auto imageVerticalAlignment = std::get<winrt::Windows::UI::Xaml::VerticalAlignment>(imageAlignment);
|
||||
switch (imageHorizontalAlignment)
|
||||
{
|
||||
case winrt::Windows::UI::Xaml::HorizontalAlignment::Left:
|
||||
switch (imageVerticalAlignment)
|
||||
{
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Top:
|
||||
return ImageAlignmentTopLeft;
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Bottom:
|
||||
return ImageAlignmentBottomLeft;
|
||||
default:
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Center:
|
||||
return ImageAlignmentLeft;
|
||||
}
|
||||
|
||||
case winrt::Windows::UI::Xaml::HorizontalAlignment::Right:
|
||||
switch (imageVerticalAlignment)
|
||||
{
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Top:
|
||||
return ImageAlignmentTopRight;
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Bottom:
|
||||
return ImageAlignmentBottomRight;
|
||||
default:
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Center:
|
||||
return ImageAlignmentRight;
|
||||
}
|
||||
|
||||
default:
|
||||
case winrt::Windows::UI::Xaml::HorizontalAlignment::Center:
|
||||
switch (imageVerticalAlignment)
|
||||
{
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Top:
|
||||
return ImageAlignmentTop;
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Bottom:
|
||||
return ImageAlignmentBottom;
|
||||
default:
|
||||
case winrt::Windows::UI::Xaml::VerticalAlignment::Center:
|
||||
return ImageAlignmentCenter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified cursor style corresponding
|
||||
// CursorStyle enum value
|
||||
|
||||
@@ -38,8 +38,6 @@ public:
|
||||
std::wstring_view GetName() const noexcept;
|
||||
bool HasTabTitle() const noexcept;
|
||||
std::wstring_view GetTabTitle() const noexcept;
|
||||
bool HasConnectionType() const noexcept;
|
||||
GUID GetConnectionType() const noexcept;
|
||||
|
||||
void SetFontFace(std::wstring fontFace) noexcept;
|
||||
void SetColorScheme(std::optional<std::wstring> schemeName) noexcept;
|
||||
@@ -51,8 +49,6 @@ public:
|
||||
void SetUseAcrylic(bool useAcrylic) noexcept;
|
||||
void SetDefaultForeground(COLORREF defaultForeground) noexcept;
|
||||
void SetDefaultBackground(COLORREF defaultBackground) noexcept;
|
||||
void SetCloseOnExit(bool defaultClose) noexcept;
|
||||
void SetConnectionType(GUID connectionType) noexcept;
|
||||
|
||||
bool HasIcon() const noexcept;
|
||||
std::wstring_view GetIconPath() const noexcept;
|
||||
@@ -66,14 +62,11 @@ private:
|
||||
static winrt::Microsoft::Terminal::Settings::ScrollbarState ParseScrollbarState(const std::wstring& scrollbarState);
|
||||
static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::string_view imageStretchMode);
|
||||
static std::string_view SerializeImageStretchMode(const winrt::Windows::UI::Xaml::Media::Stretch imageStretchMode);
|
||||
static std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> ParseImageAlignment(const std::string_view imageAlignment);
|
||||
static std::string_view SerializeImageAlignment(const std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> imageAlignment);
|
||||
static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString);
|
||||
static std::wstring_view _SerializeCursorStyle(const winrt::Microsoft::Terminal::Settings::CursorStyle cursorShape);
|
||||
|
||||
GUID _guid;
|
||||
std::wstring _name;
|
||||
std::optional<GUID> _connectionType;
|
||||
|
||||
// If this is set, then our colors should come from the associated color scheme
|
||||
std::optional<std::wstring> _schemeName;
|
||||
@@ -98,7 +91,6 @@ private:
|
||||
std::optional<std::wstring> _backgroundImage;
|
||||
std::optional<double> _backgroundImageOpacity;
|
||||
std::optional<winrt::Windows::UI::Xaml::Media::Stretch> _backgroundImageStretchMode;
|
||||
std::optional<std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment>> _backgroundImageAlignment;
|
||||
|
||||
std::optional<std::wstring> _scrollbarState;
|
||||
bool _closeOnExit;
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="InitialJsonParseErrorText" xml:space="preserve">
|
||||
<value>Settings could not be loaded from file - temporarily using the default settings. Check for syntax errors, including trailing commas.</value>
|
||||
</data>
|
||||
<data name="InitialJsonParseErrorTitle" xml:space="preserve">
|
||||
<value>Failed to load settings</value>
|
||||
</data>
|
||||
<data name="Ok" xml:space="preserve">
|
||||
<value>Ok</value>
|
||||
</data>
|
||||
<data name="ReloadJsonParseErrorText" xml:space="preserve">
|
||||
<value>Settings could not be reloaded from file. Check for syntax errors, including trailing commas.</value>
|
||||
</data>
|
||||
<data name="ReloadJsonParseErrorTitle" xml:space="preserve">
|
||||
<value>Failed to reload settings</value>
|
||||
</data>
|
||||
<data name="AboutTitleText" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="VersionLabelText" xml:space="preserve">
|
||||
<value>Version:</value>
|
||||
</data>
|
||||
<data name="DocumentationLabelText" xml:space="preserve">
|
||||
<value>Documentation
|
||||
</value>
|
||||
</data>
|
||||
<data name="GettingStartedLabelText" xml:space="preserve">
|
||||
<value>Getting Started
|
||||
</value>
|
||||
</data>
|
||||
<data name="ReleaseNotesLabelText" xml:space="preserve">
|
||||
<value>Release Notes
|
||||
</value>
|
||||
</data>
|
||||
<data name="DocumentationUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-documentation</value>
|
||||
</data>
|
||||
<data name="GettingStartedUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-getting-started</value>
|
||||
</data>
|
||||
<data name="ReleaseNotesUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-release-notes</value>
|
||||
</data>
|
||||
<data name="FeedbackUriValue" xml:space="preserve">
|
||||
<value>https://aka.ms/terminal-feedback</value>
|
||||
</data>
|
||||
<data name="AboutMenuItem" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="FeedbackMenuItem" xml:space="preserve">
|
||||
<value>Feedback</value>
|
||||
</data>
|
||||
<data name="SettingsMenuItem" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "ScopedResourceLoader.h"
|
||||
|
||||
using namespace ::winrt::Windows::ApplicationModel::Resources::Core;
|
||||
|
||||
ScopedResourceLoader::ScopedResourceLoader(const std::wstring_view resourceLocatorBase) :
|
||||
_resourceMap{ ResourceManager::Current().MainResourceMap().GetSubtree(resourceLocatorBase) },
|
||||
_resourceContext{ ResourceContext::GetForViewIndependentUse() }
|
||||
{
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the resource map associated with the scoped resource subcompartment.
|
||||
// Return Value:
|
||||
// - the resource map associated with the scoped resource subcompartment.
|
||||
ResourceMap ScopedResourceLoader::GetResourceMap()
|
||||
{
|
||||
return _resourceMap;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Loads the localized string resource with the given key from the scoped
|
||||
// resource subcompartment.
|
||||
// - This resource loader is view-independent; it cannot take into account scale
|
||||
// factors and view themes. Strings should not vary based on those things.
|
||||
// Arguments:
|
||||
// - resourceName: the key up by which to look the resource
|
||||
// Return Value:
|
||||
// - The final localized string for the given key.
|
||||
winrt::hstring ScopedResourceLoader::GetLocalizedString(const std::wstring_view resourceName)
|
||||
{
|
||||
return _resourceMap.GetValue(resourceName, _resourceContext).ValueAsString();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
class ScopedResourceLoader
|
||||
{
|
||||
public:
|
||||
ScopedResourceLoader(const std::wstring_view resourceLocatorBase);
|
||||
winrt::Windows::ApplicationModel::Resources::Core::ResourceMap GetResourceMap();
|
||||
winrt::hstring GetLocalizedString(const std::wstring_view resourceName);
|
||||
|
||||
private:
|
||||
winrt::Windows::ApplicationModel::Resources::Core::ResourceMap _resourceMap;
|
||||
winrt::Windows::ApplicationModel::Resources::Core::ResourceContext _resourceContext;
|
||||
};
|
||||
@@ -124,7 +124,7 @@ void Tab::_Focus()
|
||||
auto lastFocusedControl = _rootPane->GetFocusedTerminalControl();
|
||||
if (lastFocusedControl)
|
||||
{
|
||||
lastFocusedControl.Focus(FocusState::Programmatic);
|
||||
lastFocusedControl.GetControl().Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ void Tab::SetTabText(const winrt::hstring& text)
|
||||
void Tab::Scroll(const int delta)
|
||||
{
|
||||
auto control = GetFocusedTerminalControl();
|
||||
control.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [control, delta]() {
|
||||
control.GetControl().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [control, delta]() {
|
||||
const auto currentOffset = control.GetScrollOffset();
|
||||
control.KeyboardScrollViewport(currentOffset + delta);
|
||||
});
|
||||
@@ -237,30 +237,4 @@ void Tab::ResizePane(const winrt::TerminalApp::Direction& direction)
|
||||
_rootPane->ResizePane(direction);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Attempt to move focus between panes, as to focus the child on
|
||||
// the other side of the separator. See Pane::NavigateFocus for details.
|
||||
// Arguments:
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::NavigateFocus(const winrt::TerminalApp::Direction& direction)
|
||||
{
|
||||
_rootPane->NavigateFocus(direction);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Closes the currently focused pane in this tab. If it's the last pane in
|
||||
// this tab, our Closed event will be fired (at a later time) for anyone
|
||||
// registered as a handler of our close event.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ClosePane()
|
||||
{
|
||||
auto focused = _rootPane->GetFocusedPane();
|
||||
focused->Close();
|
||||
}
|
||||
|
||||
DEFINE_EVENT(Tab, Closed, _closedHandlers, ConnectionClosedEventArgs);
|
||||
|
||||
@@ -25,14 +25,11 @@ public:
|
||||
void UpdateFocus();
|
||||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void ResizePane(const winrt::TerminalApp::Direction& direction);
|
||||
void NavigateFocus(const winrt::TerminalApp::Direction& direction);
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::TerminalSettings& settings, const GUID& profile);
|
||||
winrt::hstring GetFocusedTitle() const;
|
||||
void SetTabText(const winrt::hstring& text);
|
||||
|
||||
void ClosePane();
|
||||
|
||||
DECLARE_EVENT(Closed, _closedHandlers, winrt::Microsoft::Terminal::TerminalControl::ConnectionClosedEventArgs);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "TabRowControl.h"
|
||||
|
||||
#include "TabRowControl.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TabRowControl::TabRowControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Bound in the Xaml editor to the [+] button.
|
||||
// Arguments:
|
||||
// <unused>
|
||||
void TabRowControl::OnNewTabButtonClick(IInspectable const&, Controls::SplitButtonClickEventArgs const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "winrt/Microsoft.UI.Xaml.Controls.h"
|
||||
|
||||
#include "TabRowControl.g.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct TabRowControl : TabRowControlT<TabRowControl>
|
||||
{
|
||||
TabRowControl();
|
||||
|
||||
void OnNewTabButtonClick(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::SplitButtonClickEventArgs const& args);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
struct TabRowControl : TabRowControlT<TabRowControl, implementation::TabRowControl>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TabRowControl : Windows.UI.Xaml.Controls.Grid
|
||||
{
|
||||
TabRowControl();
|
||||
Windows.UI.Xaml.Controls.SplitButton NewTabButton { get; };
|
||||
Microsoft.UI.Xaml.Controls.TabView TabView { get; };
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Grid
|
||||
x:Class="TerminalApp.TabRowControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<mux:TabView x:Name="TabView" VerticalAlignment="Bottom">
|
||||
|
||||
<mux:TabView.RightCustomContent>
|
||||
<SplitButton
|
||||
x:Name="NewTabButton"
|
||||
Click="OnNewTabButtonClick"
|
||||
Background="{ThemeResource SystemChromeLowColor}"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Left">
|
||||
<Viewbox MaxHeight="13"
|
||||
MaxWidth="13">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
</SplitButton>
|
||||
</mux:TabView.RightCustomContent>
|
||||
|
||||
</mux:TabView>
|
||||
|
||||
</Grid>
|
||||
@@ -30,8 +30,6 @@
|
||||
<ClInclude Include="TerminalPage.h" />
|
||||
<ClInclude Include="MinMaxCloseControl.h" />
|
||||
<ClInclude Include="AppKeyBindings.h" />
|
||||
<ClInclude Include="TitlebarControl.h" />
|
||||
<ClInclude Include="TabRowControl.h" />
|
||||
<ClInclude Include="App.h" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
@@ -99,42 +97,10 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
||||
<!-- TerminalAppLib contains a DllMain that we need to force the use of. -->
|
||||
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12</AdditionalOptions>
|
||||
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<!--
|
||||
This is a terrible, terrible rule. There exists a bug in Visual Studio 2019 16.2 and 16.3 previews
|
||||
where ResolveAssemblyReferences will try and fail to parse a .lib when it produces a .winmd.
|
||||
To fix that, we have to _temporarily_ replace the %(Implementation) on any winmd-producing
|
||||
static library references with the empty string so as to make ResolveAssemblyReferences
|
||||
not try to read it.
|
||||
|
||||
Upstream problem report:
|
||||
https://developercommunity.visualstudio.com/content/problem/629524/static-library-reference-causes-there-was-a-proble.html
|
||||
-->
|
||||
<Target Name="_RemoveTerminalAppLibImplementationFromReference" BeforeTargets="ResolveAssemblyReferences">
|
||||
<ItemGroup>
|
||||
<_TerminalAppLibProjectReference Include="@(_ResolvedProjectReferencePaths)" Condition="'%(Filename)' == 'TerminalApp'" />
|
||||
<_ResolvedProjectReferencePaths Remove="@(_TerminalAppLibProjectReference)" />
|
||||
<_ResolvedProjectReferencePaths Include="@(_TerminalAppLibProjectReference)">
|
||||
<Implementation />
|
||||
</_ResolvedProjectReferencePaths>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_RestoreTerminalAppLibImplementationFromReference" AfterTargets="ResolveAssemblyReferences">
|
||||
<ItemGroup>
|
||||
<_ResolvedProjectReferencePaths Remove="@(_TerminalAppLibProjectReference)" />
|
||||
<_ResolvedProjectReferencePaths Include="@(_TerminalAppLibProjectReference)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<!-- End "terrible, terrible rule" -->
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -15,4 +15,13 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Bound in the Xaml editor to the [+] button.
|
||||
// Arguments:
|
||||
// - sender
|
||||
// - event arguments
|
||||
void TerminalPage::OnNewTabButtonClick(IInspectable const&, Controls::SplitButtonClickEventArgs const&)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace winrt::TerminalApp::implementation
|
||||
struct TerminalPage : TerminalPageT<TerminalPage>
|
||||
{
|
||||
TerminalPage();
|
||||
|
||||
void OnNewTabButtonClick(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::SplitButtonClickEventArgs const& args);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page
|
||||
[default_interface]
|
||||
runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
TerminalPage();
|
||||
}
|
||||
|
||||
@@ -10,13 +10,39 @@ the MIT License. See LICENSE in the project root for license information. -->
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="Root" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<local:TabRowControl x:Name="TabRow" Grid.Row="0" />
|
||||
<Grid x:Name="TabRow" Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<mux:TabView x:Name="TabView" Grid.Column="0" />
|
||||
|
||||
<SplitButton
|
||||
x:Name="NewTabButton"
|
||||
Grid.Column="1"
|
||||
Click="OnNewTabButtonClick"
|
||||
Background="{ThemeResource SystemChromeLowColor}"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Left">
|
||||
<Viewbox MaxHeight="15"
|
||||
MaxWidth="15">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
</SplitButton>
|
||||
|
||||
<local:MinMaxCloseControl
|
||||
x:Name="MinMaxCloseControl"
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
|
||||
<Grid x:Name="TabContent" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||
</Grid>
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// TitlebarControl.xaml.cpp
|
||||
// Implementation of the TitlebarControl class
|
||||
//
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "TitlebarControl.h"
|
||||
|
||||
#include "TitlebarControl.g.cpp"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TitlebarControl::TitlebarControl(uint64_t handle) :
|
||||
_window{ reinterpret_cast<HWND>(handle) }
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Register our event handlers on the MMC buttons.
|
||||
MinMaxCloseControl().MinimizeClick({ this, &TitlebarControl::Minimize_Click });
|
||||
MinMaxCloseControl().MaximizeClick({ this, &TitlebarControl::Maximize_Click });
|
||||
MinMaxCloseControl().CloseClick({ this, &TitlebarControl::Close_Click });
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::UIElement TitlebarControl::Content()
|
||||
{
|
||||
return ContentRoot().Children().Size() > 0 ? ContentRoot().Children().GetAt(0) : nullptr;
|
||||
}
|
||||
|
||||
void TitlebarControl::Content(Windows::UI::Xaml::UIElement content)
|
||||
{
|
||||
ContentRoot().Children().Clear();
|
||||
ContentRoot().Children().Append(content);
|
||||
}
|
||||
|
||||
void TitlebarControl::Root_SizeChanged(const IInspectable& sender,
|
||||
const Windows::UI::Xaml::SizeChangedEventArgs& e)
|
||||
{
|
||||
const auto windowWidth = ActualWidth();
|
||||
const auto minMaxCloseWidth = MinMaxCloseControl().ActualWidth();
|
||||
const auto dragBarMinWidth = DragBar().MinWidth();
|
||||
const auto maxWidth = windowWidth - minMaxCloseWidth - dragBarMinWidth;
|
||||
// Only set our MaxWidth if it's greater than 0. Setting it to a
|
||||
// negative value will cause a crash.
|
||||
if (maxWidth >= 0)
|
||||
{
|
||||
ContentRoot().MaxWidth(maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void TitlebarControl::_OnMaximizeOrRestore(byte flag)
|
||||
{
|
||||
POINT point1 = {};
|
||||
::GetCursorPos(&point1);
|
||||
const LPARAM lParam = MAKELPARAM(point1.x, point1.y);
|
||||
WINDOWPLACEMENT placement = { sizeof(placement) };
|
||||
::GetWindowPlacement(_window, &placement);
|
||||
if (placement.showCmd == SW_SHOWNORMAL)
|
||||
{
|
||||
MinMaxCloseControl().Maximize();
|
||||
::PostMessage(_window, WM_SYSCOMMAND, SC_MAXIMIZE | flag, lParam);
|
||||
}
|
||||
else if (placement.showCmd == SW_SHOWMAXIMIZED)
|
||||
{
|
||||
MinMaxCloseControl().RestoreDown();
|
||||
::PostMessage(_window, WM_SYSCOMMAND, SC_RESTORE | flag, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
void TitlebarControl::Maximize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
|
||||
{
|
||||
_OnMaximizeOrRestore(HTMAXBUTTON);
|
||||
}
|
||||
|
||||
void TitlebarControl::DragBar_DoubleTapped(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs const& e)
|
||||
{
|
||||
_OnMaximizeOrRestore(HTCAPTION);
|
||||
}
|
||||
|
||||
void TitlebarControl::Minimize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
|
||||
{
|
||||
if (_window)
|
||||
{
|
||||
::PostMessage(_window, WM_SYSCOMMAND, SC_MINIMIZE | HTMINBUTTON, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TitlebarControl::Close_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
|
||||
{
|
||||
::PostQuitMessage(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// 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
|
||||
{
|
||||
struct TitlebarControl : TitlebarControlT<TitlebarControl>
|
||||
{
|
||||
TitlebarControl(uint64_t handle);
|
||||
|
||||
Windows::UI::Xaml::UIElement Content();
|
||||
void Content(Windows::UI::Xaml::UIElement content);
|
||||
|
||||
void Root_SizeChanged(const IInspectable& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e);
|
||||
|
||||
void Minimize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void Maximize_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void Close_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void DragBar_DoubleTapped(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs const& e);
|
||||
|
||||
private:
|
||||
void _OnMaximizeOrRestore(byte flag);
|
||||
HWND _window{ nullptr }; // non-owning handle; should not be freed in the dtor.
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
struct TitlebarControl : TitlebarControlT<TitlebarControl, implementation::TitlebarControl>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TitlebarControl : Windows.UI.Xaml.Controls.Grid
|
||||
{
|
||||
TitlebarControl(UInt64 parentWindowHandle);
|
||||
|
||||
Windows.UI.Xaml.UIElement Content;
|
||||
Windows.UI.Xaml.Controls.Border DragBar { get; };
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information. -->
|
||||
<Grid
|
||||
x:Class="TerminalApp.TitlebarControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Name="Root"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
SizeChanged="Root_SizeChanged"
|
||||
d:DesignHeight="36"
|
||||
Background="{ThemeResource SystemChromeLowColor}"
|
||||
d:DesignWidth="400">
|
||||
|
||||
<!-- TODO:GH#1988
|
||||
This xaml should probably be a template thing, where the background is a
|
||||
resource that the app hosting this control can override. Then, it App.xaml,
|
||||
we'd make sure to set the resource for our background to the appropriate
|
||||
color. SystemControlForegroundAccentBrush also works nicely, to use the
|
||||
accent color. (which is GH#1963)-->
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid x:Name="ContentRoot" Grid.Column="0" />
|
||||
|
||||
<Border
|
||||
x:Name="DragBar"
|
||||
Grid.Column="1"
|
||||
MinWidth="45.0"
|
||||
DoubleTapped="DragBar_DoubleTapped"/>
|
||||
|
||||
<local:MinMaxCloseControl
|
||||
Grid.Column="2"
|
||||
x:Name="MinMaxCloseControl"
|
||||
HorizontalAlignment="Right" />
|
||||
</Grid>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// Note: Generate GUID using TlgGuid.exe tool
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hTerminalAppProvider,
|
||||
"Microsoft.Windows.Terminal.App",
|
||||
// {24a1622f-7da7-5c77-3303-d850bd1ab2ed}
|
||||
(0x24a1622f, 0x7da7, 0x5c77, 0x33, 0x03, 0xd8, 0x50, 0xbd, 0x1a, 0xb2, 0xed),
|
||||
TraceLoggingOptionMicrosoftTelemetry());
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(hInstDll);
|
||||
TraceLoggingRegister(g_hTerminalAppProvider);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (g_hTerminalAppProvider)
|
||||
{
|
||||
TraceLoggingUnregister(g_hTerminalAppProvider);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -35,12 +35,6 @@
|
||||
<Page Include="../TerminalPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="../TitlebarControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="../TabRowControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Headers ======================== -->
|
||||
@@ -53,12 +47,6 @@
|
||||
<DependentUpon>../TerminalPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../TitlebarControl.h">
|
||||
<DependentUpon>../TitlebarControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../TabRowControl.h">
|
||||
<DependentUpon>../TabRowControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../Tab.h" />
|
||||
<ClInclude Include="../Pane.h" />
|
||||
<ClInclude Include="../ColorScheme.h" />
|
||||
@@ -77,7 +65,6 @@
|
||||
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
<ClCompile Include="../init.cpp" />
|
||||
<ClCompile Include="../MinMaxCloseControl.cpp">
|
||||
<DependentUpon>../MinMaxCloseControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -85,12 +72,6 @@
|
||||
<DependentUpon>../TerminalPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="../TitlebarControl.cpp">
|
||||
<DependentUpon>../TitlebarControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="../TabRowControl.cpp">
|
||||
<DependentUpon>../TabRowControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="../Tab.cpp" />
|
||||
<ClCompile Include="../Pane.cpp" />
|
||||
<ClCompile Include="../ColorScheme.cpp" />
|
||||
@@ -101,7 +82,6 @@
|
||||
<ClCompile Include="../AppKeyBindingsSerialization.cpp" />
|
||||
<ClCompile Include="../KeyChordSerialization.cpp" />
|
||||
<ClCompile Include="../Utils.cpp" />
|
||||
<ClCompile Include="../ScopedResourceLoader.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@@ -136,19 +116,10 @@
|
||||
<DependentUpon>../TerminalPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="../TitlebarControl.idl">
|
||||
<DependentUpon>../TitlebarControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="../TabRowControl.idl">
|
||||
<DependentUpon>../TabRowControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ========================= Misc Files ======================== -->
|
||||
<ItemGroup>
|
||||
<PRIResource Include="../Resources/en-US/Resources.resw" />
|
||||
<None Include="../packages.config" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -261,7 +232,7 @@
|
||||
<Implementation>Microsoft.UI.Xaml.dll</Implementation>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
<Private>true</Private>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<ReferenceCopyLocalPaths Include="$(_MUXRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.UI.Xaml.dll" />
|
||||
<ReferenceCopyLocalPaths Include="$(_MUXRoot)runtimes\win10-$(Native-Platform)\native\Microsoft.UI.Xaml.pri" />
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <hstring.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
|
||||
#include <winrt/Windows.ApplicationModel.Resources.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/windows.ui.core.h>
|
||||
@@ -45,7 +45,7 @@
|
||||
// Including TraceLogging essentials for the binary
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <winmeta.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalWin32Provider);
|
||||
#include <telemetry\ProjectTelemetry.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cpprest/details/web_utilities.h>
|
||||
|
||||
const utility::string_t AzureClientID = U("0");
|
||||
@@ -1,889 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AzureConnection.h"
|
||||
#include "AzureClientID.h"
|
||||
#include "AzureConnectionStrings.h"
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "AzureConnection.g.cpp"
|
||||
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
using namespace utility;
|
||||
using namespace web;
|
||||
using namespace web::json;
|
||||
using namespace web::http;
|
||||
using namespace web::http::client;
|
||||
using namespace web::websockets::client;
|
||||
using namespace concurrency::streams;
|
||||
using namespace winrt::Windows::Security::Credentials;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
{
|
||||
AzureConnection::AzureConnection(const uint32_t initialRows, const uint32_t initialCols) :
|
||||
_initialRows{ initialRows },
|
||||
_initialCols{ initialCols }
|
||||
{
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - registers an output event handler
|
||||
// Arguments:
|
||||
// - the handler
|
||||
// Return value:
|
||||
// - the event token for the handler
|
||||
winrt::event_token AzureConnection::TerminalOutput(Microsoft::Terminal::TerminalConnection::TerminalOutputEventArgs const& handler)
|
||||
{
|
||||
return _outputHandlers.add(handler);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - revokes an output event handler
|
||||
// Arguments:
|
||||
// - the event token for the handler
|
||||
void AzureConnection::TerminalOutput(winrt::event_token const& token) noexcept
|
||||
{
|
||||
_outputHandlers.remove(token);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - registers a terminal-disconnected event handler
|
||||
// Arguments:
|
||||
// - the handler
|
||||
// Return value:
|
||||
// - the event token for the handler
|
||||
winrt::event_token AzureConnection::TerminalDisconnected(Microsoft::Terminal::TerminalConnection::TerminalDisconnectedEventArgs const& handler)
|
||||
{
|
||||
return _disconnectHandlers.add(handler);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - revokes a terminal-disconnected event handler
|
||||
// Arguments:
|
||||
// - the event token for the handler
|
||||
void AzureConnection::TerminalDisconnected(winrt::event_token const& token) noexcept
|
||||
{
|
||||
_disconnectHandlers.remove(token);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - creates the output thread (where we will do the authentication and actually connect to Azure)
|
||||
void AzureConnection::Start()
|
||||
{
|
||||
// Create our own output handling thread
|
||||
// Each connection needs to make sure to drain the output from its backing host.
|
||||
_hOutputThread.reset(CreateThread(nullptr,
|
||||
0,
|
||||
StaticOutputThreadProc,
|
||||
this,
|
||||
0,
|
||||
nullptr));
|
||||
|
||||
THROW_LAST_ERROR_IF_NULL(_hOutputThread);
|
||||
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - handles the different possible inputs in the different states
|
||||
// Arguments:
|
||||
// the user's input
|
||||
void AzureConnection::WriteInput(hstring const& data)
|
||||
{
|
||||
if (!_connected || _closing.load())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the input differently depending on which state we're in
|
||||
switch (_state)
|
||||
{
|
||||
// The user has stored connection settings, let them choose one of them, create a new one or remove all stored ones
|
||||
case State::AccessStored:
|
||||
{
|
||||
const auto s = winrt::to_string(data);
|
||||
int storeNum = -1;
|
||||
try
|
||||
{
|
||||
storeNum = std::stoi(s);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (s != "n" && s != "r")
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(invalidAccessInput));
|
||||
return;
|
||||
}
|
||||
else if (s == "r")
|
||||
{
|
||||
std::lock_guard<std::mutex> lg{ _commonMutex };
|
||||
_removeOrNew = true;
|
||||
_canProceed.notify_one();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lg{ _commonMutex };
|
||||
_removeOrNew = false;
|
||||
_canProceed.notify_one();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (storeNum >= _maxStored)
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(numOutOfBoundsError));
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lg{ _commonMutex };
|
||||
_storedNumber = storeNum;
|
||||
_canProceed.notify_one();
|
||||
return;
|
||||
}
|
||||
// The user has multiple tenants in their Azure account, let them choose one of them
|
||||
case State::TenantChoice:
|
||||
{
|
||||
int tenantNum = -1;
|
||||
try
|
||||
{
|
||||
tenantNum = std::stoi(winrt::to_string(data));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(nonNumberError));
|
||||
return;
|
||||
}
|
||||
if (tenantNum >= _maxSize)
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(numOutOfBoundsError));
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> lg{ _commonMutex };
|
||||
_tenantNumber = tenantNum;
|
||||
_canProceed.notify_one();
|
||||
return;
|
||||
}
|
||||
// User has the option to save their connection settings for future logins
|
||||
case State::StoreTokens:
|
||||
{
|
||||
std::string s = winrt::to_string(data);
|
||||
if (s != "y" && s != "n")
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(invalidStoreInput));
|
||||
}
|
||||
else if (s == "y")
|
||||
{
|
||||
std::lock_guard<std::mutex> lg{ _commonMutex };
|
||||
_store = true;
|
||||
_canProceed.notify_one();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard<std::mutex> lg{ _commonMutex };
|
||||
_store = false;
|
||||
_canProceed.notify_one();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// We are connected, send user's input over the websocket
|
||||
case State::TermConnected:
|
||||
{
|
||||
websocket_outgoing_message msg;
|
||||
const auto str = winrt::to_string(data);
|
||||
msg.set_utf8_message(str);
|
||||
|
||||
_cloudShellSocket.send(msg);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - resizes the terminal
|
||||
// Arguments:
|
||||
// - the new rows/cols values
|
||||
void AzureConnection::Resize(uint32_t rows, uint32_t columns)
|
||||
{
|
||||
if (!_connected || !(_state == State::TermConnected))
|
||||
{
|
||||
_initialRows = rows;
|
||||
_initialCols = columns;
|
||||
}
|
||||
else if (!_closing.load())
|
||||
{
|
||||
// Initialize client
|
||||
http_client terminalClient(_cloudShellUri);
|
||||
|
||||
// Initialize the request
|
||||
http_request terminalRequest(L"POST");
|
||||
terminalRequest.set_request_uri(L"terminals/" + _terminalID + L"/size?cols=" + std::to_wstring(columns) + L"&rows=" + std::to_wstring(rows) + L"&version=2019-01-01");
|
||||
_HeaderHelper(terminalRequest);
|
||||
terminalRequest.set_body(json::value(L""));
|
||||
|
||||
// Send the request
|
||||
_RequestHelper(terminalClient, terminalRequest);
|
||||
}
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - ascribes to the ITerminalConnection interface
|
||||
// - closes the websocket connection and the output thread
|
||||
void AzureConnection::Close()
|
||||
{
|
||||
if (!_connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_closing.exchange(true))
|
||||
{
|
||||
_canProceed.notify_all();
|
||||
if (_state == State::TermConnected)
|
||||
{
|
||||
// Close the websocket connection
|
||||
auto closedTask = _cloudShellSocket.close();
|
||||
closedTask.wait();
|
||||
}
|
||||
|
||||
// Tear down our output thread
|
||||
WaitForSingleObject(_hOutputThread.get(), INFINITE);
|
||||
_hOutputThread.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - this method bridges the thread to the Azure connection instance
|
||||
// Arguments:
|
||||
// - lpParameter: the Azure connection parameter
|
||||
// Return value:
|
||||
// - the exit code of the thread
|
||||
DWORD WINAPI AzureConnection::StaticOutputThreadProc(LPVOID lpParameter)
|
||||
{
|
||||
AzureConnection* const pInstance = (AzureConnection*)lpParameter;
|
||||
return pInstance->_OutputThread();
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - this is the output thread, where we initiate the connection to Azure
|
||||
// and establish a websocket connection
|
||||
// Return value:
|
||||
// - return status
|
||||
DWORD AzureConnection::_OutputThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
// Initial state, check if the user has any stored connection settings and allow them to login with those
|
||||
// or allow them to login with a different account or allow them to remove the saved settings
|
||||
case State::AccessStored:
|
||||
{
|
||||
RETURN_IF_FAILED(_AccessHelper());
|
||||
break;
|
||||
}
|
||||
// User has no saved connection settings or has opted to login with a different account
|
||||
// Azure authentication happens here
|
||||
case State::DeviceFlow:
|
||||
{
|
||||
RETURN_IF_FAILED(_DeviceFlowHelper());
|
||||
break;
|
||||
}
|
||||
// User has multiple tenants in their Azure account, they need to choose which one to connect to
|
||||
case State::TenantChoice:
|
||||
{
|
||||
RETURN_IF_FAILED(_TenantChoiceHelper());
|
||||
break;
|
||||
}
|
||||
// Ask the user if they want to save these connection settings for future logins
|
||||
case State::StoreTokens:
|
||||
{
|
||||
RETURN_IF_FAILED(_StoreHelper());
|
||||
break;
|
||||
}
|
||||
// Connect to Azure, we only get here once we have everything we need (tenantID, accessToken, refreshToken)
|
||||
case State::TermConnecting:
|
||||
{
|
||||
RETURN_IF_FAILED(_ConnectHelper());
|
||||
break;
|
||||
}
|
||||
// We are connected, continuously read from the websocket until its closed
|
||||
case State::TermConnected:
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Read from websocket
|
||||
pplx::task<websocket_incoming_message> msgT;
|
||||
try
|
||||
{
|
||||
msgT = _cloudShellSocket.receive();
|
||||
msgT.wait();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Websocket has been closed
|
||||
if (!_closing.load())
|
||||
{
|
||||
_state = State::NoConnect;
|
||||
_disconnectHandlers();
|
||||
return S_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
auto msg = msgT.get();
|
||||
auto msgStringTask = msg.extract_string();
|
||||
auto msgString = msgStringTask.get();
|
||||
|
||||
// Convert to hstring
|
||||
const auto hstr = winrt::to_hstring(msgString);
|
||||
|
||||
// Pass the output to our registered event handlers
|
||||
_outputHandlers(hstr);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
case State::NoConnect:
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(internetOrServerIssue));
|
||||
_disconnectHandlers();
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_state = State::NoConnect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to get the stored credentials (if any) and let the user choose what to do next
|
||||
// Return value:
|
||||
// - S_FALSE if there are no stored credentials
|
||||
// - S_OK if the user opts to login with a stored set of credentials or login with a different account
|
||||
// - E_FAIL if the user closes the tab
|
||||
HRESULT AzureConnection::_AccessHelper()
|
||||
{
|
||||
auto vault = PasswordVault();
|
||||
winrt::Windows::Foundation::Collections::IVectorView<PasswordCredential> credList;
|
||||
// FindAllByResource throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block
|
||||
try
|
||||
{
|
||||
credList = vault.FindAllByResource(resource);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// No credentials are stored, so start the device flow
|
||||
_state = State::DeviceFlow;
|
||||
return S_FALSE;
|
||||
}
|
||||
_maxStored = credList.Size();
|
||||
// Display the user's saved connection settings
|
||||
for (int i = 0; i < _maxStored; i++)
|
||||
{
|
||||
auto entry = credList.GetAt(i);
|
||||
auto nameJson = json::value::parse(entry.UserName().c_str());
|
||||
_outputHandlers(_StrFormatHelper(ithTenant, i, nameJson.at(L"displayName").as_string().c_str(), nameJson.at(L"tenantID").as_string().c_str()));
|
||||
}
|
||||
|
||||
_outputHandlers(winrt::to_hstring(enterTenant));
|
||||
_outputHandlers(winrt::to_hstring(newLogin));
|
||||
_outputHandlers(winrt::to_hstring(removeStored));
|
||||
|
||||
std::unique_lock<std::mutex> storedLock{ _commonMutex };
|
||||
_canProceed.wait(storedLock, [=]() {
|
||||
return (_storedNumber >= 0 && _storedNumber < _maxStored) || _removeOrNew.has_value() || _closing.load();
|
||||
});
|
||||
// User might have closed the tab while we waited for input
|
||||
if (_closing.load())
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
else if (_removeOrNew.has_value() && _removeOrNew.value())
|
||||
{
|
||||
// User wants to remove the stored settings
|
||||
_RemoveCredentials();
|
||||
_state = State::DeviceFlow;
|
||||
return S_OK;
|
||||
}
|
||||
else if (_removeOrNew.has_value() && !_removeOrNew.value())
|
||||
{
|
||||
// User wants to login with a different account
|
||||
_state = State::DeviceFlow;
|
||||
return S_OK;
|
||||
}
|
||||
// User wants to login with one of the saved connection settings
|
||||
auto desiredCredential = credList.GetAt(_storedNumber);
|
||||
desiredCredential.RetrievePassword();
|
||||
auto nameJson = json::value::parse(desiredCredential.UserName().c_str());
|
||||
auto passWordJson = json::value::parse(desiredCredential.Password().c_str());
|
||||
_displayName = nameJson.at(L"displayName").as_string();
|
||||
_tenantID = nameJson.at(L"tenantID").as_string();
|
||||
_accessToken = passWordJson.at(L"accessToken").as_string();
|
||||
_refreshToken = passWordJson.at(L"refreshToken").as_string();
|
||||
_expiry = std::stoi(passWordJson.at(L"expiry").as_string());
|
||||
|
||||
const auto t1 = std::chrono::system_clock::now();
|
||||
const auto timeNow = std::chrono::duration_cast<std::chrono::seconds>(t1.time_since_epoch()).count();
|
||||
|
||||
// Check if the token is close to expiring and refresh if so
|
||||
if (timeNow + _expireLimit > _expiry)
|
||||
{
|
||||
const auto refreshResponse = _RefreshTokens();
|
||||
_accessToken = refreshResponse.at(L"access_token").as_string();
|
||||
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
|
||||
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
|
||||
// Store the updated tokens under the same username
|
||||
_StoreCredential();
|
||||
}
|
||||
|
||||
// We have everything we need, so go ahead and connect
|
||||
_state = State::TermConnecting;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to start the device code flow (required for authentication to Azure)
|
||||
// Return value:
|
||||
// - E_FAIL if the user closes the tab, does not authenticate in time or has no tenants in their Azure account
|
||||
// - S_OK otherwise
|
||||
HRESULT AzureConnection::_DeviceFlowHelper()
|
||||
{
|
||||
// Initiate device code flow
|
||||
const auto deviceCodeResponse = _GetDeviceCode();
|
||||
|
||||
// Print the message and store the device code, polling interval and expiry
|
||||
const auto message = deviceCodeResponse.at(L"message").as_string();
|
||||
_outputHandlers(message + codeExpiry);
|
||||
const auto devCode = deviceCodeResponse.at(L"device_code").as_string();
|
||||
const auto pollInterval = std::stoi(deviceCodeResponse.at(L"interval").as_string());
|
||||
const auto expiresIn = std::stoi(deviceCodeResponse.at(L"expires_in").as_string());
|
||||
|
||||
// Wait for user authentication and obtain the access/refresh tokens
|
||||
json::value authenticatedResponse;
|
||||
try
|
||||
{
|
||||
authenticatedResponse = _WaitForUser(devCode, pollInterval, expiresIn);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(exitStr));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
_accessToken = authenticatedResponse.at(L"access_token").as_string();
|
||||
_refreshToken = authenticatedResponse.at(L"refresh_token").as_string();
|
||||
|
||||
// Get the tenants and the required tenant id
|
||||
const auto tenantsResponse = _GetTenants();
|
||||
_tenantList = tenantsResponse.at(L"value");
|
||||
const auto tenantListAsArray = _tenantList.as_array();
|
||||
if (tenantListAsArray.size() == 0)
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(noTenants));
|
||||
return E_FAIL;
|
||||
}
|
||||
else if (_tenantList.size() == 1)
|
||||
{
|
||||
_tenantID = tenantListAsArray.at(0).at(L"tenantId").as_string();
|
||||
_displayName = tenantListAsArray.at(0).at(L"displayName").as_string();
|
||||
|
||||
// We have to refresh now that we have the tenantID
|
||||
const auto refreshResponse = _RefreshTokens();
|
||||
_accessToken = refreshResponse.at(L"access_token").as_string();
|
||||
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
|
||||
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
|
||||
|
||||
_state = State::StoreTokens;
|
||||
}
|
||||
else
|
||||
{
|
||||
_state = State::TenantChoice;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to list the user's tenants and let them decide which tenant they wish to connect to
|
||||
// Return value:
|
||||
// - E_FAIL if the user closes the tab
|
||||
// - S_OK otherwise
|
||||
HRESULT AzureConnection::_TenantChoiceHelper()
|
||||
{
|
||||
const auto tenantListAsArray = _tenantList.as_array();
|
||||
_maxSize = tenantListAsArray.size();
|
||||
for (int i = 0; i < _maxSize; i++)
|
||||
{
|
||||
_outputHandlers(_StrFormatHelper(ithTenant, i, tenantListAsArray.at(i).at(L"displayName").as_string().c_str(), tenantListAsArray.at(i).at(L"tenantId").as_string().c_str()));
|
||||
}
|
||||
_outputHandlers(winrt::to_hstring(enterTenant));
|
||||
// Use a lock to wait for the user to input a valid number
|
||||
std::unique_lock<std::mutex> tenantNumberLock{ _commonMutex };
|
||||
_canProceed.wait(tenantNumberLock, [=]() {
|
||||
return (_tenantNumber >= 0 && _tenantNumber < _maxSize) || _closing.load();
|
||||
});
|
||||
// User might have closed the tab while we waited for input
|
||||
if (_closing.load())
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
_tenantID = tenantListAsArray.at(_tenantNumber).at(L"tenantId").as_string();
|
||||
_displayName = tenantListAsArray.at(_tenantNumber).at(L"displayName").as_string();
|
||||
|
||||
// We have to refresh now that we have the tenantID
|
||||
const auto refreshResponse = _RefreshTokens();
|
||||
_accessToken = refreshResponse.at(L"access_token").as_string();
|
||||
_refreshToken = refreshResponse.at(L"refresh_token").as_string();
|
||||
_expiry = std::stoi(refreshResponse.at(L"expires_on").as_string());
|
||||
|
||||
_state = State::StoreTokens;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to ask the user if they wish to store their credentials
|
||||
// Return value:
|
||||
// - E_FAIL if the user closes the tab
|
||||
// - S_OK otherwise
|
||||
HRESULT AzureConnection::_StoreHelper()
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(storePrompt));
|
||||
// Wait for user input
|
||||
std::unique_lock<std::mutex> storeLock{ _commonMutex };
|
||||
_canProceed.wait(storeLock, [=]() {
|
||||
return _store.has_value() || _closing.load();
|
||||
});
|
||||
// User might have closed the tab while we waited for input
|
||||
if (_closing.load())
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (_store.value())
|
||||
{
|
||||
// User has opted to store the connection settings
|
||||
_StoreCredential();
|
||||
_outputHandlers(winrt::to_hstring(tokensStored));
|
||||
}
|
||||
|
||||
_state = State::TermConnecting;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to connect the user to the Azure cloud shell
|
||||
// Return value:
|
||||
// - E_FAIL if the user has not set up their cloud shell yet
|
||||
// - S_OK after successful connection
|
||||
HRESULT AzureConnection::_ConnectHelper()
|
||||
{
|
||||
// Get user's cloud shell settings
|
||||
const auto settingsResponse = _GetCloudShellUserSettings();
|
||||
if (settingsResponse.has_field(L"error"))
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(noCloudAccount));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Request for a cloud shell
|
||||
_outputHandlers(winrt::to_hstring(requestingCloud));
|
||||
_cloudShellUri = _GetCloudShell();
|
||||
_outputHandlers(winrt::to_hstring(success));
|
||||
|
||||
// Request for a terminal for said cloud shell
|
||||
// We only support bash for now, so don't bother with the user's preferred shell
|
||||
// fyi: we can't call powershell yet because it sends VT sequences we don't support yet
|
||||
// TODO: GitHub #1883
|
||||
//const auto shellType = settingsResponse.at(L"properties").at(L"preferredShellType").as_string();
|
||||
const auto shellType = L"bash";
|
||||
_outputHandlers(winrt::to_hstring(requestingTerminal));
|
||||
const auto socketUri = _GetTerminal(shellType);
|
||||
_outputHandlers(winrt::to_hstring("\r\n"));
|
||||
|
||||
// Step 8: connecting to said terminal
|
||||
const auto connReqTask = _cloudShellSocket.connect(socketUri);
|
||||
connReqTask.wait();
|
||||
|
||||
_state = State::TermConnected;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to send requests and extract responses as json values
|
||||
// Arguments:
|
||||
// - a http_client
|
||||
// - a http_request for the client to send
|
||||
// Return value:
|
||||
// - the response from the server as a json value
|
||||
json::value AzureConnection::_RequestHelper(http_client theClient, http_request theRequest)
|
||||
{
|
||||
json::value jsonResult;
|
||||
try
|
||||
{
|
||||
const auto responseTask = theClient.request(theRequest);
|
||||
responseTask.wait();
|
||||
const auto response = responseTask.get();
|
||||
const auto responseJsonTask = response.extract_json();
|
||||
responseJsonTask.wait();
|
||||
jsonResult = responseJsonTask.get();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(internetOrServerIssue));
|
||||
}
|
||||
return jsonResult;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to start the device code flow
|
||||
// Return value:
|
||||
// - the response to the device code flow initiation
|
||||
json::value AzureConnection::_GetDeviceCode()
|
||||
{
|
||||
// Initialize the client
|
||||
http_client loginClient(_loginUri);
|
||||
|
||||
// Initialize the request
|
||||
http_request commonRequest(L"POST");
|
||||
commonRequest.set_request_uri(L"common/oauth2/devicecode");
|
||||
const auto body = L"client_id=" + AzureClientID + L"&resource=" + _wantedResource;
|
||||
commonRequest.set_body(body, L"application/x-www-form-urlencoded");
|
||||
|
||||
// Send the request and receive the response as a json value
|
||||
return _RequestHelper(loginClient, commonRequest);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to wait for the user to authenticate using their web browser
|
||||
// Arguments:
|
||||
// - the device code that would have been received when authentication was initiated
|
||||
// - the polling interval duration
|
||||
// - the duration the code is still valid for
|
||||
// Return value:
|
||||
// - if authentication is done successfully, then return the response from the server
|
||||
// - else, throw an exception
|
||||
json::value AzureConnection::_WaitForUser(const utility::string_t deviceCode, int pollInterval, int expiresIn)
|
||||
{
|
||||
// Initialize the client
|
||||
http_client pollingClient(_loginUri);
|
||||
|
||||
// Continuously send a poll request until the user authenticates
|
||||
const auto body = L"grant_type=device_code&resource=" + _wantedResource + L"&client_id=" + AzureClientID + L"&code=" + deviceCode;
|
||||
const auto requestUri = L"common/oauth2/token";
|
||||
json::value responseJson;
|
||||
for (int count = 0; count < expiresIn / pollInterval; count++)
|
||||
{
|
||||
// User might close the tab while we wait for them to authenticate, this case handles that
|
||||
if (_closing.load())
|
||||
{
|
||||
throw "Tab closed.";
|
||||
}
|
||||
http_request pollRequest(L"POST");
|
||||
pollRequest.set_request_uri(requestUri);
|
||||
pollRequest.set_body(body, L"application/x-www-form-urlencoded");
|
||||
|
||||
responseJson = _RequestHelper(pollingClient, pollRequest);
|
||||
|
||||
if (responseJson.has_field(L"error"))
|
||||
{
|
||||
Sleep(pollInterval * 1000); // Sleep takes arguments in milliseconds
|
||||
continue; // Still waiting for authentication
|
||||
}
|
||||
else
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring("Authenticated.\r\n"));
|
||||
break; // Authentication is done, break from loop
|
||||
}
|
||||
}
|
||||
if (responseJson.has_field(L"error"))
|
||||
{
|
||||
throw "Time out.";
|
||||
}
|
||||
return responseJson;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to acquire the user's Azure tenants
|
||||
// Return value:
|
||||
// - the response which contains a list of the user's Azure tenants
|
||||
json::value AzureConnection::_GetTenants()
|
||||
{
|
||||
// Initialize the client
|
||||
http_client tenantClient(_resourceUri);
|
||||
|
||||
// Initialize the request
|
||||
http_request tenantRequest(L"GET");
|
||||
tenantRequest.set_request_uri(L"tenants?api-version=2018-01-01");
|
||||
_HeaderHelper(tenantRequest);
|
||||
|
||||
// Send the request and return the response as a json value
|
||||
return _RequestHelper(tenantClient, tenantRequest);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to refresh the access/refresh tokens
|
||||
// Return value:
|
||||
// - the response with the new tokens
|
||||
json::value AzureConnection::_RefreshTokens()
|
||||
{
|
||||
// Initialize the client
|
||||
http_client refreshClient(_loginUri);
|
||||
|
||||
// Initialize the request
|
||||
http_request refreshRequest(L"POST");
|
||||
refreshRequest.set_request_uri(_tenantID + L"/oauth2/token");
|
||||
const auto body = L"client_id=" + AzureClientID + L"&resource=" + _wantedResource + L"&grant_type=refresh_token" + L"&refresh_token=" + _refreshToken;
|
||||
refreshRequest.set_body(body, L"application/x-www-form-urlencoded");
|
||||
refreshRequest.headers().add(L"User-Agent", userAgent);
|
||||
|
||||
// Send the request and return the response as a json value
|
||||
return _RequestHelper(refreshClient, refreshRequest);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to get the user's cloud shell settings
|
||||
// Return value:
|
||||
// - the user's cloud shell settings
|
||||
json::value AzureConnection::_GetCloudShellUserSettings()
|
||||
{
|
||||
// Initialize client
|
||||
http_client settingsClient(_resourceUri);
|
||||
|
||||
// Initialize request
|
||||
http_request settingsRequest(L"GET");
|
||||
settingsRequest.set_request_uri(L"providers/Microsoft.Portal/userSettings/cloudconsole?api-version=2018-10-01");
|
||||
_HeaderHelper(settingsRequest);
|
||||
|
||||
return _RequestHelper(settingsClient, settingsRequest);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to request for a cloud shell
|
||||
// Return value:
|
||||
// - the uri for the cloud shell
|
||||
utility::string_t AzureConnection::_GetCloudShell()
|
||||
{
|
||||
// Initialize client
|
||||
http_client cloudShellClient(_resourceUri);
|
||||
|
||||
// Initialize request
|
||||
http_request shellRequest(L"PUT");
|
||||
shellRequest.set_request_uri(L"providers/Microsoft.Portal/consoles/default?api-version=2018-10-01");
|
||||
_HeaderHelper(shellRequest);
|
||||
const auto innerBody = json::value::parse(U("{ \"osType\" : \"linux\" }"));
|
||||
json::value body;
|
||||
body[U("properties")] = innerBody;
|
||||
shellRequest.set_body(body);
|
||||
|
||||
// Send the request and get the response as a json value
|
||||
const auto cloudShell = _RequestHelper(cloudShellClient, shellRequest);
|
||||
|
||||
// Return the uri
|
||||
return cloudShell.at(L"properties").at(L"uri").as_string() + L"/";
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to request for a terminal
|
||||
// Return value:
|
||||
// - the uri for the terminal
|
||||
utility::string_t AzureConnection::_GetTerminal(utility::string_t shellType)
|
||||
{
|
||||
// Initialize client
|
||||
http_client terminalClient(_cloudShellUri);
|
||||
|
||||
// Initialize the request
|
||||
http_request terminalRequest(L"POST");
|
||||
terminalRequest.set_request_uri(L"terminals?cols=" + std::to_wstring(_initialCols) + L"&rows=" + std::to_wstring(_initialRows) + L"&version=2019-01-01&shell=" + shellType);
|
||||
_HeaderHelper(terminalRequest);
|
||||
|
||||
// Send the request and get the response as a json value
|
||||
const auto terminalResponse = _RequestHelper(terminalClient, terminalRequest);
|
||||
_terminalID = terminalResponse.at(L"id").as_string();
|
||||
|
||||
// Return the uri
|
||||
return terminalResponse.at(L"socketUri").as_string();
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to set the headers of a http_request
|
||||
// Arguments:
|
||||
// - the http_request
|
||||
void AzureConnection::_HeaderHelper(http_request theRequest)
|
||||
{
|
||||
theRequest.headers().add(L"Accept", L"application/json");
|
||||
theRequest.headers().add(L"Content-Type", L"application/json");
|
||||
theRequest.headers().add(L"Authorization", L"Bearer " + _accessToken);
|
||||
theRequest.headers().add(L"User-Agent", userAgent);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to store the credentials
|
||||
// - we store the display name, tenant ID, access/refresh tokens, and token expiry
|
||||
void AzureConnection::_StoreCredential()
|
||||
{
|
||||
auto vault = PasswordVault();
|
||||
json::value userName;
|
||||
userName[U("displayName")] = json::value::string(_displayName);
|
||||
userName[U("tenantID")] = json::value::string(_tenantID);
|
||||
json::value passWord;
|
||||
passWord[U("accessToken")] = json::value::string(_accessToken);
|
||||
passWord[U("refreshToken")] = json::value::string(_refreshToken);
|
||||
passWord[U("expiry")] = json::value::string(std::to_wstring(_expiry));
|
||||
auto newCredential = PasswordCredential(resource, userName.serialize(), passWord.serialize());
|
||||
vault.Add(newCredential);
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - helper function to remove all stored credentials
|
||||
void AzureConnection::_RemoveCredentials()
|
||||
{
|
||||
auto vault = PasswordVault();
|
||||
winrt::Windows::Foundation::Collections::IVectorView<PasswordCredential> credList;
|
||||
// FindAllByResource throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block
|
||||
try
|
||||
{
|
||||
credList = vault.FindAllByResource(resource);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// No credentials are stored, so just return
|
||||
_outputHandlers(winrt::to_hstring(noTokens));
|
||||
return;
|
||||
}
|
||||
while (credList.Size() > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
vault.Remove(credList.GetAt(0));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_outputHandlers(winrt::to_hstring(tokensRemoved));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring AzureConnection::_StrFormatHelper(const wchar_t* const format, int i, const wchar_t* name, const wchar_t* ID)
|
||||
{
|
||||
const auto lengthRequired = _scwprintf(ithTenant, i, name, ID);
|
||||
std::wstring buffer;
|
||||
buffer.resize(lengthRequired + 1);
|
||||
swprintf_s(buffer.data(), buffer.size(), ithTenant, i, name, ID);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AzureConnection.g.h"
|
||||
|
||||
#include <cpprest/http_client.h>
|
||||
#include <cpprest/http_listener.h>
|
||||
#include <cpprest/ws_client.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
{
|
||||
struct AzureConnection : AzureConnectionT<AzureConnection>
|
||||
{
|
||||
AzureConnection(const uint32_t rows, const uint32_t cols);
|
||||
|
||||
winrt::event_token TerminalOutput(TerminalConnection::TerminalOutputEventArgs const& handler);
|
||||
void TerminalOutput(winrt::event_token const& token) noexcept;
|
||||
winrt::event_token TerminalDisconnected(TerminalConnection::TerminalDisconnectedEventArgs const& handler);
|
||||
void TerminalDisconnected(winrt::event_token const& token) noexcept;
|
||||
void Start();
|
||||
void WriteInput(hstring const& data);
|
||||
void Resize(uint32_t rows, uint32_t columns);
|
||||
void Close();
|
||||
|
||||
private:
|
||||
winrt::event<TerminalConnection::TerminalOutputEventArgs> _outputHandlers;
|
||||
winrt::event<TerminalConnection::TerminalDisconnectedEventArgs> _disconnectHandlers;
|
||||
|
||||
uint32_t _initialRows{};
|
||||
uint32_t _initialCols{};
|
||||
int _storedNumber{ -1 };
|
||||
int _maxStored;
|
||||
int _tenantNumber{ -1 };
|
||||
int _maxSize;
|
||||
std::condition_variable _canProceed;
|
||||
std::mutex _commonMutex;
|
||||
|
||||
enum class State
|
||||
{
|
||||
AccessStored,
|
||||
DeviceFlow,
|
||||
TenantChoice,
|
||||
StoreTokens,
|
||||
TermConnecting,
|
||||
TermConnected,
|
||||
NoConnect
|
||||
};
|
||||
|
||||
State _state{ State::AccessStored };
|
||||
|
||||
std::optional<bool> _store;
|
||||
std::optional<bool> _removeOrNew;
|
||||
|
||||
bool _connected{};
|
||||
std::atomic<bool> _closing{ false };
|
||||
|
||||
wil::unique_handle _hOutputThread;
|
||||
|
||||
static DWORD WINAPI StaticOutputThreadProc(LPVOID lpParameter);
|
||||
DWORD _OutputThread();
|
||||
HRESULT _AccessHelper();
|
||||
HRESULT _DeviceFlowHelper();
|
||||
HRESULT _TenantChoiceHelper();
|
||||
HRESULT _StoreHelper();
|
||||
HRESULT _ConnectHelper();
|
||||
|
||||
const utility::string_t _loginUri{ U("https://login.microsoftonline.com/") };
|
||||
const utility::string_t _resourceUri{ U("https://management.azure.com/") };
|
||||
const utility::string_t _wantedResource{ U("https://management.core.windows.net/") };
|
||||
int _expireLimit{ 2700 };
|
||||
web::json::value _tenantList;
|
||||
utility::string_t _displayName;
|
||||
utility::string_t _tenantID;
|
||||
utility::string_t _accessToken;
|
||||
utility::string_t _refreshToken;
|
||||
int _expiry;
|
||||
utility::string_t _cloudShellUri;
|
||||
utility::string_t _terminalID;
|
||||
|
||||
web::json::value _RequestHelper(web::http::client::http_client theClient, web::http::http_request theRequest);
|
||||
web::json::value _GetDeviceCode();
|
||||
web::json::value _WaitForUser(utility::string_t deviceCode, int pollInterval, int expiresIn);
|
||||
web::json::value _GetTenants();
|
||||
web::json::value _RefreshTokens();
|
||||
web::json::value _GetCloudShellUserSettings();
|
||||
utility::string_t _GetCloudShell();
|
||||
utility::string_t _GetTerminal(utility::string_t shellType);
|
||||
void _HeaderHelper(web::http::http_request theRequest);
|
||||
void _StoreCredential();
|
||||
void _RemoveCredentials();
|
||||
std::wstring _StrFormatHelper(const wchar_t* const format, int i, const wchar_t* name, const wchar_t* ID);
|
||||
|
||||
web::websockets::client::websocket_client _cloudShellSocket;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalConnection::factory_implementation
|
||||
{
|
||||
struct AzureConnection : AzureConnectionT<AzureConnection, implementation::AzureConnection>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ITerminalConnection.idl";
|
||||
|
||||
namespace Microsoft.Terminal.TerminalConnection
|
||||
{
|
||||
[default_interface] runtimeclass AzureConnection : ITerminalConnection
|
||||
{
|
||||
AzureConnection(UInt32 rows, UInt32 columns);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Messages to the user
|
||||
const auto resource = L"Terminal";
|
||||
const auto userAgent = L"Terminal/0.0";
|
||||
const auto codeExpiry = L"\r\nThis code will expire in 15 minutes.\r\n";
|
||||
const auto enterTenant = L"Please enter the desired tenant number\r\n";
|
||||
const auto newLogin = L"or enter n to login with a different account\r\n";
|
||||
const auto removeStored = L"or enter r to remove the above saved connection settings.\r\n";
|
||||
const auto invalidAccessInput = L"Please enter a valid number to access the stored connection settings, n to make a new one, or r to remove the stored ones.\r\n";
|
||||
const auto nonNumberError = L"Please enter a number.\r\n";
|
||||
const auto numOutOfBoundsError = L"Number out of bounds. Please enter a valid number. \r\n";
|
||||
const auto noTenants = L"Could not find any tenants.\r\n";
|
||||
const auto noCloudAccount = L"You have not set up your cloud shell account yet. Please go to https://shell.azure.com to set it up.\r\n";
|
||||
const auto storePrompt = L"Do you want to save these connection settings for future logins? [y/n]\r\n";
|
||||
const auto invalidStoreInput = L"Please enter y or n\r\n";
|
||||
const auto requestingCloud = L"Requesting for a cloud shell...";
|
||||
const auto success = L"Succeeded.\r\n";
|
||||
const auto requestingTerminal = L"Requesting for a terminal (this might take a while)...";
|
||||
const auto tokensStored = L"Your connection settings have been saved for future logins.\r\n";
|
||||
const auto noTokens = L"No tokens to remove. \r\n";
|
||||
const auto tokensRemoved = L"Tokens removed!\r\n";
|
||||
const auto exitStr = L"Exit.\r\n";
|
||||
const auto authString = L"Authenticated.\r\n";
|
||||
const auto internetOrServerIssue = L"Could not connect to Azure. You may not have internet or the server might be down.\r\n";
|
||||
|
||||
const auto ithTenant = L"Tenant %d: %s (%s)\r\n";
|
||||
@@ -5,12 +5,16 @@
|
||||
#include "ConhostConnection.h"
|
||||
#include "windows.h"
|
||||
#include <sstream>
|
||||
// STARTF_USESTDHANDLES is only defined in WINAPI_PARTITION_DESKTOP
|
||||
// We're just gonna manually define it for this prototyping code
|
||||
#ifndef STARTF_USESTDHANDLES
|
||||
#define STARTF_USESTDHANDLES 0x00000100
|
||||
#endif
|
||||
|
||||
#include "ConhostConnection.g.cpp"
|
||||
|
||||
#include <conpty-universal.h>
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
#include "../../types/inc/UTF8OutPipeReader.hpp"
|
||||
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
@@ -33,6 +37,23 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
}
|
||||
}
|
||||
|
||||
ConhostConnection::ConhostConnection(const uint64_t server,
|
||||
const uint32_t initialRows,
|
||||
const uint32_t initialCols,
|
||||
const guid& initialGuid) :
|
||||
_initialRows{ initialRows },
|
||||
_initialCols{ initialCols },
|
||||
_commandline{},
|
||||
_startingDirectory{},
|
||||
_guid{ initialGuid },
|
||||
_hServer{ reinterpret_cast<HANDLE>(server) }
|
||||
{
|
||||
if (_guid == guid{})
|
||||
{
|
||||
_guid = Utils::CreateGuid();
|
||||
}
|
||||
}
|
||||
|
||||
winrt::guid ConhostConnection::Guid() const noexcept
|
||||
{
|
||||
return _guid;
|
||||
@@ -67,6 +88,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
startingDirectory = _startingDirectory;
|
||||
}
|
||||
|
||||
std::optional<HANDLE> server;
|
||||
if (_hServer)
|
||||
{
|
||||
server = _hServer.get();
|
||||
}
|
||||
|
||||
EnvironmentVariableMapW extraEnvVars;
|
||||
{
|
||||
// Convert connection Guid to string and ignore the enclosing '{}'.
|
||||
@@ -84,6 +111,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
startingDirectory,
|
||||
static_cast<short>(_initialCols),
|
||||
static_cast<short>(_initialRows),
|
||||
server,
|
||||
&_inPipe,
|
||||
&_outPipe,
|
||||
&_signalPipe,
|
||||
@@ -117,8 +145,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
0,
|
||||
nullptr));
|
||||
|
||||
THROW_LAST_ERROR_IF_NULL(_hOutputThread);
|
||||
|
||||
// Wind up the conhost! We only do this after we've got everything in place.
|
||||
THROW_LAST_ERROR_IF(-1 == ResumeThread(_piConhost.hThread));
|
||||
|
||||
@@ -187,37 +213,39 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
|
||||
DWORD ConhostConnection::_OutputThread()
|
||||
{
|
||||
UTF8OutPipeReader pipeReader{ _outPipe.get() };
|
||||
std::string_view strView{};
|
||||
|
||||
// process the data of the output pipe in a loop
|
||||
const size_t bufferSize = 4096;
|
||||
BYTE buffer[bufferSize];
|
||||
DWORD dwRead;
|
||||
while (true)
|
||||
{
|
||||
HRESULT result = pipeReader.Read(strView);
|
||||
if (FAILED(result) || result == S_FALSE)
|
||||
dwRead = 0;
|
||||
bool fSuccess = false;
|
||||
|
||||
fSuccess = !!ReadFile(_outPipe.get(), buffer, bufferSize, &dwRead, nullptr);
|
||||
if (!fSuccess)
|
||||
{
|
||||
if (_closing.load())
|
||||
{
|
||||
// This is okay, break out to kill the thread
|
||||
return 0;
|
||||
}
|
||||
|
||||
_disconnectHandlers();
|
||||
return (DWORD)-1;
|
||||
else
|
||||
{
|
||||
_disconnectHandlers();
|
||||
return (DWORD)-1;
|
||||
}
|
||||
}
|
||||
|
||||
if (strView.empty())
|
||||
if (dwRead == 0)
|
||||
{
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert buffer to hstring
|
||||
auto hstr{ winrt::to_hstring(strView) };
|
||||
char* pchStr = (char*)(buffer);
|
||||
std::string str{ pchStr, dwRead };
|
||||
auto hstr = winrt::to_hstring(str);
|
||||
|
||||
// Pass the output to our registered event handlers
|
||||
_outputHandlers(hstr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
struct ConhostConnection : ConhostConnectionT<ConhostConnection>
|
||||
{
|
||||
ConhostConnection(const hstring& cmdline, const hstring& startingDirectory, const uint32_t rows, const uint32_t cols, const guid& guid);
|
||||
ConhostConnection(const uint64_t server, const uint32_t rows, const uint32_t cols, const guid& guid);
|
||||
|
||||
winrt::event_token TerminalOutput(TerminalConnection::TerminalOutputEventArgs const& handler);
|
||||
void TerminalOutput(winrt::event_token const& token) noexcept;
|
||||
@@ -41,6 +42,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
wil::unique_handle _hOutputThread;
|
||||
wil::unique_process_information _piConhost;
|
||||
wil::unique_handle _hJob;
|
||||
wil::unique_handle _hServer;
|
||||
|
||||
static DWORD WINAPI StaticOutputThreadProc(LPVOID lpParameter);
|
||||
DWORD _OutputThread();
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Microsoft.Terminal.TerminalConnection
|
||||
runtimeclass ConhostConnection : ITerminalConnection
|
||||
{
|
||||
ConhostConnection(String cmdline, String startingDirectory, UInt32 rows, UInt32 columns, Guid guid);
|
||||
ConhostConnection(UInt64 server, UInt32 rows, UInt32 columns, Guid guid);
|
||||
|
||||
Guid Guid { get; };
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<PropertyGroup>
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
@@ -7,24 +8,14 @@
|
||||
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}</ProjectGuid>
|
||||
<ProjectName>TerminalConnection</ProjectName>
|
||||
<RootNamespace>Microsoft.Terminal.TerminalConnection</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)' == 'x64'">
|
||||
<VcpkgTriplet Condition="'$(VcpkgTriplet)' == ''">x64-windows</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)' == 'Win32'">
|
||||
<VcpkgTriplet Condition="'$(VcpkgTriplet)' == ''">x86-windows</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)' == 'arm64'">
|
||||
<VcpkgTriplet Condition="'$(VcpkgTriplet)' == ''">arm64-windows</VcpkgTriplet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AzureClientID.h" Condition="'$(Platform)'!='ARM64'" />
|
||||
<ClInclude Include="AzureConnection.h" Condition="'$(Platform)'!='ARM64'" />
|
||||
<ClInclude Include="AzureConnectionStrings.h" Condition="'$(Platform)'!='ARM64'" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ConhostConnection.h">
|
||||
<DependentUpon>ConhostConnection.idl</DependentUpon>
|
||||
@@ -34,7 +25,6 @@
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AzureConnection.cpp" Condition="'$(Platform)'!='ARM64'" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@@ -50,16 +40,18 @@
|
||||
<Midl Include="ITerminalConnection.idl" />
|
||||
<Midl Include="ConhostConnection.idl" />
|
||||
<Midl Include="EchoConnection.idl" />
|
||||
<Midl Include="AzureConnection.idl" Condition="'$(Platform)'!='ARM64'" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<!-- ========================= Project References ======================== -->
|
||||
<ItemGroup>
|
||||
<!--
|
||||
@@ -70,6 +62,7 @@
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj">
|
||||
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
|
||||
</ProjectReference>
|
||||
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
@@ -79,13 +72,7 @@
|
||||
-->
|
||||
<NoOutputRedirection>true</NoOutputRedirection>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\common.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
<Import Project="..\..\..\packages\vcpkg-cpprestsdk.2.10.0\build\native\vcpkg-cpprestsdk.targets" Condition="Exists('..\..\..\packages\vcpkg-cpprestsdk.2.10.0\build\native\vcpkg-cpprestsdk.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\vcpkg-cpprestsdk.2.10.0\build\native\vcpkg-cpprestsdk.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\vcpkg-cpprestsdk.2.10.0\build\native\vcpkg-cpprestsdk.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
@@ -14,26 +14,22 @@
|
||||
<ClCompile Include="EchoConnection.cpp" />
|
||||
<ClCompile Include="ConhostConnection.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="AzureConnection.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="EchoConnection.h" />
|
||||
<ClInclude Include="ConhostConnection.h" />
|
||||
<ClInclude Include="AzureConnection.h" />
|
||||
<ClInclude Include="AzureClientID.h" />
|
||||
<ClInclude Include="AzureConnectionStrings.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="ITerminalConnection.idl" />
|
||||
<Midl Include="ConhostConnection.idl" />
|
||||
<Midl Include="EchoConnection.idl" />
|
||||
<Midl Include="AzureConnection.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="TerminalConnection.def" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
<Text Include="readme.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.190605.7" targetFramework="native" />
|
||||
<package id="vcpkg-cpprestsdk" version="2.10.0" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -7,15 +7,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Needs to be defined or we get redeclaration errors
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <LibraryIncludes.h>
|
||||
|
||||
// Must be included before any WinRT headers.
|
||||
#include <unknwn.h>
|
||||
|
||||
#include "winrt/Windows.Foundation.h"
|
||||
#include "winrt/Windows.Security.Credentials.h"
|
||||
#include "winrt/Windows.Foundation.Collections.h"
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "..\..\types\inc\GlyphWidth.hpp"
|
||||
|
||||
#include "TermControl.g.cpp"
|
||||
#include "TermControlAutomationPeer.h"
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
@@ -31,14 +30,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_connection{ connection },
|
||||
_initializedTerminal{ false },
|
||||
_root{ nullptr },
|
||||
_controlRoot{ nullptr },
|
||||
_swapChainPanel{ nullptr },
|
||||
_settings{ settings },
|
||||
_closing{ false },
|
||||
_lastScrollOffset{ std::nullopt },
|
||||
_autoScrollVelocity{ 0 },
|
||||
_autoScrollingPointerPoint{ std::nullopt },
|
||||
_autoScrollTimer{},
|
||||
_lastAutoScrollUpdateTime{ std::nullopt },
|
||||
_desiredFont{ DEFAULT_FONT_FACE.c_str(), 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 },
|
||||
_actualFont{ DEFAULT_FONT_FACE.c_str(), 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false },
|
||||
_touchAnchor{ std::nullopt },
|
||||
@@ -52,6 +48,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
void TermControl::_Create()
|
||||
{
|
||||
// Create a dummy UserControl to use as the "root" of our control we'll
|
||||
// build manually.
|
||||
Controls::UserControl myControl;
|
||||
_controlRoot = myControl;
|
||||
|
||||
Controls::Grid container;
|
||||
|
||||
Controls::ColumnDefinition contentColumn{};
|
||||
@@ -103,20 +104,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_bgImageLayer = bgImageLayer;
|
||||
|
||||
_swapChainPanel = swapChainPanel;
|
||||
this->Content(_root);
|
||||
_controlRoot.Content(_root);
|
||||
|
||||
_ApplyUISettings();
|
||||
|
||||
// These are important:
|
||||
// 1. When we get tapped, focus us
|
||||
this->Tapped([this](auto&, auto& e) {
|
||||
this->Focus(FocusState::Pointer);
|
||||
_controlRoot.Tapped([this](auto&, auto& e) {
|
||||
_controlRoot.Focus(FocusState::Pointer);
|
||||
e.Handled(true);
|
||||
});
|
||||
// 2. Make sure we can be focused (why this isn't `Focusable` I'll never know)
|
||||
this->IsTabStop(true);
|
||||
_controlRoot.IsTabStop(true);
|
||||
// 3. Actually not sure about this one. Maybe it isn't necessary either.
|
||||
this->AllowFocusOnInteraction(true);
|
||||
_controlRoot.AllowFocusOnInteraction(true);
|
||||
|
||||
// DON'T CALL _InitializeTerminal here - wait until the swap chain is loaded to do that.
|
||||
|
||||
@@ -180,23 +181,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_BackgroundColorChanged(bg);
|
||||
|
||||
// Apply padding as swapChainPanel's margin
|
||||
auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
|
||||
auto existingMargin = _swapChainPanel.Margin();
|
||||
_swapChainPanel.Margin(newMargin);
|
||||
|
||||
if (newMargin != existingMargin && newMargin != Thickness{ 0 })
|
||||
{
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"NonzeroPaddingApplied",
|
||||
TraceLoggingDescription("An event emitted when a control has padding applied to it"),
|
||||
TraceLoggingStruct(4, "Padding"),
|
||||
TraceLoggingFloat64(newMargin.Left, "Left"),
|
||||
TraceLoggingFloat64(newMargin.Top, "Top"),
|
||||
TraceLoggingFloat64(newMargin.Right, "Right"),
|
||||
TraceLoggingFloat64(newMargin.Bottom, "Bottom"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
}
|
||||
auto thickness = _ParseThicknessFromPadding(_settings.Padding());
|
||||
_swapChainPanel.Margin(thickness);
|
||||
|
||||
// Initialize our font information.
|
||||
const auto* fontFace = _settings.FontFace().c_str();
|
||||
@@ -286,13 +272,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// internet.
|
||||
Media::Imaging::BitmapImage image(imageUri);
|
||||
_bgImageLayer.Source(image);
|
||||
_bgImageLayer.HorizontalAlignment(HorizontalAlignment::Center);
|
||||
_bgImageLayer.VerticalAlignment(VerticalAlignment::Center);
|
||||
}
|
||||
|
||||
// Apply stretch, opacity and alignment settings
|
||||
// Apply stretch and opacity settings
|
||||
_bgImageLayer.Stretch(_settings.BackgroundImageStretchMode());
|
||||
_bgImageLayer.Opacity(_settings.BackgroundImageOpacity());
|
||||
_bgImageLayer.HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
|
||||
_bgImageLayer.VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -340,16 +326,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
Close();
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
|
||||
UIElement TermControl::GetRoot()
|
||||
{
|
||||
// create a custom automation peer with this code pattern:
|
||||
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
|
||||
return winrt::make<winrt::Microsoft::Terminal::TerminalControl::implementation::TermControlAutomationPeer>(*this);
|
||||
return _root;
|
||||
}
|
||||
|
||||
::Microsoft::Console::Render::IRenderData* TermControl::GetRenderData() const
|
||||
Controls::UserControl TermControl::GetControl()
|
||||
{
|
||||
return _terminal.get();
|
||||
return _controlRoot;
|
||||
}
|
||||
|
||||
void TermControl::SwapChainChanged()
|
||||
@@ -503,9 +487,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// through CharacterRecieved.
|
||||
// I don't believe there's a difference between KeyDown and
|
||||
// PreviewKeyDown for our purposes
|
||||
// These two handlers _must_ be on this, not _root.
|
||||
this->PreviewKeyDown({ this, &TermControl::_KeyDownHandler });
|
||||
this->CharacterReceived({ this, &TermControl::_CharacterHandler });
|
||||
// These two handlers _must_ be on _controlRoot, not _root.
|
||||
_controlRoot.PreviewKeyDown({ this, &TermControl::_KeyDownHandler });
|
||||
_controlRoot.CharacterReceived({ this, &TermControl::_CharacterHandler });
|
||||
|
||||
auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
|
||||
_terminal->SetTitleChangedCallback(pfnTitleChanged);
|
||||
@@ -516,10 +500,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
_terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged);
|
||||
|
||||
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
|
||||
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
|
||||
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
|
||||
|
||||
// Set up blinking cursor
|
||||
int blinkTime = GetCaretBlinkTime();
|
||||
if (blinkTime != INFINITE)
|
||||
@@ -539,14 +519,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// import value from WinUser (convert from milli-seconds to micro-seconds)
|
||||
_multiClickTimer = GetDoubleClickTime() * 1000;
|
||||
|
||||
_gotFocusRevoker = this->GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler });
|
||||
_lostFocusRevoker = this->LostFocus(winrt::auto_revoke, { this, &TermControl::_LostFocusHandler });
|
||||
_gotFocusRevoker = _controlRoot.GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler });
|
||||
_lostFocusRevoker = _controlRoot.LostFocus(winrt::auto_revoke, { this, &TermControl::_LostFocusHandler });
|
||||
|
||||
// Focus the control here. If we do it up above (in _Create_), then the
|
||||
// focus won't actually get passed to us. I believe this is because
|
||||
// we're not technically a part of the UI tree yet, so focusing us
|
||||
// becomes a no-op.
|
||||
this->Focus(FocusState::Programmatic);
|
||||
_controlRoot.Focus(FocusState::Programmatic);
|
||||
|
||||
_connection.Start();
|
||||
_initializedTerminal = true;
|
||||
@@ -619,41 +599,35 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
|
||||
// AltGr key combinations don't always contain any meaningful,
|
||||
// pretranslated unicode character during WM_KEYDOWN.
|
||||
// E.g. on a German keyboard AltGr+Q should result in a "@" character,
|
||||
// but actually results in "Q" with Alt and Ctrl modifier states.
|
||||
// By returning false though, we can abort handling this WM_KEYDOWN
|
||||
// event and let the WM_CHAR handler kick in, which will be
|
||||
// provided with an appropriate unicode character.
|
||||
//
|
||||
// GH#2235: Make sure to handle AltGr before trying keybindings,
|
||||
// so Ctrl+Alt keybindings won't eat an AltGr keypress.
|
||||
if (modifiers.IsAltGrPressed())
|
||||
{
|
||||
_HandleVoidKeyEvent();
|
||||
e.Handled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto vkey = static_cast<WORD>(e.OriginalKey());
|
||||
bool handled = false;
|
||||
|
||||
bool handled = false;
|
||||
auto bindings = _settings.KeyBindings();
|
||||
if (bindings)
|
||||
{
|
||||
handled = bindings.TryKeyChord({
|
||||
modifiers.IsCtrlPressed(),
|
||||
modifiers.IsAltPressed(),
|
||||
modifiers.IsShiftPressed(),
|
||||
vkey,
|
||||
});
|
||||
KeyChord chord(
|
||||
WI_IsAnyFlagSet(modifiers, CTRL_PRESSED),
|
||||
WI_IsAnyFlagSet(modifiers, ALT_PRESSED),
|
||||
WI_IsFlagSet(modifiers, SHIFT_PRESSED),
|
||||
vkey);
|
||||
handled = bindings.TryKeyChord(chord);
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
handled = _TrySendKeyEvent(vkey, modifiers);
|
||||
_terminal->ClearSelection();
|
||||
// If the terminal translated the key, mark the event as handled.
|
||||
// This will prevent the system from trying to get the character out
|
||||
// of it and sending us a CharacterRecieved event.
|
||||
handled = _terminal->SendKeyEvent(vkey, modifiers);
|
||||
|
||||
if (_cursorTimer.has_value())
|
||||
{
|
||||
// Manually show the cursor when a key is pressed. Restarting
|
||||
// the timer prevents flickering.
|
||||
_terminal->SetCursorVisible(true);
|
||||
_cursorTimer.value().Start();
|
||||
}
|
||||
}
|
||||
|
||||
// Manually prevent keyboard navigation with tab. We want to send tab to
|
||||
@@ -667,45 +641,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
e.Handled(handled);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Some key events cannot be handled (e.g. AltGr combinations) and are
|
||||
// delegated to the character handler. Just like with _TrySendKeyEvent(),
|
||||
// the character handler counts on us though to:
|
||||
// - Clears the current selection.
|
||||
// - Makes the cursor briefly visible during typing.
|
||||
void TermControl::_HandleVoidKeyEvent()
|
||||
{
|
||||
_TrySendKeyEvent(0, {});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Send this particular key event to the terminal.
|
||||
// See Terminal::SendKeyEvent for more information.
|
||||
// - Clears the current selection.
|
||||
// - Makes the cursor briefly visible during typing.
|
||||
// Arguments:
|
||||
// - vkey: The vkey of the key pressed.
|
||||
// - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
||||
bool TermControl::_TrySendKeyEvent(WORD vkey, const ControlKeyStates modifiers)
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
|
||||
// If the terminal translated the key, mark the event as handled.
|
||||
// This will prevent the system from trying to get the character out
|
||||
// of it and sending us a CharacterRecieved event.
|
||||
const auto handled = vkey ? _terminal->SendKeyEvent(vkey, modifiers) : true;
|
||||
|
||||
if (_cursorTimer.has_value())
|
||||
{
|
||||
// Manually show the cursor when a key is pressed. Restarting
|
||||
// the timer prevents flickering.
|
||||
_terminal->SetCursorVisible(true);
|
||||
_cursorTimer.value().Start();
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - handle a mouse click event. Begin selection process.
|
||||
// Arguments:
|
||||
@@ -808,33 +743,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse)
|
||||
{
|
||||
if (_terminal->IsSelectionActive() && point.Properties().IsLeftButtonPressed())
|
||||
if (point.Properties().IsLeftButtonPressed())
|
||||
{
|
||||
const auto cursorPosition = point.Position();
|
||||
_SetEndSelectionPointAtCursor(cursorPosition);
|
||||
const auto terminalPosition = _GetTerminalPosition(cursorPosition);
|
||||
|
||||
const double cursorBelowBottomDist = cursorPosition.Y - _swapChainPanel.Margin().Top - _swapChainPanel.ActualHeight();
|
||||
const double cursorAboveTopDist = -1 * cursorPosition.Y + _swapChainPanel.Margin().Top;
|
||||
|
||||
constexpr double MinAutoScrollDist = 2.0; // Arbitrary value
|
||||
double newAutoScrollVelocity = 0.0;
|
||||
if (cursorBelowBottomDist > MinAutoScrollDist)
|
||||
{
|
||||
newAutoScrollVelocity = _GetAutoScrollSpeed(cursorBelowBottomDist);
|
||||
}
|
||||
else if (cursorAboveTopDist > MinAutoScrollDist)
|
||||
{
|
||||
newAutoScrollVelocity = -1.0 * _GetAutoScrollSpeed(cursorAboveTopDist);
|
||||
}
|
||||
|
||||
if (newAutoScrollVelocity != 0)
|
||||
{
|
||||
_TryStartAutoScroll(point, newAutoScrollVelocity);
|
||||
}
|
||||
else
|
||||
{
|
||||
_TryStopAutoScroll(ptr.PointerId());
|
||||
}
|
||||
// save location (for rendering) + render
|
||||
_terminal->SetEndSelectionPosition(terminalPosition);
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
}
|
||||
else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch && _touchAnchor)
|
||||
@@ -890,8 +806,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_touchAnchor = std::nullopt;
|
||||
}
|
||||
|
||||
_TryStopAutoScroll(ptr.PointerId());
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -904,9 +818,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
|
||||
Input::PointerRoutedEventArgs const& args)
|
||||
{
|
||||
const auto point = args.GetCurrentPoint(_root);
|
||||
const auto delta = point.Properties().MouseWheelDelta();
|
||||
|
||||
auto delta = args.GetCurrentPoint(_root).Properties().MouseWheelDelta();
|
||||
// Get the state of the Ctrl & Shift keys
|
||||
// static_cast to a uint32_t because we can't use the WI_IsFlagSet macro
|
||||
// directly with a VirtualKeyModifiers
|
||||
@@ -924,7 +836,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_MouseScrollHandler(delta, point);
|
||||
_MouseScrollHandler(delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,7 +893,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - Scroll the visible viewport in response to a mouse wheel event.
|
||||
// Arguments:
|
||||
// - mouseDelta: the mouse wheel delta that triggered this event.
|
||||
void TermControl::_MouseScrollHandler(const double mouseDelta, Windows::UI::Input::PointerPoint const& pointerPoint)
|
||||
void TermControl::_MouseScrollHandler(const double mouseDelta)
|
||||
{
|
||||
const auto currentOffset = this->GetScrollOffset();
|
||||
|
||||
@@ -1003,13 +915,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// The scroll bar's ValueChanged handler will actually move the viewport
|
||||
// for us.
|
||||
_scrollBar.Value(static_cast<int>(newValue));
|
||||
|
||||
if (_terminal->IsSelectionActive() && pointerPoint.Properties().IsLeftButtonPressed())
|
||||
{
|
||||
// If user is mouse selecting and scrolls, they then point at new character.
|
||||
// Make sure selection reflects that immediately.
|
||||
_SetEndSelectionPointAtCursor(pointerPoint.Position());
|
||||
}
|
||||
}
|
||||
|
||||
void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& sender,
|
||||
@@ -1077,84 +982,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Starts new pointer related auto scroll behavior, or continues existing one.
|
||||
// Does nothing when there is already auto scroll associated with another pointer.
|
||||
// Arguments:
|
||||
// - pointerPoint: info about pointer that causes auto scroll. Pointer's position
|
||||
// is later used to update selection.
|
||||
// - scrollVelocity: target velocity of scrolling in characters / sec
|
||||
void TermControl::_TryStartAutoScroll(Windows::UI::Input::PointerPoint const& pointerPoint, const double scrollVelocity)
|
||||
{
|
||||
// Allow only one pointer at the time
|
||||
if (!_autoScrollingPointerPoint.has_value() || _autoScrollingPointerPoint.value().PointerId() == pointerPoint.PointerId())
|
||||
{
|
||||
_autoScrollingPointerPoint = pointerPoint;
|
||||
_autoScrollVelocity = scrollVelocity;
|
||||
|
||||
// If this is first time the auto scroll update is about to be called,
|
||||
// kick-start it by initializing its time delta as if it started now
|
||||
if (!_lastAutoScrollUpdateTime.has_value())
|
||||
{
|
||||
_lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
// Apparently this check is not necessary but greatly improves performance
|
||||
if (!_autoScrollTimer.IsEnabled())
|
||||
{
|
||||
_autoScrollTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Stops auto scroll if it's active and is associated with supplied pointer id.
|
||||
// Arguments:
|
||||
// - pointerId: id of pointer for which to stop auto scroll
|
||||
void TermControl::_TryStopAutoScroll(const uint32_t pointerId)
|
||||
{
|
||||
if (_autoScrollingPointerPoint.has_value() && pointerId == _autoScrollingPointerPoint.value().PointerId())
|
||||
{
|
||||
_autoScrollingPointerPoint = std::nullopt;
|
||||
_autoScrollVelocity = 0;
|
||||
_lastAutoScrollUpdateTime = std::nullopt;
|
||||
|
||||
// Apparently this check is not necessary but greatly improves performance
|
||||
if (_autoScrollTimer.IsEnabled())
|
||||
{
|
||||
_autoScrollTimer.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called continuously to gradually scroll viewport when user is
|
||||
// mouse selecting outside it (to 'follow' the cursor).
|
||||
// Arguments:
|
||||
// - none
|
||||
void TermControl::_UpdateAutoScroll(Windows::Foundation::IInspectable const& /* sender */,
|
||||
Windows::Foundation::IInspectable const& /* e */)
|
||||
{
|
||||
if (_autoScrollVelocity != 0)
|
||||
{
|
||||
const auto timeNow = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if (_lastAutoScrollUpdateTime.has_value())
|
||||
{
|
||||
static constexpr double microSecPerSec = 1000000.0;
|
||||
const double deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - _lastAutoScrollUpdateTime.value()).count() / microSecPerSec;
|
||||
_scrollBar.Value(_scrollBar.Value() + _autoScrollVelocity * deltaTime);
|
||||
|
||||
if (_autoScrollingPointerPoint.has_value())
|
||||
{
|
||||
_SetEndSelectionPointAtCursor(_autoScrollingPointerPoint.value().Position());
|
||||
}
|
||||
}
|
||||
|
||||
_lastAutoScrollUpdateTime = timeNow;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Event handler for the GotFocus event. This is used to start
|
||||
// blinking the cursor when the window is focused.
|
||||
@@ -1260,25 +1087,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
_terminal->SetCursorVisible(!_terminal->IsCursorVisible());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
|
||||
// Arguments:
|
||||
// - cursorPosition: in pixels, relative to the origin of the control
|
||||
void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition)
|
||||
{
|
||||
auto terminalPosition = _GetTerminalPosition(cursorPosition);
|
||||
|
||||
const short lastVisibleRow = std::max(_terminal->GetViewport().Height() - 1, 0);
|
||||
const short lastVisibleCol = std::max(_terminal->GetViewport().Width() - 1, 0);
|
||||
|
||||
terminalPosition.Y = std::clamp(terminalPosition.Y, short{ 0 }, lastVisibleRow);
|
||||
terminalPosition.X = std::clamp(terminalPosition.X, short{ 0 }, lastVisibleCol);
|
||||
|
||||
// save location (for rendering) + render
|
||||
_terminal->SetEndSelectionPosition(terminalPosition);
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Process a resize event that was initiated by the user. This can either
|
||||
// be due to the user resizing the window (causing the swapchain to
|
||||
@@ -1429,12 +1237,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// cursorTimer timer, now stopped, is destroyed.
|
||||
}
|
||||
|
||||
if (auto localAutoScrollTimer{ std::exchange(_autoScrollTimer, nullptr) })
|
||||
{
|
||||
localAutoScrollTimer.Stop();
|
||||
// _autoScrollTimer timer, now stopped, is destroyed.
|
||||
}
|
||||
|
||||
if (auto localConnection{ std::exchange(_connection, nullptr) })
|
||||
{
|
||||
localConnection.Close();
|
||||
@@ -1671,8 +1473,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// find out which modifiers (ctrl, alt, shift) are pressed in events that
|
||||
// don't necessarily include that state.
|
||||
// Return Value:
|
||||
// - The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
||||
ControlKeyStates TermControl::_GetPressedModifierKeys() const
|
||||
// - The combined ControlKeyState flags as a bitfield.
|
||||
DWORD TermControl::_GetPressedModifierKeys() const
|
||||
{
|
||||
CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
// DONT USE
|
||||
@@ -1686,28 +1488,24 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
struct KeyModifier
|
||||
{
|
||||
VirtualKey vkey;
|
||||
ControlKeyStates flags;
|
||||
DWORD flag;
|
||||
};
|
||||
|
||||
constexpr std::array<KeyModifier, 5> modifiers{ {
|
||||
{ VirtualKey::RightMenu, ControlKeyStates::RightAltPressed },
|
||||
{ VirtualKey::LeftMenu, ControlKeyStates::LeftAltPressed },
|
||||
{ VirtualKey::RightControl, ControlKeyStates::RightCtrlPressed },
|
||||
{ VirtualKey::LeftControl, ControlKeyStates::LeftCtrlPressed },
|
||||
{ VirtualKey::Shift, ControlKeyStates::ShiftPressed },
|
||||
{ VirtualKey::RightMenu, RIGHT_ALT_PRESSED },
|
||||
{ VirtualKey::LeftMenu, LEFT_ALT_PRESSED },
|
||||
{ VirtualKey::RightControl, RIGHT_CTRL_PRESSED },
|
||||
{ VirtualKey::LeftControl, LEFT_CTRL_PRESSED },
|
||||
{ VirtualKey::Shift, SHIFT_PRESSED },
|
||||
} };
|
||||
|
||||
ControlKeyStates flags;
|
||||
DWORD flags = 0;
|
||||
|
||||
for (const auto& mod : modifiers)
|
||||
{
|
||||
const auto state = window.GetKeyState(mod.vkey);
|
||||
const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (isDown)
|
||||
{
|
||||
flags |= mod.flags;
|
||||
}
|
||||
flags |= isDown ? mod.flag : 0;
|
||||
}
|
||||
|
||||
return flags;
|
||||
@@ -1778,20 +1576,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return _multiClickCounter;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Calculates speed of single axis of auto scrolling. It has to allow for both
|
||||
// fast and precise selection.
|
||||
// Arguments:
|
||||
// - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
|
||||
// Return Value:
|
||||
// - positive speed in characters / sec
|
||||
double TermControl::_GetAutoScrollSpeed(double cursorDistanceFromBorder) const
|
||||
{
|
||||
// The numbers below just feel well, feel free to change.
|
||||
// TODO: Maybe account for space beyond border that user has available
|
||||
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
|
||||
}
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// Winrt events need a method for adding a callback to the event and removing the callback.
|
||||
// These macros will define them both for you.
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
TermControl();
|
||||
TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection);
|
||||
|
||||
Windows::UI::Xaml::UIElement GetRoot();
|
||||
Windows::UI::Xaml::Controls::UserControl GetControl();
|
||||
void UpdateSettings(Settings::IControlSettings newSettings);
|
||||
|
||||
hstring Title();
|
||||
@@ -53,9 +55,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void SwapChainChanged();
|
||||
~TermControl();
|
||||
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
|
||||
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
|
||||
|
||||
static Windows::Foundation::Point GetProposedDimensions(Microsoft::Terminal::Settings::IControlSettings const& settings, const uint32_t dpi);
|
||||
|
||||
// clang-format off
|
||||
@@ -72,6 +71,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
TerminalConnection::ITerminalConnection _connection;
|
||||
bool _initializedTerminal;
|
||||
|
||||
Windows::UI::Xaml::Controls::UserControl _controlRoot;
|
||||
Windows::UI::Xaml::Controls::Grid _root;
|
||||
Windows::UI::Xaml::Controls::Image _bgImageLayer;
|
||||
Windows::UI::Xaml::Controls::SwapChainPanel _swapChainPanel;
|
||||
@@ -92,12 +92,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
std::optional<int> _lastScrollOffset;
|
||||
|
||||
// Auto scroll occurs when user, while selecting, drags cursor outside viewport. View is then scrolled to 'follow' the cursor.
|
||||
double _autoScrollVelocity;
|
||||
std::optional<Windows::UI::Input::PointerPoint> _autoScrollingPointerPoint;
|
||||
Windows::UI::Xaml::DispatcherTimer _autoScrollTimer;
|
||||
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
|
||||
|
||||
// storage location for the leading surrogate of a utf-16 surrogate pair
|
||||
std::optional<wchar_t> _leadingSurrogate;
|
||||
|
||||
@@ -128,6 +122,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _ApplyUISettings();
|
||||
void _InitializeBackgroundBrush();
|
||||
void _BackgroundColorChanged(const uint32_t color);
|
||||
void _ApplyConnectionSettings();
|
||||
void _InitializeTerminal();
|
||||
void _UpdateFont();
|
||||
void _KeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
|
||||
@@ -141,7 +136,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _LostFocusHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
|
||||
void _BlinkCursor(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
|
||||
void _SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition);
|
||||
void _SendInputToConnection(const std::wstring& wstr);
|
||||
void _SwapChainSizeChanged(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e);
|
||||
void _SwapChainScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender, Windows::Foundation::IInspectable const& args);
|
||||
@@ -149,27 +143,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _TerminalTitleChanged(const std::wstring_view& wstr);
|
||||
void _TerminalScrollPositionChanged(const int viewTop, const int viewHeight, const int bufferSize);
|
||||
|
||||
void _MouseScrollHandler(const double delta, Windows::UI::Input::PointerPoint const& pointerPoint);
|
||||
void _MouseScrollHandler(const double delta);
|
||||
void _MouseZoomHandler(const double delta);
|
||||
void _MouseTransparencyHandler(const double delta);
|
||||
|
||||
bool _CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
bool _ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
|
||||
void _TryStartAutoScroll(Windows::UI::Input::PointerPoint const& pointerPoint, const double scrollVelocity);
|
||||
void _TryStopAutoScroll(const uint32_t pointerId);
|
||||
void _UpdateAutoScroll(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
|
||||
|
||||
void _ScrollbarUpdater(Windows::UI::Xaml::Controls::Primitives::ScrollBar scrollbar, const int viewTop, const int viewHeight, const int bufferSize);
|
||||
static Windows::UI::Xaml::Thickness _ParseThicknessFromPadding(const hstring padding);
|
||||
|
||||
::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() const;
|
||||
void _HandleVoidKeyEvent();
|
||||
bool _TrySendKeyEvent(WORD vkey, ::Microsoft::Terminal::Core::ControlKeyStates modifiers);
|
||||
DWORD _GetPressedModifierKeys() const;
|
||||
|
||||
const COORD _GetTerminalPosition(winrt::Windows::Foundation::Point cursorPosition);
|
||||
const unsigned int _NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime);
|
||||
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,15 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
}
|
||||
|
||||
[default_interface]
|
||||
runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl
|
||||
runtimeclass TermControl
|
||||
{
|
||||
TermControl();
|
||||
TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
|
||||
static Windows.Foundation.Point GetProposedDimensions(Microsoft.Terminal.Settings.IControlSettings settings, UInt32 dpi);
|
||||
|
||||
Windows.UI.Xaml.UIElement GetRoot();
|
||||
Windows.UI.Xaml.Controls.UserControl GetControl();
|
||||
void UpdateSettings(Microsoft.Terminal.Settings.IControlSettings newSettings);
|
||||
|
||||
event TitleChangedEventArgs TitleChanged;
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include <UIAutomationCore.h>
|
||||
#include "TermControlAutomationPeer.h"
|
||||
#include "TermControl.h"
|
||||
#include "TermControlAutomationPeer.g.cpp"
|
||||
|
||||
#include "XamlUiaTextRange.h"
|
||||
|
||||
using namespace Microsoft::Console::Types;
|
||||
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
||||
|
||||
namespace UIA
|
||||
{
|
||||
using ::ITextRangeProvider;
|
||||
using ::SupportedTextSelection;
|
||||
}
|
||||
|
||||
namespace XamlAutomation
|
||||
{
|
||||
using winrt::Windows::UI::Xaml::Automation::SupportedTextSelection;
|
||||
using winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple;
|
||||
using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider;
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& owner) :
|
||||
TermControlAutomationPeerT<TermControlAutomationPeer>(owner), // pass owner to FrameworkElementAutomationPeer
|
||||
_uiaProvider{ owner.GetRenderData(), nullptr, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {};
|
||||
|
||||
winrt::hstring TermControlAutomationPeer::GetClassNameCore() const
|
||||
{
|
||||
return L"TermControl";
|
||||
}
|
||||
|
||||
AutomationControlType TermControlAutomationPeer::GetAutomationControlTypeCore() const
|
||||
{
|
||||
return AutomationControlType::Text;
|
||||
}
|
||||
|
||||
winrt::hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
|
||||
{
|
||||
// TODO GitHub #2142: Localize string
|
||||
return L"TerminalControl";
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
|
||||
{
|
||||
switch (patternInterface)
|
||||
{
|
||||
case PatternInterface::Text:
|
||||
return *this;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region ITextProvider
|
||||
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
|
||||
{
|
||||
SAFEARRAY* pReturnVal;
|
||||
THROW_IF_FAILED(_uiaProvider.GetSelection(&pReturnVal));
|
||||
return WrapArrayOfTextRangeProviders(pReturnVal);
|
||||
}
|
||||
|
||||
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
|
||||
{
|
||||
SAFEARRAY* pReturnVal;
|
||||
THROW_IF_FAILED(_uiaProvider.GetVisibleRanges(&pReturnVal));
|
||||
return WrapArrayOfTextRangeProviders(pReturnVal);
|
||||
}
|
||||
|
||||
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement)
|
||||
{
|
||||
UIA::ITextRangeProvider* returnVal;
|
||||
// ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr
|
||||
THROW_IF_FAILED(_uiaProvider.RangeFromChild(/* IRawElementProviderSimple */ nullptr,
|
||||
&returnVal));
|
||||
|
||||
auto parentProvider = this->ProviderFromPeer(*this);
|
||||
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
}
|
||||
|
||||
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation)
|
||||
{
|
||||
UIA::ITextRangeProvider* returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider.RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal));
|
||||
|
||||
auto parentProvider = this->ProviderFromPeer(*this);
|
||||
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
}
|
||||
|
||||
XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange()
|
||||
{
|
||||
UIA::ITextRangeProvider* returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider.get_DocumentRange(&returnVal));
|
||||
|
||||
auto parentProvider = this->ProviderFromPeer(*this);
|
||||
auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Automation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()
|
||||
{
|
||||
UIA::SupportedTextSelection returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider.get_SupportedTextSelection(&returnVal));
|
||||
return static_cast<XamlAutomation::SupportedTextSelection>(returnVal);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
RECT TermControlAutomationPeer::GetBoundingRectWrapped()
|
||||
{
|
||||
auto rect = GetBoundingRectangle();
|
||||
return {
|
||||
gsl::narrow<LONG>(rect.X),
|
||||
gsl::narrow<LONG>(rect.Y),
|
||||
gsl::narrow<LONG>(rect.X + rect.Width),
|
||||
gsl::narrow<LONG>(rect.Y + rect.Height)
|
||||
};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders
|
||||
// Arguments:
|
||||
// - SAFEARRAY of UIA::UiaTextRange (ITextRangeProviders)
|
||||
// Return Value:
|
||||
// - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders)
|
||||
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
|
||||
{
|
||||
// transfer ownership of UiaTextRanges to this new vector
|
||||
auto providers = SafeArrayToOwningVector<::Microsoft::Console::Types::UiaTextRange>(textRanges);
|
||||
int count = providers.size();
|
||||
|
||||
std::vector<XamlAutomation::ITextRangeProvider> vec;
|
||||
vec.reserve(count);
|
||||
auto parentProvider = this->ProviderFromPeer(*this);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
auto xutr = winrt::make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
|
||||
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>());
|
||||
}
|
||||
|
||||
winrt::com_array<XamlAutomation::ITextRangeProvider> result{ vec };
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- TermControlAutomationPeer.h
|
||||
|
||||
Abstract:
|
||||
- This module provides UI Automation access to the TermControl
|
||||
to support both automation tests and accessibility (screen
|
||||
reading) applications. This mainly interacts with ScreenInfoUiaProvider
|
||||
to allow for shared code between ConHost and Windows Terminal
|
||||
accessibility providers.
|
||||
- Based on the Custom Automation Peers guide on msdn
|
||||
(https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
|
||||
- Wraps the UIAutomationCore ITextProvider
|
||||
(https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcore/nn-uiautomationcore-itextprovider)
|
||||
with a XAML ITextProvider
|
||||
(https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.automation.provider.itextprovider)
|
||||
|
||||
Author(s):
|
||||
- Carlos Zamora (CaZamor) 2019
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TermControl.h"
|
||||
#include "TermControlAutomationPeer.g.h"
|
||||
#include <winrt/Microsoft.Terminal.TerminalControl.h>
|
||||
#include "../../renderer/inc/IRenderData.hpp"
|
||||
#include "../types/ScreenInfoUiaProvider.h"
|
||||
#include "../types/WindowUiaProviderBase.hpp"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
struct TermControlAutomationPeer :
|
||||
public TermControlAutomationPeerT<TermControlAutomationPeer>
|
||||
{
|
||||
public:
|
||||
TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& owner);
|
||||
|
||||
winrt::hstring GetClassNameCore() const;
|
||||
winrt::Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;
|
||||
winrt::hstring GetLocalizedControlTypeCore() const;
|
||||
winrt::Windows::Foundation::IInspectable GetPatternCore(winrt::Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) const;
|
||||
|
||||
#pragma region ITextProvider Pattern
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromPoint(Windows::Foundation::Point screenLocation);
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromChild(Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple childElement);
|
||||
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetVisibleRanges();
|
||||
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetSelection();
|
||||
Windows::UI::Xaml::Automation::SupportedTextSelection SupportedTextSelection();
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider DocumentRange();
|
||||
#pragma endregion
|
||||
|
||||
RECT GetBoundingRectWrapped();
|
||||
|
||||
private:
|
||||
::Microsoft::Console::Types::ScreenInfoUiaProvider _uiaProvider;
|
||||
|
||||
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
|
||||
};
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "TermControl.idl";
|
||||
|
||||
namespace Microsoft.Terminal.TerminalControl
|
||||
{
|
||||
[default_interface]
|
||||
runtimeclass TermControlAutomationPeer :
|
||||
Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer,
|
||||
Windows.UI.Xaml.Automation.Provider.ITextProvider
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -20,28 +20,18 @@
|
||||
<ClInclude Include="TermControl.h">
|
||||
<DependentUpon>TermControl.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TermControlAutomationPeer.h">
|
||||
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="XamlUiaTextRange.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="init.cpp" />
|
||||
<ClCompile Include="TermControl.cpp">
|
||||
<DependentUpon>TermControl.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="TermControlAutomationPeer.cpp">
|
||||
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="XamlUiaTextRange.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="TermControl.idl" />
|
||||
<Midl Include="TermControlAutomationPeer.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
@@ -59,7 +49,7 @@
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalCore\lib\TerminalCore-lib.vcxproj" />
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" >
|
||||
<Private>false</Private>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="KeyChord.cpp" />
|
||||
<ClCompile Include="TermControl.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="TermControlAutomationPeer.cpp" />
|
||||
<ClCompile Include="XamlUiaTextRange.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="KeyChord.h" />
|
||||
<ClInclude Include="TermControl.h" />
|
||||
<ClInclude Include="TermControlAP.h" />
|
||||
<ClInclude Include="XamlUiaTextRange.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="TermControl.idl" />
|
||||
<Midl Include="TermControlAutomationPeer.idl" />
|
||||
<Midl Include="KeyChord.idl" />
|
||||
<Midl Include="IKeyBindings.idl" />
|
||||
<Midl Include="IControlSettings.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="TerminalControl.def" />
|
||||
@@ -33,4 +33,4 @@
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,199 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "XamlUiaTextRange.h"
|
||||
#include "../types/UiaTextRange.hpp"
|
||||
|
||||
namespace UIA
|
||||
{
|
||||
using ::ITextRangeProvider;
|
||||
using ::SupportedTextSelection;
|
||||
using ::TextPatternRangeEndpoint;
|
||||
using ::TextUnit;
|
||||
}
|
||||
|
||||
namespace XamlAutomation
|
||||
{
|
||||
using winrt::Windows::UI::Xaml::Automation::SupportedTextSelection;
|
||||
using winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple;
|
||||
using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider;
|
||||
using winrt::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint;
|
||||
using winrt::Windows::UI::Xaml::Automation::Text::TextUnit;
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
XamlAutomation::ITextRangeProvider XamlUiaTextRange::Clone() const
|
||||
{
|
||||
UIA::ITextRangeProvider* pReturn;
|
||||
THROW_IF_FAILED(_uiaProvider->Clone(&pReturn));
|
||||
auto xutr = winrt::make_self<XamlUiaTextRange>(pReturn, _parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
}
|
||||
|
||||
bool XamlUiaTextRange::Compare(XamlAutomation::ITextRangeProvider pRange) const
|
||||
{
|
||||
auto self = winrt::get_self<XamlUiaTextRange>(pRange);
|
||||
|
||||
BOOL returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->Compare(self->_uiaProvider.get(), &returnVal));
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
int32_t XamlUiaTextRange::CompareEndpoints(XamlAutomation::TextPatternRangeEndpoint endpoint,
|
||||
XamlAutomation::ITextRangeProvider pTargetRange,
|
||||
XamlAutomation::TextPatternRangeEndpoint targetEndpoint)
|
||||
{
|
||||
auto self = winrt::get_self<XamlUiaTextRange>(pTargetRange);
|
||||
|
||||
int32_t returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->CompareEndpoints(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
|
||||
self->_uiaProvider.get(),
|
||||
static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint),
|
||||
&returnVal));
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
void XamlUiaTextRange::ExpandToEnclosingUnit(XamlAutomation::TextUnit unit) const
|
||||
{
|
||||
THROW_IF_FAILED(_uiaProvider->ExpandToEnclosingUnit(static_cast<UIA::TextUnit>(unit)));
|
||||
}
|
||||
|
||||
XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindAttribute(int32_t textAttributeId,
|
||||
winrt::Windows::Foundation::IInspectable val,
|
||||
bool searchBackward)
|
||||
{
|
||||
// TODO GitHub #2161: potential accessibility improvement
|
||||
// we don't support this currently
|
||||
throw winrt::hresult_not_implemented();
|
||||
}
|
||||
|
||||
XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindText(winrt::hstring text,
|
||||
bool searchBackward,
|
||||
bool ignoreCase)
|
||||
{
|
||||
// TODO GitHub #605: Search functionality
|
||||
// we need to wrap this around the UiaTextRange FindText() function
|
||||
// but right now it returns E_NOTIMPL, so let's just return nullptr for now.
|
||||
throw winrt::hresult_not_implemented();
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IInspectable XamlUiaTextRange::GetAttributeValue(int32_t textAttributeId) const
|
||||
{
|
||||
// Copied functionality from Types::UiaTextRange.cpp
|
||||
if (textAttributeId == UIA_IsReadOnlyAttributeId)
|
||||
{
|
||||
return winrt::box_value(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void XamlUiaTextRange::GetBoundingRectangles(com_array<double>& returnValue) const
|
||||
{
|
||||
returnValue = {};
|
||||
try
|
||||
{
|
||||
SAFEARRAY* pReturnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->GetBoundingRectangles(&pReturnVal));
|
||||
|
||||
double* pVals;
|
||||
THROW_IF_FAILED(SafeArrayAccessData(pReturnVal, (void**)&pVals));
|
||||
|
||||
long lBound, uBound;
|
||||
THROW_IF_FAILED(SafeArrayGetLBound(pReturnVal, 1, &lBound));
|
||||
THROW_IF_FAILED(SafeArrayGetUBound(pReturnVal, 1, &uBound));
|
||||
|
||||
long count = uBound - lBound + 1;
|
||||
|
||||
std::vector<double> vec;
|
||||
vec.reserve(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
double element = pVals[i];
|
||||
vec.push_back(element);
|
||||
}
|
||||
|
||||
winrt::com_array<double> result{ vec };
|
||||
returnValue = std::move(result);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
XamlAutomation::IRawElementProviderSimple XamlUiaTextRange::GetEnclosingElement()
|
||||
{
|
||||
return _parentProvider;
|
||||
}
|
||||
|
||||
winrt::hstring XamlUiaTextRange::GetText(int32_t maxLength) const
|
||||
{
|
||||
BSTR returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->GetText(maxLength, &returnVal));
|
||||
return winrt::to_hstring(returnVal);
|
||||
}
|
||||
|
||||
int32_t XamlUiaTextRange::Move(XamlAutomation::TextUnit unit,
|
||||
int32_t count)
|
||||
{
|
||||
int returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->Move(static_cast<UIA::TextUnit>(unit),
|
||||
count,
|
||||
&returnVal));
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
int32_t XamlUiaTextRange::MoveEndpointByUnit(XamlAutomation::TextPatternRangeEndpoint endpoint,
|
||||
XamlAutomation::TextUnit unit,
|
||||
int32_t count) const
|
||||
{
|
||||
int returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->MoveEndpointByUnit(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
|
||||
static_cast<UIA::TextUnit>(unit),
|
||||
count,
|
||||
&returnVal));
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
void XamlUiaTextRange::MoveEndpointByRange(XamlAutomation::TextPatternRangeEndpoint endpoint,
|
||||
XamlAutomation::ITextRangeProvider pTargetRange,
|
||||
XamlAutomation::TextPatternRangeEndpoint targetEndpoint) const
|
||||
{
|
||||
auto self = winrt::get_self<XamlUiaTextRange>(pTargetRange);
|
||||
THROW_IF_FAILED(_uiaProvider->MoveEndpointByRange(static_cast<UIA::TextPatternRangeEndpoint>(endpoint),
|
||||
/*pTargetRange*/ self->_uiaProvider.get(),
|
||||
static_cast<UIA::TextPatternRangeEndpoint>(targetEndpoint)));
|
||||
}
|
||||
|
||||
void XamlUiaTextRange::Select() const
|
||||
{
|
||||
THROW_IF_FAILED(_uiaProvider->Select());
|
||||
}
|
||||
|
||||
void XamlUiaTextRange::AddToSelection() const
|
||||
{
|
||||
// we don't support this
|
||||
throw winrt::hresult_not_implemented();
|
||||
}
|
||||
|
||||
void XamlUiaTextRange::RemoveFromSelection() const
|
||||
{
|
||||
// we don't support this
|
||||
throw winrt::hresult_not_implemented();
|
||||
}
|
||||
|
||||
void XamlUiaTextRange::ScrollIntoView(bool alignToTop) const
|
||||
{
|
||||
THROW_IF_FAILED(_uiaProvider->ScrollIntoView(alignToTop));
|
||||
}
|
||||
|
||||
winrt::com_array<XamlAutomation::IRawElementProviderSimple> XamlUiaTextRange::GetChildren() const
|
||||
{
|
||||
// we don't have any children
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- XamlUiaTextRange.h
|
||||
|
||||
Abstract:
|
||||
- This module is a wrapper for the UiaTextRange
|
||||
(a text range accessibility provider). It allows
|
||||
for UiaTextRange to be used in Windows Terminal.
|
||||
- Wraps the UIAutomationCore ITextRangeProvider
|
||||
(https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcore/nn-uiautomationcore-itextrangeprovider)
|
||||
with a XAML ITextRangeProvider
|
||||
(https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.automation.provider.itextrangeprovider)
|
||||
|
||||
Author(s):
|
||||
- Carlos Zamora (CaZamor) 2019
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TermControlAutomationPeer.h"
|
||||
#include <UIAutomationCore.h>
|
||||
#include "../types/UiaTextRange.hpp"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
class XamlUiaTextRange :
|
||||
public winrt::implements<XamlUiaTextRange, Windows::UI::Xaml::Automation::Provider::ITextRangeProvider>
|
||||
{
|
||||
public:
|
||||
XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider) :
|
||||
_parentProvider{ parentProvider }
|
||||
{
|
||||
_uiaProvider.attach(uiaProvider);
|
||||
}
|
||||
|
||||
#pragma region ITextRangeProvider
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider Clone() const;
|
||||
bool Compare(Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pRange) const;
|
||||
int32_t CompareEndpoints(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pTargetRange,
|
||||
Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint);
|
||||
void ExpandToEnclosingUnit(Windows::UI::Xaml::Automation::Text::TextUnit unit) const;
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider FindAttribute(int32_t textAttributeId,
|
||||
winrt::Windows::Foundation::IInspectable val,
|
||||
bool searchBackward);
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider FindText(winrt::hstring text,
|
||||
bool searchBackward,
|
||||
bool ignoreCase);
|
||||
winrt::Windows::Foundation::IInspectable GetAttributeValue(int32_t textAttributeId) const;
|
||||
void GetBoundingRectangles(winrt::com_array<double>& returnValue) const;
|
||||
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple GetEnclosingElement();
|
||||
winrt::hstring GetText(int32_t maxLength) const;
|
||||
int32_t Move(Windows::UI::Xaml::Automation::Text::TextUnit unit,
|
||||
int32_t count);
|
||||
int32_t MoveEndpointByUnit(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
|
||||
Windows::UI::Xaml::Automation::Text::TextUnit unit,
|
||||
int32_t count) const;
|
||||
void MoveEndpointByRange(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint,
|
||||
Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pTargetRange,
|
||||
Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint) const;
|
||||
void Select() const;
|
||||
void AddToSelection() const;
|
||||
void RemoveFromSelection() const;
|
||||
void ScrollIntoView(bool alignToTop) const;
|
||||
winrt::com_array<Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple> GetChildren() const;
|
||||
#pragma endregion ITextRangeProvider
|
||||
|
||||
private:
|
||||
wil::com_ptr<::ITextRangeProvider> _uiaProvider;
|
||||
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider;
|
||||
};
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// Note: Generate GUID using TlgGuid.exe tool
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hTerminalControlProvider,
|
||||
"Microsoft.Windows.Terminal.Control",
|
||||
// {28c82e50-57af-5a86-c25b-e39cd990032b}
|
||||
(0x28c82e50, 0x57af, 0x5a86, 0xc2, 0x5b, 0xe3, 0x9c, 0xd9, 0x90, 0x03, 0x2b),
|
||||
TraceLoggingOptionMicrosoftTelemetry());
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DisableThreadLibraryCalls(hInstDll);
|
||||
TraceLoggingRegister(g_hTerminalControlProvider);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (g_hTerminalControlProvider)
|
||||
{
|
||||
TraceLoggingUnregister(g_hTerminalControlProvider);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -23,8 +23,6 @@
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/windows.ui.core.h>
|
||||
#include <winrt/Windows.ui.input.h>
|
||||
#include <winrt/Windows.UI.Xaml.h>
|
||||
#include <winrt/Windows.UI.Xaml.Automation.Peers.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
|
||||
#include <winrt/Windows.ui.xaml.media.h>
|
||||
@@ -32,7 +30,3 @@
|
||||
#include <winrt/Windows.ui.xaml.input.h>
|
||||
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
#include <TraceLoggingProvider.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider);
|
||||
#include <telemetry/ProjectTelemetry.h>
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Microsoft::Terminal::Core
|
||||
{
|
||||
class ControlKeyStates;
|
||||
}
|
||||
|
||||
constexpr Microsoft::Terminal::Core::ControlKeyStates operator|(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept;
|
||||
|
||||
// This class is functionally equivalent to PowerShell's System.Management.Automation.Host.ControlKeyStates enum:
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.host.controlkeystates
|
||||
// It's flagging values are compatible to those used by the NT console subsystem (<um/wincon.h>),
|
||||
// as these are being used throughout older parts of this project.
|
||||
class Microsoft::Terminal::Core::ControlKeyStates
|
||||
{
|
||||
struct StaticValue
|
||||
{
|
||||
DWORD v;
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr StaticValue RightAltPressed{ RIGHT_ALT_PRESSED };
|
||||
static constexpr StaticValue LeftAltPressed{ LEFT_ALT_PRESSED };
|
||||
static constexpr StaticValue RightCtrlPressed{ RIGHT_CTRL_PRESSED };
|
||||
static constexpr StaticValue LeftCtrlPressed{ LEFT_CTRL_PRESSED };
|
||||
static constexpr StaticValue ShiftPressed{ SHIFT_PRESSED };
|
||||
static constexpr StaticValue NumlockOn{ NUMLOCK_ON };
|
||||
static constexpr StaticValue ScrolllockOn{ SCROLLLOCK_ON };
|
||||
static constexpr StaticValue CapslockOn{ CAPSLOCK_ON };
|
||||
static constexpr StaticValue EnhancedKey{ ENHANCED_KEY };
|
||||
|
||||
constexpr ControlKeyStates() noexcept :
|
||||
_value(0) {}
|
||||
|
||||
constexpr ControlKeyStates(StaticValue value) noexcept :
|
||||
_value(value.v) {}
|
||||
|
||||
explicit constexpr ControlKeyStates(DWORD value) noexcept :
|
||||
_value(value) {}
|
||||
|
||||
ControlKeyStates& operator|=(ControlKeyStates rhs) noexcept
|
||||
{
|
||||
_value |= rhs.Value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr DWORD Value() const noexcept
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
constexpr bool IsShiftPressed() const noexcept
|
||||
{
|
||||
return IsAnyFlagSet(ShiftPressed);
|
||||
}
|
||||
|
||||
constexpr bool IsAltPressed() const noexcept
|
||||
{
|
||||
return IsAnyFlagSet(RightAltPressed | LeftAltPressed);
|
||||
}
|
||||
|
||||
constexpr bool IsCtrlPressed() const noexcept
|
||||
{
|
||||
return IsAnyFlagSet(RightCtrlPressed | LeftCtrlPressed);
|
||||
}
|
||||
|
||||
constexpr bool IsAltGrPressed() const noexcept
|
||||
{
|
||||
return AreAllFlagsSet(RightAltPressed | LeftCtrlPressed);
|
||||
}
|
||||
|
||||
constexpr bool IsModifierPressed() const noexcept
|
||||
{
|
||||
return IsAnyFlagSet(RightAltPressed | LeftAltPressed | RightCtrlPressed | LeftCtrlPressed | ShiftPressed);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr bool AreAllFlagsSet(ControlKeyStates mask) const noexcept
|
||||
{
|
||||
return (Value() & mask.Value()) == mask.Value();
|
||||
}
|
||||
|
||||
constexpr bool IsAnyFlagSet(ControlKeyStates mask) const noexcept
|
||||
{
|
||||
return (Value() & mask.Value()) != 0;
|
||||
}
|
||||
|
||||
DWORD _value;
|
||||
};
|
||||
|
||||
constexpr Microsoft::Terminal::Core::ControlKeyStates operator|(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
|
||||
{
|
||||
return Microsoft::Terminal::Core::ControlKeyStates{ lhs.Value() | rhs.Value() };
|
||||
}
|
||||
|
||||
constexpr Microsoft::Terminal::Core::ControlKeyStates operator&(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
|
||||
{
|
||||
return Microsoft::Terminal::Core::ControlKeyStates{ lhs.Value() & rhs.Value() };
|
||||
}
|
||||
|
||||
constexpr bool operator==(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
|
||||
{
|
||||
return lhs.Value() == rhs.Value();
|
||||
}
|
||||
|
||||
constexpr bool operator!=(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
|
||||
{
|
||||
return lhs.Value() != rhs.Value();
|
||||
}
|
||||
@@ -24,11 +24,7 @@ namespace Microsoft::Terminal::Core
|
||||
virtual bool SetCursorPosition(short x, short y) = 0;
|
||||
virtual COORD GetCursorPosition() = 0;
|
||||
|
||||
virtual bool DeleteCharacter(const unsigned int uiCount) = 0;
|
||||
virtual bool InsertCharacter(const unsigned int uiCount) = 0;
|
||||
virtual bool EraseCharacters(const unsigned int numChars) = 0;
|
||||
virtual bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) = 0;
|
||||
virtual bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) = 0;
|
||||
|
||||
virtual bool SetWindowTitle(std::wstring_view title) = 0;
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ControlKeyStates.hpp"
|
||||
|
||||
namespace Microsoft::Terminal::Core
|
||||
{
|
||||
class ITerminalInput
|
||||
@@ -12,7 +9,7 @@ namespace Microsoft::Terminal::Core
|
||||
public:
|
||||
virtual ~ITerminalInput() {}
|
||||
|
||||
virtual bool SendKeyEvent(const WORD vkey, const ControlKeyStates states) = 0;
|
||||
virtual bool SendKeyEvent(const WORD vkey, const DWORD modifiers) = 0;
|
||||
|
||||
// void SendMouseEvent(uint row, uint col, KeyModifiers modifiers);
|
||||
[[nodiscard]] virtual HRESULT UserResize(const COORD size) noexcept = 0;
|
||||
|
||||
@@ -197,11 +197,11 @@ void Terminal::Write(std::wstring_view stringView)
|
||||
// real character out of the event.
|
||||
// Arguments:
|
||||
// - vkey: The vkey of the key pressed.
|
||||
// - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
|
||||
// - modifiers: The current ControlKeyState flags.
|
||||
// Return Value:
|
||||
// - true if we translated the key event, and it should not be processed any further.
|
||||
// - false if we did not translate the key, and it should be processed into a character.
|
||||
bool Terminal::SendKeyEvent(const WORD vkey, const ControlKeyStates states)
|
||||
bool Terminal::SendKeyEvent(const WORD vkey, const DWORD modifiers)
|
||||
{
|
||||
if (_snapOnInput && _scrollOffset != 0)
|
||||
{
|
||||
@@ -210,53 +210,59 @@ bool Terminal::SendKeyEvent(const WORD vkey, const ControlKeyStates states)
|
||||
_NotifyScrollEvent();
|
||||
}
|
||||
|
||||
KeyEvent keyEv{ true, 0, vkey, 0, UNICODE_NULL, modifiers };
|
||||
|
||||
// AltGr key combinations don't always contain any meaningful,
|
||||
// pretranslated unicode character during WM_KEYDOWN.
|
||||
// E.g. on a German keyboard AltGr+Q should result in a "@" character,
|
||||
// but actually results in "Q" with Alt and Ctrl modifier states.
|
||||
// By returning false though, we can abort handling this WM_KEYDOWN
|
||||
// event and let the WM_CHAR handler kick in, which will be
|
||||
// provided with an appropriate unicode character.
|
||||
if (keyEv.IsAltGrPressed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto ctrlPressed = keyEv.IsCtrlPressed();
|
||||
const auto altPressed = keyEv.IsAltPressed();
|
||||
const auto shiftPressed = keyEv.IsShiftPressed();
|
||||
|
||||
// Alt key sequences _require_ the char to be in the keyevent. If alt is
|
||||
// pressed, manually get the character that's being typed, and put it in the
|
||||
// KeyEvent.
|
||||
// DON'T manually handle Alt+Space - the system will use this to bring up
|
||||
// the system menu for restore, min/maximimize, size, move, close
|
||||
wchar_t ch = UNICODE_NULL;
|
||||
if (states.IsAltPressed() && vkey != VK_SPACE)
|
||||
if (altPressed && vkey != VK_SPACE)
|
||||
{
|
||||
ch = static_cast<wchar_t>(LOWORD(MapVirtualKey(vkey, MAPVK_VK_TO_CHAR)));
|
||||
// MapVirtualKey will give us the capitalized version of the char.
|
||||
// However, if shift isn't pressed, we want to send the lowercase version.
|
||||
// (See GH#637)
|
||||
if (!states.IsShiftPressed())
|
||||
if (!shiftPressed)
|
||||
{
|
||||
ch = towlower(ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (states.IsCtrlPressed())
|
||||
// Manually handle Ctrl+H. Ctrl+H should be handled as Backspace. To do this
|
||||
// correctly, the keyEvents's char needs to be set to Backspace.
|
||||
// 0x48 is the VKEY for 'H', which isn't named
|
||||
if (ctrlPressed && vkey == 0x48)
|
||||
{
|
||||
switch (vkey)
|
||||
{
|
||||
case 0x48:
|
||||
// Manually handle Ctrl+H. Ctrl+H should be handled as Backspace. To do this
|
||||
// correctly, the keyEvents's char needs to be set to Backspace.
|
||||
// 0x48 is the VKEY for 'H', which isn't named
|
||||
ch = UNICODE_BACKSPACE;
|
||||
break;
|
||||
case VK_SPACE:
|
||||
// Manually handle Ctrl+Space here. The terminalInput translator requires
|
||||
// the char to be set to Space for space handling to work correctly.
|
||||
ch = UNICODE_SPACE;
|
||||
break;
|
||||
}
|
||||
ch = UNICODE_BACKSPACE;
|
||||
}
|
||||
// Manually handle Ctrl+Space here. The terminalInput translator requires
|
||||
// the char to be set to Space for space handling to work correctly.
|
||||
if (ctrlPressed && vkey == VK_SPACE)
|
||||
{
|
||||
ch = UNICODE_SPACE;
|
||||
}
|
||||
|
||||
// Manually handle Escape here. If we let it fall through, it'll come
|
||||
// back up through the character handler. It's registered as a translation
|
||||
// in TerminalInput, so we'll let TerminalInput control it.
|
||||
if (vkey == VK_ESCAPE)
|
||||
{
|
||||
ch = UNICODE_ESC;
|
||||
}
|
||||
keyEv.SetCharData(ch);
|
||||
|
||||
const bool manuallyHandled = ch != UNICODE_NULL;
|
||||
|
||||
KeyEvent keyEv{ true, 0, vkey, 0, ch, states.Value() };
|
||||
const bool translated = _terminalInput->HandleKey(&keyEv);
|
||||
|
||||
return translated && manuallyHandled;
|
||||
@@ -351,11 +357,6 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
|
||||
proposedCursorPosition.X--;
|
||||
}
|
||||
}
|
||||
else if (wch == UNICODE_BEL)
|
||||
{
|
||||
// TODO: GitHub #1883
|
||||
// For now its empty just so we don't try to write the BEL character
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: MSFT 21006766
|
||||
|
||||
@@ -66,11 +66,7 @@ public:
|
||||
bool ReverseText(bool reversed) override;
|
||||
bool SetCursorPosition(short x, short y) override;
|
||||
COORD GetCursorPosition() override;
|
||||
bool DeleteCharacter(const unsigned int uiCount) override;
|
||||
bool InsertCharacter(const unsigned int uiCount) override;
|
||||
bool EraseCharacters(const unsigned int numChars) override;
|
||||
bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override;
|
||||
bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override;
|
||||
bool SetWindowTitle(std::wstring_view title) override;
|
||||
bool SetColorTableEntry(const size_t tableIndex, const COLORREF dwColor) override;
|
||||
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) override;
|
||||
@@ -80,7 +76,7 @@ public:
|
||||
|
||||
#pragma region ITerminalInput
|
||||
// These methods are defined in Terminal.cpp
|
||||
bool SendKeyEvent(const WORD vkey, const Microsoft::Terminal::Core::ControlKeyStates states) override;
|
||||
bool SendKeyEvent(const WORD vkey, const DWORD modifiers) override;
|
||||
[[nodiscard]] HRESULT UserResize(const COORD viewportSize) noexcept override;
|
||||
void UserScrollViewport(const int viewTop) override;
|
||||
int GetScrollOffset() override;
|
||||
@@ -105,23 +101,6 @@ public:
|
||||
const std::vector<Microsoft::Console::Render::RenderOverlay> GetOverlays() const noexcept override;
|
||||
const bool IsGridLineDrawingAllowed() noexcept override;
|
||||
std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override;
|
||||
bool IsAreaSelected() const override;
|
||||
void ClearSelection() override;
|
||||
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
|
||||
|
||||
// TODO GitHub #605: Search functionality
|
||||
// For now, just adding it here to make UiaTextRange easier to create (Accessibility)
|
||||
// We should actually abstract this out better once Windows Terminal has Search
|
||||
HRESULT SearchForText(_In_ BSTR text,
|
||||
_In_ BOOL searchBackward,
|
||||
_In_ BOOL ignoreCase,
|
||||
_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal,
|
||||
unsigned int _start,
|
||||
unsigned int _end,
|
||||
std::function<unsigned int(IRenderData*, const COORD)> _coordToEndpoint,
|
||||
std::function<COORD(IRenderData*, const unsigned int)> _endpointToCoord,
|
||||
std::function<IFACEMETHODIMP(ITextRangeProvider**)> Clone) override;
|
||||
|
||||
const std::wstring GetConsoleTitle() const noexcept override;
|
||||
void LockConsole() noexcept override;
|
||||
void UnlockConsole() noexcept override;
|
||||
@@ -143,6 +122,7 @@ public:
|
||||
void SetSelectionAnchor(const COORD position);
|
||||
void SetEndSelectionPosition(const COORD position);
|
||||
void SetBoxSelection(const bool isEnabled) noexcept;
|
||||
void ClearSelection() noexcept;
|
||||
|
||||
const std::wstring RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const;
|
||||
#pragma endregion
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "Terminal.hpp"
|
||||
#include "../src/inc/unicode.hpp"
|
||||
|
||||
using namespace Microsoft::Terminal::Core;
|
||||
using namespace Microsoft::Console::Types;
|
||||
@@ -127,240 +126,17 @@ COORD Terminal::GetCursorPosition()
|
||||
return newPos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - deletes uiCount characters starting from the cursor's current position
|
||||
// - it moves over the remaining text to 'replace' the deleted text
|
||||
// - for example, if the buffer looks like this ('|' is the cursor): [abc|def]
|
||||
// - calling DeleteCharacter(1) will change it to: [abc|ef],
|
||||
// - i.e. the 'd' gets deleted and the 'ef' gets shifted over 1 space and **retain their previous text attributes**
|
||||
// Arguments:
|
||||
// - uiCount, the number of characters to delete
|
||||
// Return value:
|
||||
// - true if succeeded, false otherwise
|
||||
bool Terminal::DeleteCharacter(const unsigned int uiCount)
|
||||
{
|
||||
SHORT dist;
|
||||
if (!SUCCEEDED(UIntToShort(uiCount, &dist)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
||||
const auto copyToPos = cursorPos;
|
||||
const COORD copyFromPos{ cursorPos.X + dist, cursorPos.Y };
|
||||
auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X;
|
||||
SHORT width;
|
||||
if (!SUCCEEDED(UIntToShort(sourceWidth, &width)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a rectangle of the source
|
||||
auto source = Viewport::FromDimensions(copyFromPos, width, 1);
|
||||
|
||||
// Get a rectangle of the target
|
||||
const auto target = Viewport::FromDimensions(copyToPos, source.Dimensions());
|
||||
const auto walkDirection = Viewport::DetermineWalkDirection(source, target);
|
||||
|
||||
auto sourcePos = source.GetWalkOrigin(walkDirection);
|
||||
auto targetPos = target.GetWalkOrigin(walkDirection);
|
||||
|
||||
// Iterate over the source cell data and copy it over to the target
|
||||
do
|
||||
{
|
||||
const auto data = OutputCell(*(_buffer->GetCellDataAt(sourcePos)));
|
||||
_buffer->Write(OutputCellIterator({ &data, 1 }), targetPos);
|
||||
} while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Inserts uiCount spaces starting from the cursor's current position, moving over the existing text
|
||||
// - for example, if the buffer looks like this ('|' is the cursor): [abc|def]
|
||||
// - calling InsertCharacter(1) will change it to: [abc| def],
|
||||
// - i.e. the 'def' gets shifted over 1 space and **retain their previous text attributes**
|
||||
// Arguments:
|
||||
// - uiCount, the number of spaces to insert
|
||||
// Return value:
|
||||
// - true if succeeded, false otherwise
|
||||
bool Terminal::InsertCharacter(const unsigned int uiCount)
|
||||
{
|
||||
// NOTE: the code below is _extremely_ similar to DeleteCharacter
|
||||
// We will want to use this same logic and implement a helper function instead
|
||||
// that does the 'move a region from here to there' operation
|
||||
// TODO: Github issue #2163
|
||||
SHORT dist;
|
||||
if (!SUCCEEDED(UIntToShort(uiCount, &dist)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
||||
const auto copyFromPos = cursorPos;
|
||||
const COORD copyToPos{ cursorPos.X + dist, cursorPos.Y };
|
||||
auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X;
|
||||
SHORT width;
|
||||
if (!SUCCEEDED(UIntToShort(sourceWidth, &width)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get a rectangle of the source
|
||||
auto source = Viewport::FromDimensions(copyFromPos, width, 1);
|
||||
const auto sourceOrigin = source.Origin();
|
||||
|
||||
// Get a rectangle of the target
|
||||
const auto target = Viewport::FromDimensions(copyToPos, source.Dimensions());
|
||||
const auto walkDirection = Viewport::DetermineWalkDirection(source, target);
|
||||
|
||||
auto sourcePos = source.GetWalkOrigin(walkDirection);
|
||||
auto targetPos = target.GetWalkOrigin(walkDirection);
|
||||
|
||||
// Iterate over the source cell data and copy it over to the target
|
||||
do
|
||||
{
|
||||
const auto data = OutputCell(*(_buffer->GetCellDataAt(sourcePos)));
|
||||
_buffer->Write(OutputCellIterator({ &data, 1 }), targetPos);
|
||||
} while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection));
|
||||
auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), dist);
|
||||
_buffer->Write(eraseIter, cursorPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Terminal::EraseCharacters(const unsigned int numChars)
|
||||
{
|
||||
const auto absoluteCursorPos = _buffer->GetCursor().GetPosition();
|
||||
const auto viewport = _GetMutableViewport();
|
||||
const short distanceToRight = viewport.RightExclusive() - absoluteCursorPos.X;
|
||||
const short fillLimit = std::min(static_cast<short>(numChars), distanceToRight);
|
||||
auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), fillLimit);
|
||||
auto eraseIter = OutputCellIterator(L' ', _buffer->GetCurrentAttributes(), fillLimit);
|
||||
_buffer->Write(eraseIter, absoluteCursorPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - erases a line of text, either from
|
||||
// 1. beginning to the cursor's position
|
||||
// 2. cursor's position to end
|
||||
// 3. beginning to end
|
||||
// - depending on the erase type
|
||||
// Arguments:
|
||||
// - the erase type
|
||||
// Return value:
|
||||
// - true if succeeded, false otherwise
|
||||
bool Terminal::EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType)
|
||||
{
|
||||
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
||||
const auto viewport = _GetMutableViewport();
|
||||
COORD startPos = { 0 };
|
||||
startPos.Y = cursorPos.Y;
|
||||
// nlength determines the number of spaces we need to write
|
||||
DWORD nlength = 0;
|
||||
|
||||
// Determine startPos.X and nlength by the eraseType
|
||||
switch (eraseType)
|
||||
{
|
||||
case DispatchTypes::EraseType::FromBeginning:
|
||||
nlength = cursorPos.X - viewport.Left() + 1;
|
||||
break;
|
||||
case DispatchTypes::EraseType::ToEnd:
|
||||
startPos.X = cursorPos.X;
|
||||
nlength = viewport.RightInclusive() - startPos.X;
|
||||
break;
|
||||
case DispatchTypes::EraseType::All:
|
||||
startPos.X = viewport.Left();
|
||||
nlength = viewport.RightInclusive() - startPos.X;
|
||||
break;
|
||||
case DispatchTypes::EraseType::Scrollback:
|
||||
return false;
|
||||
}
|
||||
|
||||
auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), nlength);
|
||||
_buffer->Write(eraseIter, startPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method description:
|
||||
// - erases text in the buffer in two ways depending on erase type
|
||||
// 1. 'erases' all text visible to the user (i.e. the text in the viewport)
|
||||
// 2. erases all the text in the scrollback
|
||||
// Arguments:
|
||||
// - the erase type
|
||||
// Return Value:
|
||||
// - true if succeeded, false otherwise
|
||||
bool Terminal::EraseInDisplay(const DispatchTypes::EraseType eraseType)
|
||||
{
|
||||
// Store the relative cursor position so we can restore it later after we move the viewport
|
||||
const auto cursorPos = _buffer->GetCursor().GetPosition();
|
||||
auto relativeCursor = cursorPos;
|
||||
_mutableViewport.ConvertToOrigin(&relativeCursor);
|
||||
|
||||
// Initialize the new location of the viewport
|
||||
// the top and bottom parameters are determined by the eraseType
|
||||
SMALL_RECT newWin;
|
||||
newWin.Left = _mutableViewport.Left();
|
||||
newWin.Right = _mutableViewport.RightExclusive();
|
||||
|
||||
if (eraseType == DispatchTypes::EraseType::All)
|
||||
{
|
||||
// In this case, we simply move the viewport down, effectively pushing whatever text was on the screen into the scrollback
|
||||
// and thus 'erasing' the text visible to the user
|
||||
const auto coordLastChar = _buffer->GetLastNonSpaceCharacter(_mutableViewport);
|
||||
if (coordLastChar.X == 0 && coordLastChar.Y == 0)
|
||||
{
|
||||
// Nothing to clear, just return
|
||||
return true;
|
||||
}
|
||||
|
||||
short sNewTop = coordLastChar.Y + 1;
|
||||
|
||||
// Increment the circular buffer only if the new location of the viewport would be 'below' the buffer
|
||||
const short delta = (sNewTop + _mutableViewport.Height()) - (_buffer->GetSize().Height());
|
||||
for (auto i = 0; i < delta; i++)
|
||||
{
|
||||
_buffer->IncrementCircularBuffer();
|
||||
sNewTop--;
|
||||
}
|
||||
|
||||
newWin.Top = sNewTop;
|
||||
newWin.Bottom = sNewTop + _mutableViewport.Height();
|
||||
}
|
||||
else if (eraseType == DispatchTypes::EraseType::Scrollback)
|
||||
{
|
||||
// We only want to erase the scrollback, and leave everything else on the screen as it is
|
||||
// so we grab the text in the viewport and rotate it up to the top of the buffer
|
||||
COORD scrollFromPos{ 0, 0 };
|
||||
_mutableViewport.ConvertFromOrigin(&scrollFromPos);
|
||||
_buffer->ScrollRows(scrollFromPos.Y, _mutableViewport.Height(), -scrollFromPos.Y);
|
||||
|
||||
// Since we only did a rotation, the text that was in the scrollback is now _below_ where we are going to move the viewport
|
||||
// and we have to make sure we erase that text
|
||||
auto eraseStart = _mutableViewport.Height();
|
||||
auto eraseEnd = _buffer->GetLastNonSpaceCharacter(_mutableViewport).Y;
|
||||
for (SHORT i = eraseStart; i <= eraseEnd; i++)
|
||||
{
|
||||
_buffer->GetRowByOffset(i).Reset(_buffer->GetCurrentAttributes());
|
||||
}
|
||||
|
||||
// Reset the scroll offset now because there's nothing for the user to 'scroll' to
|
||||
_scrollOffset = 0;
|
||||
|
||||
newWin.Top = 0;
|
||||
newWin.Bottom = _mutableViewport.Height();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Move the viewport, adjust the scoll bar if needed, and restore the old cursor position
|
||||
_mutableViewport = Viewport::FromExclusive(newWin);
|
||||
Terminal::_NotifyScrollEvent();
|
||||
SetCursorPosition(relativeCursor.X, relativeCursor.Y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Terminal::SetWindowTitle(std::wstring_view title)
|
||||
{
|
||||
_title = title;
|
||||
|
||||
@@ -47,20 +47,6 @@ bool TerminalDispatch::CursorForward(const unsigned int uiDistance)
|
||||
return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y);
|
||||
}
|
||||
|
||||
bool TerminalDispatch::CursorBackward(const unsigned int uiDistance)
|
||||
{
|
||||
const auto cursorPos = _terminalApi.GetCursorPosition();
|
||||
const COORD newCursorPos{ cursorPos.X - gsl::narrow<short>(uiDistance), cursorPos.Y };
|
||||
return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y);
|
||||
}
|
||||
|
||||
bool TerminalDispatch::CursorUp(const unsigned int uiDistance)
|
||||
{
|
||||
const auto cursorPos = _terminalApi.GetCursorPosition();
|
||||
const COORD newCursorPos{ cursorPos.X, cursorPos.Y + gsl::narrow<short>(uiDistance) };
|
||||
return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y);
|
||||
}
|
||||
|
||||
bool TerminalDispatch::EraseCharacters(const unsigned int uiNumChars)
|
||||
{
|
||||
return _terminalApi.EraseCharacters(uiNumChars);
|
||||
@@ -110,47 +96,3 @@ bool TerminalDispatch::SetDefaultBackground(const DWORD dwColor)
|
||||
{
|
||||
return _terminalApi.SetDefaultBackground(dwColor);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Erases characters in the buffer depending on the erase type
|
||||
// Arguments:
|
||||
// - eraseType: the erase type (from beginning, to end, or all)
|
||||
// Return Value:
|
||||
// True if handled successfully. False otherwise.
|
||||
bool TerminalDispatch::EraseInLine(const DispatchTypes::EraseType eraseType)
|
||||
{
|
||||
return _terminalApi.EraseInLine(eraseType);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Deletes uiCount number of characters starting from where the cursor is currently
|
||||
// Arguments:
|
||||
// - uiCount, the number of characters to delete
|
||||
// Return Value:
|
||||
// True if handled successfully. False otherwise.
|
||||
bool TerminalDispatch::DeleteCharacter(const unsigned int uiCount)
|
||||
{
|
||||
return _terminalApi.DeleteCharacter(uiCount);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Adds uiCount number of spaces starting from where the cursor is currently
|
||||
// Arguments:
|
||||
// - uiCount, the number of spaces to add
|
||||
// Return Value:
|
||||
// True if handled successfully, false otherwise
|
||||
bool TerminalDispatch::InsertCharacter(const unsigned int uiCount)
|
||||
{
|
||||
return _terminalApi.InsertCharacter(uiCount);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Moves the viewport and erases text from the buffer depending on the eraseType
|
||||
// Arguments:
|
||||
// - eraseType: the desired erase type
|
||||
// Return Value:
|
||||
// True if handled successfully. False otherwise
|
||||
bool TerminalDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType)
|
||||
{
|
||||
return _terminalApi.EraseInDisplay(eraseType);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ public:
|
||||
const unsigned int uiColumn) override; // CUP
|
||||
|
||||
bool CursorForward(const unsigned int uiDistance) override;
|
||||
bool CursorBackward(const unsigned int uiDistance) override;
|
||||
bool CursorUp(const unsigned int uiDistance) override;
|
||||
|
||||
bool EraseCharacters(const unsigned int uiNumChars) override;
|
||||
bool SetWindowTitle(std::wstring_view title) override;
|
||||
@@ -31,10 +29,6 @@ public:
|
||||
|
||||
bool SetDefaultForeground(const DWORD dwColor) override;
|
||||
bool SetDefaultBackground(const DWORD dwColor) override;
|
||||
bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; // ED
|
||||
bool DeleteCharacter(const unsigned int uiCount) override;
|
||||
bool InsertCharacter(const unsigned int uiCount) override;
|
||||
bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override;
|
||||
|
||||
private:
|
||||
::Microsoft::Terminal::Core::ITerminalApi& _terminalApi;
|
||||
|
||||
@@ -210,7 +210,7 @@ void Terminal::SetBoxSelection(const bool isEnabled) noexcept
|
||||
|
||||
// Method Description:
|
||||
// - clear selection data and disable rendering it
|
||||
void Terminal::ClearSelection()
|
||||
void Terminal::ClearSelection() noexcept
|
||||
{
|
||||
_selectionActive = false;
|
||||
_selectionAnchor = { 0, 0 };
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\ControlKeyStates.hpp" />
|
||||
<ClInclude Include="..\TerminalDispatch.hpp" />
|
||||
<ClInclude Include="..\ITerminalApi.hpp" />
|
||||
<ClInclude Include="..\pch.h" />
|
||||
|
||||
@@ -117,33 +117,6 @@ std::vector<Microsoft::Console::Types::Viewport> Terminal::GetSelectionRects() n
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Terminal::IsAreaSelected() const
|
||||
{
|
||||
return _selectionActive;
|
||||
}
|
||||
|
||||
void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
|
||||
{
|
||||
SetSelectionAnchor(coordStart);
|
||||
SetEndSelectionPosition(coordEnd);
|
||||
}
|
||||
|
||||
// TODO GitHub #605: Search functionality
|
||||
// For now, just adding it here to make UiaTextRange easier to create (Accessibility)
|
||||
// We should actually abstract this out better once Windows Terminal has Search
|
||||
HRESULT Terminal::SearchForText(_In_ BSTR /*text*/,
|
||||
_In_ BOOL /*searchBackward*/,
|
||||
_In_ BOOL /*ignoreCase*/,
|
||||
_Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/,
|
||||
unsigned int /*_start*/,
|
||||
unsigned int /*_end*/,
|
||||
std::function<unsigned int(IRenderData*, const COORD)> /*_coordToEndpoint*/,
|
||||
std::function<COORD(IRenderData*, const unsigned int)> /*_endpointToCoord*/,
|
||||
std::function<IFACEMETHODIMP(ITextRangeProvider**)> /*Clone*/)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
const std::wstring Terminal::GetConsoleTitle() const noexcept
|
||||
{
|
||||
return _title;
|
||||
|
||||
@@ -37,7 +37,5 @@ namespace Microsoft.Terminal.Settings
|
||||
String BackgroundImage;
|
||||
Double BackgroundImageOpacity;
|
||||
Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode;
|
||||
Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment;
|
||||
Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,8 +30,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
|
||||
_backgroundImage{},
|
||||
_backgroundImageOpacity{ 1.0 },
|
||||
_backgroundImageStretchMode{ winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill },
|
||||
_backgroundImageHorizontalAlignment{ winrt::Windows::UI::Xaml::HorizontalAlignment::Center },
|
||||
_backgroundImageVerticalAlignment{ winrt::Windows::UI::Xaml::VerticalAlignment::Center },
|
||||
_keyBindings{ nullptr },
|
||||
_scrollbarState{ ScrollbarState::Visible }
|
||||
{
|
||||
@@ -238,26 +236,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
|
||||
_backgroundImageStretchMode = value;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::HorizontalAlignment TerminalSettings::BackgroundImageHorizontalAlignment()
|
||||
{
|
||||
return _backgroundImageHorizontalAlignment;
|
||||
}
|
||||
|
||||
void TerminalSettings::BackgroundImageHorizontalAlignment(winrt::Windows::UI::Xaml::HorizontalAlignment value)
|
||||
{
|
||||
_backgroundImageHorizontalAlignment = value;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment TerminalSettings::BackgroundImageVerticalAlignment()
|
||||
{
|
||||
return _backgroundImageVerticalAlignment;
|
||||
}
|
||||
|
||||
void TerminalSettings::BackgroundImageVerticalAlignment(winrt::Windows::UI::Xaml::VerticalAlignment value)
|
||||
{
|
||||
_backgroundImageVerticalAlignment = value;
|
||||
}
|
||||
|
||||
Settings::IKeyBindings TerminalSettings::KeyBindings()
|
||||
{
|
||||
return _keyBindings;
|
||||
|
||||
@@ -69,10 +69,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
|
||||
void BackgroundImageOpacity(double value);
|
||||
winrt::Windows::UI::Xaml::Media::Stretch BackgroundImageStretchMode();
|
||||
void BackgroundImageStretchMode(winrt::Windows::UI::Xaml::Media::Stretch value);
|
||||
winrt::Windows::UI::Xaml::HorizontalAlignment BackgroundImageHorizontalAlignment();
|
||||
void BackgroundImageHorizontalAlignment(winrt::Windows::UI::Xaml::HorizontalAlignment value);
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment BackgroundImageVerticalAlignment();
|
||||
void BackgroundImageVerticalAlignment(winrt::Windows::UI::Xaml::VerticalAlignment value);
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::IKeyBindings KeyBindings();
|
||||
void KeyBindings(winrt::Microsoft::Terminal::Settings::IKeyBindings const& value);
|
||||
@@ -111,8 +107,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
|
||||
hstring _backgroundImage;
|
||||
double _backgroundImageOpacity;
|
||||
winrt::Windows::UI::Xaml::Media::Stretch _backgroundImageStretchMode;
|
||||
winrt::Windows::UI::Xaml::HorizontalAlignment _backgroundImageHorizontalAlignment;
|
||||
winrt::Windows::UI::Xaml::VerticalAlignment _backgroundImageVerticalAlignment;
|
||||
hstring _commandline;
|
||||
hstring _startingDir;
|
||||
hstring _envVars;
|
||||
|
||||
@@ -48,12 +48,12 @@ namespace TerminalCoreUnitTests
|
||||
// Verify that Alt+a generates a lowercase a on the input
|
||||
expectedinput = L"\x1b"
|
||||
"a";
|
||||
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', ControlKeyStates::LeftAltPressed));
|
||||
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', LEFT_ALT_PRESSED));
|
||||
|
||||
// Verify that Alt+shift+a generates a uppercase a on the input
|
||||
expectedinput = L"\x1b"
|
||||
"A";
|
||||
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', ControlKeyStates::LeftAltPressed | ControlKeyStates::ShiftPressed));
|
||||
VERIFY_IS_TRUE(term.SendKeyEvent(L'A', LEFT_ALT_PRESSED | SHIFT_PRESSED));
|
||||
}
|
||||
|
||||
void InputTest::AltSpace()
|
||||
@@ -61,6 +61,6 @@ namespace TerminalCoreUnitTests
|
||||
// Make sure we don't handle Alt+Space. The system will use this to
|
||||
// bring up the system menu for restore, min/maximimize, size, move,
|
||||
// close
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', ControlKeyStates::LeftAltPressed));
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', LEFT_ALT_PRESSED));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,27 +61,25 @@ AppHost::~AppHost()
|
||||
void AppHost::Initialize()
|
||||
{
|
||||
_window->Initialize();
|
||||
|
||||
if (_useNonClientArea)
|
||||
{
|
||||
// Register our callbar for when the app's non-client content changes.
|
||||
// This has to be done _before_ App::Create, as the app might set the
|
||||
// content in Create.
|
||||
_app.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent });
|
||||
}
|
||||
_app.RequestedThemeChanged({ this, &AppHost::_UpdateTheme });
|
||||
|
||||
_app.Create();
|
||||
const auto handle = _window->GetHandle();
|
||||
_app.Create(reinterpret_cast<uint64_t>(handle));
|
||||
|
||||
_app.TitleChanged({ this, &AppHost::AppTitleChanged });
|
||||
_app.LastTabClosed({ this, &AppHost::LastTabClosed });
|
||||
|
||||
AppTitleChanged(_app.GetTitle());
|
||||
|
||||
// Set up the content of the application. If the app has a custom titlebar,
|
||||
// set that content as well.
|
||||
_window->SetContent(_app.GetRoot());
|
||||
_window->OnAppInitialized();
|
||||
_window->OnAppInitialized(_app);
|
||||
}
|
||||
|
||||
void AppHost::IncomingConnectionByHandle(HANDLE handle)
|
||||
{
|
||||
_app.IncomingConnection(reinterpret_cast<uint64_t>(handle));
|
||||
}
|
||||
|
||||
void AppHost::IncomingConnectionByLaunch(std::wstring_view cmdline, std::wstring_view workingDir)
|
||||
{
|
||||
_app.IncomingConnection(winrt::hstring(cmdline), winrt::hstring(workingDir));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -195,32 +193,3 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect)
|
||||
// the originally proposed window size.
|
||||
LOG_LAST_ERROR_IF(!succeeded);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the app wants to set its titlebar content. We'll take the
|
||||
// UIElement and set the Content property of our Titlebar that element.
|
||||
// Arguments:
|
||||
// - sender: unused
|
||||
// - arg: the UIElement to use as the new Titlebar content.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::_UpdateTitleBarContent(const winrt::TerminalApp::App&, const winrt::Windows::UI::Xaml::UIElement& arg)
|
||||
{
|
||||
if (_useNonClientArea)
|
||||
{
|
||||
(static_cast<NonClientIslandWindow*>(_window.get()))->SetTitlebarContent(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the app wants to change its theme. We'll forward this to the
|
||||
// IslandWindow, so it can update the root UI element of the entire XAML tree.
|
||||
// Arguments:
|
||||
// - sender: unused
|
||||
// - arg: the ElementTheme to use as the new theme for the UI
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::_UpdateTheme(const winrt::TerminalApp::App&, const winrt::Windows::UI::Xaml::ElementTheme& arg)
|
||||
{
|
||||
_window->UpdateTheme(arg);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ public:
|
||||
AppHost() noexcept;
|
||||
virtual ~AppHost();
|
||||
|
||||
void IncomingConnectionByHandle(HANDLE handle);
|
||||
void IncomingConnectionByLaunch(std::wstring_view cmdline, std::wstring_view workingDir);
|
||||
void AppTitleChanged(winrt::hstring newTitle);
|
||||
void LastTabClosed();
|
||||
void Initialize();
|
||||
@@ -25,8 +27,4 @@ private:
|
||||
winrt::TerminalApp::App _app;
|
||||
|
||||
void _HandleCreateWindow(const HWND hwnd, const RECT proposedRect);
|
||||
void _UpdateTitleBarContent(const winrt::TerminalApp::App& sender,
|
||||
const winrt::Windows::UI::Xaml::UIElement& arg);
|
||||
void _UpdateTheme(const winrt::TerminalApp::App&,
|
||||
const winrt::Windows::UI::Xaml::ElementTheme& arg);
|
||||
};
|
||||
|
||||
@@ -3,16 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\types\IConsoleWindow.hpp"
|
||||
#include "..\types\WindowUiaProviderBase.hpp"
|
||||
|
||||
// Custom window messages
|
||||
#define CM_UPDATE_TITLE (WM_USER)
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
||||
using namespace Microsoft::Console::Types;
|
||||
|
||||
template<typename T>
|
||||
class BaseWindow
|
||||
{
|
||||
@@ -56,11 +51,6 @@ public:
|
||||
return HandleDpiChange(_window.get(), wparam, lparam);
|
||||
}
|
||||
|
||||
case WM_GETOBJECT:
|
||||
{
|
||||
return HandleGetObject(_window.get(), wparam, lparam);
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
@@ -131,22 +121,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] LRESULT HandleGetObject(const HWND hWnd, const WPARAM wParam, const LPARAM lParam)
|
||||
{
|
||||
LRESULT retVal = 0;
|
||||
|
||||
// If we are receiving a request from Microsoft UI Automation framework, then return the basic UIA COM interface.
|
||||
if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId))
|
||||
{
|
||||
retVal = UiaReturnRawElementProvider(hWnd, wParam, lParam, _GetUiaProvider());
|
||||
}
|
||||
// Otherwise, return 0. We don't implement MS Active Accessibility (the other framework that calls WM_GETOBJECT).
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
virtual IRawElementProviderSimple* _GetUiaProvider() = 0;
|
||||
|
||||
virtual void OnResize(const UINT width, const UINT height) = 0;
|
||||
virtual void OnMinimize() = 0;
|
||||
virtual void OnRestore() = 0;
|
||||
@@ -161,7 +135,7 @@ public:
|
||||
HWND GetHandle() const noexcept
|
||||
{
|
||||
return _window.get();
|
||||
}
|
||||
};
|
||||
|
||||
float GetCurrentDpiScale() const noexcept
|
||||
{
|
||||
@@ -213,7 +187,7 @@ public:
|
||||
{
|
||||
_title = newTitle;
|
||||
PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast<LPARAM>(nullptr));
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
using base_type = BaseWindow<T>;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user