Compare commits

..

4 Commits

124 changed files with 2879 additions and 4561 deletions

View File

@@ -294,10 +294,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{D3EF
build\scripts\Test-WindowsTerminalPackage.ps1 = build\scripts\Test-WindowsTerminalPackage.ps1
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.Tests.Feature", "src\winconpty\ft_pty\winconpty.FeatureTests.vcxproj", "{024052DE-83FB-4653-AEA4-90790D29D5BD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAzBridge", "src\cascadia\TerminalAzBridge\TerminalAzBridge.vcxproj", "{067F0A06-FCB7-472C-96E9-B03B54E8E18D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|Any CPU = AuditMode|Any CPU
@@ -1423,34 +1419,6 @@ Global
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x64.Build.0 = Release|x64
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.ActiveCfg = Release|Win32
{A602A555-BAAC-46E1-A91D-3DAB0475C5A1}.Release|x86.Build.0 = Release|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|Any CPU.ActiveCfg = Debug|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.ActiveCfg = Debug|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|ARM64.Build.0 = Debug|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x64.ActiveCfg = Debug|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x64.Build.0 = Debug|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x86.ActiveCfg = Debug|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Debug|x86.Build.0 = Debug|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|Any CPU.ActiveCfg = Release|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|ARM64.ActiveCfg = Release|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|ARM64.Build.0 = Release|ARM64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x64.ActiveCfg = Release|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x64.Build.0 = Release|x64
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.ActiveCfg = Release|Win32
{024052DE-83FB-4653-AEA4-90790D29D5BD}.Release|x86.Build.0 = Release|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|ARM64.Build.0 = Debug|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x64.ActiveCfg = Debug|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x64.Build.0 = Debug|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x86.ActiveCfg = Debug|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Debug|x86.Build.0 = Debug|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|Any CPU.ActiveCfg = Release|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|ARM64.ActiveCfg = Release|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|ARM64.Build.0 = Release|ARM64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x64.ActiveCfg = Release|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x64.Build.0 = Release|x64
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x86.ActiveCfg = Release|Win32
{067F0A06-FCB7-472C-96E9-B03B54E8E18D}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1525,8 +1493,6 @@ Global
{53DD5520-E64C-4C06-B472-7CE62CA539C9} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
{6B5A44ED-918D-4747-BFB1-2472A1FCA173} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
{D3EF7B96-CD5E-47C9-B9A9-136259563033} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
{024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}

View File

@@ -17,15 +17,7 @@ Related repositories include:
> 👉 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
### Microsoft Store [Recommended]
Install the [Windows Terminal from the Microsoft Store][store-install-link]. This allows you to always be on the latest version when we release new builds with automatic upgrades.
This is our preferred method.
### Other install methods
#### Via GitHub
### Manually installing builds from this repository
For users who are unable to install Terminal from the Microsoft Store, Terminal builds can be manually downloaded from this repository's [Releases page](https://github.com/microsoft/terminal/releases).
@@ -34,7 +26,7 @@ For users who are unable to install Terminal from the Microsoft Store, Terminal
> * Be sure to install the [Desktop Bridge VC++ v14 Redistributable Package](https://www.microsoft.com/en-us/download/details.aspx?id=53175) otherwise Terminal may not install and/or run and may crash at startup
> * Terminal will not auto-update when new builds are released so you will need to regularly install the latest Terminal release to receive all the latest fixes and improvements!
#### Via Chocolatey (unofficial)
### Install via Chocolatey (unofficial)
[Chocolatey](https://chocolatey.org) users can download and install the latest Terminal release by installing the `microsoft-windows-terminal` package:
@@ -227,4 +219,3 @@ For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [open
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
[conduct-email]: mailto:opencode@microsoft.com
[store-install-link]: https://aka.ms/windowsterminal

View File

@@ -6,7 +6,6 @@ jobs:
steps:
- checkout: self
fetchDepth: 1
submodules: false
clean: true

View File

@@ -1,38 +1,13 @@
# How to build OpenConsole
# How to build Openconsole
This repository uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for some of its dependencies. To make sure submodules are restored or updated, be sure to run the following prior to building:
```shell
git submodule update --init --recursive
```
OpenConsole.sln may be built from within Visual Studio or from the command-line using a set of convenience scripts & tools in the **/tools** directory:
Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and PowerShell in /tools.
When using Visual Studio, be sure to set up the path for code formatting. This can be done in Visual Studio by going to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" and choosing the clang-format.exe in the repository at /dep/llvm/clang-format.exe by clicking "browse" right under the check box.
### Building in PowerShell
## Building with cmd
```powershell
Import-Module .\tools\OpenConsole.psm1
Set-MsBuildDevEnvironment
Invoke-OpenConsoleBuild
```
There are a few additional exported functions (look at their documentation for further details):
- `Invoke-OpenConsoleBuild` - builds the solution. Can be passed msbuild arguments.
- `Invoke-OpenConsoleTests` - runs the various tests. Will run the unit tests by default.
- `Start-OpenConsole` - starts Openconsole.exe from the output directory. x64 is run by default.
- `Debug-OpenConsole` - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
- `Invoke-CodeFormat` - uses clang-format to format all c++ files to match our coding style.
### Building in Cmd
```shell
.\tools\razzle.cmd
bcz
```
The cmd scripts are set up to emulate a portion of the OS razzle build environment. razzle.cmd is the first script that should be run. bcz.cmd will build clean and bz.cmd should build incrementally.
There are also scripts for running the tests:
- `runut.cmd` - run the unit tests
@@ -40,13 +15,15 @@ There are also scripts for running the tests:
- `runuia.cmd` - run the UIA tests
- `runformat` - uses clang-format to format all c++ files to match our coding style.
## Running & Debugging
## Build with Powershell
To debug the Windows Terminal in VS, right click on `CascadiaPackage` (in the Solution Explorer) and go to properties. In the Debug menu, change "Application process" and "Background task process" to "Native Only".
Openconsole.psm1 should be loaded with `Import-Module`. From there `Set-MsbuildDevEnvironment` will set up environment variables required to build. There are a few exported functions (look at their documentation for further details):
You should then be able to build & debug the Terminal project by hitting <kbd>F5</kbd>.
> 👉 You will _not_ be able to launch the Terminal directly by running the WindowsTerminal.exe. For more details on why, see [#926](https://github.com/microsoft/terminal/issues/926), [#4043](https://github.com/microsoft/terminal/issues/4043)
- `Invoke-OpenConsolebuild` - builds the solution. Can be passed msbuild arguments.
- `Invoke-OpenConsoleTests` - runs the various tests. Will run the unit tests by default.
- `Start-OpenConsole` - starts Openconsole.exe from the output directory. x64 is run by default.
- `Debug-OpenConsole` - starts Openconsole.exe and attaches it to the default debugger. x64 is run by default.
- `Invoke-CodeFormat` - uses clang-format to format all c++ files to match our coding style.
## Configuration Types

View File

@@ -29,7 +29,6 @@ Properties listed below are specific to each unique profile.
| `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. |
| `name` | _Required_ | String | | Name of the profile. Displays in the dropdown menu. <br>Additionally, this value will be used as the "title" to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. This "title" behavior can be overridden by using `tabTitle`. |
| `acrylicOpacity` | Optional | Number | `0.5` | When `useAcrylic` is set to `true`, it sets the transparency of the window for the profile. Accepts floating point values from 0-1. |
| `antialiasingMode` | Optional | String | `"grayscale"` | Controls how text is antialiased in the renderer. Possible values are "grayscale", "cleartype" and "aliased". Note that changing this setting will require starting a new terminal instance. |
| `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"` |

View File

@@ -3,10 +3,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Microsoft's Windows Terminal Settings Profile Schema'",
"definitions": {
"KeyChordSegment": {
"pattern": "^(?<modifier>(ctrl|alt|shift)\\+?((ctrl|alt|shift)(?<!\\2)\\+?)?((ctrl|alt|shift)(?<!\\2|\\4))?\\+?)?(?<key>[^+\\s]+?)?(?<=[^+\\s])$",
"type": "string"
},
"Color": {
"default": "#",
"pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
@@ -248,18 +244,12 @@
},
"keys": {
"description": "Defines the key combinations used to call the command.",
"oneOf": [
{
"$ref": "#/definitions/KeyChordSegment"
},
{
"items": {
"$ref": "#/definitions/KeyChordSegment"
},
"minItems": 1,
"type": "array"
}
]
"items": {
"pattern": "^(?<modifier>(ctrl|alt|shift)\\+?((ctrl|alt|shift)(?<!\\2)\\+?)?((ctrl|alt|shift)(?<!\\2|\\4))?\\+?)?(?<key>[^+\\s]+?)?(?<=[^+\\s])$",
"type": "string"
},
"minItems": 1,
"type": "array"
}
},
"required": [
@@ -388,16 +378,6 @@
"minimum": 0,
"type": "number"
},
"antialiasingMode": {
"default": "grayscale",
"description": "Controls how text is antialiased in the renderer. Possible values are \"grayscale\", \"cleartype\" and \"aliased\". Note that changing this setting will require starting a new terminal instance.",
"enum": [
"grayscale",
"cleartype",
"aliased"
],
"type": "string"
},
"background": {
"$ref": "#/definitions/Color",
"default": "#0c0c0c",

View File

@@ -15,7 +15,7 @@ Assuming that you've installed Anaconda into `%USERPROFILE%\Anaconda3`:
```json
{
"commandline" : "cmd.exe /k \"%USERPROFILE%\\Anaconda3\\Scripts\\activate.bat %USERPROFILE%\\Anaconda3\"",
"commandline" : "cmd.exe /K %USERPROFILE%\\Anaconda3\\Scripts\\activate.bat %USERPROFILE%\\Anaconda3",
"icon" : "%USERPROFILE%/Anaconda3/Menu/anaconda-navigator.ico",
"name" : "Anaconda3",
"startingDirectory" : "%USERPROFILE%"
@@ -28,7 +28,7 @@ Assuming that you've installed cmder into `%CMDER_ROOT%`:
```json
{
"commandline" : "cmd.exe /k \"%CMDER_ROOT%\\vendor\\init.bat\"",
"commandline" : "cmd.exe /k %CMDER_ROOT%\\vendor\\init.bat",
"name" : "cmder",
"startingDirectory" : "%USERPROFILE%"
}

View File

@@ -22,7 +22,6 @@ pass, and gives some examples of how to use the `wt` commandline.
### Options
#### `--help,-h,-?,/?,`
Display the help message.
## Subcommands
@@ -36,9 +35,9 @@ opens a new window. Subsequent `new-tab` commands will all open new tabs in the
same window.
**Parameters**:
* `[terminal_parameters]`: See [[terminal_parameters]](#terminal_parameters).
#### `split-pane`
`split-pane [--target,-t target-pane] [-H]|[-V] [terminal_parameters]`
@@ -47,7 +46,6 @@ Creates a new pane in the currently focused tab by splitting the given pane
vertically or horizontally.
**Parameters**:
* `--target,-t target-pane`: Creates a new split in the given `target-pane`.
Each pane has a unique index (per-tab) which can be used to identify them.
These indicies are assigned in the order the panes were created. If omitted,
@@ -74,6 +72,7 @@ Moves focus to a given tab.
* `-p,--previous`: Move focus to the previous tab. Will display an error if
combined with either of `--next` or `--target`.
#### `[terminal_parameters]`
Some of the preceding commands are used to create a new terminal instance.
@@ -93,11 +92,12 @@ following:
selected profile. If the user wants to use a `;` in this commandline, it
should be escaped as `\;`.
## Examples
### Open Windows Terminal in the current directory
```powershell
```
wt -d .
```
@@ -114,12 +114,11 @@ the `split-pane` command to create new panes.
Consider the following commandline:
```powershell
```
wt ; split-pane -p "Windows PowerShell" ; split-pane -H wsl.exe
```
This creates a new Windows Terminal window with one tab, and 3 panes:
* `wt`: Creates the new tab with the default profile
* `split-pane -p "Windows PowerShell"`: This will create a new pane, split from
the parent with the default profile. This pane will open with the "Windows

View File

@@ -54,8 +54,8 @@ object under a root property `"globals"`.
This is an array of key chords and shortcuts to invoke various commands.
Each command can have more than one key binding.
> 👉 **Note**: Key bindings is a subfield of the global settings and
> key bindings apply to all profiles in the same manner.
NOTE: Key bindings is a subfield of the global settings and
key bindings apply to all profiles in the same manner.
For example, here's a sample of the default keybindings:
@@ -69,26 +69,9 @@ For example, here's a sample of the default keybindings:
// etc.
]
}
```
You can also use a single key chord string as the value of `"keys"`.
It will be treated as a chord of length one.
This will allow you to simplify the above snippet as follows:
```json
{
"keybindings":
[
{ "command": "closePane", "keys": "ctrl+shift+w" },
{ "command": "copy", "keys": "ctrl+shift+c" },
{ "command": "newTab", "keys": "ctrl+shift+t" },
// etc.
]
}
```
### Unbinding keys
If you ever come across a key binding that you're unhappy with, it's possible to
@@ -153,7 +136,7 @@ the property `"hidden": true` to the profile's json. This can also be used to
remove the default `cmd` and PowerShell profiles, if the user does not wish to
see them.
## Color Schemes
## Color Schemes
Each scheme defines the color values to be used for various terminal escape sequences.
Each schema is identified by the name field. Examples include
@@ -176,7 +159,6 @@ The schema name can then be referenced in one or more profiles.
## Settings layering
The runtime settings are actually constructed from _three_ sources:
* The default settings, which are hardcoded into the application, and available
in `defaults.json`. This includes the default keybindings, color schemes, and
profiles for both Windows PowerShell and Command Prompt (`cmd.exe`).
@@ -305,7 +287,6 @@ properties for all your profiles, like so:
Note that the `profiles` property has changed in this example from a _list_ of
profiles, to an _object_ with two properties:
* a `list` that contains the list of all the profiles
* the new `defaults` object, which contains all the settings that should apply to
every profile.
@@ -348,11 +329,12 @@ could achieve that with the following:
In the above settings, the `"fontFace"` in the `cmd.exe` profile overrides the
`"fontFace"` from the `defaults`.
## Configuration Examples
## Configuration Examples:
### Add a custom background to the WSL Debian terminal profile
1. Download the [Debian JPG logo](https://www.debian.org/logos/openlogo-100.jpg)
1. Download the Debian JPG logo https://www.debian.org/logos/openlogo-100.jpg
2. Put the image in the
`$env:LocalAppData\Packages\Microsoft.WindowsTerminal_<randomString>\LocalState\`
directory (same directory as your `profiles.json` file).
@@ -360,20 +342,17 @@ In the above settings, the `"fontFace"` in the `cmd.exe` profile overrides the
__NOTE__: You can put the image anywhere you like, the above suggestion happens to be convenient.
3. Open your WT json properties file.
4. Under the Debian Linux profile, add the following fields:
```json
"backgroundImage": "ms-appdata:///Local/openlogo-100.jpg",
"backgroundImageOpacity": 1,
"backgroundImageStretchMode" : "none",
"backgroundImageAlignment" : "topRight",
```
5. Make sure that `useAcrylic` is `false`.
6. Save the file.
7. Jump over to WT and verify your changes.
Notes:
1. You will need to experiment with different color settings
and schemes to make your terminal text visible on top of your image
2. If you store the image in the UWP directory (the same directory as your profiles.json file),
@@ -435,6 +414,7 @@ no text selection. Additionally, if you set `paste` to `"ctrl+v"`, commandline
applications won't be able to read a ctrl+v from the input. For these reasons,
we suggest `"ctrl+shift+c"` and `"ctrl+shift+v"`
### Setting the `startingDirectory` of WSL Profiles to `~`
By default, the `startingDirectory` of a profile is `%USERPROFILE%`

View File

@@ -75,14 +75,14 @@ To open the settings file from Windows Terminal:
For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [profiles.json documentation](../cascadia/SettingsSchema.md) section.
## Tips and Tricks
## Tips and Tricks:
1. In PowerShell you can discover if the Windows Terminal is being used by checking for the existence of the environment variable `WT_SESSION`.
Under pwsh you can also use
`(Get-Process -Id $pid).Parent.ProcessName -eq 'WindowsTerminal'`
(ref [https://twitter.com/r_keith_hill/status/1142871145852440576](https://twitter.com/r_keith_hill/status/1142871145852440576))
(ref https://twitter.com/r_keith_hill/status/1142871145852440576)
2. Terminal zoom can be changed by holding <kbd>Ctrl</kbd> and scrolling with mouse.
3. If `useAcrylic` is enabled in profiles.json, background opacity can be changed by holding <kbd>Ctrl</kbd>+<kbd>Shift</kbd> and scrolling with mouse. Note that acrylic transparency is limited by the OS only to focused windows.

View File

@@ -1314,101 +1314,6 @@ TextBuffer::DelimiterClass TextBuffer::_GetDelimiterClass(const std::wstring_vie
}
}
// Method Description:
// - Determines the line-by-line rectangles based on two COORDs
// - expands the rectangles to support wide glyphs
// - used for selection rects and UIA bounding rects
// Arguments:
// - start: a corner of the text region of interest (inclusive)
// - end: the other corner of the text region of interest (inclusive)
// - blockSelection: when enabled, only get the rectangular text region,
// as opposed to the text extending to the left/right
// buffer margins
// Return Value:
// - the delimiter class for the given char
const std::vector<SMALL_RECT> TextBuffer::GetTextRects(COORD start, COORD end, bool blockSelection) const
{
std::vector<SMALL_RECT> textRects;
const auto bufferSize = GetSize();
// (0,0) is the top-left of the screen
// the physically "higher" coordinate is closer to the top-left
// the physically "lower" coordinate is closer to the bottom-right
const auto [higherCoord, lowerCoord] = bufferSize.CompareInBounds(start, end) <= 0 ?
std::make_tuple(start, end) :
std::make_tuple(end, start);
const auto textRectSize = base::ClampedNumeric<short>(1) + lowerCoord.Y - higherCoord.Y;
textRects.reserve(textRectSize);
for (auto row = higherCoord.Y; row <= lowerCoord.Y; row++)
{
SMALL_RECT textRow;
textRow.Top = row;
textRow.Bottom = row;
if (blockSelection || higherCoord.Y == lowerCoord.Y)
{
// set the left and right margin to the left-/right-most respectively
textRow.Left = std::min(higherCoord.X, lowerCoord.X);
textRow.Right = std::max(higherCoord.X, lowerCoord.X);
}
else
{
textRow.Left = (row == higherCoord.Y) ? higherCoord.X : bufferSize.Left();
textRow.Right = (row == lowerCoord.Y) ? lowerCoord.X : bufferSize.RightInclusive();
}
_ExpandTextRow(textRow);
textRects.emplace_back(textRow);
}
return textRects;
}
// Method Description:
// - Expand the selection row according to include wide glyphs fully
// - this is particularly useful for box selections (ALT + selection)
// Arguments:
// - selectionRow: the selection row to be expanded
// Return Value:
// - modifies selectionRow's Left and Right values to expand properly
void TextBuffer::_ExpandTextRow(SMALL_RECT& textRow) const
{
const auto bufferSize = GetSize();
// expand left side of rect
COORD targetPoint{ textRow.Left, textRow.Top };
if (GetCellDataAt(targetPoint)->DbcsAttr().IsTrailing())
{
if (targetPoint.X == bufferSize.Left())
{
bufferSize.IncrementInBounds(targetPoint);
}
else
{
bufferSize.DecrementInBounds(targetPoint);
}
textRow.Left = targetPoint.X;
}
// expand right side of rect
targetPoint = { textRow.Right, textRow.Bottom };
if (GetCellDataAt(targetPoint)->DbcsAttr().IsLeading())
{
if (targetPoint.X == bufferSize.RightInclusive())
{
bufferSize.DecrementInBounds(targetPoint);
}
else
{
bufferSize.IncrementInBounds(targetPoint);
}
textRow.Right = targetPoint.X;
}
}
// Routine Description:
// - Retrieves the text data from the selected region and presents it in a clipboard-ready format (given little post-processing).
// Arguments:

View File

@@ -135,8 +135,6 @@ public:
bool MoveToNextWord(COORD& pos, const std::wstring_view wordDelimiters, COORD lastCharPos) const;
bool MoveToPreviousWord(COORD& pos, const std::wstring_view wordDelimiters) const;
const std::vector<SMALL_RECT> GetTextRects(COORD start, COORD end, bool blockSelection = false) const;
class TextAndColor
{
public:
@@ -195,8 +193,6 @@ private:
ROW& _GetFirstRow();
ROW& _GetPrevRowNoWrap(const ROW& row);
void _ExpandTextRow(SMALL_RECT& selectionRow) const;
enum class DelimiterClass
{
ControlChar,

View File

@@ -43,8 +43,7 @@
<!-- Resources -->
<!-- This resw only defines things that are used in this package's AppxManifest,
so it's not in the common resource items. -->
<PRIResource Include="Resources\Resources.language-en.resw" />
<PRIResource Include="Resources\Resources.resw" />
<PRIResource Include="Resources\en-US\Resources.resw" />
</ItemGroup>
<PropertyGroup Condition="'$(WindowsTerminalReleaseBuild)'!='true'">
<!-- This is picked up by CascadiaResources.build.items. -->
@@ -55,7 +54,6 @@
<ItemGroup>
<ProjectReference Include="..\WindowsTerminal\WindowsTerminal.vcxproj" />
<ProjectReference Include="..\..\host\exe\Host.EXE.vcxproj" />
<ProjectReference Include="..\TerminalAzBridge\TerminalAzBridge.vcxproj" />
</ItemGroup>
<Target Name="OpenConsoleStompSourceProjectForWapProject" BeforeTargets="_ConvertItems">
<ItemGroup>
@@ -93,7 +91,9 @@
roll up our subproject resources. We have to suppress that rule but keep part of its logic, because that rule is
where the AppxPackagePayload items are created. -->
<PropertyGroup>
<WapProjBeforeGenerateAppxManifestDependsOn>
<!-- Only for MSBuild versions <= 16.4.0 -->
<!-- TODO: Change this to hard less than once the 16.4.0 previews fix the bug. -->
<WapProjBeforeGenerateAppxManifestDependsOn Condition="$(MSBuildVersion) &lt;= '16.4.0'">
$([MSBuild]::Unescape('$(WapProjBeforeGenerateAppxManifestDependsOn.Replace('_RemoveAllNonWapUWPItems', '_OpenConsoleRemoveAllNonWapUWPItems'))'))
</WapProjBeforeGenerateAppxManifestDependsOn>
</PropertyGroup>

View File

@@ -1,73 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<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="AppName" xml:space="preserve">
<value>Windows Terminal (Preview)</value>
</data>
<data name="AppNameDev" xml:space="preserve">
<value>Windows Terminal (Dev Build)</value>
</data>
<data name="AppShortName" xml:space="preserve">
<value>Terminal</value>
</data>
<data name="AppShortNameDev" xml:space="preserve">
<value>Terminal (Dev)</value>
</data>
</root>

View File

@@ -120,7 +120,19 @@
<data name="AppDescription" xml:space="preserve">
<value>The New Windows Terminal</value>
</data>
<data name="AppName" xml:space="preserve">
<value>Windows Terminal (Preview)</value>
</data>
<data name="AppDescriptionDev" xml:space="preserve">
<value>The Windows Terminal, but Unofficial</value>
</data>
</root>
<data name="AppNameDev" xml:space="preserve">
<value>Windows Terminal (Dev Build)</value>
</data>
<data name="AppShortName" xml:space="preserve">
<value>Terminal</value>
</data>
<data name="AppShortNameDev" xml:space="preserve">
<value>Terminal (Dev)</value>
</data>
</root>

View File

@@ -42,8 +42,6 @@ namespace TerminalAppLocalTests
TEST_METHOD(TestArbitraryArgs);
TEST_METHOD(TestSplitPaneArgs);
TEST_METHOD(TestStringOverload);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
@@ -460,27 +458,4 @@ namespace TerminalAppLocalTests
}
}
void KeyBindingsTests::TestStringOverload()
{
const std::string bindings0String{ R"([
{ "command": "copy", "keys": "ctrl+c" }
])" };
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
auto appKeyBindings = winrt::make_self<implementation::AppKeyBindings>();
VERIFY_IS_NOT_NULL(appKeyBindings);
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
appKeyBindings->LayerJson(bindings0Json);
VERIFY_ARE_EQUAL(1u, appKeyBindings->_keyShortcuts.size());
{
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
const auto& realArgs = actionAndArgs.Args().try_as<CopyTextArgs>();
VERIFY_IS_NOT_NULL(realArgs);
// Verify the args have the expected value
VERIFY_IS_TRUE(realArgs.TrimWhitespace());
}
}
}

View File

@@ -3,8 +3,6 @@
#include "pch.h"
#include "HwndTerminal.hpp"
#include <windowsx.h>
#include "../../types/TermControlUiaProvider.hpp"
#include <DefaultSettings.h>
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
@@ -16,54 +14,13 @@ using namespace ::Microsoft::Terminal::Core;
static LPCWSTR term_window_class = L"HwndTerminalClass";
LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
static LRESULT CALLBACK HwndTerminalWndProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam) noexcept
{
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
HwndTerminal* terminal = reinterpret_cast<HwndTerminal*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (terminal)
{
switch (uMsg)
{
case WM_GETOBJECT:
if (lParam == UiaRootObjectId)
{
return UiaReturnRawElementProvider(hwnd, wParam, lParam, terminal->_GetUiaProvider());
}
break;
case WM_LBUTTONDOWN:
LOG_IF_FAILED(terminal->_StartSelection(lParam));
return 0;
case WM_MOUSEMOVE:
if (WI_IsFlagSet(wParam, MK_LBUTTON))
{
LOG_IF_FAILED(terminal->_MoveSelection(lParam));
return 0;
}
break;
case WM_RBUTTONDOWN:
if (terminal->_terminal->IsSelectionActive())
{
try
{
const auto bufferData = terminal->_terminal->RetrieveSelectedTextFromBuffer(false);
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
terminal->_terminal->ClearSelection();
}
CATCH_LOG();
}
else
{
terminal->_PasteTextFromClipboard();
}
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static bool RegisterTermClass(HINSTANCE hInstance) noexcept
@@ -75,7 +32,7 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
}
wc.style = 0;
wc.lpfnWndProc = HwndTerminal::HwndTerminalWndProc;
wc.lpfnWndProc = HwndTerminalWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
@@ -90,11 +47,7 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
HwndTerminal::HwndTerminal(HWND parentHwnd) :
_desiredFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8 },
_actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false },
_uiaProvider{ nullptr },
_uiaProviderInitialized{ false },
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
_pfnWriteCallback{ nullptr }
_actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, 14 }, CP_UTF8, false }
{
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
@@ -116,9 +69,6 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
nullptr,
hInstance,
nullptr));
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
SetWindowLongPtr(_hwnd.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}
}
@@ -157,7 +107,7 @@ HRESULT HwndTerminal::Initialize()
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
_terminal->SetDefaultBackground(RGB(5, 27, 80));
_terminal->SetDefaultForeground(RGB(255, 255, 255));
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept { _WriteTextToConnection(input); });
localPointerToThread->EnablePainting();
return S_OK;
@@ -168,39 +118,31 @@ void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> cal
_terminal->SetScrollPositionChangedCallback(callback);
}
void HwndTerminal::_WriteTextToConnection(const std::wstring& input) noexcept
{
if (!_pfnWriteCallback)
{
return;
}
try
{
auto callingText{ wil::make_cotaskmem_string(input.data(), input.size()) };
_pfnWriteCallback(callingText.release());
}
CATCH_LOG();
}
void HwndTerminal::RegisterWriteCallback(const void _stdcall callback(wchar_t*))
{
_pfnWriteCallback = callback;
}
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept {
const wchar_t* text = input.c_str();
const size_t textChars = wcslen(text) + 1;
const size_t textBytes = textChars * sizeof(wchar_t);
wchar_t* callingText = nullptr;
::Microsoft::Console::Types::IUiaData* HwndTerminal::GetUiaData() const noexcept
{
return _terminal.get();
}
callingText = static_cast<wchar_t*>(::CoTaskMemAlloc(textBytes));
HWND HwndTerminal::GetHwnd() const noexcept
{
return _hwnd.get();
if (callingText == nullptr)
{
callback(nullptr);
}
else
{
wcscpy_s(callingText, textChars, text);
callback(callingText);
}
});
}
void HwndTerminal::_UpdateFont(int newDpi)
{
_currentDpi = newDpi;
auto lock = _terminal->LockForWriting();
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
@@ -208,33 +150,6 @@ void HwndTerminal::_UpdateFont(int newDpi)
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
}
IRawElementProviderSimple* HwndTerminal::_GetUiaProvider() noexcept
{
if (nullptr == _uiaProvider && !_uiaProviderInitialized)
{
std::unique_lock<std::shared_mutex> lock;
try
{
#pragma warning(suppress : 26441) // The lock is named, this appears to be a false positive
lock = _terminal->LockForWriting();
if (_uiaProviderInitialized)
{
return _uiaProvider.Get();
}
LOG_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, this->GetUiaData(), this));
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
_uiaProvider = nullptr;
}
_uiaProviderInitialized = true;
}
return _uiaProvider.Get();
}
HRESULT HwndTerminal::Refresh(const SIZE windowSize, _Out_ COORD* dimensions)
{
RETURN_HR_IF_NULL(E_INVALIDARG, dimensions);
@@ -271,29 +186,10 @@ void HwndTerminal::SendOutput(std::wstring_view data)
HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal)
{
// In order for UIA to hook up properly there needs to be a "static" window hosting the
// inner win32 control. If the static window is not present then WM_GETOBJECT messages
// will not reach the child control, and the uia element will not be present in the tree.
auto _hostWindow = CreateWindowEx(
0,
L"static",
nullptr,
WS_CHILD |
WS_CLIPCHILDREN |
WS_CLIPSIBLINGS |
WS_VISIBLE,
0,
0,
0,
0,
parentHwnd,
nullptr,
nullptr,
0);
auto _terminal = std::make_unique<HwndTerminal>(_hostWindow);
auto _terminal = std::make_unique<HwndTerminal>(parentHwnd);
RETURN_IF_FAILED(_terminal->Initialize());
*hwnd = _hostWindow;
*hwnd = _terminal->_hwnd.get();
*terminal = _terminal.release();
return S_OK;
@@ -321,15 +217,6 @@ HRESULT _stdcall TerminalTriggerResize(void* terminal, double width, double heig
{
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
LOG_IF_WIN32_BOOL_FALSE(SetWindowPos(
publicTerminal->GetHwnd(),
nullptr,
0,
0,
static_cast<int>(width),
static_cast<int>(height),
0));
const SIZE windowSize{ static_cast<short>(width), static_cast<short>(height) };
return publicTerminal->Refresh(windowSize, dimensions);
}
@@ -346,54 +233,45 @@ void _stdcall TerminalUserScroll(void* terminal, int viewTop)
publicTerminal->_terminal->UserScrollViewport(viewTop);
}
HRESULT HwndTerminal::_StartSelection(LPARAM lParam) noexcept
try
HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed)
{
const bool altPressed = GetKeyState(VK_MENU) < 0;
COORD cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};
COORD terminalPosition = { cursorPosition };
const auto fontSize = this->_actualFont.GetSize();
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto fontSize = publicTerminal->_actualFont.GetSize();
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);
cursorPosition.X /= fontSize.X;
cursorPosition.Y /= fontSize.Y;
terminalPosition.X /= fontSize.X;
terminalPosition.Y /= fontSize.Y;
this->_terminal->SetSelectionAnchor(cursorPosition);
this->_terminal->SetBlockSelection(altPressed);
publicTerminal->_terminal->SetSelectionAnchor(terminalPosition);
publicTerminal->_terminal->SetBoxSelection(altPressed);
this->_renderer->TriggerSelection();
publicTerminal->_renderer->TriggerSelection();
return S_OK;
}
CATCH_RETURN();
HRESULT HwndTerminal::_MoveSelection(LPARAM lParam) noexcept
try
HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition)
{
COORD cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};
COORD terminalPosition = { cursorPosition };
const auto fontSize = this->_actualFont.GetSize();
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
const auto fontSize = publicTerminal->_actualFont.GetSize();
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.X == 0);
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.Y == 0);
cursorPosition.X /= fontSize.X;
cursorPosition.Y /= fontSize.Y;
terminalPosition.X /= fontSize.X;
terminalPosition.Y /= fontSize.Y;
this->_terminal->SetSelectionEnd(cursorPosition);
this->_renderer->TriggerSelection();
publicTerminal->_terminal->SetEndSelectionPosition(terminalPosition);
publicTerminal->_renderer->TriggerSelection();
return S_OK;
}
CATCH_RETURN();
void _stdcall TerminalClearSelection(void* terminal)
{
@@ -535,173 +413,3 @@ void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
publicTerminal->_terminal->SetCursorVisible(visible);
}
// Routine Description:
// - Copies the text given onto the global system clipboard.
// Arguments:
// - rows - Rows of text data to copy
// - fAlsoCopyFormatting - true if the color and formatting should also be copied, false otherwise
HRESULT HwndTerminal::_CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting)
{
std::wstring finalString;
// Concatenate strings into one giant string to put onto the clipboard.
for (const auto& str : rows.text)
{
finalString += str;
}
// allocate the final clipboard data
const size_t cchNeeded = finalString.size() + 1;
const size_t cbNeeded = sizeof(wchar_t) * cchNeeded;
wil::unique_hglobal globalHandle(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbNeeded));
RETURN_LAST_ERROR_IF_NULL(globalHandle.get());
PWSTR pwszClipboard = static_cast<PWSTR>(GlobalLock(globalHandle.get()));
RETURN_LAST_ERROR_IF_NULL(pwszClipboard);
// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr = StringCchCopyW(pwszClipboard, cchNeeded, finalString.data());
GlobalUnlock(globalHandle.get());
RETURN_IF_FAILED(hr);
// Set global data to clipboard
RETURN_LAST_ERROR_IF(!OpenClipboard(_hwnd.get()));
{ // Clipboard Scope
auto clipboardCloser = wil::scope_exit([]() noexcept {
LOG_LAST_ERROR_IF(!CloseClipboard());
});
RETURN_LAST_ERROR_IF(!EmptyClipboard());
RETURN_LAST_ERROR_IF_NULL(SetClipboardData(CF_UNICODETEXT, globalHandle.get()));
if (fAlsoCopyFormatting)
{
const auto& fontData = _actualFont;
int const iFontHeightPoints = fontData.GetUnscaledSize().Y * 72 / this->_currentDpi;
const COLORREF bgColor = _terminal->GetBackgroundColor(_terminal->GetDefaultBrushColors());
std::string HTMLToPlaceOnClip = TextBuffer::GenHTML(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor, "Hwnd Console Host");
_CopyToSystemClipboard(HTMLToPlaceOnClip, L"HTML Format");
std::string RTFToPlaceOnClip = TextBuffer::GenRTF(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor);
_CopyToSystemClipboard(RTFToPlaceOnClip, L"Rich Text Format");
}
}
// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandle.release();
return S_OK;
}
// Routine Description:
// - Copies the given string onto the global system clipboard in the specified format
// Arguments:
// - stringToCopy - The string to copy
// - lpszFormat - the name of the format
HRESULT HwndTerminal::_CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat)
{
const size_t cbData = stringToCopy.size() + 1; // +1 for '\0'
if (cbData)
{
wil::unique_hglobal globalHandleData(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbData));
RETURN_LAST_ERROR_IF_NULL(globalHandleData.get());
PSTR pszClipboardHTML = static_cast<PSTR>(GlobalLock(globalHandleData.get()));
RETURN_LAST_ERROR_IF_NULL(pszClipboardHTML);
// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr2 = StringCchCopyA(pszClipboardHTML, cbData, stringToCopy.data());
GlobalUnlock(globalHandleData.get());
RETURN_IF_FAILED(hr2);
UINT const CF_FORMAT = RegisterClipboardFormatW(lpszFormat);
RETURN_LAST_ERROR_IF(0 == CF_FORMAT);
RETURN_LAST_ERROR_IF_NULL(SetClipboardData(CF_FORMAT, globalHandleData.get()));
// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandleData.release();
}
return S_OK;
}
void HwndTerminal::_PasteTextFromClipboard() noexcept
{
// Get paste data from clipboard
if (!OpenClipboard(_hwnd.get()))
{
return;
}
HANDLE ClipboardDataHandle = GetClipboardData(CF_UNICODETEXT);
if (ClipboardDataHandle == nullptr)
{
CloseClipboard();
return;
}
PCWCH pwstr = static_cast<PCWCH>(GlobalLock(ClipboardDataHandle));
_StringPaste(pwstr);
GlobalUnlock(ClipboardDataHandle);
CloseClipboard();
}
void HwndTerminal::_StringPaste(const wchar_t* const pData) noexcept
{
if (pData == nullptr)
{
return;
}
try
{
std::wstring text(pData);
_WriteTextToConnection(text);
}
CATCH_LOG();
}
COORD HwndTerminal::GetFontSize() const
{
return _actualFont.GetSize();
}
RECT HwndTerminal::GetBounds() const noexcept
{
RECT windowRect;
GetWindowRect(_hwnd.get(), &windowRect);
return windowRect;
}
RECT HwndTerminal::GetPadding() const noexcept
{
return { 0 };
}
double HwndTerminal::GetScaleFactor() const noexcept
{
return static_cast<double>(_currentDpi) / static_cast<double>(USER_DEFAULT_SCREEN_DPI);
}
void HwndTerminal::ChangeViewport(const SMALL_RECT NewWindow)
{
_terminal->UserScrollViewport(NewWindow.Top);
}
HRESULT HwndTerminal::GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept
{
return UiaHostProviderFromHwnd(_hwnd.get(), provider);
}

View File

@@ -6,9 +6,6 @@
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include <UIAutomationCore.h>
#include "../../types/IControlAccessibilityInfo.h"
#include "../../types/TermControlUiaProvider.hpp"
using namespace Microsoft::Console::VirtualTerminal;
@@ -28,6 +25,8 @@ __declspec(dllexport) HRESULT _stdcall TerminalTriggerResize(void* terminal, dou
__declspec(dllexport) HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
__declspec(dllexport) void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
__declspec(dllexport) void _stdcall TerminalUserScroll(void* terminal, int viewTop);
__declspec(dllexport) HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
__declspec(dllexport) HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
__declspec(dllexport) void _stdcall TerminalClearSelection(void* terminal);
__declspec(dllexport) const wchar_t* _stdcall TerminalGetSelection(void* terminal);
__declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
@@ -40,35 +39,20 @@ __declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
};
struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
struct HwndTerminal
{
public:
HwndTerminal(HWND hwnd);
HwndTerminal(const HwndTerminal&) = default;
HwndTerminal(HwndTerminal&&) = default;
HwndTerminal& operator=(const HwndTerminal&) = default;
HwndTerminal& operator=(HwndTerminal&&) = default;
~HwndTerminal() = default;
HRESULT Initialize();
void SendOutput(std::wstring_view data);
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
void RegisterWriteCallback(const void _stdcall callback(wchar_t*));
::Microsoft::Console::Types::IUiaData* GetUiaData() const noexcept;
HWND GetHwnd() const noexcept;
static LRESULT CALLBACK HwndTerminalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
private:
wil::unique_hwnd _hwnd;
FontInfoDesired _desiredFont;
FontInfo _actualFont;
int _currentDpi;
bool _uiaProviderInitialized;
std::function<void(wchar_t*)> _pfnWriteCallback;
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
@@ -79,6 +63,8 @@ private:
friend HRESULT _stdcall TerminalResize(void* terminal, COORD dimensions);
friend void _stdcall TerminalDpiChanged(void* terminal, int newDpi);
friend void _stdcall TerminalUserScroll(void* terminal, int viewTop);
friend HRESULT _stdcall TerminalStartSelection(void* terminal, COORD cursorPosition, bool altPressed);
friend HRESULT _stdcall TerminalMoveSelection(void* terminal, COORD cursorPosition);
friend void _stdcall TerminalClearSelection(void* terminal);
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
@@ -87,23 +73,5 @@ private:
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
friend void _stdcall TerminalBlinkCursor(void* terminal);
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
void _UpdateFont(int newDpi);
void _WriteTextToConnection(const std::wstring& text) noexcept;
HRESULT _CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting);
HRESULT _CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat);
void _PasteTextFromClipboard() noexcept;
void _StringPaste(const wchar_t* const pData) noexcept;
HRESULT _StartSelection(LPARAM lParam) noexcept;
HRESULT _MoveSelection(LPARAM lParam) noexcept;
IRawElementProviderSimple* _GetUiaProvider() noexcept;
// Inherited via IControlAccessibilityInfo
COORD GetFontSize() const override;
RECT GetBounds() const noexcept override;
double GetScaleFactor() const noexcept override;
void ChangeViewport(const SMALL_RECT NewWindow) override;
HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) noexcept override;
RECT GetPadding() const noexcept override;
};

View File

@@ -54,7 +54,7 @@
instead of APISet forwarders for easier Windows 7 compatibility. -->
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>Uiautomationcore.lib;onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>onecoreuap.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@@ -1,8 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // If this is not defined, windows.h includes commdlg.h which defines FindText globally and conflicts with UIAutomation ITextRangeProvider.
#endif
#include <LibraryIncludes.h>

View File

@@ -441,13 +441,11 @@ void winrt::TerminalApp::implementation::AppKeyBindings::LayerJson(const Json::V
if (keys)
{
const auto validString = keys.isString();
const auto validArray = keys.isArray() && keys.size() == 1;
if (!validString && !validArray)
if (!keys.isArray() || keys.size() != 1)
{
continue;
}
const auto keyChordString = keys.isString() ? winrt::to_hstring(keys.asString()) : winrt::to_hstring(keys[0].asString());
const auto keyChordString = winrt::to_hstring(keys[0].asString());
// Invalid is our placeholder that the action was not parsed.
ShortcutAction action = ShortcutAction::Invalid;

View File

@@ -49,8 +49,6 @@ 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 RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" };
// Possible values for closeOnExit
static constexpr std::string_view CloseOnExitAlways{ "always" };
@@ -85,10 +83,8 @@ static constexpr std::string_view ImageAlignmentTopRight{ "topRight" };
static constexpr std::string_view ImageAlignmentBottomLeft{ "bottomLeft" };
static constexpr std::string_view ImageAlignmentBottomRight{ "bottomRight" };
// Possible values for TextAntialiasingMode
static constexpr std::wstring_view AntialiasingModeGrayscale{ L"grayscale" };
static constexpr std::wstring_view AntialiasingModeCleartype{ L"cleartype" };
static constexpr std::wstring_view AntialiasingModeAliased{ L"aliased" };
// Terminal effects
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
Profile::Profile() :
Profile(std::nullopt)
@@ -128,8 +124,7 @@ Profile::Profile(const std::optional<GUID>& guid) :
_backgroundImageOpacity{},
_backgroundImageStretchMode{},
_backgroundImageAlignment{},
_retroTerminalEffect{},
_antialiasingMode{ TextAntialiasingMode::Grayscale }
_retroTerminalEffect{}
{
}
@@ -183,7 +178,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
terminalSettings.CursorShape(_cursorShape);
// Fill in the remaining properties from the profile
terminalSettings.ProfileName(_name);
terminalSettings.UseAcrylic(_useAcrylic);
terminalSettings.TintOpacity(_acrylicTransparency);
@@ -263,8 +257,6 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
terminalSettings.RetroTerminalEffect(_retroTerminalEffect.value());
}
terminalSettings.AntialiasingMode(_antialiasingMode);
return terminalSettings;
}
@@ -385,8 +377,6 @@ Json::Value Profile::ToJson() const
root[JsonKey(RetroTerminalEffectKey)] = _retroTerminalEffect.value();
}
root[JsonKey(AntialiasingModeKey)] = SerializeTextAntialiasingMode(_antialiasingMode).data();
return root;
}
@@ -752,12 +742,6 @@ void Profile::LayerJson(const Json::Value& json)
JsonUtils::GetOptionalValue(json, BackgroundImageAlignmentKey, _backgroundImageAlignment, &Profile::_ConvertJsonToAlignment);
JsonUtils::GetOptionalValue(json, RetroTerminalEffectKey, _retroTerminalEffect, Profile::_ConvertJsonToBool);
if (json.isMember(JsonKey(AntialiasingModeKey)))
{
auto antialiasingMode{ json[JsonKey(AntialiasingModeKey)] };
_antialiasingMode = ParseTextAntialiasingMode(GetWstringFromJson(antialiasingMode));
}
}
void Profile::SetFontFace(std::wstring fontFace) noexcept
@@ -1365,49 +1349,3 @@ void Profile::SetRetroTerminalEffect(bool value) noexcept
{
_retroTerminalEffect = value;
}
// Method Description:
// - Helper function for converting a user-specified antialiasing mode
// corresponding TextAntialiasingMode enum value
// Arguments:
// - antialiasingMode: The string value from the settings file to parse
// Return Value:
// - The corresponding enum value which maps to the string provided by the user
TextAntialiasingMode Profile::ParseTextAntialiasingMode(const std::wstring& antialiasingMode)
{
if (antialiasingMode == AntialiasingModeCleartype)
{
return TextAntialiasingMode::Cleartype;
}
else if (antialiasingMode == AntialiasingModeAliased)
{
return TextAntialiasingMode::Aliased;
}
else if (antialiasingMode == AntialiasingModeGrayscale)
{
return TextAntialiasingMode::Grayscale;
}
// default behavior for invalid data
return TextAntialiasingMode::Grayscale;
}
// Method Description:
// - Helper function for converting a TextAntialiasingMode to its corresponding
// string value.
// Arguments:
// - antialiasingMode: The enum value to convert to a string.
// Return Value:
// - The string value for the given TextAntialiasingMode
std::wstring_view Profile::SerializeTextAntialiasingMode(const TextAntialiasingMode antialiasingMode)
{
switch (antialiasingMode)
{
case TextAntialiasingMode::Cleartype:
return AntialiasingModeCleartype;
case TextAntialiasingMode::Aliased:
return AntialiasingModeAliased;
default:
case TextAntialiasingMode::Grayscale:
return AntialiasingModeGrayscale;
}
}

View File

@@ -123,9 +123,6 @@ private:
static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString);
static std::wstring_view _SerializeCursorStyle(const winrt::Microsoft::Terminal::Settings::CursorStyle cursorShape);
static winrt::Microsoft::Terminal::Settings::TextAntialiasingMode ParseTextAntialiasingMode(const std::wstring& antialiasingMode);
static std::wstring_view SerializeTextAntialiasingMode(const winrt::Microsoft::Terminal::Settings::TextAntialiasingMode antialiasingMode);
static GUID _GenerateGuidForProfile(const std::wstring& name, const std::optional<std::wstring>& source) noexcept;
static bool _ConvertJsonToBool(const Json::Value& json);
@@ -169,8 +166,6 @@ private:
std::optional<std::wstring> _icon;
winrt::Microsoft::Terminal::Settings::TextAntialiasingMode _antialiasingMode;
friend class TerminalAppLocalTests::SettingsTests;
friend class TerminalAppLocalTests::ProfileTests;
friend class TerminalAppUnitTests::JsonTests;

View File

@@ -602,10 +602,8 @@ namespace winrt::TerminalApp::implementation
profile->GetConnectionType() == AzureConnectionType &&
TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
{
// TODO GH#4661: Replace this with directly using the AzCon when our VT is better
std::filesystem::path azBridgePath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
azBridgePath.replace_filename(L"TerminalAzBridge.exe");
connection = TerminalConnection::ConptyConnection(azBridgePath.wstring(), L".", L"Azure", settings.InitialRows(), settings.InitialCols(), winrt::guid());
connection = TerminalConnection::AzureConnection(settings.InitialRows(),
settings.InitialCols());
}
else if (profile->HasConnectionType() &&

View File

@@ -30,8 +30,7 @@
"icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
"padding": "8, 8, 8, 8",
"snapOnInput": true,
"useAcrylic": false,
"antialiasingMode": "grayscale"
"useAcrylic": false
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
@@ -49,8 +48,7 @@
"icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
"padding": "8, 8, 8, 8",
"snapOnInput": true,
"useAcrylic": false,
"antialiasingMode": "grayscale"
"useAcrylic": false
}
],
"schemes":
@@ -205,55 +203,55 @@
],
"keybindings":
[
{ "command": "closePane", "keys": "ctrl+shift+w" },
{ "command": "closeWindow", "keys": "alt+f4" },
{ "command": "copy", "keys": "ctrl+shift+c" },
{ "command": "copy", "keys": "ctrl+insert" },
{ "command": "decreaseFontSize", "keys": "ctrl+-" },
{ "command": "duplicateTab", "keys": "ctrl+shift+d" },
{ "command": "increaseFontSize", "keys": "ctrl+=" },
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": "alt+down" },
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": "alt+left" },
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": "alt+right" },
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": "alt+up" },
{ "command": "newTab", "keys": "ctrl+shift+t" },
{ "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+shift+1" },
{ "command": { "action": "newTab", "index": 1 }, "keys": "ctrl+shift+2" },
{ "command": { "action": "newTab", "index": 2 }, "keys": "ctrl+shift+3" },
{ "command": { "action": "newTab", "index": 3 }, "keys": "ctrl+shift+4" },
{ "command": { "action": "newTab", "index": 4 }, "keys": "ctrl+shift+5" },
{ "command": { "action": "newTab", "index": 5 }, "keys": "ctrl+shift+6" },
{ "command": { "action": "newTab", "index": 6 }, "keys": "ctrl+shift+7" },
{ "command": { "action": "newTab", "index": 7 }, "keys": "ctrl+shift+8" },
{ "command": { "action": "newTab", "index": 8 }, "keys": "ctrl+shift+9" },
{ "command": "nextTab", "keys": "ctrl+tab" },
{ "command": "openNewTabDropdown", "keys": "ctrl+shift+space" },
{ "command": "openSettings", "keys": "ctrl+," },
{ "command": "paste", "keys": "ctrl+shift+v" },
{ "command": "paste", "keys": "shift+insert" },
{ "command": "prevTab", "keys": "ctrl+shift+tab" },
{ "command": "resetFontSize", "keys": "ctrl+0" },
{ "command": { "action": "resizePane", "direction": "down" }, "keys": "alt+shift+down" },
{ "command": { "action": "resizePane", "direction": "left" }, "keys": "alt+shift+left" },
{ "command": { "action": "resizePane", "direction": "right" }, "keys": "alt+shift+right" },
{ "command": { "action": "resizePane", "direction": "up" }, "keys": "alt+shift+up" },
{ "command": "scrollDown", "keys": "ctrl+shift+down" },
{ "command": "scrollDownPage", "keys": "ctrl+shift+pgdn" },
{ "command": "scrollUp", "keys": "ctrl+shift+up" },
{ "command": "scrollUpPage", "keys": "ctrl+shift+pgup" },
{ "command": { "action": "splitPane", "split": "horizontal"}, "keys": "alt+shift+-" },
{ "command": { "action": "splitPane", "split": "vertical"}, "keys": "alt+shift+plus" },
{ "command": { "action": "switchToTab", "index": 0 }, "keys": "ctrl+alt+1" },
{ "command": { "action": "switchToTab", "index": 1 }, "keys": "ctrl+alt+2" },
{ "command": { "action": "switchToTab", "index": 2 }, "keys": "ctrl+alt+3" },
{ "command": { "action": "switchToTab", "index": 3 }, "keys": "ctrl+alt+4" },
{ "command": { "action": "switchToTab", "index": 4 }, "keys": "ctrl+alt+5" },
{ "command": { "action": "switchToTab", "index": 5 }, "keys": "ctrl+alt+6" },
{ "command": { "action": "switchToTab", "index": 6 }, "keys": "ctrl+alt+7" },
{ "command": { "action": "switchToTab", "index": 7 }, "keys": "ctrl+alt+8" },
{ "command": { "action": "switchToTab", "index": 8 }, "keys": "ctrl+alt+9" },
{ "command": "find", "keys": "ctrl+shift+f" },
{ "command": "toggleFullscreen", "keys": "alt+enter" },
{ "command": "toggleFullscreen", "keys": "f11" }
{ "command": "closePane", "keys": [ "ctrl+shift+w" ] },
{ "command": "closeWindow", "keys": [ "alt+f4" ] },
{ "command": "copy", "keys": [ "ctrl+shift+c" ] },
{ "command": "copy", "keys": [ "ctrl+insert" ] },
{ "command": "decreaseFontSize", "keys": [ "ctrl+-" ] },
{ "command": "duplicateTab", "keys": [ "ctrl+shift+d" ] },
{ "command": "increaseFontSize", "keys": [ "ctrl+=" ] },
{ "command": { "action": "moveFocus", "direction": "down" }, "keys": [ "alt+down" ] },
{ "command": { "action": "moveFocus", "direction": "left" }, "keys": [ "alt+left" ] },
{ "command": { "action": "moveFocus", "direction": "right" }, "keys": [ "alt+right" ] },
{ "command": { "action": "moveFocus", "direction": "up" }, "keys": [ "alt+up" ] },
{ "command": "newTab", "keys": [ "ctrl+shift+t" ] },
{ "command": { "action": "newTab", "index": 0 }, "keys": ["ctrl+shift+1"] },
{ "command": { "action": "newTab", "index": 1 }, "keys": ["ctrl+shift+2"] },
{ "command": { "action": "newTab", "index": 2 }, "keys": ["ctrl+shift+3"] },
{ "command": { "action": "newTab", "index": 3 }, "keys": ["ctrl+shift+4"] },
{ "command": { "action": "newTab", "index": 4 }, "keys": ["ctrl+shift+5"] },
{ "command": { "action": "newTab", "index": 5 }, "keys": ["ctrl+shift+6"] },
{ "command": { "action": "newTab", "index": 6 }, "keys": ["ctrl+shift+7"] },
{ "command": { "action": "newTab", "index": 7 }, "keys": ["ctrl+shift+8"] },
{ "command": { "action": "newTab", "index": 8 }, "keys": ["ctrl+shift+9"] },
{ "command": "nextTab", "keys": [ "ctrl+tab" ] },
{ "command": "openNewTabDropdown", "keys": [ "ctrl+shift+space" ] },
{ "command": "openSettings", "keys": [ "ctrl+," ] },
{ "command": "paste", "keys": [ "ctrl+shift+v" ] },
{ "command": "paste", "keys": [ "shift+insert" ] },
{ "command": "prevTab", "keys": [ "ctrl+shift+tab" ] },
{ "command": "resetFontSize", "keys": ["ctrl+0"]},
{ "command": { "action": "resizePane", "direction": "down" }, "keys": [ "alt+shift+down" ] },
{ "command": { "action": "resizePane", "direction": "left" }, "keys": [ "alt+shift+left" ] },
{ "command": { "action": "resizePane", "direction": "right" }, "keys": [ "alt+shift+right" ] },
{ "command": { "action": "resizePane", "direction": "up" }, "keys": [ "alt+shift+up" ] },
{ "command": "scrollDown", "keys": [ "ctrl+shift+down" ] },
{ "command": "scrollDownPage", "keys": [ "ctrl+shift+pgdn" ] },
{ "command": "scrollUp", "keys": [ "ctrl+shift+up" ] },
{ "command": "scrollUpPage", "keys": [ "ctrl+shift+pgup" ] },
{ "command": { "action": "splitPane", "split": "horizontal"}, "keys": [ "alt+shift+-" ] },
{ "command": { "action": "splitPane", "split": "vertical"}, "keys": [ "alt+shift+plus" ] },
{ "command": { "action": "switchToTab", "index": 0 }, "keys": ["ctrl+alt+1"] },
{ "command": { "action": "switchToTab", "index": 1 }, "keys": ["ctrl+alt+2"] },
{ "command": { "action": "switchToTab", "index": 2 }, "keys": ["ctrl+alt+3"] },
{ "command": { "action": "switchToTab", "index": 3 }, "keys": ["ctrl+alt+4"] },
{ "command": { "action": "switchToTab", "index": 4 }, "keys": ["ctrl+alt+5"] },
{ "command": { "action": "switchToTab", "index": 5 }, "keys": ["ctrl+alt+6"] },
{ "command": { "action": "switchToTab", "index": 6 }, "keys": ["ctrl+alt+7"] },
{ "command": { "action": "switchToTab", "index": 7 }, "keys": ["ctrl+alt+8"] },
{ "command": { "action": "switchToTab", "index": 8 }, "keys": ["ctrl+alt+9"] },
{ "command": "find", "keys": [ "ctrl+shift+f" ] },
{ "command": "toggleFullscreen", "keys": [ "alt+enter" ] },
{ "command": "toggleFullscreen", "keys": [ "f11" ] }
]
}

View File

@@ -213,7 +213,7 @@
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
<PRIResource Include="../Resources/Resources.language-en.resw" />
<PRIResource Include="../Resources/en-US/Resources.resw" />
<None Include="../packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
@@ -354,4 +354,4 @@
<Target Name="_TerminalAppGenerateUserSettingsH" Inputs="..\userDefaults.json" Outputs="Generated Files\userDefaults.h" BeforeTargets="BeforeClCompile">
<Exec Command="powershell.exe -noprofile ExecutionPolicy Unrestricted $(OpenConsoleDir)\tools\GenerateHeaderForJson.ps1 -JsonFile ..\userDefaults.json -OutPath '&quot;Generated Files\userDefaults.h&quot;' -VariableName UserSettingsJson" />
</Target>
</Project>
</Project>

View File

@@ -1,87 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ConsoleInputReader.h"
#include "unicode.hpp"
ConsoleInputReader::ConsoleInputReader(HANDLE handle) :
_handle(handle)
{
_buffer.resize(BufferSize);
_convertedString.reserve(BufferSize);
}
void ConsoleInputReader::SetWindowSizeChangedCallback(std::function<void()> callback)
{
_windowSizeChangedCallback = std::move(callback);
}
std::optional<std::wstring_view> ConsoleInputReader::Read()
{
DWORD readCount{ 0 };
_convertedString.clear();
while (_convertedString.empty())
{
_buffer.resize(BufferSize);
BOOL succeeded =
ReadConsoleInputW(_handle, _buffer.data(), gsl::narrow_cast<DWORD>(_buffer.size()), &readCount);
if (!succeeded)
{
return std::nullopt;
}
_buffer.resize(readCount);
for (auto it = _buffer.begin(); it != _buffer.end(); ++it)
{
if (it->EventType == WINDOW_BUFFER_SIZE_EVENT && _windowSizeChangedCallback)
{
_windowSizeChangedCallback();
}
else if (it->EventType == KEY_EVENT)
{
const auto& keyEvent = it->Event.KeyEvent;
if (keyEvent.bKeyDown || (!keyEvent.bKeyDown && keyEvent.wVirtualKeyCode == VK_MENU))
{
// Got a high surrogate at the end of the buffer
if (IS_HIGH_SURROGATE(keyEvent.uChar.UnicodeChar))
{
_highSurrogate.emplace(keyEvent.uChar.UnicodeChar);
continue; // we've consumed it -- only dispatch it if we get a low
}
if (IS_LOW_SURROGATE(keyEvent.uChar.UnicodeChar))
{
// No matter what we do, we want to destructively consume the high surrogate
if (const auto oldHighSurrogate{ std::exchange(_highSurrogate, std::nullopt) })
{
_convertedString.push_back(*_highSurrogate);
}
else
{
// If we get a low without a high surrogate, we've done everything we can.
// This is an illegal state.
_convertedString.push_back(UNICODE_REPLACEMENT);
continue; // onto the next event
}
}
// (\0 with a scancode is probably a modifier key, not a VT input key)
if (keyEvent.uChar.UnicodeChar != L'\0' || keyEvent.wVirtualScanCode == 0)
{
if (_highSurrogate) // non-destructive: we don't want to set it to nullopt needlessly for every character
{
// If we get a high surrogate *here*, we didn't find a low surrogate.
// This state is also illegal.
_convertedString.push_back(UNICODE_REPLACEMENT);
_highSurrogate.reset();
}
_convertedString.push_back(keyEvent.uChar.UnicodeChar);
}
}
}
}
}
return _convertedString;
}

View File

@@ -1,33 +0,0 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
Module Name:
ConsoleInputReader.h
Abstract:
This file contains a class whose sole purpose is to
abstract away a bunch of details you usually need to
know to read VT from a console input handle.
--*/
class ConsoleInputReader
{
public:
ConsoleInputReader(HANDLE handle);
void SetWindowSizeChangedCallback(std::function<void()> callback);
std::optional<std::wstring_view> Read();
private:
static constexpr size_t BufferSize{ 128 };
HANDLE _handle;
std::wstring _convertedString;
std::vector<INPUT_RECORD> _buffer;
std::optional<wchar_t> _highSurrogate;
std::function<void()> _windowSizeChangedCallback;
};

View File

@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{067F0A06-FCB7-472C-96E9-B03B54E8E18D}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>TerminalAzBridge</RootNamespace>
<ProjectName>TerminalAzBridge</ProjectName>
<TargetName>TerminalAzBridge</TargetName>
<ConfigurationType>Application</ConfigurationType>
<OpenConsoleUniversalApp>false</OpenConsoleUniversalApp>
<ApplicationType>Windows Store</ApplicationType>
<NoOutputRedirection>true</NoOutputRedirection>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<PropertyGroup>
<GenerateManifest>true</GenerateManifest>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<!-- Source Files -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="ConsoleInputReader.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="ConsoleInputReader.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<!-- Dependencies -->
<ItemGroup>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
<Project>{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}</Project>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
</ItemGroup>
<!--
This ItemGroup and the Globals PropertyGroup below it are required in order
to enable F5 debugging for the unpackaged application
-->
<ItemGroup>
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_general.xml" />
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_local_windows.xml" />
</ItemGroup>
<PropertyGroup Label="Globals">
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
</Project>

View File

@@ -1,104 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "winrt/Microsoft.Terminal.TerminalConnection.h"
#include "ConsoleInputReader.h"
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
static COORD GetConsoleScreenSize(HANDLE outputHandle)
{
CONSOLE_SCREEN_BUFFER_INFOEX csbiex{};
csbiex.cbSize = sizeof(csbiex);
GetConsoleScreenBufferInfoEx(outputHandle, &csbiex);
return {
(csbiex.srWindow.Right - csbiex.srWindow.Left) + 1,
(csbiex.srWindow.Bottom - csbiex.srWindow.Top) + 1
};
}
static ConnectionState RunConnectionToCompletion(const ITerminalConnection& connection, HANDLE outputHandle, HANDLE inputHandle)
{
connection.TerminalOutput([outputHandle](const winrt::hstring& output) {
WriteConsoleW(outputHandle, output.data(), output.size(), nullptr, nullptr);
});
// Detach a thread to spin the console read indefinitely.
// This application exits when the connection is closed, so
// the connection's lifetime will outlast this thread.
std::thread([connection, outputHandle, inputHandle] {
ConsoleInputReader reader{ inputHandle };
reader.SetWindowSizeChangedCallback([&]() {
const auto size = GetConsoleScreenSize(outputHandle);
connection.Resize(size.Y, size.X);
});
while (true)
{
auto input = reader.Read();
if (input)
{
connection.WriteInput(*input);
}
}
}).detach();
std::condition_variable stateChangeVar;
std::optional<ConnectionState> state;
std::mutex stateMutex;
connection.StateChanged([&](auto&& /*s*/, auto&& /*e*/) {
std::unique_lock<std::mutex> lg{ stateMutex };
state = connection.State();
stateChangeVar.notify_all();
});
connection.Start();
std::unique_lock<std::mutex> lg{ stateMutex };
stateChangeVar.wait(lg, [&]() {
if (!state.has_value())
{
return false;
}
return state.value() == ConnectionState::Closed || state.value() == ConnectionState::Failed;
});
return state.value();
}
int wmain(int /*argc*/, wchar_t** /*argv*/)
{
winrt::init_apartment(winrt::apartment_type::single_threaded);
DWORD inputMode{}, outputMode{};
HANDLE conIn{ GetStdHandle(STD_INPUT_HANDLE) }, conOut{ GetStdHandle(STD_OUTPUT_HANDLE) };
UINT codepage{ GetConsoleCP() }, outputCodepage{ GetConsoleOutputCP() };
RETURN_IF_WIN32_BOOL_FALSE(GetConsoleMode(conIn, &inputMode));
RETURN_IF_WIN32_BOOL_FALSE(GetConsoleMode(conOut, &outputMode));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleMode(conIn, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleMode(conOut, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_WRAP_AT_EOL_OUTPUT | DISABLE_NEWLINE_AUTO_RETURN));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleCP(CP_UTF8));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(CP_UTF8));
auto restoreConsoleModes = wil::scope_exit([&]() {
SetConsoleMode(conIn, inputMode);
SetConsoleMode(conOut, outputMode);
SetConsoleCP(codepage);
SetConsoleOutputCP(outputCodepage);
});
const auto size = GetConsoleScreenSize(conOut);
AzureConnection azureConn{ gsl::narrow_cast<uint32_t>(size.Y), gsl::narrow_cast<uint32_t>(size.X) };
const auto state = RunConnectionToCompletion(azureConn, conOut, conIn);
return state == ConnectionState::Closed ? 0 : 1;
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.190730.2" targetFramework="native" />
</packages>

View File

@@ -1,4 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"

View File

@@ -1,36 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- pch.h
Abstract:
- Contains external headers to include in the precompile phase of console build process.
- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
--*/
#pragma once
// Ignore checked iterators warning from VC compiler.
#define _SCL_SECURE_NO_WARNINGS
// Block minwindef.h min/max macros to prevent <algorithm> conflict
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <unknwn.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#include <windows.h>
#include "../inc/LibraryIncludes.h"
#include <wil/cppwinrt.h>
#include <winrt/Windows.system.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <wil/resource.h>
#include <wil/win32_helpers.h>

View File

@@ -59,7 +59,7 @@
<Midl Include="TelnetConnection.idl" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/Resources.language-en.resw" />
<PRIResource Include="Resources/en-US/Resources.resw" />
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
@@ -95,4 +95,4 @@
</Link>
</ItemDefinitionGroup>
<Import Project="..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets" Condition="Exists('..\..\..\packages\vcpkg-telnetpp.1.0.1\build\native\vcpkg-telnetpp.targets')" />
</Project>
</Project>

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
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
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>
@@ -26,36 +26,36 @@
<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
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
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
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
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
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
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
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -117,48 +117,27 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SearchBox_CaseSensitivity.ToolTipService.ToolTip" xml:space="preserve">
<data name="CaseSensitivityButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Match Case</value>
<comment>The tooltip text for the case sensitivity button on the search box control.</comment>
<comment>The tooltip text for CaseSensitivityButton</comment>
</data>
<data name="SearchBox_Close.ToolTipService.ToolTip" xml:space="preserve">
<data name="CloseButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Close</value>
<comment>The tooltip text for the close button on the search box control.</comment>
<comment>The tooltip text for CloseButton</comment>
</data>
<data name="SearchBox_SearchBackwards.ToolTipService.ToolTip" xml:space="preserve">
<data name="GoBackwardButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Find Up</value>
<comment>The tooltip text for the search backward button.</comment>
<comment>The tooltip text for GoBackward Button</comment>
</data>
<data name="SearchBox_SearchForwards.ToolTipService.ToolTip" xml:space="preserve">
<data name="GoForwardButtonLocalizedText.ToolTipService.ToolTip" xml:space="preserve">
<value>Find Down</value>
<comment>The tooltip text for the search forward button.</comment>
<comment>The tooltip text for GoForward Button</comment>
</data>
<data name="SearchBox_TextBox.PlaceholderText" xml:space="preserve">
<data name="TextBoxLocalizedText.PlaceholderText" xml:space="preserve">
<value>Find...</value>
<comment>The placeholder text in the search box control.</comment>
<comment>The placeholder text in the search dialog TextBox</comment>
</data>
<data name="DragFileCaption" xml:space="preserve">
<value>Copy path to file</value>
<comment>The displayed caption for dragging a file onto a terminal.</comment>
</data>
<data name="SearchBox_CaseSensitivity.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Case Sensitivity</value>
<comment>The name of the case sensitivity button on the search box control for accessibility.</comment>
</data>
<data name="SearchBox_SearchForwards.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Search Forward</value>
<comment>The name of the search forward button for accessibility.</comment>
</data>
<data name="SearchBox_SearchBackwards.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Search Backward</value>
<comment>The name of the search backward button for accessibility.</comment>
</data>
<data name="SearchBox_TextBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Search Text</value>
<comment>The name of the text box on the search box control for accessibility.</comment>
</data>
<data name="TerminalControl_ControlType" xml:space="preserve">
<value>terminal</value>
<comment>The type of control that the terminal ahderes to. Used to identify how a user can interact with this kind of control.</comment>
</data>
</root>
</root>

View File

@@ -153,44 +153,29 @@
</UserControl.Resources>
<StackPanel Orientation="Horizontal" Style="{ThemeResource SearchBoxBackground}" Padding="5" CornerRadius="0,0,2,2">
<TextBox x:Name="TextBox"
x:Uid="SearchBox_TextBox"
CornerRadius="2"
Width="160"
PlaceholderForeground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
FontSize="15"
KeyDown="TextBoxKeyDown"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<TextBox x:Name="TextBox" x:Uid="TextBoxLocalizedText" AutomationProperties.Name="Search Text" CornerRadius="2" Width="160"
PlaceholderForeground="{ThemeResource TextBoxPlaceholderTextThemeBrush}" FontSize="15" KeyDown = "TextBoxKeyDown"
Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center">
</TextBox>
<ToggleButton x:Name="GoBackwardButton"
x:Uid="SearchBox_SearchBackwards"
HorizontalAlignment="Right"
Style="{StaticResource ToggleButtonStyle}"
Click="GoBackwardClicked"
IsChecked="True">
<ToggleButton x:Name="GoBackwardButton" x:Uid="GoBackwardButtonLocalizedText" AutomationProperties.Name="Set Search Backward"
HorizontalAlignment="Right" Style="{StaticResource ToggleButtonStyle}"
Click="GoBackwardClicked" IsChecked="True">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE74A;" Style="{ThemeResource FontIconStyle}"/>
</ToggleButton>
<ToggleButton x:Name="GoForwardButton"
x:Uid="SearchBox_SearchForwards"
Style="{StaticResource ToggleButtonStyle}"
<ToggleButton x:Name="GoForwardButton" x:Uid="GoForwardButtonLocalizedText"
AutomationProperties.Name="Set Search Forward" Style="{StaticResource ToggleButtonStyle}"
Click="GoForwardClicked">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE74B;" Style="{ThemeResource FontIconStyle}"/>
</ToggleButton>
<ToggleButton x:Name="CaseSensitivityButton"
x:Uid="SearchBox_CaseSensitivity"
Style="{StaticResource ToggleButtonStyle}">
<ToggleButton x:Name="CaseSensitivityButton" x:Uid="CaseSensitivityButtonLocalizedText"
AutomationProperties.Name="CaseSensitivity" Style="{StaticResource ToggleButtonStyle}">
<PathIcon Data="M8.87305 10H7.60156L6.5625 7.25195H2.40625L1.42871 10H0.150391L3.91016 0.197266H5.09961L8.87305 10ZM6.18652 6.21973L4.64844 2.04297C4.59831 1.90625 4.54818 1.6875 4.49805 1.38672H4.4707C4.42513 1.66471 4.37272 1.88346 4.31348 2.04297L2.78906 6.21973H6.18652ZM15.1826 10H14.0615V8.90625H14.0342C13.5465 9.74479 12.8288 10.1641 11.8809 10.1641C11.1836 10.1641 10.6367 9.97949 10.2402 9.61035C9.84831 9.24121 9.65234 8.7513 9.65234 8.14062C9.65234 6.83268 10.4225 6.07161 11.9629 5.85742L14.0615 5.56348C14.0615 4.37402 13.5807 3.7793 12.6191 3.7793C11.776 3.7793 11.015 4.06641 10.3359 4.64062V3.49219C11.0241 3.05469 11.8171 2.83594 12.7148 2.83594C14.36 2.83594 15.1826 3.70638 15.1826 5.44727V10ZM14.0615 6.45898L12.373 6.69141C11.8535 6.76432 11.4616 6.89421 11.1973 7.08105C10.9329 7.26335 10.8008 7.58919 10.8008 8.05859C10.8008 8.40039 10.9215 8.68066 11.1631 8.89941C11.4092 9.11361 11.735 9.2207 12.1406 9.2207C12.6966 9.2207 13.1546 9.02702 13.5146 8.63965C13.8792 8.24772 14.0615 7.75326 14.0615 7.15625V6.45898Z"/>
</ToggleButton>
<Button x:Name="CloseButton"
x:Uid="SearchBox_Close"
Padding="0"
Click="CloseClick"
Style="{ThemeResource ButtonStyle}">
<Button x:Name="CloseButton" x:Uid="CloseButtonLocalizedText" AutomationProperties.Name="Close"
Padding="0" Click="CloseClick" Style="{ ThemeResource ButtonStyle}">
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE711;" FontSize="12"/>
</Button>
</StackPanel>

View File

@@ -17,8 +17,7 @@ using namespace winrt::Windows::UI::Xaml;
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
TSFInputControl::TSFInputControl() :
_editContext{ nullptr },
_inComposition{ false }
_editContext{ nullptr }
{
_Create();
}
@@ -139,31 +138,32 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Get the cursor position in text buffer position
auto cursorArgs = winrt::make_self<CursorPositionEventArgs>();
_CurrentCursorPositionHandlers(*this, *cursorArgs);
const COORD cursorPos = { ::base::ClampedNumeric<short>(cursorArgs->CurrentPosition().X), ::base::ClampedNumeric<short>(cursorArgs->CurrentPosition().Y) };
const COORD cursorPos = { gsl::narrow_cast<SHORT>(cursorArgs->CurrentPosition().X), gsl::narrow_cast<SHORT>(cursorArgs->CurrentPosition().Y) };
// Get Font Info as we use this is the pixel size for characters in the display
auto fontArgs = winrt::make_self<FontInfoEventArgs>();
_CurrentFontInfoHandlers(*this, *fontArgs);
const auto fontWidth = fontArgs->FontSize().Width;
const auto fontHeight = fontArgs->FontSize().Height;
const float fontWidth = fontArgs->FontSize().Width;
const float fontHeight = fontArgs->FontSize().Height;
// Convert text buffer cursor position to client coordinate position within the window
COORD clientCursorPos;
clientCursorPos.X = ::base::ClampMul(cursorPos.X, ::base::ClampedNumeric<short>(fontWidth));
clientCursorPos.Y = ::base::ClampMul(cursorPos.Y, ::base::ClampedNumeric<short>(fontHeight));
COORD screenCursorPos;
THROW_IF_FAILED(ShortMult(cursorPos.X, gsl::narrow<SHORT>(fontWidth), &clientCursorPos.X));
THROW_IF_FAILED(ShortMult(cursorPos.Y, gsl::narrow<SHORT>(fontHeight), &clientCursorPos.Y));
// Convert from client coordinate to screen coordinate by adding window position
COORD screenCursorPos;
screenCursorPos.X = ::base::ClampAdd(clientCursorPos.X, ::base::ClampedNumeric<short>(windowBounds.X));
screenCursorPos.Y = ::base::ClampAdd(clientCursorPos.Y, ::base::ClampedNumeric<short>(windowBounds.Y));
THROW_IF_FAILED(ShortAdd(clientCursorPos.X, gsl::narrow_cast<SHORT>(windowBounds.X), &screenCursorPos.X));
THROW_IF_FAILED(ShortAdd(clientCursorPos.Y, gsl::narrow_cast<SHORT>(windowBounds.Y), &screenCursorPos.Y));
// get any offset (margin + tabs, etc..) of the control within the window
const auto offsetPoint = this->TransformToVisual(nullptr).TransformPoint(winrt::Windows::Foundation::Point(0, 0));
// add the margin offsets if any
screenCursorPos.X = ::base::ClampAdd(screenCursorPos.X, ::base::ClampedNumeric<short>(offsetPoint.X));
screenCursorPos.Y = ::base::ClampAdd(screenCursorPos.Y, ::base::ClampedNumeric<short>(offsetPoint.Y));
const auto currentMargin = this->Margin();
THROW_IF_FAILED(ShortAdd(screenCursorPos.X, gsl::narrow_cast<SHORT>(offsetPoint.X), &screenCursorPos.X));
THROW_IF_FAILED(ShortAdd(screenCursorPos.Y, gsl::narrow_cast<SHORT>(offsetPoint.Y), &screenCursorPos.Y));
// Get scale factor for view
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
@@ -178,9 +178,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// position textblock to cursor position
_canvas.SetLeft(_textBlock, clientCursorPos.X);
_canvas.SetTop(_textBlock, ::base::ClampedNumeric<double>(clientCursorPos.Y));
_canvas.SetTop(_textBlock, static_cast<double>(clientCursorPos.Y));
// width is cursor to end of canvas
_textBlock.Width(200); // TODO GitHub #3640: Determine proper Width
_textBlock.Height(fontHeight);
// calculate FontSize in pixels from DIPs
const double fontSizePx = (fontHeight * 72) / USER_DEFAULT_SCREEN_DPI;
_textBlock.FontSize(fontSizePx);
@@ -198,7 +201,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TSFInputControl::_compositionStartedHandler(CoreTextEditContext sender, CoreTextCompositionStartedEventArgs const& /*args*/)
{
_inComposition = true;
_canvas.Visibility(Visibility::Visible);
_textBlock.Visibility(Visibility::Visible);
}
// Method Description:
@@ -211,19 +215,30 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TSFInputControl::_compositionCompletedHandler(CoreTextEditContext sender, CoreTextCompositionCompletedEventArgs const& /*args*/)
{
_inComposition = false;
// only need to do work if the current buffer has text
if (!_inputBuffer.empty())
{
_SendAndClearText();
// call event handler with data handled by parent
_compositionCompletedHandlers(_inputBuffer);
// clear the buffer for next round
const auto bufferLength = gsl::narrow_cast<int32_t>(_inputBuffer.length());
_inputBuffer.clear();
_textBlock.Text(L"");
// indicate text is now 0
_editContext.NotifyTextChanged({ 0, bufferLength }, 0, { 0, 0 });
// hide the controls until composition starts again
_canvas.Visibility(Visibility::Collapsed);
_textBlock.Visibility(Visibility::Collapsed);
}
}
// Method Description:
// - Handler for FocusRemoved event by CoreEditContext responsible
// for removing focus for the TSFInputControl control accordingly
// when focus was forcibly removed from text input control.
// when focus was forcibly removed from text input control. (TODO GitHub #3644)
// NOTE: Documentation says application should handle this event
// Arguments:
// - sender: CoreTextEditContext sending the request. Not used in method.
@@ -250,9 +265,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
try
{
const auto textEnd = ::base::ClampMin<size_t>(range.EndCaretPosition, _inputBuffer.length());
const auto length = ::base::ClampSub<size_t>(textEnd, range.StartCaretPosition);
const auto textRequested = _inputBuffer.substr(range.StartCaretPosition, length);
const auto textRequested = _inputBuffer.substr(range.StartCaretPosition, static_cast<size_t>(range.EndCaretPosition) - static_cast<size_t>(range.StartCaretPosition));
args.Request().Text(textRequested);
}
@@ -302,24 +315,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
try
{
_canvas.Visibility(Visibility::Visible);
_textBlock.Visibility(Visibility::Visible);
const auto length = ::base::ClampSub<size_t>(range.EndCaretPosition, range.StartCaretPosition);
_inputBuffer = _inputBuffer.replace(
range.StartCaretPosition,
length,
static_cast<size_t>(range.EndCaretPosition) - static_cast<size_t>(range.StartCaretPosition),
text);
_textBlock.Text(_inputBuffer);
// If we receive tabbed IME input like emoji, kaomojis, and symbols, send it to the terminal immediately.
// They aren't composition, so we don't want to wait for the user to start and finish a composition to send the text.
if (!_inComposition)
{
_SendAndClearText();
}
// Notify the TSF that the update succeeded
args.Result(CoreTextTextUpdatingResult::Succeeded);
}
@@ -332,35 +334,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
// Method Description:
// - Sends the currently held text in the input buffer to the parent and
// clears the input buffer and text block for the next round of input.
// Then hides the text block control until the next time text received.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TSFInputControl::_SendAndClearText()
{
// call event handler with data handled by parent
_compositionCompletedHandlers(_inputBuffer);
// clear the buffer for next round
const auto bufferLength = ::base::ClampedNumeric<int32_t>(_inputBuffer.length());
_inputBuffer.clear();
_textBlock.Text(L"");
// Leaving focus before NotifyTextChanged seems to guarantee that the next
// composition will send us a CompositionStarted event.
_editContext.NotifyFocusLeave();
_editContext.NotifyTextChanged({ 0, bufferLength }, 0, { 0, 0 });
_editContext.NotifyFocusEnter();
// hide the controls until text input starts again
_canvas.Visibility(Visibility::Collapsed);
_textBlock.Visibility(Visibility::Collapsed);
}
// Method Description:
// - Handler for FormatUpdating event by CoreEditContext responsible
// for handling different format updates for a particular range of text.

View File

@@ -75,9 +75,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
std::wstring _inputBuffer;
void _Create();
bool _inComposition;
void _SendAndClearText();
};
}
namespace winrt::Microsoft::Terminal::TerminalControl::factory_implementation

View File

@@ -18,7 +18,6 @@
using namespace ::Microsoft::Console::Types;
using namespace ::Microsoft::Terminal::Core;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Input;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::System;
@@ -54,6 +53,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
TermControl::TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection) :
_connection{ connection },
_initializedTerminal{ false },
_root{ nullptr },
_swapChainPanel{ nullptr },
_settings{ settings },
_closing{ false },
_isTerminalInitiatedScroll{ false },
@@ -68,15 +69,54 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_lastMouseClick{},
_lastMouseClickPos{},
_searchBox{ nullptr },
_unfocusedClickPos{ std::nullopt },
_isClickDragSelection{ false }
_tsfInputControl{ nullptr }
{
_EnsureStaticInitialization();
InitializeComponent();
_Create();
}
void TermControl::_Create()
{
Controls::Grid container;
Controls::ColumnDefinition contentColumn{};
Controls::ColumnDefinition scrollbarColumn{};
contentColumn.Width(GridLength{ 1.0, GridUnitType::Star });
scrollbarColumn.Width(GridLength{ 1.0, GridUnitType::Auto });
container.ColumnDefinitions().Append(contentColumn);
container.ColumnDefinitions().Append(scrollbarColumn);
_scrollBar = Controls::Primitives::ScrollBar{};
_scrollBar.Orientation(Controls::Orientation::Vertical);
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
_scrollBar.HorizontalAlignment(HorizontalAlignment::Right);
_scrollBar.VerticalAlignment(VerticalAlignment::Stretch);
// Initialize the scrollbar with some placeholder values.
// The scrollbar will be updated with real values on _Initialize
_scrollBar.Maximum(1);
_scrollBar.ViewportSize(10);
_scrollBar.IsTabStop(false);
_scrollBar.SmallChange(1);
_scrollBar.LargeChange(4);
_scrollBar.Visibility(Visibility::Visible);
_tsfInputControl = TSFInputControl();
_tsfInputControl.CompositionCompleted({ this, &TermControl::_CompositionCompleted });
_tsfInputControl.CurrentCursorPosition({ this, &TermControl::_CurrentCursorPositionHandler });
_tsfInputControl.CurrentFontInfo({ this, &TermControl::_FontInfoHandler });
container.Children().Append(_tsfInputControl);
// Create the SwapChainPanel that will display our content
Controls::SwapChainPanel swapChainPanel;
_sizeChangedRevoker = swapChainPanel.SizeChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainSizeChanged });
_compositionScaleChangedRevoker = swapChainPanel.CompositionScaleChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainScaleChanged });
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
_layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
_layoutUpdatedRevoker = swapChainPanel.LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
// This event fires every time the layout changes, but it is always the last one to fire
// in any layout change chain. That gives us great flexibility in finding the right point
// at which to initialize our renderer (and our terminal).
@@ -89,29 +129,74 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
});
container.Children().Append(swapChainPanel);
container.Children().Append(_scrollBar);
Controls::Grid::SetColumn(swapChainPanel, 0);
Controls::Grid::SetColumn(_scrollBar, 1);
Controls::Grid root{};
Controls::Image bgImageLayer{};
root.Children().Append(bgImageLayer);
root.Children().Append(container);
_root = root;
_bgImageLayer = bgImageLayer;
_swapChainPanel = swapChainPanel;
this->Content(_root);
_ApplyUISettings();
// These are important:
// 1. When we get tapped, focus us
_tappedRevoker = this->Tapped(winrt::auto_revoke, [this](auto&, auto& e) {
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);
// 3. Actually not sure about this one. Maybe it isn't necessary either.
this->AllowFocusOnInteraction(true);
// DON'T CALL _InitializeTerminal here - wait until the swap chain is loaded to do that.
// Subscribe to the connection's disconnected event and call our connection closed handlers.
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
_ConnectionStateChangedHandlers(*this, nullptr);
});
_ApplyUISettings();
_root.AllowDrop(true);
_root.Drop({ get_weak(), &TermControl::_DragDropHandler });
_root.DragOver({ get_weak(), &TermControl::_DragOverHandler });
}
// Method Description:
// - Loads the search box from the xaml UI and focuses it.
// - Create the SearchBoxControl object, and attach it
// to the Terminal Control root
// Arguments:
// - <none>
// Return Value:
// - <none>
void TermControl::CreateSearchBoxControl()
{
// Lazy load the search box control.
if (auto loadedSearchBox{ FindName(L"SearchBox") })
if (!_searchBox)
{
if (auto searchBox{ loadedSearchBox.try_as<::winrt::Microsoft::Terminal::TerminalControl::SearchBoxControl>() })
{
// get at its private implementation
_searchBox.copy_from(winrt::get_self<implementation::SearchBoxControl>(searchBox));
_searchBox->Visibility(Visibility::Visible);
_searchBox->SetFocusOnTextbox();
}
_searchBox = winrt::make_self<SearchBoxControl>();
_searchBox->HorizontalAlignment(HorizontalAlignment::Right);
_searchBox->VerticalAlignment(VerticalAlignment::Top);
// We need to make sure the searchbox does not overlap
// with the scroll bar
Thickness searchBoxPadding = { 0, 0, _scrollBar.ActualWidth(), 0 };
_searchBox->Margin(searchBoxPadding);
_root.Children().Append(*_searchBox);
// Event handlers
_searchBox->Search({ get_weak(), &TermControl::_Search });
_searchBox->Closed({ get_weak(), &TermControl::_CloseSearchBoxControl });
}
_searchBox->SetFocusOnTextbox();
}
// Method Description:
@@ -142,7 +227,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto lock = _terminal->LockForWriting();
if (search.FindNext())
{
_terminal->SetBlockSelection(false);
_terminal->SetBoxSelection(false);
search.Select();
_renderer->TriggerSelection();
}
@@ -151,6 +236,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Method Description:
// - The handler for the close button or pressing "Esc" when focusing on the
// search dialog.
// This removes the SearchBoxControl object from the XAML tree,
// reset smart pointer and set focus back to Terminal
// Arguments:
// - IInspectable: not used
// - RoutedEventArgs: not used
@@ -158,7 +245,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
{
_searchBox->Visibility(Visibility::Collapsed);
unsigned int idx;
_root.Children().IndexOf(*_searchBox, idx);
_root.Children().RemoveAt(idx);
_searchBox = nullptr;
// Set focus back to terminal control
this->Focus(FocusState::Programmatic);
@@ -177,7 +268,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Dispatch a call to the UI thread to apply the new settings to the
// terminal.
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_root.Dispatcher());
// If 'weakThis' is locked, then we can safely work with 'this'
if (auto control{ weakThis.get() })
@@ -194,8 +285,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Refresh our font with the renderer
_UpdateFont();
const auto width = SwapChainPanel().ActualWidth();
const auto height = SwapChainPanel().ActualHeight();
const auto width = _swapChainPanel.ActualWidth();
const auto height = _swapChainPanel.ActualHeight();
if (width != 0 && height != 0)
{
// If the font size changed, or the _swapchainPanel's size changed
@@ -204,6 +295,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto lock = _terminal->LockForWriting();
_DoResize(width, height);
}
// set TSF Foreground
Media::SolidColorBrush foregroundBrush{};
foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
_tsfInputControl.Foreground(foregroundBrush);
}
}
@@ -228,11 +324,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Apply padding as swapChainPanel's margin
auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
SwapChainPanel().Margin(newMargin);
auto existingMargin = _swapChainPanel.Margin();
_swapChainPanel.Margin(newMargin);
// Initialize our font information.
const auto fontFace = _settings.FontFace();
const short fontHeight = gsl::narrow_cast<short>(_settings.FontSize());
const short fontHeight = gsl::narrow<short>(_settings.FontSize());
// The font width doesn't terribly matter, we'll only be using the
// height to look it up
// The other params here also largely don't matter.
@@ -245,24 +342,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// set TSF Foreground
Media::SolidColorBrush foregroundBrush{};
foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
TSFInputControl().Foreground(foregroundBrush);
TSFInputControl().Margin(newMargin);
// Apply settings for scrollbar
if (_settings.ScrollState() == ScrollbarState::Hidden)
{
// In the scenario where the user has turned off the OS setting to automatically hide scollbars, the
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
// achieve the intended effect.
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
ScrollBar().Visibility(Visibility::Collapsed);
}
else // (default or Visible)
{
// Default behavior
ScrollBar().IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
ScrollBar().Visibility(Visibility::Visible);
}
_tsfInputControl.Foreground(foregroundBrush);
_tsfInputControl.Margin(newMargin);
// set number of rows to scroll at a time
_rowsToScroll = _settings.RowsToScroll();
@@ -288,7 +369,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
// See if we've already got an acrylic background brush
// to avoid the flicker when setting up a new one
auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>();
auto acrylic = _root.Background().try_as<Media::AcrylicBrush>();
// Instantiate a brush if there's not already one there
if (acrylic == nullptr)
@@ -313,15 +394,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
acrylic.TintOpacity(_settings.TintOpacity());
// Apply brush to control if it's not already there
if (RootGrid().Background() != acrylic)
if (_root.Background() != acrylic)
{
RootGrid().Background(acrylic);
_root.Background(acrylic);
}
}
else
{
Media::SolidColorBrush solidColor{};
RootGrid().Background(solidColor);
_root.Background(solidColor);
}
if (!_settings.BackgroundImage().empty())
@@ -331,7 +412,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Check if the image brush is already pointing to the image
// in the modified settings; if it isn't (or isn't there),
// set a new image source for the brush
auto imageSource = BackgroundImage().Source().try_as<Media::Imaging::BitmapImage>();
auto imageSource = _bgImageLayer.Source().try_as<Media::Imaging::BitmapImage>();
if (imageSource == nullptr ||
imageSource.UriSource() == nullptr ||
@@ -342,18 +423,18 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// may well be both large and somewhere out on the
// internet.
Media::Imaging::BitmapImage image(imageUri);
BackgroundImage().Source(image);
_bgImageLayer.Source(image);
}
// Apply stretch, opacity and alignment settings
BackgroundImage().Stretch(_settings.BackgroundImageStretchMode());
BackgroundImage().Opacity(_settings.BackgroundImageOpacity());
BackgroundImage().HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
BackgroundImage().VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
_bgImageLayer.Stretch(_settings.BackgroundImageStretchMode());
_bgImageLayer.Opacity(_settings.BackgroundImageOpacity());
_bgImageLayer.HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
_bgImageLayer.VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
}
else
{
BackgroundImage().Source(nullptr);
_bgImageLayer.Source(nullptr);
}
}
@@ -367,7 +448,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_root.Dispatcher());
if (auto control{ weakThis.get() })
{
@@ -381,12 +462,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
bgColor.B = B;
bgColor.A = 255;
if (auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>())
if (auto acrylic = _root.Background().try_as<Media::AcrylicBrush>())
{
acrylic.FallbackColor(bgColor);
acrylic.TintColor(bgColor);
}
else if (auto solidColor = RootGrid().Background().try_as<Media::SolidColorBrush>())
else if (auto solidColor = _root.Background().try_as<Media::SolidColorBrush>())
{
solidColor.Color(bgColor);
}
@@ -439,9 +520,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return _actualFont;
}
const Windows::UI::Xaml::Thickness TermControl::GetPadding()
const Windows::UI::Xaml::Thickness TermControl::GetPadding() const
{
return SwapChainPanel().Margin();
return _swapChainPanel.Margin();
}
TerminalConnection::ConnectionState TermControl::ConnectionState() const
@@ -459,13 +540,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto chain = _renderEngine->GetSwapChain();
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
// If 'weakThis' is locked, then we can safely work with 'this'
if (auto control{ weakThis.get() })
{
auto lock = _terminal->LockForWriting();
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
auto nativePanel = _swapChainPanel.as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
}
}
@@ -475,12 +556,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto chain = _renderEngine->GetSwapChain();
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
if (auto control{ weakThis.get() })
{
_terminal->LockConsole();
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
auto nativePanel = _swapChainPanel.as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(chain.Get());
_terminal->UnlockConsole();
}
@@ -493,8 +574,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return false;
}
const auto windowWidth = SwapChainPanel().ActualWidth(); // Width() and Height() are NaN?
const auto windowHeight = SwapChainPanel().ActualHeight();
const auto windowWidth = _swapChainPanel.ActualWidth(); // Width() and Height() are NaN?
const auto windowHeight = _swapChainPanel.ActualHeight();
if (windowWidth == 0 || windowHeight == 0)
{
@@ -555,22 +636,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// here, the setting will only be used when the Terminal is initialized.
dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
// TODO:GH#3927 - hot-reload this one too
// Update DxEngine's AntialiasingMode
switch (_settings.AntialiasingMode())
{
case TextAntialiasingMode::Cleartype:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
break;
case TextAntialiasingMode::Aliased:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
break;
case TextAntialiasingMode::Grayscale:
default:
dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
break;
}
THROW_IF_FAILED(dxEngine->Enable());
_renderEngine = std::move(dxEngine);
@@ -589,13 +654,62 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto bottom = _terminal->GetViewport().BottomExclusive();
auto bufferHeight = bottom;
ScrollBar().Maximum(bufferHeight - bufferHeight);
ScrollBar().Minimum(0);
ScrollBar().Value(0);
ScrollBar().ViewportSize(bufferHeight);
const auto originalMaximum = _scrollBar.Maximum();
const auto originalMinimum = _scrollBar.Minimum();
const auto originalValue = _scrollBar.Value();
const auto originalViewportSize = _scrollBar.ViewportSize();
_scrollBar.Maximum(bufferHeight - bufferHeight);
_scrollBar.Minimum(0);
_scrollBar.Value(0);
_scrollBar.ViewportSize(bufferHeight);
_scrollBar.ValueChanged({ this, &TermControl::_ScrollbarChangeHandler });
_scrollBar.PointerPressed({ this, &TermControl::_CapturePointer });
_scrollBar.PointerReleased({ this, &TermControl::_ReleasePointerCapture });
// Apply settings for scrollbar
if (_settings.ScrollState() == ScrollbarState::Visible)
{
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
}
else if (_settings.ScrollState() == ScrollbarState::Hidden)
{
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
// In the scenario where the user has turned off the OS setting to automatically hide scollbars, the
// Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
// achieve the intended effect.
_scrollBar.Visibility(Visibility::Collapsed);
}
else
{
// Default behavior
_scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
}
_root.PointerWheelChanged({ this, &TermControl::_MouseWheelHandler });
// These need to be hooked up to the SwapChainPanel because we don't want the scrollbar to respond to pointer events (GitHub #950)
_swapChainPanel.PointerPressed({ this, &TermControl::_PointerPressedHandler });
_swapChainPanel.PointerMoved({ this, &TermControl::_PointerMovedHandler });
_swapChainPanel.PointerReleased({ this, &TermControl::_PointerReleasedHandler });
localPointerToThread->EnablePainting();
// No matter what order these guys are in, The KeyDown's will fire
// before the CharacterReceived, so we can't easily get characters
// first, then fallback to getting keys from vkeys.
// TODO: This apparently handles keys and characters correctly, though
// I'd keep an eye on it, and test more.
// I presume that the characters that aren't translated by terminalInput
// just end up getting ignored, and the rest of the input comes
// through CharacterReceived.
// 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 });
auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
_terminal->SetTitleChangedCallback(pfnTitleChanged);
@@ -628,7 +742,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// import value from WinUser (convert from milli-seconds to micro-seconds)
_multiClickTimer = GetDoubleClickTime() * 1000;
// Focus the control here. If we do it during control initialization, then
_gotFocusRevoker = this->GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler });
_lostFocusRevoker = this->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.
@@ -733,7 +850,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (_terminal->IsSelectionActive())
{
_terminal->ClearSelection();
_renderer->TriggerSelection();
if (vkey == VK_ESCAPE)
{
@@ -757,17 +873,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return handled;
}
// Method Description:
// - handle a tap event by taking focus
// Arguments:
// - sender: the XAML element responding to the tap event
// - args: event data
void TermControl::_TappedHandler(const IInspectable& /*sender*/, const TappedRoutedEventArgs& e)
{
Focus(FocusState::Pointer);
e.Handled(true);
}
// Method Description:
// - handle a mouse click event. Begin selection process.
// Arguments:
@@ -779,7 +884,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_CapturePointer(sender, args);
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
const auto point = args.GetCurrentPoint(_root);
if (!_focused)
{
@@ -790,7 +895,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// only want to start the selection when the user moves the pointer with
// the left mouse button held down, the PointerMovedHandler will use
// this saved position to set the SelectionAnchor.
_unfocusedClickPos = point.Position();
_clickDragStartPos = point.Position();
}
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
@@ -803,11 +908,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (point.Properties().IsLeftButtonPressed())
{
// _unfocusedClickPos having a value signifies to us that
// _clickDragStartPos having a value signifies to us that
// the user clicked on an unfocused terminal. We don't want
// a single left click from out of focus to start a selection,
// so we return fast here.
if (_unfocusedClickPos)
if (_clickDragStartPos)
{
args.Handled(true);
return;
@@ -817,7 +922,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto terminalPosition = _GetTerminalPosition(cursorPosition);
// handle ALT key
_terminal->SetBlockSelection(altEnabled);
_terminal->SetBoxSelection(altEnabled);
auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp());
@@ -828,17 +933,17 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (multiClickMapper == 3)
{
_terminal->MultiClickSelection(terminalPosition, ::Terminal::SelectionExpansionMode::Line);
_terminal->TripleClickSelection(terminalPosition);
}
else if (multiClickMapper == 2)
{
_terminal->MultiClickSelection(terminalPosition, ::Terminal::SelectionExpansionMode::Word);
_terminal->DoubleClickSelection(terminalPosition);
}
else
{
if (shiftEnabled && _terminal->IsSelectionActive())
{
_terminal->SetSelectionEnd(terminalPosition, ::Terminal::SelectionExpansionMode::Cell);
_terminal->SetEndSelectionPosition(terminalPosition);
}
else
{
@@ -883,28 +988,26 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Input::PointerRoutedEventArgs const& args)
{
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
const auto point = args.GetCurrentPoint(_root);
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
{
if (point.Properties().IsLeftButtonPressed())
{
_isClickDragSelection = true;
// If this does not have a value, it means that PointerPressedHandler already
// set the SelectionAnchor. If it does have a value, that means the user is
// performing a click-drag selection on an unfocused terminal, so
// a SelectionAnchor isn't set yet. We'll have to set it here.
if (_unfocusedClickPos)
if (_clickDragStartPos)
{
_terminal->SetSelectionAnchor(_GetTerminalPosition(*_unfocusedClickPos));
_terminal->SetSelectionAnchor(_GetTerminalPosition(*_clickDragStartPos));
}
const auto cursorPosition = point.Position();
_SetEndSelectionPointAtCursor(cursorPosition);
const double cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
const double cursorAboveTopDist = -1 * cursorPosition.Y + SwapChainPanel().Margin().Top;
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;
@@ -938,8 +1041,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const float dy = newTouchPoint.Y - anchor.Y;
// Start viewport scroll after we've moved more than a half row of text
if (std::abs(dy) > (fontHeight / 2.0f))
// If we've moved more than one row of text, we'll want to scroll the viewport
if (std::abs(dy) > fontHeight)
{
// Multiply by -1, because moving the touch point down will
// create a positive delta, but we want the viewport to move up,
@@ -947,10 +1050,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// panning down)
const float numRows = -1.0f * (dy / fontHeight);
const auto currentOffset = ::base::ClampedNumeric<double>(ScrollBar().Value());
const auto newValue = numRows + currentOffset;
const auto currentOffset = this->GetScrollOffset();
const double newValue = (numRows) + (currentOffset);
ScrollBar().Value(newValue);
_scrollBar.Value(static_cast<int>(newValue));
// Use this point as our new scroll anchor.
_touchAnchor = newTouchPoint;
@@ -972,6 +1075,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const auto ptr = args.Pointer();
_clickDragStartPos = std::nullopt;
if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
{
const auto modifiers = static_cast<uint32_t>(args.KeyModifiers());
@@ -981,12 +1086,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (_terminal->IsCopyOnSelectActive())
{
// If the terminal was in focus, copy to clipboard.
// If the terminal was unfocused AND a click-drag selection happened, copy to clipboard.
if (!_unfocusedClickPos || (_unfocusedClickPos && _isClickDragSelection))
{
CopySelectionToClipboard(!shiftEnabled);
}
CopySelectionToClipboard(!shiftEnabled);
}
}
else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
@@ -994,9 +1094,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_touchAnchor = std::nullopt;
}
_unfocusedClickPos = std::nullopt;
_isClickDragSelection = false;
_TryStopAutoScroll(ptr.PointerId());
args.Handled(true);
@@ -1011,7 +1108,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
Input::PointerRoutedEventArgs const& args)
{
const auto point = args.GetCurrentPoint(*this);
const auto point = args.GetCurrentPoint(_root);
const auto delta = point.Properties().MouseWheelDelta();
// Get the state of the Ctrl & Shift keys
@@ -1049,7 +1146,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
try
{
auto acrylicBrush = RootGrid().Background().as<Media::AcrylicBrush>();
auto acrylicBrush = _root.Background().as<Media::AcrylicBrush>();
acrylicBrush.TintOpacity(acrylicBrush.TintOpacity() + effectiveDelta);
}
CATCH_LOG();
@@ -1092,11 +1189,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - mouseDelta: the mouse wheel delta that triggered this event.
void TermControl::_MouseScrollHandler(const double mouseDelta, Windows::UI::Input::PointerPoint const& pointerPoint)
{
const auto currentOffset = ScrollBar().Value();
const auto currentOffset = this->GetScrollOffset();
// negative = down, positive = up
// However, for us, the signs are flipped.
const auto rowDelta = mouseDelta / (-1.0 * WHEEL_DELTA);
const auto rowDelta = mouseDelta < 0 ? 1.0 : -1.0;
// With one of the precision mouses, one click is always a multiple of 120,
// but the "smooth scrolling" mode results in non-int values
@@ -1105,7 +1202,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// The scroll bar's ValueChanged handler will actually move the viewport
// for us.
ScrollBar().Value(newValue);
_scrollBar.Value(static_cast<int>(newValue));
if (_terminal->IsSelectionActive() && pointerPoint.Properties().IsLeftButtonPressed())
{
@@ -1236,7 +1333,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
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);
_scrollBar.Value(_scrollBar.Value() + _autoScrollVelocity * deltaTime);
if (_autoScrollingPointerPoint.has_value())
{
@@ -1260,25 +1357,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
return;
}
_focused = true;
// If the searchbox is focused, we don't want TSFInputControl to think
// it has focus so it doesn't intercept IME input. We also don't want the
// terminal's cursor to start blinking. So, we'll just return quickly here.
if (_searchBox && _searchBox->ContainsFocus())
{
return;
}
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Enable());
}
if (TSFInputControl() != nullptr)
if (_tsfInputControl != nullptr)
{
TSFInputControl().NotifyFocusEnter();
_tsfInputControl.NotifyFocusEnter();
}
if (_cursorTimer.has_value())
@@ -1308,9 +1396,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
THROW_IF_FAILED(_uiaEngine->Disable());
}
if (TSFInputControl() != nullptr)
if (_tsfInputControl != nullptr)
{
TSFInputControl().NotifyFocusLeave();
_tsfInputControl.NotifyFocusLeave();
}
if (_cursorTimer.has_value())
@@ -1374,7 +1462,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
auto lock = _terminal->LockForWriting();
const int newDpi = static_cast<int>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * SwapChainPanel().CompositionScaleX());
const int newDpi = static_cast<int>(static_cast<double>(USER_DEFAULT_SCREEN_DPI) * _swapChainPanel.CompositionScaleX());
// TODO: MSFT:20895307 If the font doesn't exist, this doesn't
// actually fail. We need a way to gracefully fallback.
@@ -1393,7 +1481,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
try
{
// Make sure we have a non-zero font size
const auto newSize = std::max<short>(gsl::narrow_cast<short>(fontSize), 1);
const auto newSize = std::max(gsl::narrow<short>(fontSize), static_cast<short>(1));
const auto fontFace = _settings.FontFace();
_actualFont = { fontFace, 0, 10, { 0, newSize }, CP_UTF8, false };
_desiredFont = { _actualFont };
@@ -1405,7 +1493,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// problems (like what happens when you change the font size while the
// window is maximized?)
auto lock = _terminal->LockForWriting();
_DoResize(SwapChainPanel().ActualWidth(), SwapChainPanel().ActualHeight());
_DoResize(_swapChainPanel.ActualWidth(), _swapChainPanel.ActualHeight());
}
CATCH_LOG();
}
@@ -1474,7 +1562,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
terminalPosition.X = std::clamp<short>(terminalPosition.X, 0, lastVisibleCol);
// save location (for rendering) + render
_terminal->SetSelectionEnd(terminalPosition);
_terminal->SetEndSelectionPosition(terminalPosition);
_renderer->TriggerSelection();
}
@@ -1582,7 +1670,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
co_await winrt::resume_foreground(_scrollBar.Dispatcher());
// Even if we weren't closed/closing few lines above, we might be
// while waiting for this block of code to be dispatched.
@@ -1592,7 +1680,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (!_closing.load())
{
// Update our scrollbar
_ScrollbarUpdater(ScrollBar(), viewTop, viewHeight, bufferSize);
_ScrollbarUpdater(_scrollBar, viewTop, viewHeight, bufferSize);
}
}
}
@@ -1606,11 +1694,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return hstr;
}
hstring TermControl::GetProfileName() const
{
return _settings.ProfileName();
}
// Method Description:
// - Given a copy-able selection, get the selected text from the buffer and send it to the
// Windows Clipboard (CascadiaWin32:main.cpp).
@@ -1651,7 +1734,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
if (!_terminal->IsCopyOnSelectActive())
{
_terminal->ClearSelection();
_renderer->TriggerSelection();
}
// send data up for clipboard
@@ -1683,7 +1765,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_connection.TerminalOutput(_connectionOutputEventToken);
_connectionStateChangedRevoker.revoke();
TSFInputControl().Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
_tsfInputControl.Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
if (auto localConnection{ std::exchange(_connection, nullptr) })
{
@@ -1715,7 +1797,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - viewTop: the viewTop to scroll to
void TermControl::ScrollViewport(int viewTop)
{
ScrollBar().Value(viewTop);
_scrollBar.Value(viewTop);
}
int TermControl::GetScrollOffset()
@@ -1750,7 +1832,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
// Initialize our font information.
const auto fontFace = settings.FontFace();
const short fontHeight = gsl::narrow_cast<short>(settings.FontSize());
const short fontHeight = gsl::narrow<short>(settings.FontSize());
// The font width doesn't terribly matter, we'll only be using the
// height to look it up
// The other params here also largely don't matter.
@@ -1784,8 +1866,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// the Terminal). At runtime, this is fine, as we'll transform
// everything by our scaling, so it'll work out. However, right now we
// need to get the exact pixel count.
const float fFontWidth = gsl::narrow_cast<float>(fontSize.X * scale);
const float fFontHeight = gsl::narrow_cast<float>(fontSize.Y * scale);
const float fFontWidth = gsl::narrow<float>(fontSize.X * scale);
const float fFontHeight = gsl::narrow<float>(fontSize.Y * scale);
// UWP XAML scrollbars aren't guaranteed to be the same size as the
// ComCtl scrollbars, but it's certainly close enough.
@@ -1830,7 +1912,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Return Value:
// - The minimum size that this terminal control can be resized to and still
// have a visible character.
winrt::Windows::Foundation::Size TermControl::MinimumSize()
winrt::Windows::Foundation::Size TermControl::MinimumSize() const
{
const auto fontSize = _actualFont.GetSize();
double width = fontSize.X;
@@ -1838,11 +1920,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// Reserve additional space if scrollbar is intended to be visible
if (_settings.ScrollState() == ScrollbarState::Visible)
{
width += ScrollBar().ActualWidth();
width += _scrollBar.ActualWidth();
}
// Account for the size of any padding
const auto padding = SwapChainPanel().Margin();
const auto padding = _swapChainPanel.Margin();
width += padding.Left + padding.Right;
height += padding.Top + padding.Bottom;
@@ -1857,19 +1939,19 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - dimension: a dimension (width or height) to be snapped
// Return Value:
// - A dimension that would be aligned to the character grid.
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension)
float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const
{
const auto fontSize = _actualFont.GetSize();
const auto fontDimension = widthOrHeight ? fontSize.X : fontSize.Y;
const auto padding = SwapChainPanel().Margin();
const auto padding = _swapChainPanel.Margin();
auto nonTerminalArea = gsl::narrow_cast<float>(widthOrHeight ?
padding.Left + padding.Right :
padding.Top + padding.Bottom);
if (widthOrHeight && _settings.ScrollState() == ScrollbarState::Visible)
{
nonTerminalArea += gsl::narrow_cast<float>(ScrollBar().ActualWidth());
nonTerminalArea += gsl::narrow_cast<float>(_scrollBar.ActualWidth());
}
const auto gridSize = dimension - nonTerminalArea;
@@ -1994,8 +2076,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
// Exclude padding from cursor position calculation
COORD terminalPosition = {
static_cast<SHORT>(cursorPosition.X - SwapChainPanel().Margin().Left),
static_cast<SHORT>(cursorPosition.Y - SwapChainPanel().Margin().Top)
static_cast<SHORT>(cursorPosition.X - _swapChainPanel.Margin().Left),
static_cast<SHORT>(cursorPosition.Y - _swapChainPanel.Margin().Top)
};
const auto fontSize = _actualFont.GetSize();
@@ -2031,7 +2113,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void TermControl::_CurrentCursorPositionHandler(const IInspectable& /*sender*/, const CursorPositionEventArgs& eventArgs)
{
const COORD cursorPos = _terminal->GetCursorPosition();
Windows::Foundation::Point p = { gsl::narrow_cast<float>(cursorPos.X), gsl::narrow_cast<float>(cursorPos.Y) };
Windows::Foundation::Point p = { gsl::narrow<float>(cursorPos.X), gsl::narrow<float>(cursorPos.Y) };
eventArgs.CurrentPosition(p);
}

View File

@@ -61,14 +61,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
winrt::fire_and_forget UpdateSettings(Settings::IControlSettings newSettings);
hstring Title();
hstring GetProfileName() const;
bool CopySelectionToClipboard(bool trimTrailingWhitespace);
void PasteTextFromClipboard();
void Close();
Windows::Foundation::Size CharacterDimensions() const;
Windows::Foundation::Size MinimumSize();
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
Windows::Foundation::Size MinimumSize() const;
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const;
void ScrollViewport(int viewTop);
int GetScrollOffset();
@@ -86,7 +85,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
::Microsoft::Console::Types::IUiaData* GetUiaData() const;
const FontInfo GetActualFont() const;
const Windows::UI::Xaml::Thickness GetPadding();
const Windows::UI::Xaml::Thickness GetPadding() const;
TerminalConnection::ConnectionState ConnectionState() const;
@@ -106,13 +105,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// clang-format on
private:
friend struct TermControlT<TermControl>; // friend our parent so it can bind private event handlers
TerminalConnection::ITerminalConnection _connection;
bool _initializedTerminal;
Windows::UI::Xaml::Controls::Grid _root;
Windows::UI::Xaml::Controls::Image _bgImageLayer;
Windows::UI::Xaml::Controls::SwapChainPanel _swapChainPanel;
Windows::UI::Xaml::Controls::Primitives::ScrollBar _scrollBar;
winrt::com_ptr<SearchBoxControl> _searchBox;
TSFInputControl _tsfInputControl;
event_token _connectionOutputEventToken;
TermControl::Tapped_revoker _tappedRevoker;
TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker;
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
@@ -156,18 +162,23 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Timestamp _lastMouseClick;
unsigned int _multiClickCounter;
std::optional<winrt::Windows::Foundation::Point> _lastMouseClickPos;
std::optional<winrt::Windows::Foundation::Point> _unfocusedClickPos;
bool _isClickDragSelection;
std::optional<winrt::Windows::Foundation::Point> _clickDragStartPos{ std::nullopt };
// Event revokers -- we need to deregister ourselves before we die,
// lest we get callbacks afterwards.
winrt::Windows::UI::Xaml::Controls::Control::SizeChanged_revoker _sizeChangedRevoker;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::CompositionScaleChanged_revoker _compositionScaleChangedRevoker;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker;
winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
void _Create();
void _ApplyUISettings();
void _InitializeBackgroundBrush();
winrt::fire_and_forget _BackgroundColorChanged(const uint32_t color);
bool _InitializeTerminal();
void _UpdateFont(const bool initialUpdate = false);
void _SetFontSize(int fontSize);
void _TappedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs const& e);
void _KeyDownHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
void _CharacterHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs const& e);
void _PointerPressedHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);

View File

@@ -1,81 +0,0 @@
<UserControl
x:Class="Microsoft.Terminal.TerminalControl.TermControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.Terminal.TerminalControl"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DesignHeight="768"
d:DesignWidth="1024"
TabNavigation="Cycle"
IsTabStop="True"
AllowFocusOnInteraction="True"
AllowDrop="True"
Drop="_DragDropHandler"
DragOver="_DragOverHandler"
Tapped="_TappedHandler"
PointerWheelChanged="_MouseWheelHandler"
PreviewKeyDown="_KeyDownHandler"
CharacterReceived="_CharacterHandler"
GotFocus="_GotFocusHandler"
LostFocus="_LostFocusHandler">
<!--
TODO GH#4031: We've investigated whether we should be using KeyDown or PreviewKeyDown
but not necessarily come to a consensus. It's possible that moving input to the SwapChainPanel
will solve a bunch of the search input issues we found last time we switched.
-->
<Grid x:Name="RootGrid">
<Image x:Name="BackgroundImage" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<SwapChainPanel x:Name="SwapChainPanel"
SizeChanged="_SwapChainSizeChanged"
CompositionScaleChanged="_SwapChainScaleChanged"
PointerPressed="_PointerPressedHandler"
PointerMoved="_PointerMovedHandler"
PointerReleased="_PointerReleasedHandler" />
<!-- Putting this in a grid w/ the SwapChainPanel
ensures that it's always aligned w/ the scrollbar -->
<local:SearchBoxControl x:Name="SearchBox"
x:Load="False"
Visibility="Collapsed"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Search="_Search"
Closed="_CloseSearchBoxControl" />
</Grid>
<ScrollBar Grid.Column="1"
x:Name="ScrollBar"
Orientation="Vertical"
IndicatorMode="MouseIndicator"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Maximum="1"
ViewportSize="10"
IsTabStop="False"
SmallChange="1"
LargeChange="4"
ValueChanged="_ScrollbarChangeHandler"
PointerPressed="_CapturePointer"
PointerReleased="_ReleasePointerCapture" />
</Grid>
<local:TSFInputControl x:Name="TSFInputControl"
CompositionCompleted="_CompositionCompleted"
CurrentCursorPosition="_CurrentCursorPositionHandler"
CurrentFontInfo="_FontInfoHandler" />
</Grid>
</UserControl>

View File

@@ -3,7 +3,6 @@
#include "pch.h"
#include <UIAutomationCore.h>
#include <LibraryResources.h>
#include "TermControlAutomationPeer.h"
#include "TermControl.h"
#include "TermControlAutomationPeer.g.cpp"
@@ -12,7 +11,6 @@
using namespace Microsoft::Console::Types;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::Graphics::Display;
namespace UIA
{
@@ -30,10 +28,9 @@ namespace XamlAutomation
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner) :
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner), // pass owner to FrameworkElementAutomationPeer
_termControl{ owner }
TermControlAutomationPeerT<TermControlAutomationPeer>(*owner) // pass owner to FrameworkElementAutomationPeer
{
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, _termControl->GetUiaData(), this));
THROW_IF_FAILED(::Microsoft::WRL::MakeAndInitialize<::Microsoft::Terminal::TermControlUiaProvider>(&_uiaProvider, owner, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this)));
};
// Method Description:
@@ -78,7 +75,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
});
}
hstring TermControlAutomationPeer::GetClassNameCore() const
winrt::hstring TermControlAutomationPeer::GetClassNameCore() const
{
return L"TermControl";
}
@@ -88,12 +85,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return AutomationControlType::Text;
}
hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
winrt::hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
{
return RS_(L"TerminalControl_ControlType");
// TODO GitHub #2142: Localize string
return L"TerminalControl";
}
Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
winrt::Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
{
switch (patternInterface)
{
@@ -105,41 +103,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
AutomationOrientation TermControlAutomationPeer::GetOrientationCore() const
{
return AutomationOrientation::Vertical;
}
hstring TermControlAutomationPeer::GetNameCore() const
{
// fallback to title if profile name is empty
auto profileName = _termControl->GetProfileName();
if (profileName.empty())
{
return _termControl->Title();
}
return profileName;
}
hstring TermControlAutomationPeer::GetHelpTextCore() const
{
return _termControl->Title();
}
AutomationLiveSetting TermControlAutomationPeer::GetLiveSettingCore() const
{
return AutomationLiveSetting::Polite;
}
#pragma region ITextProvider
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetSelection()
{
SAFEARRAY* pReturnVal;
THROW_IF_FAILED(_uiaProvider->GetSelection(&pReturnVal));
return WrapArrayOfTextRangeProviders(pReturnVal);
}
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::GetVisibleRanges()
{
SAFEARRAY* pReturnVal;
THROW_IF_FAILED(_uiaProvider->GetVisibleRanges(&pReturnVal));
@@ -178,7 +150,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return xutr.as<XamlAutomation::ITextRangeProvider>();
}
XamlAutomation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()
Windows::UI::Xaml::Automation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection()
{
UIA::SupportedTextSelection returnVal;
THROW_IF_FAILED(_uiaProvider->get_SupportedTextSelection(&returnVal));
@@ -187,63 +159,27 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
#pragma endregion
#pragma region IControlAccessibilityInfo
COORD TermControlAutomationPeer::GetFontSize() const
{
return _termControl->GetActualFont().GetSize();
}
RECT TermControlAutomationPeer::GetBounds() const
RECT TermControlAutomationPeer::GetBoundingRectWrapped()
{
auto rect = GetBoundingRectangle();
return {
gsl::narrow_cast<LONG>(rect.X),
gsl::narrow_cast<LONG>(rect.Y),
gsl::narrow_cast<LONG>(rect.X + rect.Width),
gsl::narrow_cast<LONG>(rect.Y + rect.Height)
gsl::narrow<LONG>(rect.X),
gsl::narrow<LONG>(rect.Y),
gsl::narrow<LONG>(rect.X + rect.Width),
gsl::narrow<LONG>(rect.Y + rect.Height)
};
}
HRESULT TermControlAutomationPeer::GetHostUiaProvider(IRawElementProviderSimple** provider)
{
RETURN_HR_IF(E_INVALIDARG, provider == nullptr);
*provider = nullptr;
return S_OK;
}
RECT TermControlAutomationPeer::GetPadding() const
{
auto padding = _termControl->GetPadding();
return {
gsl::narrow_cast<LONG>(padding.Left),
gsl::narrow_cast<LONG>(padding.Top),
gsl::narrow_cast<LONG>(padding.Right),
gsl::narrow_cast<LONG>(padding.Bottom)
};
}
double TermControlAutomationPeer::GetScaleFactor() const
{
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
}
void TermControlAutomationPeer::ChangeViewport(const SMALL_RECT NewWindow)
{
_termControl->ScrollViewport(NewWindow.Top);
}
#pragma endregion
// 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)
com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
winrt::com_array<XamlAutomation::ITextRangeProvider> TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges)
{
// transfer ownership of UiaTextRanges to this new vector
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::TermControlUiaTextRange>(textRanges);
auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges);
int count = gsl::narrow<int>(providers.size());
std::vector<XamlAutomation::ITextRangeProvider> vec;
@@ -251,11 +187,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto parentProvider = this->ProviderFromPeer(*this);
for (int i = 0; i < count; i++)
{
auto xutr = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
auto xutr = winrt::make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
vec.emplace_back(xutr.as<XamlAutomation::ITextRangeProvider>());
}
com_array<XamlAutomation::ITextRangeProvider> result{ vec };
winrt::com_array<XamlAutomation::ITextRangeProvider> result{ vec };
return result;
}

View File

@@ -27,30 +27,22 @@ Author(s):
#include "TermControl.h"
#include "TermControlAutomationPeer.g.h"
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include "../types/TermControlUiaProvider.hpp"
#include "TermControlUiaProvider.hpp"
#include "../types/IUiaEventDispatcher.h"
#include "../types/IControlAccessibilityInfo.h"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
struct TermControlAutomationPeer :
public TermControlAutomationPeerT<TermControlAutomationPeer>,
::Microsoft::Console::Types::IUiaEventDispatcher,
::Microsoft::Console::Types::IControlAccessibilityInfo
::Microsoft::Console::Types::IUiaEventDispatcher
{
public:
TermControlAutomationPeer(Microsoft::Terminal::TerminalControl::implementation::TermControl* owner);
TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* owner);
#pragma region FrameworkElementAutomationPeer
hstring GetClassNameCore() const;
Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;
hstring GetLocalizedControlTypeCore() const;
Windows::Foundation::IInspectable GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) const;
Windows::UI::Xaml::Automation::Peers::AutomationOrientation GetOrientationCore() const;
hstring GetNameCore() const;
hstring GetHelpTextCore() const;
Windows::UI::Xaml::Automation::Peers::AutomationLiveSetting GetLiveSettingCore() const;
#pragma endregion
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 IUiaEventDispatcher
void SignalSelectionChanged() override;
@@ -61,27 +53,17 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
#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);
com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetVisibleRanges();
com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> GetSelection();
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
#pragma region IControlAccessibilityInfo Pattern
// Inherited via IControlAccessibilityInfo
virtual COORD GetFontSize() const override;
virtual RECT GetBounds() const override;
virtual RECT GetPadding() const override;
virtual double GetScaleFactor() const override;
virtual void ChangeViewport(SMALL_RECT NewWindow) override;
virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) override;
#pragma endregion
RECT GetBoundingRectWrapped();
private:
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* _termControl;
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
};
}

View File

@@ -1,21 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "pch.h"
#include "TermControlUiaProvider.hpp"
#include "TermControl.h"
using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL;
#pragma warning(suppress : 26434) // WRL RuntimeClassInitialize base is a no-op and we need this for MakeAndInitialize
HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ ::Microsoft::Console::Types::IUiaData* const uiaData,
_In_ ::Microsoft::Console::Types::IControlAccessibilityInfo* controlInfo) noexcept
HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* termControl,
_In_ std::function<RECT(void)> GetBoundingRect)
{
RETURN_HR_IF_NULL(E_INVALIDARG, uiaData);
RETURN_IF_FAILED(ScreenInfoUiaProviderBase::RuntimeClassInitialize(uiaData));
RETURN_HR_IF_NULL(E_INVALIDARG, termControl);
RETURN_IF_FAILED(ScreenInfoUiaProviderBase::RuntimeClassInitialize(termControl->GetUiaData()));
_controlInfo = controlInfo;
_getBoundingRect = GetBoundingRect;
_termControl = termControl;
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr);
@@ -23,10 +24,8 @@ HRESULT TermControlUiaProvider::RuntimeClassInitialize(_In_ ::Microsoft::Console
}
IFACEMETHODIMP TermControlUiaProvider::Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
/*ApiMsgNavigate apiMsg;
apiMsg.Direction = direction;
@@ -57,29 +56,18 @@ IFACEMETHODIMP TermControlUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRec
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr);
const RECT rc = _controlInfo->GetBounds();
RECT rc = _getBoundingRect();
pRect->left = rc.left;
pRect->top = rc.top;
pRect->width = static_cast<double>(rc.right) - static_cast<double>(rc.left);
pRect->height = static_cast<double>(rc.bottom) - static_cast<double>(rc.top);
pRect->width = rc.right - rc.left;
pRect->height = rc.bottom - rc.top;
return S_OK;
}
IFACEMETHODIMP TermControlUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) noexcept
IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider)
{
try
{
return _controlInfo->GetHostUiaProvider(ppProvider);
}
CATCH_RETURN();
}
IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider);
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr);
try
@@ -99,48 +87,47 @@ IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybe
const COORD TermControlUiaProvider::GetFontSize() const
{
return _controlInfo->GetFontSize();
return _termControl->GetActualFont().GetSize();
}
const RECT TermControlUiaProvider::GetPadding() const
const winrt::Windows::UI::Xaml::Thickness TermControlUiaProvider::GetPadding() const
{
return _controlInfo->GetPadding();
}
const double TermControlUiaProvider::GetScaleFactor() const
{
return _controlInfo->GetScaleFactor();
return _termControl->GetPadding();
}
void TermControlUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
{
_controlInfo->ChangeViewport(NewWindow);
_termControl->ScrollViewport(NewWindow.Top);
}
HRESULT TermControlUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
HRESULT TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<ComPtr<UiaTextRangeBase>>& result)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
try
{
result.clear();
typename std::remove_reference<decltype(result)>::type temporaryResult;
const auto start = _pData->GetSelectionAnchor();
std::deque<ComPtr<UiaTextRange>> ranges;
RETURN_IF_FAILED(UiaTextRange::GetSelectionRanges(_pData, pProvider, wordDelimiters, ranges));
// we need to make end exclusive
auto end = _pData->GetSelectionEnd();
_pData->GetTextBuffer().GetSize().IncrementInBounds(end, true);
while (!ranges.empty())
{
temporaryResult.emplace_back(std::move(ranges.back()));
ranges.pop_back();
}
// TODO GH #4509: Box Selection is misrepresented here as a line selection.
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
*ppUtr = result;
return S_OK;
std::swap(result, temporaryResult);
return S_OK;
}
CATCH_RETURN();
}
HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, wordDelimiters));
*ppUtr = result;
return S_OK;
}
@@ -152,8 +139,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, cursor, wordDelimiters));
*ppUtr = result;
return S_OK;
}
@@ -166,8 +153,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
*ppUtr = result;
return S_OK;
}
@@ -179,8 +166,8 @@ HRESULT TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple*
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
TermControlUiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<TermControlUiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
UiaTextRange* result = nullptr;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, point, wordDelimiters));
*ppUtr = result;
return S_OK;
}

View File

@@ -19,33 +19,36 @@ Author(s):
#pragma once
#include "ScreenInfoUiaProviderBase.h"
#include "UiaTextRangeBase.hpp"
#include "IControlAccessibilityInfo.h"
#include "TermControlUiaTextRange.hpp"
#include "..\types\ScreenInfoUiaProviderBase.h"
#include "..\types\UiaTextRangeBase.hpp"
#include "UiaTextRange.hpp"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
struct TermControl;
}
namespace Microsoft::Terminal
{
class TermControlUiaProvider : public Microsoft::Console::Types::ScreenInfoUiaProviderBase
{
public:
TermControlUiaProvider() = default;
HRESULT RuntimeClassInitialize(_In_ ::Microsoft::Console::Types::IUiaData* const uiaData,
_In_ ::Microsoft::Console::Types::IControlAccessibilityInfo* controlInfo) noexcept;
HRESULT RuntimeClassInitialize(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* termControl,
_In_ std::function<RECT()> GetBoundingRect);
// IRawElementProviderFragment methods
IFACEMETHODIMP Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept override;
IFACEMETHODIMP get_HostRawElementProvider(IRawElementProviderSimple** ppProvider) noexcept override;
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override;
IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override;
IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept override;
IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override;
const COORD GetFontSize() const;
const RECT GetPadding() const;
const double GetScaleFactor() const;
const winrt::Windows::UI::Xaml::Thickness GetPadding() const;
void ChangeViewport(const SMALL_RECT NewWindow) override;
protected:
HRESULT GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<WRL::ComPtr<Microsoft::Console::Types::UiaTextRangeBase>>& selectionRanges) override;
// degenerate range
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
@@ -70,6 +73,7 @@ namespace Microsoft::Terminal
_COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
private:
::Microsoft::Console::Types::IControlAccessibilityInfo* _controlInfo{ nullptr };
std::function<RECT(void)> _getBoundingRect;
winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl* _termControl;
};
}

View File

@@ -36,8 +36,9 @@
<ClInclude Include="SearchBoxControl.h">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="TermControlUiaProvider.hpp" />
<ClInclude Include="TermControl.h">
<DependentUpon>TermControl.xaml</DependentUpon>
<DependentUpon>TermControl.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TermControlAutomationPeer.h">
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
@@ -45,6 +46,7 @@
<ClInclude Include="TSFInputControl.h">
<DependentUpon>TSFInputControl.idl</DependentUpon>
</ClInclude>
<ClInclude Include="UiaTextRange.hpp" />
<ClInclude Include="XamlUiaTextRange.h" />
</ItemGroup>
<ItemGroup>
@@ -55,8 +57,9 @@
<ClCompile Include="SearchBoxControl.cpp">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TermControlUiaProvider.cpp" />
<ClCompile Include="TermControl.cpp">
<DependentUpon>TermControl.xaml</DependentUpon>
<DependentUpon>TermControl.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TSFInputControl.cpp">
<DependentUpon>TSFInputControl.idl</DependentUpon>
@@ -65,15 +68,14 @@
<ClCompile Include="TermControlAutomationPeer.cpp">
<DependentUpon>TermControlAutomationPeer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="UiaTextRange.cpp" />
<ClCompile Include="XamlUiaTextRange.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="SearchBoxControl.idl">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</Midl>
<Midl Include="TermControl.idl">
<DependentUpon>TermControl.xaml</DependentUpon>
</Midl>
<Midl Include="TermControl.idl" />
<Midl Include="TermControlAutomationPeer.idl" />
<Midl Include="TSFInputControl.idl" />
</ItemGroup>
@@ -82,7 +84,7 @@
<None Include="TerminalControl.def" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/Resources.language-en.resw" />
<PRIResource Include="Resources/en-US/Resources.resw" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
@@ -111,9 +113,6 @@
<Page Include="SearchBoxControl.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="TermControl.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />

View File

@@ -18,7 +18,6 @@
<ClCompile Include="TermControlUiaProvider.cpp" />
<ClCompile Include="UiaTextRange.cpp" />
<ClCompile Include="SearchBoxControl.cpp" />
<ClCompile Include="init.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -27,12 +26,12 @@
<ClInclude Include="XamlUiaTextRange.h" />
<ClInclude Include="TermControlUiaProvider.hpp" />
<ClInclude Include="UiaTextRange.hpp" />
<ClCompile Include="SearchBoxControl.h" />
</ItemGroup>
<ItemGroup>
<Midl Include="TermControl.idl" />
<Midl Include="TermControlAutomationPeer.idl" />
<Midl Include="SearchBoxControl.idl" />
<Midl Include="TSFInputControl.idl" />
</ItemGroup>
<ItemGroup>
<None Include="TerminalControl.def" />
@@ -44,9 +43,4 @@
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
</ItemGroup>
<ItemGroup>
<PRIResource Include="Resources/Resources.language-en.resw">
<Filter>Resources</Filter>
</PRIResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,188 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "UiaTextRange.hpp"
#include "TermControlUiaProvider.hpp"
using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL;
using namespace winrt::Windows::Graphics::Display;
HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* pProvider,
_In_ const std::wstring_view wordDelimiters,
_Out_ std::deque<ComPtr<UiaTextRange>>& ranges)
{
try
{
typename std::remove_reference<decltype(ranges)>::type temporaryResult;
// get the selection rects
const auto rectangles = pData->GetSelectionRects();
// create a range for each row
for (const auto& rect : rectangles)
{
const auto start = rect.Origin();
const auto end = rect.EndExclusive();
ComPtr<UiaTextRange> range;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&range, pData, pProvider, start, end, wordDelimiters));
temporaryResult.emplace_back(std::move(range));
}
std::swap(temporaryResult, ranges);
return S_OK;
}
CATCH_RETURN();
}
// degenerate range constructor.
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters);
}
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor,
const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters);
}
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const COORD start,
const COORD end,
const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, wordDelimiters);
}
// returns a degenerate text range of the start of the row closest to the y value of point
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const UiaPoint point,
const std::wstring_view wordDelimiters)
{
RETURN_IF_FAILED(UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters));
Initialize(point);
return S_OK;
}
HRESULT UiaTextRange::RuntimeClassInitialize(const UiaTextRange& a)
{
return UiaTextRangeBase::RuntimeClassInitialize(a);
}
IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal)
{
RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr);
*ppRetVal = nullptr;
auto hr = MakeAndInitialize<UiaTextRange>(ppRetVal, *this);
if (hr != S_OK)
{
*ppRetVal = nullptr;
return hr;
}
#if defined(_DEBUG) && defined(UiaTextRangeBase_DEBUG_MSGS)
OutputDebugString(L"Clone\n");
std::wstringstream ss;
ss << _id << L" cloned to " << (static_cast<UiaTextRangeBase*>(*ppRetVal))->_id;
std::wstring str = ss.str();
OutputDebugString(str.c_str());
OutputDebugString(L"\n");
#endif
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
// tracing
/*ApiMsgClone apiMsg;
apiMsg.CloneId = static_cast<UiaTextRangeBase*>(*ppRetVal)->GetId();
Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg);*/
return S_OK;
}
void UiaTextRange::_ChangeViewport(const SMALL_RECT NewWindow)
{
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
provider->ChangeViewport(NewWindow);
}
// Method Description:
// - Transform coordinates relative to the client to relative to the screen
// Arguments:
// - clientPoint: coordinates relative to the client where
// (0,0) is the top-left of the app window
// Return Value:
// - <none>
void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const
{
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
auto includeOffsets = [](long clientPos, double termControlPos, double padding, double scaleFactor) {
auto result = base::ClampedNumeric<double>(clientPos);
result += padding;
result *= scaleFactor;
result += termControlPos;
return result;
};
// update based on TermControl location (important for Panes)
UiaRect boundingRect;
THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect));
// update based on TermControl padding
const auto padding = provider->GetPadding();
// Get scale factor for display
const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
clientPoint->x = includeOffsets(clientPoint->x, boundingRect.left, padding.Left, scaleFactor);
clientPoint->y = includeOffsets(clientPoint->y, boundingRect.top, padding.Top, scaleFactor);
}
// Method Description:
// - Transform coordinates relative to the screen to relative to the client
// Arguments:
// - screenPoint: coordinates relative to the screen where
// (0,0) is the top-left of the screen
// Return Value:
// - <none>
void UiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) const
{
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
auto includeOffsets = [](long screenPos, double termControlPos, double padding, double scaleFactor) {
auto result = base::ClampedNumeric<double>(screenPos);
result -= termControlPos;
result /= scaleFactor;
result -= padding;
return result;
};
// update based on TermControl location (important for Panes)
UiaRect boundingRect;
THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect));
// update based on TermControl padding
const auto padding = provider->GetPadding();
// Get scale factor for display
const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
screenPoint->x = includeOffsets(screenPoint->x, boundingRect.left, padding.Left, scaleFactor);
screenPoint->y = includeOffsets(screenPoint->y, boundingRect.top, padding.Top, scaleFactor);
}
const COORD UiaTextRange::_getScreenFontSize() const
{
// Do NOT get the font info from IRenderData. It is a dummy font info.
// Instead, the font info is saved in the TermControl. So we have to
// ask our parent to get it for us.
auto provider = static_cast<TermControlUiaProvider*>(_pProvider);
return provider->GetFontSize();
}

View File

@@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- TermControlUiaTextRange.hpp
- UiaTextRange.hpp
Abstract:
- This module provides UI Automation access to the text of the console
@@ -20,28 +20,33 @@ Author(s):
namespace Microsoft::Terminal
{
class TermControlUiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase
class UiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase
{
public:
TermControlUiaTextRange() = default;
static HRESULT GetSelectionRanges(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* pProvider,
_In_ const std::wstring_view wordDelimiters,
_Out_ std::deque<WRL::ComPtr<UiaTextRange>>& ranges);
UiaTextRange() = default;
// degenerate range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// degenerate range at cursor position
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor,
const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// specific endpoint range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const COORD start,
const COORD end,
const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// range from a UiaPoint
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
@@ -49,7 +54,7 @@ namespace Microsoft::Terminal
const UiaPoint point,
const std::wstring_view wordDelimiters = DefaultWordDelimiter);
HRESULT RuntimeClassInitialize(const TermControlUiaTextRange& a) noexcept;
HRESULT RuntimeClassInitialize(const UiaTextRange& a);
IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override;

View File

@@ -3,7 +3,7 @@
#include "pch.h"
#include "XamlUiaTextRange.h"
#include "../types/TermControlUiaTextRange.hpp"
#include "UiaTextRange.hpp"
#include <UIAutomationClient.h>
// the same as COR_E_NOTSUPPORTED

View File

@@ -22,7 +22,7 @@ Author(s):
#include "TermControlAutomationPeer.h"
#include <UIAutomationCore.h>
#include "../types/TermControlUiaTextRange.hpp"
#include "UiaTextRange.hpp"
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{

View File

@@ -44,10 +44,12 @@ Terminal::Terminal() :
_pfnWriteInput{ nullptr },
_scrollOffset{ 0 },
_snapOnInput{ true },
_blockSelection{ false },
_selection{ std::nullopt },
_boxSelection{ false },
_selectionActive{ false },
_allowSingleCharSelection{ true },
_copyOnSelect{ false }
_copyOnSelect{ false },
_selectionAnchor{ 0, 0 },
_endSelectionPosition{ 0, 0 }
{
auto dispatch = std::make_unique<TerminalDispatch>(*this);
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
@@ -452,22 +454,6 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView)
// With well behaving shells during normal operation this safeguard should normally not be encountered.
proposedCursorPosition.X = 0;
proposedCursorPosition.Y++;
// Try the character again.
i--;
// Mark the line we're currently on as wrapped
// TODO: GH#780 - This should really be a _deferred_ newline. If
// the next character to come in is a newline or a cursor
// movement or anything, then we should _not_ wrap this line
// here.
//
// This is more WriteCharsLegacy2ElectricBoogaloo work. I'm
// leaving it like this for now - it'll break for lines that
// _exactly_ wrap, but we can't re-wrap lines now anyways, so it
// doesn't matter.
_buffer->GetRowByOffset(cursorPosBefore.Y).GetCharRow().SetWrapForced(true);
}
_AdjustCursorPosition(proposedCursorPosition);

View File

@@ -141,8 +141,7 @@ public:
const bool IsSelectionActive() const noexcept override;
void ClearSelection() override;
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
const COORD GetSelectionAnchor() const noexcept override;
const COORD GetSelectionEnd() const noexcept override;
const COORD GetSelectionAnchor() const override;
const std::wstring GetConsoleTitle() const noexcept override;
void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute) override;
#pragma endregion
@@ -157,17 +156,12 @@ public:
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
enum class SelectionExpansionMode
{
Cell,
Word,
Line
};
const bool IsCopyOnSelectActive() const noexcept;
void MultiClickSelection(const COORD viewportPos, SelectionExpansionMode expansionMode);
void DoubleClickSelection(const COORD position);
void TripleClickSelection(const COORD position);
void SetSelectionAnchor(const COORD position);
void SetSelectionEnd(const COORD position, std::optional<SelectionExpansionMode> newExpansionMode = std::nullopt);
void SetBlockSelection(const bool isEnabled) noexcept;
void SetEndSelectionPosition(const COORD position);
void SetBoxSelection(const bool isEnabled) noexcept;
const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const;
#pragma endregion
@@ -192,20 +186,19 @@ private:
bool _suppressApplicationTitle;
#pragma region Text Selection
// a selection is represented as a range between two COORDs (start and end)
// the pivot is the COORD that remains selected when you extend a selection in any direction
// this is particularly useful when a word selection is extended over its starting point
// see TerminalSelection.cpp for more information
struct SelectionAnchors
enum class SelectionExpansionMode
{
COORD start;
COORD end;
COORD pivot;
Cell,
Word,
Line
};
std::optional<SelectionAnchors> _selection;
bool _blockSelection;
COORD _selectionAnchor;
COORD _endSelectionPosition;
bool _boxSelection;
bool _selectionActive;
bool _allowSingleCharSelection;
bool _copyOnSelect;
SHORT _selectionVerticalOffset;
std::wstring _wordDelimiters;
SelectionExpansionMode _multiClickSelectionMode;
#pragma endregion
@@ -254,10 +247,15 @@ private:
#pragma region TextSelection
// These methods are defined in TerminalSelection.cpp
std::vector<SMALL_RECT> _GetSelectionRects() const noexcept;
std::pair<COORD, COORD> _PivotSelection(const COORD targetPos) const;
std::pair<COORD, COORD> _ExpandSelectionAnchors(std::pair<COORD, COORD> anchors) const;
SHORT _ExpandWideGlyphSelectionLeft(const SHORT xPos, const SHORT yPos) const;
SHORT _ExpandWideGlyphSelectionRight(const SHORT xPos, const SHORT yPos) const;
COORD _ExpandDoubleClickSelectionLeft(const COORD position) const;
COORD _ExpandDoubleClickSelectionRight(const COORD position) const;
COORD _ConvertToBufferCell(const COORD viewportPos) const;
const bool _IsSingleCellSelection() const noexcept;
std::tuple<COORD, COORD> _PreprocessSelectionCoords() const;
SMALL_RECT _GetSelectionRow(const SHORT row, const COORD higherCoord, const COORD lowerCoord) const;
void _ExpandSelectionRow(SMALL_RECT& selectionRow) const;
#pragma endregion
#ifdef UNIT_TESTING

View File

@@ -7,38 +7,6 @@
using namespace Microsoft::Terminal::Core;
/* Selection Pivot Description:
* The pivot helps properly update the selection when a user moves a selection over itself
* See SelectionTest::DoubleClickDrag_Left for an example of the functionality mentioned here
* As an example, consider the following scenario...
* 1. Perform a word selection (double-click) on a word
*
* |-position where we double-clicked
* _|_
* |word|
* |--|
* start & pivot-| |-end
*
* 2. Drag your mouse down a line
*
*
* start & pivot-|__________
* __|word_______|
* |______|
* |
* |-end & mouse position
*
* 3. Drag your mouse up two lines
*
* |-start & mouse position
* |________
* ____| ______|
* |___w|ord
* |-end & pivot
*
* The pivot never moves until a new selection is created. It ensures that that cell will always be selected.
*/
// Method Description:
// - Helper to determine the selected region of the buffer. Used for rendering.
// Return Value:
@@ -54,32 +22,183 @@ std::vector<SMALL_RECT> Terminal::_GetSelectionRects() const noexcept
try
{
return _buffer->GetTextRects(_selection->start, _selection->end, _blockSelection);
// NOTE: (0,0) is the top-left of the screen
// the physically "higher" coordinate is closer to the top-left
// the physically "lower" coordinate is closer to the bottom-right
const auto [higherCoord, lowerCoord] = _PreprocessSelectionCoords();
SHORT selectionRectSize;
THROW_IF_FAILED(ShortSub(lowerCoord.Y, higherCoord.Y, &selectionRectSize));
THROW_IF_FAILED(ShortAdd(selectionRectSize, 1, &selectionRectSize));
std::vector<SMALL_RECT> selectionArea;
selectionArea.reserve(selectionRectSize);
for (auto row = higherCoord.Y; row <= lowerCoord.Y; row++)
{
SMALL_RECT selectionRow = _GetSelectionRow(row, higherCoord, lowerCoord);
_ExpandSelectionRow(selectionRow);
selectionArea.emplace_back(selectionRow);
}
result.swap(selectionArea);
}
CATCH_LOG();
return result;
}
// Method Description:
// - convert selection anchors to proper coordinates for rendering
// NOTE: (0,0) is top-left so vertical comparison is inverted
// Arguments:
// - None
// Return Value:
// - tuple.first: the physically "higher" coordinate (closer to the top-left)
// - tuple.second: the physically "lower" coordinate (closer to the bottom-right)
std::tuple<COORD, COORD> Terminal::_PreprocessSelectionCoords() const
{
// create these new anchors for comparison and rendering
COORD selectionAnchorWithOffset{ _selectionAnchor };
COORD endSelectionPositionWithOffset{ _endSelectionPosition };
// Add anchor offset here to update properly on new buffer output
THROW_IF_FAILED(ShortAdd(selectionAnchorWithOffset.Y, _selectionVerticalOffset, &selectionAnchorWithOffset.Y));
THROW_IF_FAILED(ShortAdd(endSelectionPositionWithOffset.Y, _selectionVerticalOffset, &endSelectionPositionWithOffset.Y));
// clamp anchors to be within buffer bounds
const auto bufferSize = _buffer->GetSize();
bufferSize.Clamp(selectionAnchorWithOffset);
bufferSize.Clamp(endSelectionPositionWithOffset);
// NOTE: (0,0) is top-left so vertical comparison is inverted
// CompareInBounds returns whether A is to the left of (rv<0), equal to (rv==0), or to the right of (rv>0) B.
// Here, we want the "left"most coordinate to be the one "higher" on the screen. The other gets the dubious honor of
// being the "lower."
return bufferSize.CompareInBounds(selectionAnchorWithOffset, endSelectionPositionWithOffset) <= 0 ?
std::make_tuple(selectionAnchorWithOffset, endSelectionPositionWithOffset) :
std::make_tuple(endSelectionPositionWithOffset, selectionAnchorWithOffset);
}
// Method Description:
// - constructs the selection row at the given row
// NOTE: (0,0) is top-left so vertical comparison is inverted
// Arguments:
// - row: the buffer y-value under observation
// - higherCoord: the physically "higher" coordinate (closer to the top-left)
// - lowerCoord: the physically "lower" coordinate (closer to the bottom-right)
// Return Value:
// - the selection row needed for rendering
SMALL_RECT Terminal::_GetSelectionRow(const SHORT row, const COORD higherCoord, const COORD lowerCoord) const
{
SMALL_RECT selectionRow;
selectionRow.Top = row;
selectionRow.Bottom = row;
if (_boxSelection || higherCoord.Y == lowerCoord.Y)
{
selectionRow.Left = std::min(higherCoord.X, lowerCoord.X);
selectionRow.Right = std::max(higherCoord.X, lowerCoord.X);
}
else
{
selectionRow.Left = (row == higherCoord.Y) ? higherCoord.X : _buffer->GetSize().Left();
selectionRow.Right = (row == lowerCoord.Y) ? lowerCoord.X : _buffer->GetSize().RightInclusive();
}
return selectionRow;
}
// Method Description:
// - Get the current anchor position relative to the whole text buffer
// Arguments:
// - None
// Return Value:
// - None
const COORD Terminal::GetSelectionAnchor() const noexcept
const COORD Terminal::GetSelectionAnchor() const
{
return _selection->start;
COORD selectionAnchorPos{ _selectionAnchor };
THROW_IF_FAILED(ShortAdd(selectionAnchorPos.Y, _selectionVerticalOffset, &selectionAnchorPos.Y));
return selectionAnchorPos;
}
// Method Description:
// - Get the current end anchor position relative to the whole text buffer
// - Expand the selection row according to selection mode and wide glyphs
// - this is particularly useful for box selections (ALT + selection)
// Arguments:
// - None
// - selectionRow: the selection row to be expanded
// Return Value:
// - None
const COORD Terminal::GetSelectionEnd() const noexcept
// - modifies selectionRow's Left and Right values to expand properly
void Terminal::_ExpandSelectionRow(SMALL_RECT& selectionRow) const
{
return _selection->end;
const auto row = selectionRow.Top;
// expand selection for Double/Triple Click
if (_multiClickSelectionMode == SelectionExpansionMode::Word)
{
selectionRow.Left = _ExpandDoubleClickSelectionLeft({ selectionRow.Left, row }).X;
selectionRow.Right = _ExpandDoubleClickSelectionRight({ selectionRow.Right, row }).X;
}
else if (_multiClickSelectionMode == SelectionExpansionMode::Line)
{
selectionRow.Left = _buffer->GetSize().Left();
selectionRow.Right = _buffer->GetSize().RightInclusive();
}
// expand selection for Wide Glyphs
selectionRow.Left = _ExpandWideGlyphSelectionLeft(selectionRow.Left, row);
selectionRow.Right = _ExpandWideGlyphSelectionRight(selectionRow.Right, row);
}
// Method Description:
// - Expands the selection left-wards to cover a wide glyph, if necessary
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
// Return Value:
// - updated x position to encapsulate the wide glyph
SHORT Terminal::_ExpandWideGlyphSelectionLeft(const SHORT xPos, const SHORT yPos) const
{
// don't change the value if at/outside the boundary
const auto bufferSize = _buffer->GetSize();
if (xPos <= bufferSize.Left() || xPos > bufferSize.RightInclusive())
{
return xPos;
}
COORD position{ xPos, yPos };
const auto attr = _buffer->GetCellDataAt(position)->DbcsAttr();
if (attr.IsTrailing())
{
// move off by highlighting the lead half too.
// alters position.X
bufferSize.DecrementInBounds(position);
}
return position.X;
}
// Method Description:
// - Expands the selection right-wards to cover a wide glyph, if necessary
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
// Return Value:
// - updated x position to encapsulate the wide glyph
SHORT Terminal::_ExpandWideGlyphSelectionRight(const SHORT xPos, const SHORT yPos) const
{
// don't change the value if at/outside the boundary
const auto bufferSize = _buffer->GetSize();
if (xPos < bufferSize.Left() || xPos >= bufferSize.RightInclusive())
{
return xPos;
}
COORD position{ xPos, yPos };
const auto attr = _buffer->GetCellDataAt(position)->DbcsAttr();
if (attr.IsLeading())
{
// move off by highlighting the trailing half too.
// alters position.X
bufferSize.IncrementInBounds(position);
}
return position.X;
}
// Method Description:
@@ -88,7 +207,7 @@ const COORD Terminal::GetSelectionEnd() const noexcept
// - bool representing if selection is only a single cell. Used for copyOnSelect
const bool Terminal::_IsSingleCellSelection() const noexcept
{
return (_selection->start == _selection->end);
return (_selectionAnchor == _endSelectionPosition);
}
// Method Description:
@@ -103,7 +222,7 @@ const bool Terminal::IsSelectionActive() const noexcept
{
return false;
}
return _selection.has_value();
return _selectionActive;
}
// Method Description:
@@ -116,60 +235,79 @@ const bool Terminal::IsCopyOnSelectActive() const noexcept
}
// Method Description:
// - Perform a multi-click selection at viewportPos expanding according to the expansionMode
// - Select the sequence between delimiters defined in Settings
// Arguments:
// - viewportPos: the (x,y) coordinate on the visible viewport
// - expansionMode: the SelectionExpansionMode to dictate the boundaries of the selection anchors
void Terminal::MultiClickSelection(const COORD viewportPos, SelectionExpansionMode expansionMode)
// - position: the (x,y) coordinate on the visible viewport
void Terminal::DoubleClickSelection(const COORD position)
{
// set the selection pivot to expand the selection using SetSelectionEnd()
_selection = SelectionAnchors{};
_selection->pivot = _ConvertToBufferCell(viewportPos);
#pragma warning(suppress : 26496) // cpp core checks wants this const but .Clamp() can write it.
COORD positionWithOffsets = _ConvertToBufferCell(position);
_multiClickSelectionMode = expansionMode;
SetSelectionEnd(viewportPos);
// scan leftwards until delimiter is found and
// set selection anchor to one right of that spot
_selectionAnchor = _ExpandDoubleClickSelectionLeft(positionWithOffsets);
THROW_IF_FAILED(ShortSub(_selectionAnchor.Y, gsl::narrow<SHORT>(ViewStartIndex()), &_selectionAnchor.Y));
_selectionVerticalOffset = gsl::narrow<SHORT>(ViewStartIndex());
// we need to set the _selectionPivot again
// for future shift+clicks
_selection->pivot = _selection->start;
// scan rightwards until delimiter is found and
// set endSelectionPosition to one left of that spot
_endSelectionPosition = _ExpandDoubleClickSelectionRight(positionWithOffsets);
THROW_IF_FAILED(ShortSub(_endSelectionPosition.Y, gsl::narrow<SHORT>(ViewStartIndex()), &_endSelectionPosition.Y));
_selectionActive = true;
_multiClickSelectionMode = SelectionExpansionMode::Word;
}
// Method Description:
// - Select the entire row of the position clicked
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
void Terminal::TripleClickSelection(const COORD position)
{
SetSelectionAnchor({ 0, position.Y });
SetEndSelectionPosition({ _buffer->GetSize().RightInclusive(), position.Y });
_multiClickSelectionMode = SelectionExpansionMode::Line;
}
// Method Description:
// - Record the position of the beginning of a selection
// Arguments:
// - position: the (x,y) coordinate on the visible viewport
void Terminal::SetSelectionAnchor(const COORD viewportPos)
void Terminal::SetSelectionAnchor(const COORD position)
{
_selection = SelectionAnchors{};
_selection->pivot = _ConvertToBufferCell(viewportPos);
_selectionAnchor = position;
// include _scrollOffset here to ensure this maps to the right spot of the original viewport
THROW_IF_FAILED(ShortSub(_selectionAnchor.Y, gsl::narrow<SHORT>(_scrollOffset), &_selectionAnchor.Y));
// copy value of ViewStartIndex to support scrolling
// and update on new buffer output (used in _GetSelectionRects())
_selectionVerticalOffset = gsl::narrow<SHORT>(ViewStartIndex());
_selectionActive = true;
_allowSingleCharSelection = (_copyOnSelect) ? false : true;
_multiClickSelectionMode = SelectionExpansionMode::Cell;
SetSelectionEnd(viewportPos);
SetEndSelectionPosition(position);
_selection->start = _selection->pivot;
_multiClickSelectionMode = SelectionExpansionMode::Cell;
}
// Method Description:
// - Update selection anchors when dragging to a position
// - based on the selection expansion mode
// - Record the position of the end of a selection
// Arguments:
// - viewportPos: the (x,y) coordinate on the visible viewport
// - newExpansionMode: overwrites the _multiClickSelectionMode for this function call. Used for ShiftClick
void Terminal::SetSelectionEnd(const COORD viewportPos, std::optional<SelectionExpansionMode> newExpansionMode)
// - position: the (x,y) coordinate on the visible viewport
void Terminal::SetEndSelectionPosition(const COORD position)
{
const auto textBufferPos = _ConvertToBufferCell(viewportPos);
_endSelectionPosition = position;
// if this is a shiftClick action, we need to overwrite the _multiClickSelectionMode value (even if it's the same)
// Otherwise, we may accidentally expand during other selection-based actions
_multiClickSelectionMode = newExpansionMode.has_value() ? *newExpansionMode : _multiClickSelectionMode;
// include _scrollOffset here to ensure this maps to the right spot of the original viewport
THROW_IF_FAILED(ShortSub(_endSelectionPosition.Y, gsl::narrow<SHORT>(_scrollOffset), &_endSelectionPosition.Y));
const auto anchors = _PivotSelection(textBufferPos);
std::tie(_selection->start, _selection->end) = _ExpandSelectionAnchors(anchors);
// copy value of ViewStartIndex to support scrolling
// and update on new buffer output (used in _GetSelectionRects())
_selectionVerticalOffset = gsl::narrow<SHORT>(ViewStartIndex());
// moving the endpoint of what used to be a single cell selection
// allows the user to drag back and select just one cell
if (_copyOnSelect && !_IsSingleCellSelection())
{
_allowSingleCharSelection = true;
@@ -177,74 +315,25 @@ void Terminal::SetSelectionEnd(const COORD viewportPos, std::optional<SelectionE
}
// Method Description:
// - returns a new pair of selection anchors for selecting around the pivot
// - This ensures start < end when compared
// - enable/disable box selection (ALT + selection)
// Arguments:
// - targetPos: the (x,y) coordinate we are moving to on the text buffer
// Return Value:
// - the new start/end for a selection
std::pair<COORD, COORD> Terminal::_PivotSelection(const COORD targetPos) const
// - isEnabled: new value for _boxSelection
void Terminal::SetBoxSelection(const bool isEnabled) noexcept
{
if (_buffer->GetSize().CompareInBounds(targetPos, _selection->pivot) <= 0)
{
// target is before pivot
// treat target as start
return std::make_pair(targetPos, _selection->pivot);
}
else
{
// target is after pivot
// treat pivot as start
return std::make_pair(_selection->pivot, targetPos);
}
}
// Method Description:
// - Update the selection anchors to expand according to the expansion mode
// Arguments:
// - anchors: a pair of selection anchors representing a desired selection
// Return Value:
// - the new start/end for a selection
std::pair<COORD, COORD> Terminal::_ExpandSelectionAnchors(std::pair<COORD, COORD> anchors) const
{
COORD start = anchors.first;
COORD end = anchors.second;
const auto bufferSize = _buffer->GetSize();
switch (_multiClickSelectionMode)
{
case SelectionExpansionMode::Line:
start = { bufferSize.Left(), start.Y };
end = { bufferSize.RightInclusive(), end.Y };
break;
case SelectionExpansionMode::Word:
start = _buffer->GetWordStart(start, _wordDelimiters);
end = _buffer->GetWordEnd(end, _wordDelimiters);
break;
case SelectionExpansionMode::Cell:
default:
// no expansion is necessary
break;
}
return std::make_pair(start, end);
}
// Method Description:
// - enable/disable block selection (ALT + selection)
// Arguments:
// - isEnabled: new value for _blockSelection
void Terminal::SetBlockSelection(const bool isEnabled) noexcept
{
_blockSelection = isEnabled;
_boxSelection = isEnabled;
}
// Method Description:
// - clear selection data and disable rendering it
#pragma warning(disable : 26440) // changing this to noexcept would require a change to ConHost's selection model
void Terminal::ClearSelection()
{
_selectionActive = false;
_allowSingleCharSelection = false;
_selection = std::nullopt;
_selectionAnchor = { 0, 0 };
_endSelectionPosition = { 0, 0 };
_selectionVerticalOffset = 0;
_buffer->GetRenderTarget().TriggerSelection();
}
// Method Description:
@@ -259,13 +348,47 @@ const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool tri
std::function<COLORREF(TextAttribute&)> GetForegroundColor = std::bind(&Terminal::GetForegroundColor, this, std::placeholders::_1);
std::function<COLORREF(TextAttribute&)> GetBackgroundColor = std::bind(&Terminal::GetBackgroundColor, this, std::placeholders::_1);
return _buffer->GetTextForClipboard(!_blockSelection,
return _buffer->GetTextForClipboard(!_boxSelection,
trimTrailingWhitespace,
_GetSelectionRects(),
GetForegroundColor,
GetBackgroundColor);
}
// Method Description:
// - expand the double click selection to the left
// - stopped by delimiter if started on delimiter
// Arguments:
// - position: buffer coordinate for selection
// Return Value:
// - updated copy of "position" to new expanded location (with vertical offset)
COORD Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) const
{
// force position to be within bounds
#pragma warning(suppress : 26496) // cpp core checks wants this const but .Clamp() can write it.
COORD positionWithOffsets = position;
_buffer->GetSize().Clamp(positionWithOffsets);
return _buffer->GetWordStart(positionWithOffsets, _wordDelimiters);
}
// Method Description:
// - expand the double click selection to the right
// - stopped by delimiter if started on delimiter
// Arguments:
// - position: buffer coordinate for selection
// Return Value:
// - updated copy of "position" to new expanded location (with vertical offset)
COORD Terminal::_ExpandDoubleClickSelectionRight(const COORD position) const
{
// force position to be within bounds
#pragma warning(suppress : 26496) // cpp core checks wants this const but .Clamp() can write it.
COORD positionWithOffsets = position;
_buffer->GetSize().Clamp(positionWithOffsets);
return _buffer->GetWordEnd(positionWithOffsets, _wordDelimiters);
}
// Method Description:
// - convert viewport position to the corresponding location on the buffer
// Arguments:
@@ -274,10 +397,13 @@ const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool tri
// - the corresponding location on the buffer
COORD Terminal::_ConvertToBufferCell(const COORD viewportPos) const
{
const auto yPos = base::ClampedNumeric<short>(_VisibleStartIndex()) + viewportPos.Y;
COORD bufferPos = { viewportPos.X, yPos };
_buffer->GetSize().Clamp(bufferPos);
return bufferPos;
// Force position to be valid
COORD positionWithOffsets = viewportPos;
_buffer->GetSize().Clamp(positionWithOffsets);
THROW_IF_FAILED(ShortSub(viewportPos.Y, gsl::narrow<SHORT>(_scrollOffset), &positionWithOffsets.Y));
THROW_IF_FAILED(ShortAdd(positionWithOffsets.Y, gsl::narrow<SHORT>(ViewStartIndex()), &positionWithOffsets.Y));
return positionWithOffsets;
}
// Method Description:

View File

@@ -173,7 +173,8 @@ void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
realCoordEnd.Y -= gsl::narrow<short>(_VisibleStartIndex());
SetSelectionAnchor(realCoordStart);
SetSelectionEnd(realCoordEnd, SelectionExpansionMode::Cell);
SetEndSelectionPosition(realCoordEnd);
_buffer->GetRenderTarget().TriggerSelection();
}
const std::wstring Terminal::GetConsoleTitle() const noexcept

View File

@@ -12,13 +12,6 @@ namespace Microsoft.Terminal.Settings
Hidden
};
enum TextAntialiasingMode
{
Grayscale = 0,
Cleartype,
Aliased
};
// Class Description:
// TerminalSettings encapsulates all settings that control the
// TermControl's behavior. In these settings there is both the entirety
@@ -26,8 +19,6 @@ namespace Microsoft.Terminal.Settings
// for specifically the control.
interface IControlSettings requires Microsoft.Terminal.Settings.ICoreSettings
{
String ProfileName;
Boolean UseAcrylic;
Double TintOpacity;
ScrollbarState ScrollState;
@@ -50,7 +41,5 @@ namespace Microsoft.Terminal.Settings
UInt32 SelectionBackground;
Boolean RetroTerminalEffect;
TextAntialiasingMode AntialiasingMode;
};
}

View File

@@ -28,7 +28,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_cursorHeight{ DEFAULT_CURSOR_HEIGHT },
_wordDelimiters{ DEFAULT_WORD_DELIMITERS },
_copyOnSelect{ false },
_profileName{},
_useAcrylic{ false },
_tintOpacity{ 0.5 },
_padding{ DEFAULT_PADDING },
@@ -40,9 +39,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_backgroundImageHorizontalAlignment{ winrt::Windows::UI::Xaml::HorizontalAlignment::Center },
_backgroundImageVerticalAlignment{ winrt::Windows::UI::Xaml::VerticalAlignment::Center },
_keyBindings{ nullptr },
_scrollbarState{ ScrollbarState::Visible },
_antialiasingMode{ TextAntialiasingMode::Grayscale }
_scrollbarState{ ScrollbarState::Visible }
{
}
@@ -197,16 +194,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_copyOnSelect = value;
}
void TerminalSettings::ProfileName(hstring const& value)
{
_profileName = value;
}
hstring TerminalSettings::ProfileName()
{
return _profileName;
}
bool TerminalSettings::UseAcrylic() noexcept
{
return _useAcrylic;
@@ -387,14 +374,4 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
_retroTerminalEffect = value;
}
Settings::TextAntialiasingMode TerminalSettings::AntialiasingMode() const noexcept
{
return _antialiasingMode;
}
void TerminalSettings::AntialiasingMode(const Settings::TextAntialiasingMode& value) noexcept
{
_antialiasingMode = value;
}
}

View File

@@ -55,8 +55,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
void CopyOnSelect(bool value) noexcept;
// ------------------------ End of Core Settings -----------------------
hstring ProfileName();
void ProfileName(hstring const& value);
bool UseAcrylic() noexcept;
void UseAcrylic(bool value) noexcept;
double TintOpacity() noexcept;
@@ -104,9 +102,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
bool RetroTerminalEffect() noexcept;
void RetroTerminalEffect(bool value) noexcept;
TextAntialiasingMode AntialiasingMode() const noexcept;
void AntialiasingMode(winrt::Microsoft::Terminal::Settings::TextAntialiasingMode const& value) noexcept;
private:
uint32_t _defaultForeground;
uint32_t _defaultBackground;
@@ -122,7 +117,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
uint32_t _cursorHeight;
hstring _wordDelimiters;
hstring _profileName;
bool _useAcrylic;
double _tintOpacity;
hstring _fontFace;
@@ -143,8 +137,6 @@ namespace winrt::Microsoft::Terminal::Settings::implementation
Settings::ScrollbarState _scrollbarState;
bool _retroTerminalEffect;
Settings::TextAntialiasingMode _antialiasingMode;
};
}

View File

@@ -44,7 +44,7 @@ using namespace Microsoft::Terminal::Core;
namespace TerminalCoreUnitTests
{
class ConptyRoundtripTests;
class TerminalBufferTests;
};
using namespace TerminalCoreUnitTests;
@@ -152,14 +152,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
TEST_METHOD(SimpleWriteOutputTest);
TEST_METHOD(WriteTwoLinesUsesNewline);
TEST_METHOD(WriteAFewSimpleLines);
TEST_METHOD(PassthroughClearScrollback);
TEST_METHOD(TestWrappingALongString);
TEST_METHOD(TestAdvancedWrapping);
TEST_METHOD(TestExactWrappingWithoutSpaces);
TEST_METHOD(TestExactWrappingWithSpaces);
TEST_METHOD(MoveCursorAtEOL);
private:
@@ -354,262 +348,6 @@ void ConptyRoundtripTests::WriteAFewSimpleLines()
verifyData(termTb);
}
void ConptyRoundtripTests::TestWrappingALongString()
{
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
_flushFirstFrame();
_checkConptyOutput = false;
const auto initialTermView = term->GetViewport();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(100, charsToWrite);
VERIFY_ARE_EQUAL(0, initialTermView.Top());
VERIFY_ARE_EQUAL(32, initialTermView.BottomExclusive());
hostSm.ProcessString(TestUtils::Test100CharsString);
const auto secondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, secondView.Top());
VERIFY_ARE_EQUAL(32, secondView.BottomExclusive());
auto verifyBuffer = [&](const TextBuffer& tb) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(charsToWrite % initialTermView.Width(), cursor.GetPosition().X);
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, TestUtils::Test100CharsString, { 0, 0 });
};
verifyBuffer(hostTb);
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb);
}
void ConptyRoundtripTests::TestAdvancedWrapping()
{
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
const auto initialTermView = term->GetViewport();
_flushFirstFrame();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(100, charsToWrite);
hostSm.ProcessString(TestUtils::Test100CharsString);
hostSm.ProcessString(L"\n");
hostSm.ProcessString(L" ");
hostSm.ProcessString(L"1234567890");
auto verifyBuffer = [&](const TextBuffer& tb) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(2, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(20, cursor.GetPosition().X);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, TestUtils::Test100CharsString, { 0, 0 });
TestUtils::VerifyExpectedString(tb, L" 1234567890", { 0, 2 });
};
verifyBuffer(hostTb);
// First write the first 80 characters from the string
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
// Without line breaking, write the remaining 20 chars
expectedOutput.push_back(R"(qrstuvwxyz{|}~!"#$%&)");
// Clear the rest of row 1
expectedOutput.push_back("\x1b[K");
// This is the hard line break
expectedOutput.push_back("\r\n");
// Now write row 2 of the buffer
expectedOutput.push_back(" 1234567890");
// and clear everything after the text, because the buffer is empty.
expectedOutput.push_back("\x1b[K");
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb);
}
void ConptyRoundtripTests::TestExactWrappingWithoutSpaces()
{
// This test (and TestExactWrappingWitSpaces) reveals a bug in the old
// implementation.
//
// If a line _exactly_ wraps to the next line, we can't tell if the line
// should really wrap, or manually break. The client app is writing a line
// that's exactly the width of the buffer that manually linebreaked at the
// end of the line, followed by another line.
//
// With the old PaintBufferLine interface, there's no way to know if this
// case is because the line wrapped or not. Hence, the addition of the
// `lineWrapped` parameter
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
const auto initialTermView = term->GetViewport();
_flushFirstFrame();
const auto charsToWrite = initialTermView.Width();
VERIFY_ARE_EQUAL(80, charsToWrite);
for (auto i = 0; i < charsToWrite; i++)
{
// This is a handy way of just printing the printable characters that
// _aren't_ the space character.
const wchar_t wch = static_cast<wchar_t>(33 + (i % 94));
hostSm.ProcessCharacter(wch);
}
hostSm.ProcessString(L"\n");
hostSm.ProcessString(L"1234567890");
auto verifyBuffer = [&](const TextBuffer& tb, const bool isTerminal) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(10, cursor.GetPosition().X);
// TODO: GH#780 - In the Terminal, neither line should be wrapped.
// Unfortunately, until WriteCharsLegacy2ElectricBoogaloo is complete,
// the Terminal will still treat the first line as wrapped. When #780 is
// implemented, these tests will fail, and should again expect the first
// line to not be wrapped.
// Verify that we marked the 0th row as _not wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_ARE_EQUAL(isTerminal, row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, LR"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)", { 0, 0 });
TestUtils::VerifyExpectedString(tb, L"1234567890", { 0, 1 });
};
verifyBuffer(hostTb, false);
// First write the first 80 characters from the string
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
// This is the hard line break
expectedOutput.push_back("\r\n");
// Now write row 2 of the buffer
expectedOutput.push_back("1234567890");
// and clear everything after the text, because the buffer is empty.
expectedOutput.push_back("\x1b[K");
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb, true);
}
void ConptyRoundtripTests::TestExactWrappingWithSpaces()
{
// This test is also explained by the comment at the top of TestExactWrappingWithoutSpaces
auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
const auto initialTermView = term->GetViewport();
_flushFirstFrame();
const auto charsToWrite = initialTermView.Width();
VERIFY_ARE_EQUAL(80, charsToWrite);
for (auto i = 0; i < charsToWrite; i++)
{
// This is a handy way of just printing the printable characters that
// _aren't_ the space character.
const wchar_t wch = static_cast<wchar_t>(33 + (i % 94));
hostSm.ProcessCharacter(wch);
}
hostSm.ProcessString(L"\n");
hostSm.ProcessString(L" ");
hostSm.ProcessString(L"1234567890");
auto verifyBuffer = [&](const TextBuffer& tb, const bool isTerminal) {
auto& cursor = tb.GetCursor();
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
VERIFY_ARE_EQUAL(20, cursor.GetPosition().X);
// TODO: GH#780 - In the Terminal, neither line should be wrapped.
// Unfortunately, until WriteCharsLegacy2ElectricBoogaloo is complete,
// the Terminal will still treat the first line as wrapped. When #780 is
// implemented, these tests will fail, and should again expect the first
// line to not be wrapped.
// Verify that we marked the 0th row as _not wrapped_
const auto& row0 = tb.GetRowByOffset(0);
VERIFY_ARE_EQUAL(isTerminal, row0.GetCharRow().WasWrapForced());
const auto& row1 = tb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(tb, LR"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)", { 0, 0 });
TestUtils::VerifyExpectedString(tb, L" 1234567890", { 0, 1 });
};
verifyBuffer(hostTb, false);
// First write the first 80 characters from the string
expectedOutput.push_back(R"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnop)");
// This is the hard line break
expectedOutput.push_back("\r\n");
// Now write row 2 of the buffer
expectedOutput.push_back(" 1234567890");
// and clear everything after the text, because the buffer is empty.
expectedOutput.push_back("\x1b[K");
VERIFY_SUCCEEDED(renderer.PaintFrame());
verifyBuffer(termTb, true);
}
void ConptyRoundtripTests::MoveCursorAtEOL()
{
// This is a test for GH#1245
@@ -622,6 +360,7 @@ void ConptyRoundtripTests::MoveCursorAtEOL()
auto& hostSm = si.GetStateMachine();
auto& hostTb = si.GetTextBuffer();
auto& termTb = *term->_buffer;
_flushFirstFrame();
Log::Comment(NoThrowString().Format(

View File

@@ -72,7 +72,7 @@ namespace TerminalCoreUnitTests
term.SetSelectionAnchor({ 5, rowValue });
// Simulate move to (x,y) = (15,20)
term.SetSelectionEnd({ 15, 20 });
term.SetEndSelectionPosition({ 15, 20 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -110,19 +110,19 @@ namespace TerminalCoreUnitTests
{
const COORD maxCoord = { SHRT_MAX, SHRT_MAX };
// Test SetSelectionAnchor(COORD) and SetSelectionEnd(COORD)
// Test SetSelectionAnchor(COORD) and SetEndSelectionPosition(COORD)
// Behavior: clamp coord to viewport.
auto ValidateSingleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) {
Terminal term;
DummyRenderTarget emptyRT;
term.Create({ 10, 10 }, scrollback, emptyRT);
// NOTE: SetSelectionEnd(COORD) is called within SetSelectionAnchor(COORD)
// NOTE: SetEndSelectionPosition(COORD) is called within SetSelectionAnchor(COORD)
term.SetSelectionAnchor(maxCoord);
ValidateSingleRowSelection(term, expected);
};
// Test a Double Click Selection
// Test DoubleClickSelection(COORD)
// Behavior: clamp coord to viewport.
// Then, do double click selection.
auto ValidateDoubleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) {
@@ -130,11 +130,11 @@ namespace TerminalCoreUnitTests
DummyRenderTarget emptyRT;
term.Create({ 10, 10 }, scrollback, emptyRT);
term.MultiClickSelection(maxCoord, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(maxCoord);
ValidateSingleRowSelection(term, expected);
};
// Test a Triple Click Selection
// Test TripleClickSelection(COORD)
// Behavior: clamp coord to viewport.
// Then, do triple click selection.
auto ValidateTripleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) {
@@ -142,7 +142,7 @@ namespace TerminalCoreUnitTests
DummyRenderTarget emptyRT;
term.Create({ 10, 10 }, scrollback, emptyRT);
term.MultiClickSelection(maxCoord, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(maxCoord);
ValidateSingleRowSelection(term, expected);
};
@@ -226,17 +226,17 @@ namespace TerminalCoreUnitTests
// Case 1: Move out of right boundary
Log::Comment(L"Out of bounds: X-value too large");
term.SetSelectionEnd({ 20, 5 });
term.SetEndSelectionPosition({ 20, 5 });
ValidateSingleRowSelection(term, SMALL_RECT({ 5, 5, rightBoundary, 5 }));
// Case 2: Move out of left boundary
Log::Comment(L"Out of bounds: X-value negative");
term.SetSelectionEnd({ -20, 5 });
term.SetEndSelectionPosition({ -20, 5 });
ValidateSingleRowSelection(term, { leftBoundary, 5, 5, 5 });
// Case 3: Move out of top boundary
Log::Comment(L"Out of bounds: Y-value negative");
term.SetSelectionEnd({ 5, -20 });
term.SetEndSelectionPosition({ 5, -20 });
{
auto selectionRects = term.GetSelectionRects();
@@ -267,7 +267,7 @@ namespace TerminalCoreUnitTests
// Case 4: Move out of bottom boundary
Log::Comment(L"Out of bounds: Y-value too large");
term.SetSelectionEnd({ 5, 20 });
term.SetEndSelectionPosition({ 5, 20 });
{
auto selectionRects = term.GetSelectionRects();
@@ -310,10 +310,10 @@ namespace TerminalCoreUnitTests
// Simulate ALT + click at (x,y) = (5,10)
term.SetSelectionAnchor({ 5, rowValue });
term.SetBlockSelection(true);
term.SetBoxSelection(true);
// Simulate move to (x,y) = (15,20)
term.SetSelectionEnd({ 15, 20 });
term.SetEndSelectionPosition({ 15, 20 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -349,7 +349,7 @@ namespace TerminalCoreUnitTests
term.SetSelectionAnchor({ 5, rowValue });
// Simulate move to (x,y) = (15,20)
term.SetSelectionEnd({ 15, 20 });
term.SetEndSelectionPosition({ 15, 20 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -449,10 +449,10 @@ namespace TerminalCoreUnitTests
// Simulate ALT + click at (x,y) = (5,8)
term.SetSelectionAnchor({ 5, 8 });
term.SetBlockSelection(true);
term.SetBoxSelection(true);
// Simulate move to (x,y) = (7,12)
term.SetSelectionEnd({ 7, 12 });
term.SetEndSelectionPosition({ 7, 12 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -501,7 +501,7 @@ namespace TerminalCoreUnitTests
// Simulate double click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(clickPos);
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 4, 10, (4 + gsl::narrow<SHORT>(text.size()) - 1), 10 }));
@@ -519,7 +519,7 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(clickPos);
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -546,7 +546,7 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (15,10)
// this is over the '>' char
auto clickPos = COORD{ 15, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection(clickPos);
// ---Validate selection area---
// "Terminal" is in class 2
@@ -572,14 +572,14 @@ namespace TerminalCoreUnitTests
term.Write(text);
// Simulate double click at (x,y) = (5,10)
term.MultiClickSelection({ 5, 10 }, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection({ 5, 10 });
// Simulate move to (x,y) = (21,10)
//
// buffer: doubleClickMe dragThroughHere
// ^ ^
// start finish
term.SetSelectionEnd({ 21, 10 });
term.SetEndSelectionPosition({ 21, 10 });
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 4, 10, 32, 10 }));
@@ -601,14 +601,14 @@ namespace TerminalCoreUnitTests
term.Write(text);
// Simulate double click at (x,y) = (21,10)
term.MultiClickSelection({ 21, 10 }, Terminal::SelectionExpansionMode::Word);
term.DoubleClickSelection({ 21, 10 });
// Simulate move to (x,y) = (5,10)
//
// buffer: doubleClickMe dragThroughHere
// ^ ^
// finish start
term.SetSelectionEnd({ 5, 10 });
term.SetEndSelectionPosition({ 5, 10 });
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 4, 10, 32, 10 }));
@@ -622,7 +622,7 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(clickPos);
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 0, 10, 99, 10 }));
@@ -636,10 +636,10 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(clickPos);
// Simulate move to (x,y) = (7,10)
term.SetSelectionEnd({ 7, 10 });
term.SetEndSelectionPosition({ 7, 10 });
// Validate selection area
ValidateSingleRowSelection(term, SMALL_RECT({ 0, 10, 99, 10 }));
@@ -653,10 +653,10 @@ namespace TerminalCoreUnitTests
// Simulate click at (x,y) = (5,10)
auto clickPos = COORD{ 5, 10 };
term.MultiClickSelection(clickPos, Terminal::SelectionExpansionMode::Line);
term.TripleClickSelection(clickPos);
// Simulate move to (x,y) = (5,11)
term.SetSelectionEnd({ 5, 11 });
term.SetEndSelectionPosition({ 5, 11 });
// Simulate renderer calling TriggerSelection and acquiring selection area
auto selectionRects = term.GetSelectionRects();
@@ -689,7 +689,7 @@ namespace TerminalCoreUnitTests
// Simulate move to (x,y) = (5,10)
// (So, no movement)
term.SetSelectionEnd({ 5, 10 });
term.SetEndSelectionPosition({ 5, 10 });
// Case 1: single cell selection not allowed
{
@@ -705,12 +705,12 @@ namespace TerminalCoreUnitTests
}
// Case 2: move off of single cell
term.SetSelectionEnd({ 6, 10 });
term.SetEndSelectionPosition({ 6, 10 });
ValidateSingleRowSelection(term, { 5, 10, 6, 10 });
VERIFY_IS_TRUE(term.IsSelectionActive());
// Case 3: move back onto single cell (now allowed)
term.SetSelectionEnd({ 5, 10 });
term.SetEndSelectionPosition({ 5, 10 });
ValidateSingleRowSelection(term, { 5, 10, 5, 10 });
// single cell selection should now be allowed

View File

@@ -29,9 +29,6 @@ class TerminalCoreUnitTests::TerminalBufferTests final
TEST_METHOD(TestSimpleBufferWriting);
TEST_METHOD(TestWrappingCharByChar);
TEST_METHOD(TestWrappingALongString);
TEST_METHOD_SETUP(MethodSetup)
{
// STEP 1: Set up the Terminal
@@ -69,76 +66,3 @@ void TerminalBufferTests::TestSimpleBufferWriting()
TestUtils::VerifyExpectedString(termTb, L"Hello World", { 0, 0 });
}
void TerminalBufferTests::TestWrappingCharByChar()
{
auto& termTb = *term->_buffer;
auto& termSm = *term->_stateMachine;
const auto initialView = term->GetViewport();
auto& cursor = termTb.GetCursor();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(0, initialView.Top());
VERIFY_ARE_EQUAL(32, initialView.BottomExclusive());
for (auto i = 0; i < charsToWrite; i++)
{
// This is a handy way of just printing the printable characters that
// _aren't_ the space character.
const wchar_t wch = static_cast<wchar_t>(33 + (i % 94));
termSm.ProcessCharacter(wch);
}
const auto secondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, secondView.Top());
VERIFY_ARE_EQUAL(32, secondView.BottomExclusive());
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(charsToWrite % initialView.Width(), cursor.GetPosition().X);
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = termTb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = termTb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(termTb, TestUtils::Test100CharsString, { 0, 0 });
}
void TerminalBufferTests::TestWrappingALongString()
{
auto& termTb = *term->_buffer;
auto& termSm = *term->_stateMachine;
const auto initialView = term->GetViewport();
auto& cursor = termTb.GetCursor();
const auto charsToWrite = gsl::narrow_cast<short>(TestUtils::Test100CharsString.size());
VERIFY_ARE_EQUAL(100, charsToWrite);
VERIFY_ARE_EQUAL(0, initialView.Top());
VERIFY_ARE_EQUAL(32, initialView.BottomExclusive());
termSm.ProcessString(TestUtils::Test100CharsString);
const auto secondView = term->GetViewport();
VERIFY_ARE_EQUAL(0, secondView.Top());
VERIFY_ARE_EQUAL(32, secondView.BottomExclusive());
// Verify the cursor wrapped to the second line
VERIFY_ARE_EQUAL(charsToWrite % initialView.Width(), cursor.GetPosition().X);
VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y);
// Verify that we marked the 0th row as _wrapped_
const auto& row0 = termTb.GetRowByOffset(0);
VERIFY_IS_TRUE(row0.GetCharRow().WasWrapForced());
const auto& row1 = termTb.GetRowByOffset(1);
VERIFY_IS_FALSE(row1.GetCharRow().WasWrapForced());
TestUtils::VerifyExpectedString(termTb, TestUtils::Test100CharsString, { 0, 0 });
}

View File

@@ -21,10 +21,6 @@ namespace TerminalCoreUnitTests
class TerminalCoreUnitTests::TestUtils
{
public:
static constexpr std::wstring_view Test100CharsString{
LR"(!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&)"
};
// Function Description:
// - Helper function to validate that a number of characters in a row are all
// the same. Validates that the next end-start characters are all equal to the

View File

@@ -28,10 +28,10 @@ inline winrt::Windows::UI::Color ColorRefToColor(const COLORREF& colorref)
// - Rect scaled by scale
inline winrt::Windows::Foundation::Rect ScaleRect(winrt::Windows::Foundation::Rect rect, double scale)
{
const auto scaleLocal = base::ClampedNumeric<float>(scale);
rect.X = base::ClampMul(rect.X, scaleLocal);
rect.Y = base::ClampMul(rect.Y, scaleLocal);
rect.Width = base::ClampMul(rect.Width, scaleLocal);
rect.Height = base::ClampMul(rect.Height, scaleLocal);
const float scaleLocal = gsl::narrow_cast<float>(scale);
rect.X *= scaleLocal;
rect.Y *= scaleLocal;
rect.Width *= scaleLocal;
rect.Height *= scaleLocal;
return rect;
}

View File

@@ -7,7 +7,6 @@ namespace Microsoft.Terminal.Wpf
{
using System;
using System.Runtime.InteropServices;
using System.Windows.Automation.Provider;
#pragma warning disable SA1600 // Elements should be documented
internal static class NativeMethods
@@ -37,8 +36,6 @@ namespace Microsoft.Terminal.Wpf
/// </summary>
WM_MOUSEACTIVATE = 0x0021,
WM_GETOBJECT = 0x003D,
/// <summary>
/// The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place in the Z order has changed as a result of a call to the SetWindowPos function or another window-management function.
/// </summary>

View File

@@ -26,7 +26,7 @@ namespace Microsoft.Terminal.Wpf
private DispatcherTimer blinkTimer;
private NativeMethods.ScrollCallback scrollCallback;
private NativeMethods.WriteCallback writeCallback;
/// <summary>
/// Initializes a new instance of the <see cref="TerminalContainer"/> class.
/// </summary>
@@ -35,7 +35,7 @@ namespace Microsoft.Terminal.Wpf
this.MessageHook += this.TerminalContainer_MessageHook;
this.GotFocus += this.TerminalContainer_GotFocus;
this.Focusable = true;
var blinkTime = NativeMethods.GetCaretBlinkTime();
if (blinkTime != uint.MaxValue)
@@ -72,8 +72,6 @@ namespace Microsoft.Terminal.Wpf
/// </summary>
internal int Columns { get; private set; }
internal IntPtr Hwnd => this.hwnd;
/// <summary>
/// Sets the connection to the terminal backend.
/// </summary>
@@ -174,6 +172,7 @@ namespace Microsoft.Terminal.Wpf
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
var dpiScale = VisualTreeHelper.GetDpi(this);
NativeMethods.CreateTerminal(hwndParent.Handle, out this.hwnd, out this.terminal);
this.scrollCallback = this.OnScroll;
@@ -230,6 +229,23 @@ namespace Microsoft.Terminal.Wpf
this.Focus();
NativeMethods.SetFocus(this.hwnd);
break;
case NativeMethods.WindowMessage.WM_LBUTTONDOWN:
this.LeftClickHandler((int)lParam);
break;
case NativeMethods.WindowMessage.WM_RBUTTONDOWN:
if (NativeMethods.TerminalIsSelectionActive(this.terminal))
{
Clipboard.SetText(NativeMethods.TerminalGetSelection(this.terminal));
}
else
{
this.connection.WriteInput(Clipboard.GetText());
}
break;
case NativeMethods.WindowMessage.WM_MOUSEMOVE:
this.MouseMoveHandler((int)wParam, (int)lParam);
break;
case NativeMethods.WindowMessage.WM_KEYDOWN:
NativeMethods.TerminalSetCursorVisible(this.terminal, true);
NativeMethods.TerminalClearSelection(this.terminal);

View File

@@ -9,7 +9,7 @@ namespace Microsoft.Terminal.Wpf
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
/// <summary>
/// A basic terminal control. This control can receive and render standard VT100 sequences.
/// </summary>

View File

@@ -12,7 +12,7 @@
#pragma hdrstop
using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Interactivity;
using namespace Microsoft::Console::Interactivity::Win32;
using Microsoft::Console::Interactivity::ServiceLocator;
#pragma region IBaseData
@@ -389,50 +389,11 @@ void RenderData::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
// - none
// Return Value:
// - current selection anchor
const COORD RenderData::GetSelectionAnchor() const noexcept
const COORD RenderData::GetSelectionAnchor() const
{
return Selection::Instance().GetSelectionAnchor();
}
// Routine Description:
// - Gets the current end selection anchor position
// Arguments:
// - none
// Return Value:
// - current selection anchor
const COORD RenderData::GetSelectionEnd() const noexcept
{
// The selection area in ConHost is encoded as two things...
// - SelectionAnchor: the initial position where the selection was started
// - SelectionRect: the rectangular region denoting a portion of the buffer that is selected
// The following is an exerpt from Selection::s_GetSelectionRects
// if the anchor (start of select) was in the top right or bottom left of the box,
// we need to remove rectangular overlap in the middle.
// e.g.
// For selections with the anchor in the top left (A) or bottom right (B),
// it is valid to maintain the inner rectangle (+) as part of the selection
// A+++++++================
// ==============++++++++B
// + and = are valid highlights in this scenario.
// For selections with the anchor in in the top right (A) or bottom left (B),
// we must remove a portion of the first/last line that lies within the rectangle (+)
// +++++++A=================
// ==============B+++++++
// Only = is valid for highlight in this scenario.
// This is only needed for line selection. Box selection doesn't need to account for this.
const auto selectionRect = Selection::Instance().GetSelectionRectangle();
// To extract the end anchor from this rect, we need to know which corner of the rect is the SelectionAnchor
// Then choose the opposite corner.
const auto anchor = Selection::Instance().GetSelectionAnchor();
const short x_pos = (selectionRect.Left == anchor.X) ? selectionRect.Right : selectionRect.Left;
const short y_pos = (selectionRect.Top == anchor.Y) ? selectionRect.Bottom : selectionRect.Top;
return { x_pos, y_pos };
}
// Routine Description:
// - Given two points in the buffer space, color the selection between the two with the given attribute.
// - This will create an internal selection rectangle covering the two points, assume a line selection,

View File

@@ -60,8 +60,7 @@ public:
const bool IsSelectionActive() const override;
void ClearSelection() override;
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
const COORD GetSelectionAnchor() const noexcept;
const COORD GetSelectionEnd() const noexcept;
const COORD GetSelectionAnchor() const;
void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute attr);
#pragma endregion
};

View File

@@ -34,6 +34,111 @@ Selection& Selection::Instance()
return *_instance;
}
// Routine Description:
// - Determines the line-by-line selection rectangles based on global selection state.
// Arguments:
// - selectionRect - The selection rectangle outlining the region to be selected
// - selectionAnchor - The corner of the selection rectangle that selection started from
// - lineSelection - True to process in line mode. False to process in block mode.
// Return Value:
// - Returns a vector where each SMALL_RECT is one Row worth of the area to be selected.
// - Returns empty vector if no rows are selected.
// - Throws exceptions for out of memory issues
std::vector<SMALL_RECT> Selection::s_GetSelectionRects(const SMALL_RECT& selectionRect,
const COORD selectionAnchor,
const bool lineSelection)
{
std::vector<SMALL_RECT> selectionAreas;
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& screenInfo = gci.GetActiveOutputBuffer();
// if the anchor (start of select) was in the top right or bottom left of the box,
// we need to remove rectangular overlap in the middle.
// e.g.
// For selections with the anchor in the top left (A) or bottom right (B),
// it is valid to maintain the inner rectangle (+) as part of the selection
// A+++++++================
// ==============++++++++B
// + and = are valid highlights in this scenario.
// For selections with the anchor in in the top right (A) or bottom left (B),
// we must remove a portion of the first/last line that lies within the rectangle (+)
// +++++++A=================
// ==============B+++++++
// Only = is valid for highlight in this scenario.
// This is only needed for line selection. Box selection doesn't need to account for this.
bool removeRectPortion = false;
if (lineSelection)
{
const auto selectionStart = selectionAnchor;
// only if top and bottom aren't the same line... we need the whole rectangle if we're on the same line.
// e.g. A++++++++++++++B
// All the + are valid select points.
if (selectionRect.Top != selectionRect.Bottom)
{
if ((selectionStart.X == selectionRect.Right && selectionStart.Y == selectionRect.Top) ||
(selectionStart.X == selectionRect.Left && selectionStart.Y == selectionRect.Bottom))
{
removeRectPortion = true;
}
}
}
// for each row within the selection rectangle
for (short i = selectionRect.Top; i <= selectionRect.Bottom; i++)
{
// create a rectangle representing the highlight on one row
SMALL_RECT highlightRow;
highlightRow.Top = i;
highlightRow.Bottom = i;
highlightRow.Left = selectionRect.Left;
highlightRow.Right = selectionRect.Right;
// compensate for line selection by extending one or both ends of the rectangle to the edge
if (lineSelection)
{
// if not the first row, pad the left selection to the buffer edge
if (i != selectionRect.Top)
{
highlightRow.Left = 0;
}
// if not the last row, pad the right selection to the buffer edge
if (i != selectionRect.Bottom)
{
highlightRow.Right = screenInfo.GetBufferSize().RightInclusive();
}
// if we've determined we're in a scenario where we must remove the inner rectangle from the lines...
if (removeRectPortion)
{
if (i == selectionRect.Top)
{
// from the top row, move the left edge of the highlight line to the right edge of the rectangle
highlightRow.Left = selectionRect.Right;
}
else if (i == selectionRect.Bottom)
{
// from the bottom row, move the right edge of the highlight line to the left edge of the rectangle
highlightRow.Right = selectionRect.Left;
}
}
}
// compensate for double width characters by calling double-width measuring/limiting function
const COORD targetPoint{ highlightRow.Left, highlightRow.Top };
const SHORT stringLength = highlightRow.Right - highlightRow.Left + 1;
highlightRow = s_BisectSelection(stringLength, targetPoint, screenInfo, highlightRow);
selectionAreas.emplace_back(highlightRow);
}
return selectionAreas;
}
// Routine Description:
// - Determines the line-by-line selection rectangles based on global selection state.
// Arguments:
@@ -49,17 +154,65 @@ std::vector<SMALL_RECT> Selection::GetSelectionRects() const
return std::vector<SMALL_RECT>();
}
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& screenInfo = gci.GetActiveOutputBuffer();
return s_GetSelectionRects(_srSelectionRect, _coordSelectionAnchor, IsLineSelection());
}
// _coordSelectionAnchor is at one of the corners of _srSelectionRects
// endSelectionAnchor is at the exact opposite corner
COORD endSelectionAnchor;
endSelectionAnchor.X = (_coordSelectionAnchor.X == _srSelectionRect.Left) ? _srSelectionRect.Right : _srSelectionRect.Left;
endSelectionAnchor.Y = (_coordSelectionAnchor.Y == _srSelectionRect.Top) ? _srSelectionRect.Bottom : _srSelectionRect.Top;
// Routine Description:
// - This routine checks to ensure that clipboard selection isn't trying to cut a double byte character in half.
// It will adjust the SmallRect rectangle size to ensure this.
// Arguments:
// - sStringLength - The length of the string we're attempting to clip.
// - coordTargetPoint - The row/column position within the text buffer that we're about to try to clip.
// - screenInfo - Screen information structure containing relevant text and dimension information.
// - rect - The region of the text that we want to clip, and then adjusted to the region that should be
// clipped without splicing double-width characters.
// Return Value:
// - the clipped region
SMALL_RECT Selection::s_BisectSelection(const short sStringLength,
const COORD coordTargetPoint,
const SCREEN_INFORMATION& screenInfo,
const SMALL_RECT rect)
{
SMALL_RECT outRect = rect;
try
{
auto iter = screenInfo.GetCellDataAt(coordTargetPoint);
if (iter->DbcsAttr().IsTrailing())
{
if (coordTargetPoint.X == 0)
{
outRect.Left++;
}
else
{
outRect.Left--;
}
}
const auto blockSelection = !IsLineSelection();
return screenInfo.GetTextBuffer().GetTextRects(_coordSelectionAnchor, endSelectionAnchor, blockSelection);
// Check end position of strings
if (coordTargetPoint.X + sStringLength < screenInfo.GetBufferSize().Width())
{
iter += sStringLength;
if (iter->DbcsAttr().IsTrailing())
{
outRect.Right++;
}
}
else
{
if (coordTargetPoint.Y + 1 < screenInfo.GetBufferSize().Height())
{
const auto nextLineIter = screenInfo.GetCellDataAt({ 0, coordTargetPoint.Y + 1 });
if (nextLineIter->DbcsAttr().IsTrailing())
{
outRect.Right--;
}
}
}
}
CATCH_LOG();
return outRect;
}
// Routine Description:
@@ -411,13 +564,18 @@ void Selection::ColorSelection(const SMALL_RECT& srRect, const TextAttribute att
// - attr - Color to apply to region.
void Selection::ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute attr)
{
// Make a rectangle for the region as if it were selected by a mouse.
// We will use the first one as the "anchor" to represent where the mouse went down.
SMALL_RECT srSelection;
srSelection.Top = std::min(coordSelectionStart.Y, coordSelectionEnd.Y);
srSelection.Bottom = std::max(coordSelectionStart.Y, coordSelectionEnd.Y);
srSelection.Left = std::min(coordSelectionStart.X, coordSelectionEnd.X);
srSelection.Right = std::max(coordSelectionStart.X, coordSelectionEnd.X);
// Extract row-by-row selection rectangles for the selection area.
try
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& screenInfo = gci.GetActiveOutputBuffer();
const auto rectangles = screenInfo.GetTextBuffer().GetTextRects(coordSelectionStart, coordSelectionEnd);
const auto rectangles = s_GetSelectionRects(srSelection, coordSelectionStart, true);
for (const auto& rect : rectangles)
{
ColorSelection(rect, attr);

View File

@@ -72,6 +72,15 @@ private:
void _PaintSelection() const;
static SMALL_RECT s_BisectSelection(const short sStringLength,
const COORD coordTargetPoint,
const SCREEN_INFORMATION& screenInfo,
const SMALL_RECT rect);
static std::vector<SMALL_RECT> s_GetSelectionRects(const SMALL_RECT& selectionRect,
const COORD selectionAnchor,
const bool lineSelection);
void _CancelMarkSelection();
void _CancelMouseSelection();

View File

@@ -9,6 +9,7 @@
#include "../types/WindowUiaProviderBase.hpp"
using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Interactivity::Win32;
enum TraceKeywords
{
@@ -397,3 +398,718 @@ void __stdcall Tracing::TraceFailure(const wil::FailureInfo& failure) noexcept
TraceLoggingString(failure.pszCode, "Code"),
TraceLoggingLevel(WINEVENT_LEVEL_ERROR));
}
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
#if 0
void Tracing::s_TraceUia(const UiaTextRange* const range,
const UiaTextRangeTracing::ApiCall apiCall,
const UiaTextRangeTracing::IApiMsg* const apiMsg)
{
unsigned long long id = 0u;
bool degenerate = true;
Endpoint start = 0u;
Endpoint end = 0u;
if (range)
{
id = range->GetId();
degenerate = range->IsDegenerate();
start = range->GetStart();
end = range->GetEnd();
}
switch (apiCall)
{
case UiaTextRangeTracing::ApiCall::Constructor:
{
id = static_cast<const UiaTextRangeTracing::ApiMsgConstructor* const>(apiMsg)->Id;
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::Constructor",
TraceLoggingValue(id, "_id"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::AddRef:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::AddRef",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::Release:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::Release",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::QueryInterface:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::QueryInterface",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::Clone:
{
if (apiMsg == nullptr)
{
return;
}
auto cloneId = reinterpret_cast<const UiaTextRangeTracing::ApiMsgClone* const>(apiMsg)->CloneId;
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::Clone",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(cloneId, "clone's _id"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::Compare:
{
const UiaTextRangeTracing::ApiMsgCompare* const msg = static_cast<const UiaTextRangeTracing::ApiMsgCompare* const>(apiMsg);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::Compare",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(msg->OtherId, "Other's Id"),
TraceLoggingValue(msg->Equal, "Equal"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::CompareEndpoints:
{
const UiaTextRangeTracing::ApiMsgCompareEndpoints* const msg = static_cast<const UiaTextRangeTracing::ApiMsgCompareEndpoints* const>(apiMsg);
const wchar_t* const pEndpoint = _textPatternRangeEndpointToString(msg->Endpoint);
const wchar_t* const pTargetEndpoint = _textPatternRangeEndpointToString(msg->TargetEndpoint);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::CompareEndpoints",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(msg->OtherId, "Other's Id"),
TraceLoggingValue(pEndpoint, "endpoint"),
TraceLoggingValue(pTargetEndpoint, "targetEndpoint"),
TraceLoggingValue(msg->Result, "Result"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::ExpandToEnclosingUnit:
{
const UiaTextRangeTracing::ApiMsgExpandToEnclosingUnit* const msg = static_cast<const UiaTextRangeTracing::ApiMsgExpandToEnclosingUnit* const>(apiMsg);
const wchar_t* const pUnitName = _textUnitToString(msg->Unit);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::ExpandToEnclosingUnit",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(pUnitName, "Unit"),
TraceLoggingValue(msg->OriginalStart, "Original Start"),
TraceLoggingValue(msg->OriginalEnd, "Original End"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::FindAttribute:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::FindAttribute",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::FindText:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::FindText",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::GetAttributeValue:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::GetAttributeValue",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::GetBoundingRectangles:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::GetBoundingRectangles",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::GetEnclosingElement:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::GetEnclosingElement",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::GetText:
{
const UiaTextRangeTracing::ApiMsgGetText* const msg = static_cast<const UiaTextRangeTracing::ApiMsgGetText* const>(apiMsg);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::GetText",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(msg->Text, "Text"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::Move:
{
const UiaTextRangeTracing::ApiMsgMove* const msg = static_cast<const UiaTextRangeTracing::ApiMsgMove* const>(apiMsg);
const wchar_t* const unitStr = _textUnitToString(msg->Unit);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::Move",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(msg->OriginalStart, "Original Start"),
TraceLoggingValue(msg->OriginalEnd, "Original End"),
TraceLoggingValue(unitStr, "unit"),
TraceLoggingValue(msg->RequestedCount, "Requested Count"),
TraceLoggingValue(msg->MovedCount, "Moved Count"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::MoveEndpointByUnit:
{
const UiaTextRangeTracing::ApiMsgMoveEndpointByUnit* const msg = static_cast<const UiaTextRangeTracing::ApiMsgMoveEndpointByUnit* const>(apiMsg);
const wchar_t* const pEndpoint = _textPatternRangeEndpointToString(msg->Endpoint);
const wchar_t* const unitStr = _textUnitToString(msg->Unit);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::MoveEndpointByUnit",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(msg->OriginalStart, "Original Start"),
TraceLoggingValue(msg->OriginalEnd, "Original End"),
TraceLoggingValue(pEndpoint, "endpoint"),
TraceLoggingValue(unitStr, "unit"),
TraceLoggingValue(msg->RequestedCount, "Requested Count"),
TraceLoggingValue(msg->MovedCount, "Moved Count"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::MoveEndpointByRange:
{
const UiaTextRangeTracing::ApiMsgMoveEndpointByRange* const msg = static_cast<const UiaTextRangeTracing::ApiMsgMoveEndpointByRange* const>(apiMsg);
const wchar_t* const pEndpoint = _textPatternRangeEndpointToString(msg->Endpoint);
const wchar_t* const pTargetEndpoint = _textPatternRangeEndpointToString(msg->TargetEndpoint);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::MoveEndpointByRange",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(msg->OriginalStart, "Original Start"),
TraceLoggingValue(msg->OriginalEnd, "Original End"),
TraceLoggingValue(pEndpoint, "endpoint"),
TraceLoggingValue(pTargetEndpoint, "targetEndpoint"),
TraceLoggingValue(msg->OtherId, "Other's _id"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::Select:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::Select",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::AddToSelection:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::AddToSelection",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::RemoveFromSelection:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::RemoveFromSelection",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case UiaTextRangeTracing::ApiCall::ScrollIntoView:
{
const UiaTextRangeTracing::ApiMsgScrollIntoView* const msg = static_cast<const UiaTextRangeTracing::ApiMsgScrollIntoView* const>(apiMsg);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::ScrollIntoView",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingValue(msg->AlignToTop, "alignToTop"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case UiaTextRangeTracing::ApiCall::GetChildren:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"UiaTextRange::GetChildren",
TraceLoggingValue(id, "_id"),
TraceLoggingValue(start, "_start"),
TraceLoggingValue(end, "_end"),
TraceLoggingValue(degenerate, "_degenerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
default:
break;
}
}
void Tracing::s_TraceUia(const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProvider* const /*pProvider*/,
const ScreenInfoUiaProviderTracing::ApiCall apiCall,
const ScreenInfoUiaProviderTracing::IApiMsg* const apiMsg)
{
switch (apiCall)
{
case ScreenInfoUiaProviderTracing::ApiCall::Constructor:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::Constructor",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::Signal:
{
const ScreenInfoUiaProviderTracing::ApiMsgSignal* const msg = static_cast<const ScreenInfoUiaProviderTracing::ApiMsgSignal* const>(apiMsg);
const wchar_t* const signalName = _eventIdToString(msg->Signal);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::Signal",
TraceLoggingValue(msg->Signal),
TraceLoggingValue(signalName, "Event Name"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case ScreenInfoUiaProviderTracing::ApiCall::AddRef:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::AddRef",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::Release:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::Release",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::QueryInterface:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::QueryInterface",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetProviderOptions:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetProviderOptions",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetPatternProvider:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetPatternProvider",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetPropertyValue:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetPropertyValue",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetHostRawElementProvider:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetHostRawElementProvider",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::Navigate:
{
const ScreenInfoUiaProviderTracing::ApiMsgNavigate* const msg = static_cast<const ScreenInfoUiaProviderTracing::ApiMsgNavigate* const>(apiMsg);
const wchar_t* const direction = _directionToString(msg->Direction);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::Navigate",
TraceLoggingValue(direction, "direction"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case ScreenInfoUiaProviderTracing::ApiCall::GetRuntimeId:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetRuntimeId",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetBoundingRectangle:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetBoundingRectangles",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetEmbeddedFragmentRoots:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetEmbeddedFragmentRoots",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::SetFocus:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::SetFocus",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetFragmentRoot:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetFragmentRoot",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetSelection:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetSelection",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetVisibleRanges:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetVisibleRanges",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::RangeFromChild:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::RangeFromChild",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::RangeFromPoint:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::RangeFromPoint",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetDocumentRange:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetDocumentRange",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case ScreenInfoUiaProviderTracing::ApiCall::GetSupportedTextSelection:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"ScreenInfoUiaProvider::GetSupportedTextSelection",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
default:
break;
}
}
void Tracing::s_TraceUia(const Microsoft::Console::Types::WindowUiaProvider* const /*pProvider*/,
const Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall apiCall,
const Microsoft::Console::Types::WindowUiaProviderTracing::IApiMsg* const apiMsg)
{
switch (apiCall)
{
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Create:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::Create",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Signal:
{
const Microsoft::Console::Types::WindowUiaProviderTracing::ApiMessageSignal* const msg = static_cast<const Microsoft::Console::Types::WindowUiaProviderTracing::ApiMessageSignal* const>(apiMsg);
const wchar_t* const eventName = _eventIdToString(msg->Signal);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::Signal",
TraceLoggingValue(msg->Signal, "Signal"),
TraceLoggingValue(eventName, "Signal Name"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::AddRef:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::AddRef",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Release:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::Release",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::QueryInterface:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::QueryInterface",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetProviderOptions:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetProviderOptions",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetPatternProvider:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetPatternProvider",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetPropertyValue:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetPropertyValue",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetHostRawElementProvider:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetHostRawElementProvider",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Navigate:
{
const Microsoft::Console::Types::WindowUiaProviderTracing::ApiMsgNavigate* const msg = static_cast<const Microsoft::Console::Types::WindowUiaProviderTracing::ApiMsgNavigate* const>(apiMsg);
const wchar_t* const direction = _directionToString(msg->Direction);
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::Navigate",
TraceLoggingValue(direction, "direction"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
}
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetRuntimeId:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetRuntimeId",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetBoundingRectangle:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetBoundingRectangle",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetEmbeddedFragmentRoots:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetEmbeddedFragmentRoots",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::SetFocus:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::SetFocus",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetFragmentRoot:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetFragmentRoot",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::ElementProviderFromPoint:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::ElementProviderFromPoint",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetFocus:
TraceLoggingWrite(
g_hConhostV2EventTraceProvider,
"WindowUiaProvider::GetFocus",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TraceKeywords::UIA));
break;
default:
break;
}
}
#endif
const wchar_t* const Tracing::_textPatternRangeEndpointToString(int endpoint)
{
switch (endpoint)
{
case TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start:
return L"Start";
case TextPatternRangeEndpoint::TextPatternRangeEndpoint_End:
return L"End";
default:
return L"Unknown";
}
}
const wchar_t* const Tracing::_textUnitToString(int unit)
{
switch (unit)
{
case TextUnit::TextUnit_Character:
return L"TextUnit_Character";
case TextUnit::TextUnit_Format:
return L"TextUnit_Format";
case TextUnit::TextUnit_Word:
return L"TextUnit_Word";
case TextUnit::TextUnit_Line:
return L"TextUnit_Line";
case TextUnit::TextUnit_Paragraph:
return L"TextUnit_Paragraph";
case TextUnit::TextUnit_Page:
return L"TextUnit_Page";
case TextUnit::TextUnit_Document:
return L"TextUnit_Document";
default:
return L"Unknown";
}
}
const wchar_t* const Tracing::_eventIdToString(long eventId)
{
switch (eventId)
{
case UIA_AutomationFocusChangedEventId:
return L"UIA_AutomationFocusChangedEventId";
case UIA_Text_TextChangedEventId:
return L"UIA_Text_TextChangedEventId";
case UIA_Text_TextSelectionChangedEventId:
return L"UIA_Text_TextSelectionChangedEventId";
default:
return L"Unknown";
}
}
const wchar_t* const Tracing::_directionToString(int direction)
{
switch (direction)
{
case NavigateDirection::NavigateDirection_FirstChild:
return L"NavigateDirection_FirstChild";
case NavigateDirection::NavigateDirection_LastChild:
return L"NavigateDirection_LastChild";
case NavigateDirection::NavigateDirection_NextSibling:
return L"NavigateDirection_NextSibling";
case NavigateDirection::NavigateDirection_Parent:
return L"NavigateDirection_Parent";
case NavigateDirection::NavigateDirection_PreviousSibling:
return L"NavigateDirection_PreviousSibling";
default:
return L"Unknown";
}
}

View File

@@ -21,6 +21,25 @@ Author(s):
#include "../types/inc/Viewport.hpp"
namespace Microsoft::Console::Interactivity::Win32
{
class UiaTextRange;
namespace UiaTextRangeTracing
{
enum class ApiCall;
struct IApiMsg;
}
class ScreenInfoUiaProvider;
namespace ScreenInfoUiaProviderTracing
{
enum class ApiCall;
struct IApiMsg;
}
}
#if DBG
#define DBGCHARS(_params_) \
{ \
@@ -64,10 +83,30 @@ public:
static void __stdcall TraceFailure(const wil::FailureInfo& failure) noexcept;
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
#if 0
static void s_TraceUia(const Microsoft::Console::Interactivity::Win32::UiaTextRange* const range,
const Microsoft::Console::Interactivity::Win32::UiaTextRangeTracing::ApiCall apiCall,
const Microsoft::Console::Interactivity::Win32::UiaTextRangeTracing::IApiMsg* const apiMsg);
static void s_TraceUia(const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProvider* const pProvider,
const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProviderTracing::ApiCall apiCall,
const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProviderTracing::IApiMsg* const apiMsg);
static void s_TraceUia(const Microsoft::Console::Types::WindowUiaProvider* const pProvider,
const Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall apiCall,
const Microsoft::Console::Types::WindowUiaProviderTracing::IApiMsg* const apiMsg);
#endif
private:
static ULONG s_ulDebugFlag;
Tracing(std::function<void()> onExit);
std::function<void()> _onExit;
static const wchar_t* const _textPatternRangeEndpointToString(int endpoint);
static const wchar_t* const _textUnitToString(int unit);
static const wchar_t* const _eventIdToString(long eventId);
static const wchar_t* const _directionToString(int direction);
};

View File

@@ -323,7 +323,7 @@ class SelectionTests
// selection rectangle starts from the target and goes for the length requested
srSelection.Left = coordTargetPoint.X;
srSelection.Right = coordTargetPoint.X + sStringLength;
srSelection.Right = coordTargetPoint.X + sStringLength - 1;
// save original for comparison
srOriginal.Top = srSelection.Top;
@@ -331,12 +331,7 @@ class SelectionTests
srOriginal.Left = srSelection.Left;
srOriginal.Right = srSelection.Right;
COORD startPos{ sTargetX, sTargetY };
COORD endPos{ base::ClampAdd(sTargetX, sLength), sTargetY };
const auto selectionRects = screenInfo.GetTextBuffer().GetTextRects(startPos, endPos);
VERIFY_ARE_EQUAL(static_cast<size_t>(1), selectionRects.size());
srSelection = selectionRects.at(0);
srSelection = Selection::s_BisectSelection(sStringLength, coordTargetPoint, screenInfo, srSelection);
VERIFY_ARE_EQUAL(srOriginal.Top, srSelection.Top);
VERIFY_ARE_EQUAL(srOriginal.Bottom, srSelection.Bottom);
@@ -383,10 +378,10 @@ class SelectionTests
// start from position 10 before end of row (80 length row)
// row is 2
// selection is 9 characters long
// selection is 10 characters long
// the left edge shouldn't move
// the right edge should move one to the left (-1) to not select the leading byte
TestBisectSelectionDelta(70, 2, 9, 0, -1);
TestBisectSelectionDelta(70, 2, 10, 0, -1);
// 2b. End position is leading half and is elsewhere in the row
@@ -394,16 +389,16 @@ class SelectionTests
// row is 2
// selection is 10 characters long
// the left edge shouldn't move
// the right edge should not move, because it is already on the trailing byte
TestBisectSelectionDelta(58, 2, 10, 0, 0);
// the right edge should move one to the right (+1) to add the trailing byte to the selection
TestBisectSelectionDelta(58, 2, 10, 0, 1);
// 2c. End position is leading half and is at end of buffer
// start from position 10 before end of row (80 length row)
// row is 300 (or 299 for the index)
// selection is 9 characters long
// selection is 10 characters long
// the left edge shouldn't move
// the right edge should move one to the left (-1) to not select the leading byte
TestBisectSelectionDelta(70, 299, 9, 0, -1);
// the right edge shouldn't move
TestBisectSelectionDelta(70, 299, 10, 0, 0);
}
};

View File

@@ -148,8 +148,6 @@ class TextBufferTests
void WriteLinesToBuffer(const std::vector<std::wstring>& text, TextBuffer& buffer);
TEST_METHOD(GetWordBoundaries);
TEST_METHOD(GetTextRects);
};
void TextBufferTests::TestBufferCreate()
@@ -2138,68 +2136,3 @@ void TextBufferTests::GetWordBoundaries()
VERIFY_ARE_EQUAL(expected, result);
}
}
void TextBufferTests::GetTextRects()
{
// GetTextRects() is used to...
// - Represent selection rects
// - Represent UiaTextRanges for accessibility
// This is the burrito emoji: 🌯
// It's encoded in UTF-16, as needed by the buffer.
const auto burrito = std::wstring(L"\xD83C\xDF2F");
COORD bufferSize{ 20, 50 };
UINT cursorSize = 12;
TextAttribute attr{ 0x7f };
auto _buffer = std::make_unique<TextBuffer>(bufferSize, attr, cursorSize, _renderTarget);
// Setup: Write lines of text to the buffer
const std::vector<std::wstring> text = { L"0123456789",
L" " + burrito + L"3456" + burrito,
L" " + burrito + L"45" + burrito,
burrito + L"234567" + burrito,
L"0123456789" };
WriteLinesToBuffer(text, *_buffer);
// - - - Text Buffer Contents - - -
// |0123456789
// | 🌯3456🌯
// | 🌯45🌯
// |🌯234567🌯
// |0123456789
// - - - - - - - - - - - - - - - -
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"Data:blockSelection", L"{false, true}")
END_TEST_METHOD_PROPERTIES();
bool blockSelection;
VERIFY_SUCCEEDED(TestData::TryGetValue(L"blockSelection", blockSelection), L"Get 'blockSelection' variant");
std::vector<SMALL_RECT> expected{};
if (blockSelection)
{
expected.push_back({ 1, 0, 7, 0 });
expected.push_back({ 1, 1, 8, 1 }); // expand right
expected.push_back({ 1, 2, 7, 2 });
expected.push_back({ 0, 3, 7, 3 }); // expand left
expected.push_back({ 1, 4, 7, 4 });
}
else
{
expected.push_back({ 1, 0, 19, 0 });
expected.push_back({ 0, 1, 19, 1 });
expected.push_back({ 0, 2, 19, 2 });
expected.push_back({ 0, 3, 19, 3 });
expected.push_back({ 0, 4, 7, 4 });
}
COORD start{ 1, 0 };
COORD end{ 7, 4 };
const auto result = _buffer->GetTextRects(start, end, blockSelection);
VERIFY_ARE_EQUAL(expected.size(), result.size());
for (size_t i = 0; i < expected.size(); ++i)
{
VERIFY_ARE_EQUAL(expected.at(i), result.at(i));
}
}

View File

@@ -604,7 +604,7 @@ void VtRendererTest::Xterm256TestCursor()
clusters.emplace_back(std::wstring_view{ &line[i], 1 }, static_cast<size_t>(rgWidths[i]));
}
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters.data(), clusters.size() }, { 1, 1 }, false, false));
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters.data(), clusters.size() }, { 1, 1 }, false));
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
VERIFY_SUCCEEDED(engine->_MoveCursor({ 10, 1 }));
@@ -1020,7 +1020,7 @@ void VtRendererTest::XtermTestCursor()
clusters.emplace_back(std::wstring_view{ &line[i], 1 }, static_cast<size_t>(rgWidths[i]));
}
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters.data(), clusters.size() }, { 1, 1 }, false, false));
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters.data(), clusters.size() }, { 1, 1 }, false));
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
VERIFY_SUCCEEDED(engine->_MoveCursor({ 10, 1 }));
@@ -1250,7 +1250,7 @@ void VtRendererTest::WinTelnetTestCursor()
clusters.emplace_back(std::wstring_view{ &line[i], 1 }, static_cast<size_t>(rgWidths[i]));
}
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters.data(), clusters.size() }, { 1, 1 }, false, false));
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters.data(), clusters.size() }, { 1, 1 }, false));
qExpectedInput.push_back(EMPTY_CALLBACK_SENTINEL);
VERIFY_SUCCEEDED(engine->_MoveCursor({ 10, 1 }));
@@ -1315,8 +1315,8 @@ void VtRendererTest::TestWrapping()
clusters2.emplace_back(std::wstring_view{ &line2[i], 1 }, static_cast<size_t>(rgWidths[i]));
}
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters1.data(), clusters1.size() }, { 0, 0 }, false, false));
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters2.data(), clusters2.size() }, { 0, 1 }, false, false));
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters1.data(), clusters1.size() }, { 0, 0 }, false));
VERIFY_SUCCEEDED(engine->PaintBufferLine({ clusters2.data(), clusters2.size() }, { 0, 1 }, false));
});
}

View File

@@ -84,8 +84,8 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
if ((*backIter & _Utf8BitMasks::MaskAsciiByte) > _Utf8BitMasks::IsAsciiByte)
{
// Check only up to 3 last bytes, if no Lead Byte was found then the byte before must be the Lead Byte and no partials are in the string
const size_t stopLen{ std::min(in.length(), gsl::narrow_cast<size_t>(3u)) };
for (size_t sequenceLen{ 1u }; sequenceLen <= stopLen; ++sequenceLen, --backIter)
const size_t stopLen{ std::min(in.length(), gsl::narrow_cast<size_t>(4u)) };
for (size_t sequenceLen{ 1u }; sequenceLen < stopLen; ++sequenceLen, --backIter)
{
// If Lead Byte found
if ((*backIter & _Utf8BitMasks::MaskContinuationByte) > _Utf8BitMasks::IsContinuationByte)

View File

@@ -147,8 +147,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
[[nodiscard]] HRESULT BgfxEngine::PaintBufferLine(const std::basic_string_view<Cluster> clusters,
const COORD coord,
const bool /*trimLeft*/,
const bool /*lineWrapped*/) noexcept
const bool /*trimLeft*/) noexcept
{
try
{

View File

@@ -51,8 +51,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(const std::basic_string_view<Cluster> clusters,
const COORD coord,
const bool trimLeft,
const bool lineWrapped) noexcept override;
const bool trimLeft) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override;

View File

@@ -97,22 +97,26 @@ void ScreenInfoUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
_pUiaParent->ChangeViewport(NewWindow);
}
HRESULT ScreenInfoUiaProvider::GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr)
HRESULT ScreenInfoUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<ComPtr<UiaTextRangeBase>>& result)
{
RETURN_HR_IF_NULL(E_INVALIDARG, ppUtr);
*ppUtr = nullptr;
try
{
result.clear();
typename std::remove_reference<decltype(result)>::type temporaryResult;
const auto start = _pData->GetSelectionAnchor();
std::deque<ComPtr<UiaTextRange>> ranges;
RETURN_IF_FAILED(UiaTextRange::GetSelectionRanges(_pData, pProvider, wordDelimiters, ranges));
// we need to make end exclusive
auto end = _pData->GetSelectionEnd();
_pData->GetTextBuffer().GetSize().IncrementInBounds(end, true);
while (!ranges.empty())
{
temporaryResult.emplace_back(std::move(ranges.back()));
ranges.pop_back();
}
// TODO GH #4509: Box Selection is misrepresented here as a line selection.
UiaTextRange* result;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&result, _pData, pProvider, start, end, wordDelimiters));
*ppUtr = result;
return S_OK;
std::swap(result, temporaryResult);
return S_OK;
}
CATCH_RETURN();
}
HRESULT ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ UiaTextRangeBase** ppUtr)

View File

@@ -44,7 +44,7 @@ namespace Microsoft::Console::Interactivity::Win32
void ChangeViewport(const SMALL_RECT NewWindow) override;
protected:
HRESULT GetSelectionRange(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque<WRL::ComPtr<Microsoft::Console::Types::UiaTextRangeBase>>& selectionRanges) override;
// degenerate range
HRESULT CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, const std::wstring_view wordDelimiters, _COM_Outptr_result_maybenull_ Microsoft::Console::Types::UiaTextRangeBase** ppUtr) override;

View File

@@ -12,8 +12,36 @@ using namespace Microsoft::Console::Interactivity::Win32;
using namespace Microsoft::WRL;
using Microsoft::Console::Interactivity::ServiceLocator;
HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* pProvider,
const std::wstring_view wordDelimiters,
_Out_ std::deque<ComPtr<UiaTextRange>>& ranges)
{
try
{
typename std::remove_reference<decltype(ranges)>::type temporaryResult;
// get the selection rects
const auto rectangles = pData->GetSelectionRects();
// create a range for each row
for (const auto& rect : rectangles)
{
const auto start = rect.Origin();
const auto end = rect.EndExclusive();
ComPtr<UiaTextRange> range;
RETURN_IF_FAILED(MakeAndInitialize<UiaTextRange>(&range, pData, pProvider, start, end, wordDelimiters));
temporaryResult.emplace_back(std::move(range));
}
std::swap(temporaryResult, ranges);
return S_OK;
}
CATCH_RETURN();
}
// degenerate range constructor.
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters) noexcept
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, _In_ const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, wordDelimiters);
}
@@ -22,7 +50,7 @@ HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ IRawElem
HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor,
const std::wstring_view wordDelimiters) noexcept
const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, cursor, wordDelimiters);
}
@@ -32,7 +60,7 @@ HRESULT UiaTextRange::RuntimeClassInitialize(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const COORD start,
const COORD end,
const std::wstring_view wordDelimiters) noexcept
const std::wstring_view wordDelimiters)
{
return UiaTextRangeBase::RuntimeClassInitialize(pData, pProvider, start, end, wordDelimiters);
}

View File

@@ -24,25 +24,30 @@ namespace Microsoft::Console::Interactivity::Win32
class UiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase
{
public:
static HRESULT GetSelectionRanges(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* pProvider,
_In_ const std::wstring_view wordDelimiters,
_Out_ std::deque<WRL::ComPtr<UiaTextRange>>& ranges);
UiaTextRange() = default;
// degenerate range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// degenerate range at cursor position
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
const Cursor& cursor,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// specific endpoint range
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,
_In_ IRawElementProviderSimple* const pProvider,
_In_ const COORD start,
_In_ const COORD end,
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter) noexcept override;
_In_ const std::wstring_view wordDelimiters = DefaultWordDelimiter);
// range from a UiaPoint
HRESULT RuntimeClassInitialize(_In_ Microsoft::Console::Types::IUiaData* pData,

View File

@@ -597,23 +597,15 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
// Retrieve the cell information iterator limited to just this line we want to redraw.
auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine);
// Calculate if two things are true:
// 1. this row wrapped
// 2. We're painting the last col of the row.
// In that case, set lineWrapped=true for the _PaintBufferOutputHelper call.
const auto lineWrapped = (buffer.GetRowByOffset(bufferLine.Origin().Y).GetCharRow().WasWrapForced()) &&
(bufferLine.RightExclusive() == buffer.GetSize().Width());
// Ask the helper to paint through this specific line.
_PaintBufferOutputHelper(pEngine, it, screenLine.Origin(), lineWrapped);
_PaintBufferOutputHelper(pEngine, it, screenLine.Origin());
}
}
}
void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
TextBufferCellIterator it,
const COORD target,
const bool lineWrapped)
const COORD target)
{
// If we have valid data, let's figure out how to draw it.
if (it)
@@ -672,7 +664,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// Do the painting.
// TODO: Calculate when trim left should be TRUE
THROW_IF_FAILED(pEngine->PaintBufferLine({ clusters.data(), clusters.size() }, screenPoint, false, lineWrapped));
THROW_IF_FAILED(pEngine->PaintBufferLine({ clusters.data(), clusters.size() }, screenPoint, false));
// If we're allowed to do grid drawing, draw that now too (since it will be coupled with the color data)
if (_pData->IsGridLineDrawingAllowed())
@@ -821,7 +813,7 @@ void Renderer::_PaintOverlay(IRenderEngine& engine,
auto it = overlay.buffer.GetCellLineDataAt(source);
_PaintBufferOutputHelper(&engine, it, target, false);
_PaintBufferOutputHelper(&engine, it, target);
}
}
}

View File

@@ -96,8 +96,7 @@ namespace Microsoft::Console::Render
void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
TextBufferCellIterator it,
const COORD target,
const bool lineWrapped);
const COORD target);
static IRenderEngine::GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept;

View File

@@ -15,10 +15,10 @@ RenderThread::RenderThread() :
_hEvent(nullptr),
_hPaintCompletedEvent(nullptr),
_fKeepRunning(true),
_hPaintEnabledEvent(nullptr),
_fNextFrameRequested(false),
_fWaiting(false)
_hPaintEnabledEvent(nullptr)
{
_fNextFrameRequested.clear();
_fPainting.clear();
}
RenderThread::~RenderThread()
@@ -161,45 +161,20 @@ DWORD WINAPI RenderThread::_ThreadProc()
{
WaitForSingleObject(_hPaintEnabledEvent, INFINITE);
if (!_fNextFrameRequested.exchange(false))
// Skip waiting if next frame is requested.
if (_fNextFrameRequested.test_and_set(std::memory_order_relaxed))
{
// <--
// If `NotifyPaint` is called at this point, then it will not
// set the event because `_fWaiting` is not `true` yet so we have
// to check again below.
_fWaiting.store(true);
// check again now (see comment above)
if (!_fNextFrameRequested.exchange(false))
{
// Wait until a next frame is requested.
WaitForSingleObject(_hEvent, INFINITE);
}
// <--
// If `NotifyPaint` is called at this point, then it _will_ set
// the event because `_fWaiting` is `true`, but we're not waiting
// anymore!
// This can probably happen quite often: imagine a scenario where
// we are waiting, and the terminal calls `NotifyPaint` twice
// very quickly.
// In that case, both calls might end up calling `SetEvent`. The
// first one will resume this thread and the second one will
// `SetEvent` the event. So the next time we wait, the event will
// already be set and we won't actually wait.
// Because it can happen often, and because rendering is an
// expensive operation, we should reset the event to not render
// again if nothing changed.
_fWaiting.store(false);
// see comment above
ResetEvent(_hEvent);
_fNextFrameRequested.clear(std::memory_order_relaxed);
}
else
{
WaitForSingleObject(_hEvent, INFINITE);
}
ResetEvent(_hPaintCompletedEvent);
_fPainting.test_and_set(std::memory_order_acquire);
LOG_IF_FAILED(_pRenderer->PaintFrame());
SetEvent(_hPaintCompletedEvent);
@@ -209,6 +184,8 @@ DWORD WINAPI RenderThread::_ThreadProc()
{
Sleep(s_FrameLimitMilliseconds);
}
_fPainting.clear(std::memory_order_release);
}
return S_OK;
@@ -216,14 +193,15 @@ DWORD WINAPI RenderThread::_ThreadProc()
void RenderThread::NotifyPaint()
{
if (_fWaiting.load())
// If we are currently painting a frame, set _fNextFrameRequested flag
// to indicate we want to paint next frame immediately.
if (_fPainting.test_and_set(std::memory_order_acquire))
{
SetEvent(_hEvent);
}
else
{
_fNextFrameRequested.store(true);
_fNextFrameRequested.test_and_set(std::memory_order_relaxed);
return;
}
SetEvent(_hEvent);
}
void RenderThread::EnablePainting()

View File

@@ -47,7 +47,7 @@ namespace Microsoft::Console::Render
IRenderer* _pRenderer; // Non-ownership pointer
bool _fKeepRunning;
std::atomic<bool> _fNextFrameRequested;
std::atomic<bool> _fWaiting;
std::atomic_flag _fNextFrameRequested;
std::atomic_flag _fPainting;
};
}

View File

@@ -20,8 +20,6 @@
using namespace DirectX;
std::atomic<size_t> Microsoft::Console::Render::DxEngine::_tracelogCount{ 0 };
#pragma warning(suppress : 26477)
TRACELOGGING_DEFINE_PROVIDER(g_hDxRenderProvider,
"Microsoft.Windows.Terminal.Renderer.DirectX",
// {c93e739e-ae50-5a14-78e7-f171e947535d}
@@ -82,19 +80,15 @@ DxEngine::DxEngine() :
_glyphCell{ 0 },
_haveDeviceResources{ false },
_retroTerminalEffects{ false },
_antialiasingMode{ D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE },
_hwndTarget{ static_cast<HWND>(INVALID_HANDLE_VALUE) },
_sizeTarget{ 0 },
_dpi{ USER_DEFAULT_SCREEN_DPI },
_scale{ 1.0f },
_chainMode{ SwapChainMode::ForComposition },
_customRenderer{ ::Microsoft::WRL::Make<CustomTextRenderer>() }
_customRenderer{ ::Microsoft::WRL::Make<CustomTextRenderer>() },
_hasEverPresented{ false }
{
const auto was = _tracelogCount.fetch_add(1);
if (0 == was)
{
TraceLoggingRegister(g_hDxRenderProvider);
}
TraceLoggingRegister(g_hDxRenderProvider);
THROW_IF_FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(&_d2dFactory)));
@@ -114,11 +108,7 @@ DxEngine::~DxEngine()
{
_ReleaseDeviceResources();
const auto was = _tracelogCount.fetch_sub(1);
if (1 == was)
{
TraceLoggingUnregister(g_hDxRenderProvider);
}
TraceLoggingUnregister(g_hDxRenderProvider);
}
// Routine Description:
@@ -164,6 +154,10 @@ DxEngine::~DxEngine()
{
_ReleaseDeviceResources();
}
else
{
RETURN_IF_FAILED(_CreateDeviceResources(true));
}
return S_OK;
}
@@ -286,24 +280,12 @@ HRESULT DxEngine::_SetupTerminalEffects()
RETURN_IF_FAILED(_d3dDevice->CreateBuffer(&bd, &InitData, &_screenQuadVertexBuffer));
D3D11_BUFFER_DESC pixelShaderSettingsBufferDesc{};
pixelShaderSettingsBufferDesc.Usage = D3D11_USAGE_DEFAULT;
pixelShaderSettingsBufferDesc.ByteWidth = sizeof(_pixelShaderSettings);
pixelShaderSettingsBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
_ComputePixelShaderSettings();
D3D11_SUBRESOURCE_DATA pixelShaderSettingsInitData{};
pixelShaderSettingsInitData.pSysMem = &_pixelShaderSettings;
RETURN_IF_FAILED(_d3dDevice->CreateBuffer(&pixelShaderSettingsBufferDesc, &pixelShaderSettingsInitData, &_pixelShaderSettingsBuffer));
// Sampler state is needed to use texture as input to shader.
D3D11_SAMPLER_DESC samplerDesc{};
samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
@@ -320,22 +302,6 @@ HRESULT DxEngine::_SetupTerminalEffects()
return S_OK;
}
// Routine Description:
// - Puts the correct values in _pixelShaderSettings, so the struct can be
// passed the GPU.
// Arguments:
// - <none>
// Return Value:
// - <none>
void DxEngine::_ComputePixelShaderSettings() noexcept
{
// Retro scan lines alternate every pixel row at 100% scaling.
_pixelShaderSettings.ScaledScanLinePeriod = _scale * 1.0f;
// Gaussian distribution sigma used for blurring.
_pixelShaderSettings.ScaledGaussianSigma = _scale * 2.0f;
}
// Routine Description;
// - Creates device-specific resources required for drawing
// which generally means those that are represented on the GPU and can
@@ -370,7 +336,7 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
// You can find out how to install it here:
// https://docs.microsoft.com/en-us/windows/uwp/gaming/use-the-directx-runtime-and-visual-studio-graphics-diagnostic-features
// clang-format on
// D3D11_CREATE_DEVICE_DEBUG |
D3D11_CREATE_DEVICE_DEBUG |
D3D11_CREATE_DEVICE_SINGLETHREADED;
const std::array<D3D_FEATURE_LEVEL, 5> FeatureLevels{ D3D_FEATURE_LEVEL_11_1,
@@ -424,8 +390,7 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
{
switch (_chainMode)
{
case SwapChainMode::ForHwnd:
{
case SwapChainMode::ForHwnd: {
// use the HWND's dimensions for the swap chain dimensions.
RECT rect = { 0 };
RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect));
@@ -454,8 +419,7 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
break;
}
case SwapChainMode::ForComposition:
{
case SwapChainMode::ForComposition: {
// Use the given target size for compositions.
SwapChainDesc.Width = _displaySizePixels.cx;
SwapChainDesc.Height = _displaySizePixels.cy;
@@ -475,6 +439,8 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
THROW_HR(E_NOTIMPL);
}
_hasEverPresented = false;
if (_retroTerminalEffects)
{
const HRESULT hr = _SetupTerminalEffects();
@@ -533,8 +499,7 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
&props,
&_d2dRenderTarget));
_d2dRenderTarget->SetTextAntialiasMode(_antialiasingMode);
_d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
RETURN_IF_FAILED(_d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::DarkRed),
&_d2dBrushBackground));
@@ -567,6 +532,9 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
RETURN_IF_FAILED(sc2->SetMatrixTransform(&inverseScale));
}
_hasEverPresented = false;
return S_OK;
}
CATCH_RETURN();
@@ -595,6 +563,7 @@ void DxEngine::_ReleaseDeviceResources() noexcept
_dxgiSurface.Reset();
_dxgiSwapChain.Reset();
_hasEverPresented = false;
if (nullptr != _d3dDeviceContext.Get())
{
@@ -826,8 +795,7 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
{
switch (_chainMode)
{
case SwapChainMode::ForHwnd:
{
case SwapChainMode::ForHwnd: {
RECT clientRect = { 0 };
LOG_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &clientRect));
@@ -837,8 +805,7 @@ Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
return clientSize;
}
case SwapChainMode::ForComposition:
{
case SwapChainMode::ForComposition: {
SIZE size = _sizeTarget;
size.cx = static_cast<LONG>(size.cx * _scale);
size.cy = static_cast<LONG>(size.cy * _scale);
@@ -971,24 +938,19 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
// - Any DirectX error, a memory error, etc.
[[nodiscard]] HRESULT DxEngine::StartPaint() noexcept
{
FAIL_FAST_IF_FAILED(InvalidateAll());
RETURN_HR_IF(E_NOT_VALID_STATE, _isPainting); // invalid to start a paint while painting.
#pragma warning(suppress : 26477 26485 26494 26482 26446 26447) // We don't control TraceLoggingWrite
// TODO: not sure why this is happening at all, but it is
RETURN_HR_IF(S_FALSE, IsRectEmpty(&_invalidRect) && _invalidScroll.cx == 0 && _invalidScroll.cy == 0);
TraceLoggingWrite(g_hDxRenderProvider,
"Invalid",
TraceLoggingInt32(_invalidRect.bottom - _invalidRect.top, "InvalidHeight"),
TraceLoggingInt32((_invalidRect.bottom - _invalidRect.top) / _glyphCell.cy, "InvalidHeightChars"),
TraceLoggingInt32(_invalidRect.right - _invalidRect.left, "InvalidWidth"),
TraceLoggingInt32((_invalidRect.right - _invalidRect.left) / _glyphCell.cx, "InvalidWidthChars"),
TraceLoggingInt32(_invalidRect.left, "InvalidX"),
TraceLoggingInt32(_invalidRect.left / _glyphCell.cx, "InvalidXChars"),
TraceLoggingInt32(_invalidRect.top, "InvalidY"),
TraceLoggingInt32(_invalidRect.top / _glyphCell.cy, "InvalidYChars"),
TraceLoggingInt32(_invalidScroll.cx, "ScrollWidth"),
TraceLoggingInt32(_invalidScroll.cx / _glyphCell.cx, "ScrollWidthChars"),
TraceLoggingInt32(_invalidScroll.cy, "ScrollHeight"),
TraceLoggingInt32(_invalidScroll.cy / _glyphCell.cy, "ScrollHeightChars"));
TraceLoggingInt32(_invalidScroll.cy, "ScrollHeight"));
if (_isEnabled)
{
@@ -1052,18 +1014,18 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
if (SUCCEEDED(hr))
{
_presentDirty = _invalidRect;
_presentParams.DirtyRectsCount = 1;
_presentParams.pDirtyRects = &_presentDirty;
if (_invalidScroll.cy != 0 || _invalidScroll.cx != 0)
{
_presentDirty = _invalidRect;
const RECT display = _GetDisplayRect();
SubtractRect(&_presentScroll, &display, &_presentDirty);
_presentOffset.x = _invalidScroll.cx;
_presentOffset.y = _invalidScroll.cy;
_presentParams.DirtyRectsCount = 1;
_presentParams.pDirtyRects = &_presentDirty;
_presentParams.pScrollOffset = &_presentOffset;
_presentParams.pScrollRect = &_presentScroll;
@@ -1125,24 +1087,31 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
// - S_OK on success, E_PENDING to indicate a retry or a relevant DirectX error
[[nodiscard]] HRESULT DxEngine::Present() noexcept
{
if (_retroTerminalEffects)
{
const HRESULT hr2 = _PaintTerminalEffects();
if (FAILED(hr2))
{
_retroTerminalEffects = false;
LOG_HR_MSG(hr2, "Failed to paint terminal effects. Disabling.");
}
}
if (_presentReady)
{
if (_retroTerminalEffects)
{
const HRESULT hr2 = _PaintTerminalEffects();
if (FAILED(hr2))
{
_retroTerminalEffects = false;
LOG_HR_MSG(hr2, "Failed to paint terminal effects. Disabling.");
}
}
try
{
HRESULT hr = S_OK;
hr = _dxgiSwapChain->Present(1, 0);
/*hr = _dxgiSwapChain->Present1(1, 0, &_presentParams);*/
if (_hasEverPresented)
{
hr = _dxgiSwapChain->Present1(1, 0, &_presentParams);
}
else
{
hr = _dxgiSwapChain->Present(1, 0);
_hasEverPresented = true;
}
if (FAILED(hr))
{
@@ -1191,19 +1160,23 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
// - S_OK
[[nodiscard]] HRESULT DxEngine::PaintBackground() noexcept
{
const auto rect = D2D1::RectF(static_cast<float>(_invalidRect.left),
static_cast<float>(_invalidRect.top),
static_cast<float>(_invalidRect.right),
static_cast<float>(_invalidRect.bottom));
switch (_chainMode)
{
case SwapChainMode::ForHwnd:
_d2dRenderTarget->FillRectangle(D2D1::RectF(static_cast<float>(_invalidRect.left),
static_cast<float>(_invalidRect.top),
static_cast<float>(_invalidRect.right),
static_cast<float>(_invalidRect.bottom)),
_d2dBrushBackground.Get());
break;
case SwapChainMode::ForComposition:
D2D1_COLOR_F nothing = { 0 };
_d2dRenderTarget->PushAxisAlignedClip(rect,
D2D1_ANTIALIAS_MODE::D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
D2D1_COLOR_F nothing = { 0 };
_d2dRenderTarget->Clear(nothing);
_d2dRenderTarget->PopAxisAlignedClip();
break;
}
@@ -1220,8 +1193,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept
// - S_OK or relevant DirectX error
[[nodiscard]] HRESULT DxEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
COORD const coord,
const bool /*trimLeft*/,
const bool /*lineWrapped*/) noexcept
const bool /*trimLeft*/) noexcept
{
try
{
@@ -1411,8 +1383,7 @@ enum class CursorPaintType
switch (options.cursorType)
{
case CursorType::Legacy:
{
case CursorType::Legacy: {
// Enforce min/max cursor height
ULONG ulHeight = std::clamp(options.ulCursorHeightPercent, s_ulMinCursorHeightPercent, s_ulMaxCursorHeightPercent);
@@ -1420,26 +1391,22 @@ enum class CursorPaintType
rect.top = rect.bottom - ulHeight;
break;
}
case CursorType::VerticalBar:
{
case CursorType::VerticalBar: {
// It can't be wider than one cell or we'll have problems in invalidation, so restrict here.
// It's either the left + the proposed width from the ease of access setting, or
// it's the right edge of the block cursor as a maximum.
rect.right = std::min(rect.right, rect.left + options.cursorPixelWidth);
break;
}
case CursorType::Underscore:
{
case CursorType::Underscore: {
rect.top = rect.bottom - 1;
break;
}
case CursorType::EmptyBox:
{
case CursorType::EmptyBox: {
paintType = CursorPaintType::Outline;
break;
}
case CursorType::FullBox:
{
case CursorType::FullBox: {
break;
}
default:
@@ -1456,13 +1423,11 @@ enum class CursorPaintType
switch (paintType)
{
case CursorPaintType::Fill:
{
case CursorPaintType::Fill: {
_d2dRenderTarget->FillRectangle(rect, brush.Get());
break;
}
case CursorPaintType::Outline:
{
case CursorPaintType::Outline: {
// DrawRectangle in straddles physical pixels in an attempt to draw a line
// between them. To avoid this, bump the rectangle around by half the stroke width.
rect.top += 0.5f;
@@ -1521,7 +1486,6 @@ try
_d3dDeviceContext->PSSetShader(_pixelShader.Get(), nullptr, 0);
_d3dDeviceContext->PSSetShaderResources(0, 1, shaderResource.GetAddressOf());
_d3dDeviceContext->PSSetSamplers(0, 1, _samplerState.GetAddressOf());
_d3dDeviceContext->PSSetConstantBuffers(0, 1, _pixelShaderSettingsBuffer.GetAddressOf());
_d3dDeviceContext->Draw(ARRAYSIZE(_screenQuadVertices), 0);
return S_OK;
@@ -1619,16 +1583,6 @@ CATCH_RETURN()
RETURN_IF_FAILED(InvalidateAll());
if (_retroTerminalEffects && _d3dDeviceContext && _pixelShaderSettingsBuffer)
{
_ComputePixelShaderSettings();
try
{
_d3dDeviceContext->UpdateSubresource(_pixelShaderSettingsBuffer.Get(), 0, NULL, &_pixelShaderSettings, 0, 0);
}
CATCH_RETURN();
}
return S_OK;
}
@@ -2127,12 +2081,10 @@ float DxEngine::GetScaling() const noexcept
switch (_chainMode)
{
case SwapChainMode::ForHwnd:
{
case SwapChainMode::ForHwnd: {
return D2D1::ColorF(rgb);
}
case SwapChainMode::ForComposition:
{
case SwapChainMode::ForComposition: {
// Get the A value we've snuck into the highest byte
const BYTE a = ((color >> 24) & 0xFF);
const float aFloat = a / 255.0f;
@@ -2157,17 +2109,3 @@ void DxEngine::SetSelectionBackground(const COLORREF color) noexcept
GetBValue(color) / 255.0f,
0.5f);
}
// Routine Description:
// - Changes the antialiasing mode of the renderer. This must be called before
// _PrepareRenderTarget, otherwise the renderer will default to
// D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE.
// Arguments:
// - antialiasingMode: a value from the D2D1_TEXT_ANTIALIAS_MODE enum. See:
// https://docs.microsoft.com/en-us/windows/win32/api/d2d1/ne-d2d1-d2d1_text_antialias_mode
// Return Value:
// - N/A
void DxEngine::SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept
{
_antialiasingMode = antialiasingMode;
}

View File

@@ -76,8 +76,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
COORD const coord,
bool const fTrimLeft,
const bool lineWrapped) noexcept override;
bool const fTrimLeft) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override;
@@ -105,7 +104,6 @@ namespace Microsoft::Console::Render
float GetScaling() const noexcept;
void SetSelectionBackground(const COLORREF color) noexcept;
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept;
protected:
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring& newTitle) noexcept override;
@@ -151,14 +149,13 @@ namespace Microsoft::Console::Render
void _InvalidOffset(POINT pt);
bool _hasEverPresented;
bool _presentReady;
RECT _presentDirty;
RECT _presentScroll;
POINT _presentOffset;
DXGI_PRESENT_PARAMETERS _presentParams;
static std::atomic<size_t> _tracelogCount;
static const ULONG s_ulMinCursorHeightPercent = 25;
static const ULONG s_ulMaxCursorHeightPercent = 100;
@@ -189,23 +186,11 @@ namespace Microsoft::Console::Render
::Microsoft::WRL::ComPtr<ID3D11PixelShader> _pixelShader;
::Microsoft::WRL::ComPtr<ID3D11InputLayout> _vertexLayout;
::Microsoft::WRL::ComPtr<ID3D11Buffer> _screenQuadVertexBuffer;
::Microsoft::WRL::ComPtr<ID3D11Buffer> _pixelShaderSettingsBuffer;
::Microsoft::WRL::ComPtr<ID3D11SamplerState> _samplerState;
::Microsoft::WRL::ComPtr<ID3D11Texture2D> _framebufferCapture;
D2D1_TEXT_ANTIALIAS_MODE _antialiasingMode;
// DirectX constant buffers need to be a multiple of 16; align to pad the size.
__declspec(align(16)) struct
{
float ScaledScanLinePeriod;
float ScaledGaussianSigma;
#pragma warning(suppress : 4324) // structure was padded due to __declspec(align())
} _pixelShaderSettings;
[[nodiscard]] HRESULT _CreateDeviceResources(const bool createSwapChain) noexcept;
HRESULT _SetupTerminalEffects();
void _ComputePixelShaderSettings() noexcept;
[[nodiscard]] HRESULT _PrepareRenderTarget() noexcept;

View File

@@ -7,13 +7,8 @@ const char screenPixelShaderString[] = R"(
Texture2D shaderTexture;
SamplerState samplerState;
cbuffer PixelShaderSettings
{
float ScaledScanLinePeriod;
float ScaledGaussianSigma;
};
#define SCANLINE_FACTOR 0.5
#define SCANLINE_PERIOD 1
static const float M_PI = 3.14159265f;
@@ -31,6 +26,7 @@ float4 Blur(Texture2D input, float2 tex_coord, float sigma)
float texelHeight = 1.0f/height;
float4 color = { 0, 0, 0, 0 };
float factor = 1;
int sampleCount = 13;
@@ -53,7 +49,7 @@ float4 Blur(Texture2D input, float2 tex_coord, float sigma)
float SquareWave(float y)
{
return 1 - (floor(y / ScaledScanLinePeriod) % 2) * SCANLINE_FACTOR;
return 1 - (floor(y / SCANLINE_PERIOD) % 2) * SCANLINE_FACTOR;
}
float4 Scanline(float4 color, float4 pos)
@@ -78,7 +74,7 @@ float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
// TODO:GH#3930 Make these configurable in some way.
float4 color = input.Sample(samplerState, tex);
color += Blur(input, tex, ScaledGaussianSigma)*0.3;
color += Blur(input, tex, 2)*0.3;
color = Scanline(color, pos);
return color;

View File

@@ -44,8 +44,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
const COORD coord,
const bool trimLeft,
const bool lineWrapped) noexcept override;
const bool trimLeft) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(const GridLines lines,
const COLORREF color,
const size_t cchLine,

View File

@@ -286,8 +286,7 @@ using namespace Microsoft::Console::Render;
//#define MAX_POLY_LINES 80
[[nodiscard]] HRESULT GdiEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
const COORD coord,
const bool trimLeft,
const bool /*lineWrapped*/) noexcept
const bool trimLeft) noexcept
{
try
{

View File

@@ -93,8 +93,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
const COORD coord,
const bool fTrimLeft,
const bool lineWrapped) noexcept = 0;
const bool fTrimLeft) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBufferGridLines(const GridLines lines,
const COLORREF color,
const size_t cchLine,

View File

@@ -276,8 +276,7 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
// - S_FALSE
[[nodiscard]] HRESULT UiaEngine::PaintBufferLine(std::basic_string_view<Cluster> const /*clusters*/,
COORD const /*coord*/,
const bool /*trimLeft*/,
const bool /*lineWrapped*/) noexcept
const bool /*trimLeft*/) noexcept
{
return S_FALSE;
}

View File

@@ -53,8 +53,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
COORD const coord,
bool const fTrimLeft,
const bool lineWrapped) noexcept override;
bool const fTrimLeft) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override;

View File

@@ -19,6 +19,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
_ColorTable(ColorTable),
_cColorTable(cColorTable),
_fUseAsciiOnly(fUseAsciiOnly),
_previousLineWrapped(false),
_usingUnderLine(false),
_needToDisableCursor(false),
_lastCursorIsVisible(false),
@@ -234,8 +235,6 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
{
HRESULT hr = S_OK;
_trace.TraceMoveCursor(_lastText, coord);
if (coord.X != _lastText.X || coord.Y != _lastText.Y)
{
if (coord.X == 0 && coord.Y == 0)
@@ -249,15 +248,8 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
// If the previous line wrapped, then the cursor is already at this
// position, we just don't know it yet. Don't emit anything.
bool previousLineWrapped = false;
if (_wrappedRow.has_value())
if (_previousLineWrapped)
{
previousLineWrapped = coord.Y == _wrappedRow.value() + 1;
}
if (previousLineWrapped)
{
_trace.TraceWrapped();
hr = S_OK;
}
else
@@ -320,11 +312,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
_newBottomLine = false;
}
_deferredCursorPos = INVALID_COORDS;
_wrappedRow = std::nullopt;
_delayedEolWrap = false;
return hr;
}
@@ -442,19 +430,15 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
// - trimLeft - This specifies whether to trim one character width off the left
// side of the output. Used for drawing the right-half only of a
// double-wide character.
// - lineWrapped: true if this run we're painting is the end of a line that
// wrapped. If we're not painting the last column of a wrapped line, then this
// will be false.
// Return Value:
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT XtermEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
const COORD coord,
const bool /*trimLeft*/,
const bool lineWrapped) noexcept
const bool /*trimLeft*/) noexcept
{
return _fUseAsciiOnly ?
VtEngine::_PaintAsciiBufferLine(clusters, coord) :
VtEngine::_PaintUtf8BufferLine(clusters, coord, lineWrapped);
VtEngine::_PaintUtf8BufferLine(clusters, coord);
}
// Method Description:

View File

@@ -48,8 +48,7 @@ namespace Microsoft::Console::Render
const bool isSettingDefaultBrushes) noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view<Cluster> const clusters,
const COORD coord,
const bool trimLeft,
const bool lineWrapped) noexcept override;
const bool trimLeft) noexcept override;
[[nodiscard]] HRESULT ScrollFrame() noexcept override;
[[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override;
@@ -60,6 +59,7 @@ namespace Microsoft::Console::Render
const COLORREF* const _ColorTable;
const WORD _cColorTable;
const bool _fUseAsciiOnly;
bool _previousLineWrapped;
bool _usingUnderLine;
bool _needToDisableCursor;
bool _lastCursorIsVisible;

View File

@@ -115,15 +115,11 @@ using namespace Microsoft::Console::Types;
// - trimLeft - This specifies whether to trim one character width off the left
// side of the output. Used for drawing the right-half only of a
// double-wide character.
// - lineWrapped: true if this run we're painting is the end of a line that
// wrapped. If we're not painting the last column of a wrapped line, then this
// will be false.
// Return Value:
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT VtEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
const COORD coord,
const bool /*trimLeft*/,
const bool /*lineWrapped*/) noexcept
const bool /*trimLeft*/) noexcept
{
return VtEngine::_PaintAsciiBufferLine(clusters, coord);
}
@@ -153,8 +149,6 @@ using namespace Microsoft::Console::Types;
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT VtEngine::PaintCursor(const IRenderEngine::CursorOptions& options) noexcept
{
_trace.TracePaintCursor(options.coordCursor);
// MSFT:15933349 - Send the terminal the updated cursor information, if it's changed.
LOG_IF_FAILED(_MoveCursor(options.coordCursor));
@@ -375,14 +369,15 @@ using namespace Microsoft::Console::Types;
// Return Value:
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT VtEngine::_PaintUtf8BufferLine(std::basic_string_view<Cluster> const clusters,
const COORD coord,
const bool lineWrapped) noexcept
const COORD coord) noexcept
{
if (coord.Y < _virtualTop)
{
return S_OK;
}
RETURN_IF_FAILED(_MoveCursor(coord));
std::wstring unclusteredString;
unclusteredString.reserve(clusters.size());
short totalWidth = 0;
@@ -450,37 +445,10 @@ using namespace Microsoft::Console::Types;
(totalWidth - numSpaces) :
totalWidth;
if (cchActual == 0)
{
// If the previous row wrapped, but this line is empty, then we actually
// do want to move the cursor down. Otherwise, we'll possibly end up
// accidentally erasing the last character from the previous line, as
// the cursor is still waiting on that character for the next character
// to follow it.
_wrappedRow = std::nullopt;
}
// Move the cursor to the start of this run.
RETURN_IF_FAILED(_MoveCursor(coord));
// Write the actual text string
std::wstring wstr = std::wstring(unclusteredString.data(), cchActual);
RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8(wstr));
// If we've written text to the last column of the viewport, then mark
// that we've wrapped this line. The next time we attempt to move the
// cursor, if we're trying to move it to the start of the next line,
// we'll remember that this line was wrapped, and not manually break the
// line.
// Don't do this if the last character we're writing is a space - The last
// char will always be a space, but if we see that, we shouldn't wrap.
const short lastWrittenChar = base::ClampAdd(_lastText.X, base::ClampSub(totalWidth, numSpaces));
if (lineWrapped &&
lastWrittenChar > _lastViewport.RightInclusive())
{
_wrappedRow = coord.Y;
}
// Update our internal tracker of the cursor's position.
// See MSFT:20266233 (which is also GH#357)
// If the cursor is at the rightmost column of the terminal, and we write a

View File

@@ -225,48 +225,3 @@ void RenderTracing::TraceLastText(const COORD lastTextPos) const
UNREFERENCED_PARAMETER(lastTextPos);
#endif UNIT_TESTING
}
void RenderTracing::TraceMoveCursor(const COORD lastTextPos, const COORD cursor) const
{
#ifndef UNIT_TESTING
const auto lastTextStr = _CoordToString(lastTextPos);
const auto lastText = lastTextStr.c_str();
const auto cursorStr = _CoordToString(cursor);
const auto cursorPos = cursorStr.c_str();
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
"VtEngine_TraceMoveCursor",
TraceLoggingString(lastText),
TraceLoggingString(cursorPos),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
#else
UNREFERENCED_PARAMETER(lastTextPos);
UNREFERENCED_PARAMETER(cursor);
#endif UNIT_TESTING
}
void RenderTracing::TraceWrapped() const
{
#ifndef UNIT_TESTING
const auto* const msg = "Wrapped instead of \\r\\n";
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
"VtEngine_TraceWrapped",
TraceLoggingString(msg),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
#else
#endif UNIT_TESTING
}
void RenderTracing::TracePaintCursor(const COORD coordCursor) const
{
#ifndef UNIT_TESTING
const auto cursorPosString = _CoordToString(coordCursor);
const auto cursorPos = cursorPosString.c_str();
TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
"VtEngine_TracePaintCursor",
TraceLoggingString(cursorPos),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
#else
UNREFERENCED_PARAMETER(coordCursor);
#endif UNIT_TESTING
}

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