Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/honk

This commit is contained in:
Mike Griese 2021-11-01 10:40:56 -05:00
commit 0bb42efc0d
99 changed files with 1057 additions and 1937 deletions

View file

@ -67,6 +67,7 @@ SUMS$
^src/terminal/parser/ft_fuzzer/run\.bat$
^src/terminal/parser/ft_fuzzer/VTCommandFuzzer\.cpp$
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
^src/terminal/parser/ut_parser/Base64Test.cpp$
^src/terminal/parser/ut_parser/run\.bat$
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
^src/tools/lnkd/lnkd\.bat$

View file

@ -938,6 +938,7 @@ GTP
guc
gui
guidatom
guiddef
GValue
GWL
GWLP
@ -1535,6 +1536,7 @@ NOCOLOR
NOCOMM
NOCONTEXTHELP
NOCOPYBITS
nodefaultlib
nodiscard
NODUP
noexcept
@ -2232,6 +2234,7 @@ STARTWPARMSW
Statusline
stdafx
STDAPI
stdc
stdcall
stdcpp
stderr

View file

@ -125,7 +125,7 @@ Team members will be happy to help review specs and guide them to completion.
### Help Wanted
Once the team have approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/terminal/labels/Help%20Wanted).
Once the team has approved an issue/spec, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/terminal/labels/Help%20Wanted).
---

View file

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

View file

@ -4,7 +4,7 @@
Introducing exceptions to an existing non-exception-based codebase can be perilous. The console was originally written
in C at a time when C++ was relatively unused in the Windows operating system. As part of our project to modernize the
Windows console, we converted to use C++, but still had an aversion to using exception-based error handling in
our code for fear that it introduce unexpected failures. However, the STL and other libraries like it are so useful that
our code for fear that it might introduce unexpected failures. However, the STL and other libraries like it are so useful that
sometimes it's significantly simpler to use them. Given that, we have a set of rules that we follow when considering
exception use.

View file

@ -189,7 +189,7 @@ I think there might be a bit of a misunderstanding here - there are two differen
* shell applications, like `cmd.exe`, `powershell`, `zsh`, etc. These are text-only applications that emit streams of characters. They don't care at all about how they're eventually rendered to the user. These are also sometimes referred to as "commandline client" applications.
* terminal applications, like the Windows Terminal, gnome-terminal, xterm, iterm2, hyper. These are graphical applications that can be used to render the output of commandline clients.
On Windows, if you just run `cmd.exe` directly, the OS will create an instance of `conhost.exe` as the _terminal_ for `cmd.exe`. The same thing happens for `powershell.exe`, the system will creates a new conhost window for any client that's not already connected to a terminal of some sort. This has lead to an enormous amount of confusion for people thinking that a conhost window is actually a "`cmd` window". `cmd` can't have a window, it's just a commandline application. Its window is always some other terminal.
On Windows, if you just run `cmd.exe` directly, the OS will create an instance of `conhost.exe` as the _terminal_ for `cmd.exe`. The same thing happens for `powershell.exe`, the system will create a new conhost window for any client that's not already connected to a terminal of some sort. This has lead to an enormous amount of confusion for people thinking that a conhost window is actually a "`cmd` window". `cmd` can't have a window, it's just a commandline application. Its window is always some other terminal.
Any terminal can run any commandline client application. So you can use the Windows Terminal to run whatever shell you want. I use mine for both `cmd` and `powershell`, and also WSL:

View file

@ -459,7 +459,7 @@
},
"suppressApplicationTitle": {
"type": "boolean",
"default": "false",
"default": false,
"description": "When set to true, tabTitle overrides the default title of the tab and any title change messages from the application will be suppressed. When set to false, tabTitle behaves as normal"
},
"colorScheme": {
@ -506,7 +506,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "adjustFontSize"
"const": "adjustFontSize"
},
"delta": {
"type": "integer",
@ -530,7 +530,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "copy"
"const": "copy"
},
"singleLine": {
"type": "boolean",
@ -566,7 +566,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "newTab"
"const": "newTab"
}
}
}
@ -582,7 +582,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "switchToTab"
"const": "switchToTab"
},
"index": {
"type": "integer",
@ -606,7 +606,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "movePane"
"const": "movePane"
},
"index": {
"type": "integer",
@ -630,7 +630,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "moveFocus"
"const": "moveFocus"
},
"direction": {
"$ref": "#/$defs/FocusDirection",
@ -654,7 +654,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "swapPane"
"const": "swapPane"
},
"direction": {
"$ref": "#/$defs/FocusDirection",
@ -678,7 +678,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "resizePane"
"const": "resizePane"
},
"direction": {
"$ref": "#/$defs/ResizeDirection",
@ -702,7 +702,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "sendInput"
"const": "sendInput"
},
"input": {
"type": "string",
@ -729,7 +729,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "splitPane"
"const": "splitPane"
},
"split": {
"$ref": "#/$defs/SplitDirection",
@ -761,7 +761,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "openSettings"
"const": "openSettings"
},
"target": {
"type": "string",
@ -788,7 +788,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "setTabColor"
"const": "setTabColor"
},
"color": {
"$ref": "#/$defs/Color",
@ -809,7 +809,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "setColorScheme"
"const": "setColorScheme"
},
"colorScheme": {
"type": "string",
@ -833,7 +833,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "wt"
"const": "wt"
},
"commandline": {
"type": "string",
@ -857,7 +857,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "closeOtherTabs"
"const": "closeOtherTabs"
},
"index": {
"oneOf": [
@ -885,7 +885,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "closeTabsAfter"
"const": "closeTabsAfter"
},
"index": {
"oneOf": [
@ -913,7 +913,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "closeTab"
"const": "closeTab"
},
"index": {
"oneOf": [
@ -941,7 +941,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "scrollUp"
"const": "scrollUp"
},
"rowsToScroll": {
"type": [
@ -965,7 +965,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "scrollDown"
"const": "scrollDown"
},
"rowsToScroll": {
"type": [
@ -989,7 +989,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "moveTab"
"const": "moveTab"
},
"direction": {
"$ref": "#/$defs/MoveTabDirection",
@ -1012,7 +1012,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "multipleActions"
"const": "multipleActions"
},
"actions": {
"$ref": "#/$defs/ShortcutAction",
@ -1037,7 +1037,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "commandPalette"
"const": "commandPalette"
},
"launchMode": {
"$ref": "#/$defs/CommandPaletteLaunchMode",
@ -1058,7 +1058,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "findMatch"
"const": "findMatch"
},
"direction": {
"$ref": "#/$defs/FindMatchDirection",
@ -1085,7 +1085,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "newWindow"
"const": "newWindow"
}
}
}
@ -1101,7 +1101,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "prevTab"
"const": "prevTab"
},
"tabSwitcherMode": {
"$ref": "#/$defs/SwitchToAdjacentTabArgs",
@ -1122,7 +1122,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "nextTab"
"const": "nextTab"
},
"tabSwitcherMode": {
"$ref": "#/$defs/SwitchToAdjacentTabArgs",
@ -1143,7 +1143,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "renameTab"
"const": "renameTab"
},
"title": {
"type": "string",
@ -1164,7 +1164,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "renameWindow"
"const": "renameWindow"
},
"name": {
"type": "string",
@ -1185,7 +1185,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "focusPane"
"const": "focusPane"
},
"id": {
"type": "integer",
@ -1207,7 +1207,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "globalSummon"
"const": "globalSummon"
},
"desktop": {
"type": "string",
@ -1258,7 +1258,7 @@
"properties": {
"action": {
"type": "string",
"pattern": "quakeMode"
"const": "quakeMode"
}
}
}
@ -1333,6 +1333,12 @@
{
"$ref": "#/$defs/MoveTabAction"
},
{
"$ref": "#/$defs/MultipleActionsAction"
},
{
"$ref": "#/$defs/CommandPaletteAction"
},
{
"$ref": "#/$defs/FindMatchAction"
},
@ -1575,22 +1581,22 @@
"deprecated": true
},
"minimizeToNotificationArea": {
"default": "false",
"default": false,
"description": "When set to true, minimizing a Terminal window will no longer appear in the taskbar. Instead, a Terminal icon will appear in the notification area through which the user can access their windows.",
"type": "boolean"
},
"alwaysShowNotificationIcon": {
"default": "false",
"default": false,
"description": "When set to true, the Terminal's notification icon will always be shown in the notification area.",
"type": "boolean"
},
"showAdminShield": {
"default": "true",
"default": true,
"description": "When set to true, the Terminal's tab row will display a shield icon when the Terminal is running with administrator privileges",
"type": "boolean"
},
"useAcrylicInTabRow": {
"default": "false",
"default": false,
"description": "When set to true, the tab row will have an acrylic background with 50% opacity.",
"type": "boolean"
},

View file

@ -2,7 +2,7 @@
## Overview
This document outlines the roadmap towards delivering Windows Terminal 2.0 by Winter 2021.
This document outlines the roadmap towards delivering Windows Terminal 2.0.
## Milestones
@ -31,9 +31,9 @@ Below is the schedule for when milestones will be included in release builds of
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/) |
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/) |
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
| 2021-10-31 | 1.12 in Windows Terminal Preview<br>1.11 in Windows Terminal | |
| 2021-11-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
| 2021-12-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
| 2021-10-20 | [1.12] in Windows Terminal Preview<br>[1.11] in Windows Terminal | [Windows Terminal Preview 1.12 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-12-release/) |
| | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
| | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
## Issue Triage & Prioritization
@ -49,28 +49,32 @@ The following are a list of the key scenarios we're aiming to deliver for Termin
> 👉 Note: There are many other features that don't fit within 2.0, but will be re-assessed and prioritized for 3.0, the plan for which will be published in 2021.
| Priority\* | Scenario | Description/Notes |
| ---------- | -------- | ----------------- |
| 0 | Settings UI | A user interface that connects to settings.json. This provides a way for people to edit their settings without having to edit a JSON file.<br><br>Issue: [#1564]<br>Specs: [#6720], [#6904]<br>Implementation: [#7283], [#7370], [#8048] |
| 0 | Command palette | A popup menu to list possible actions and commands.<br><br>Issues: [#5400], [#2046]<br>Spec: [#2193]<br>Implementation: [#6635] |
| 1 | Tab tear-off | The ability to tear a tab out of the current window and spawn a new window or attach it to a separate window.<br><br>Issue: [#1256], [#5000]<br>Spec: [#2080], [#7240] |
| 1 | Clickable links | Hyperlinking any links that appear in the text buffer. When clicking on the link, the link will open in your default browser.<br><br>Issue: [#574]<br>Implementation: [#7251] |
| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal<br><br>Issue: [#492]<br>Spec: [#2080], [#7414] |
| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme<br><br>Issue: [#3327]<br>Spec: [#5772] |
| 1 | Open profile elevated | Configure profiles to always open elevated (if Terminal was run unelevated)<br><br>Issue: [#5000], [#632]<br>Spec: [#8455] |
| 1 | Open tab in existing window | Open new tabs in existing Terminal windows<br><br>Issue: [#5000], [#4472]<br>Spec: [#8135] |
| 1 | Traditional opacity | Have a transparent background without the acrylic blur.<br><br>Issue: [#603] <br>**Current State**: Blocked on WinUI 3.0 |
| 2 | SnapOnOutput, scroll lock | Pause output or scrolling on click.<br><br>Issue: [#980]<br>Spec: [#2529]<br>Implementation: [#6062] |
| 2 | Infinite scrollback | Have an infinite history for the text buffer.<br><br>Issue: [#1410] |
| 2 | Pane management | All issues listed out in the original issue. Some features include pane resizing with mouse, pane zooming, and opening a pane by prompting which profile to use.<br><br>Issue: [#1000] |
| 2 | Theme marketplace | Marketplace for creation and distribution of themes.<br>Dependent on overall theming |
| 2 | Jump list | Show profiles from task bar (on right click)/start menu.<br><br>Issue: [#576]<br>Implementation: [#7515] |
| 2 | Open with multiple tabs | A setting that allows Windows Terminal to launch with a specific tab configuration (not using only command line arguments).<br><br>Issue: [#756] |
| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.<br><br>Issue: [#1060]<br>Implementation: [#6100] |
| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.<br><br>Issues: [#961], [#960], [#766] |
| 3 | Quake mode | Provide a quick launch terminal that appears and disappears when a hotkey is pressed.<br><br>Issue: [#653] |
| 3 | Settings migration infrastructure | Migrate people's settings without breaking them. Hand-in-hand with settings UI. |
| 3 | Pointer bindings | Provide settings that can be bound to the mouse.<br><br>Issue: [#1553] |
| Priority\* | Scenario | Description/Notes | State |
| ---------- | -------- | ----------------- | ----- |
| 0 | Settings UI | A user interface that connects to settings.json. This provides a way for people to edit their settings without having to edit a JSON file.<br><br>Issue: [#1564]<br>Specs: [#6720], [#6904]<br>Implementation: [#7283], [#7370], [#8048] | ✔️ |
| 0 | Command palette | A popup menu to list possible actions and commands.<br><br>Issues: [#5400], [#2046]<br>Spec: [#2193]<br>Implementation: [#6635] | ✔️ |
| 1 | Tab tear-off | The ability to tear a tab out of the current window and spawn a new window or attach it to a separate window.<br><br>Issue: [#1256], [#5000]<br>Spec: [#2080], [#7240] | 📝 |
| 1 | Clickable links | Hyperlinking any links that appear in the text buffer. When clicking on the link, the link will open in your default browser.<br><br>Issue: [#574]<br>Implementation: [#7251] | ✔️ |
| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal<br><br>Issue: [#492]<br>Spec: [#2080], [#7414] | ✔️ |
| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme<br><br>Issue: [#3327]<br>Spec: [#5772] | 🦶 |
| 1 | Open profile elevated | Configure profiles to always open elevated (if Terminal was run unelevated)<br><br>Issue: [#5000], [#632]<br>Spec: [#8455] | 📝 |
| 1 | Open tab in existing window | Open new tabs in existing Terminal windows<br><br>Issue: [#5000], [#4472]<br>Spec: [#8135] | ✔️ |
| 1 | Traditional opacity | Have a transparent background without the acrylic blur.<br><br>Issue: [#603] | ✔️ |
| 2 | SnapOnOutput, scroll lock | Pause output or scrolling on click.<br><br>Issue: [#980]<br>Spec: [#2529]<br>Implementation: [#6062] | ✔️ |
| 2 | Infinite scrollback | Have an infinite history for the text buffer.<br><br>Issue: [#1410] | 🦶 |
| 2 | Pane management | All issues listed out in the original issue. Some features include pane resizing with mouse, pane zooming, and opening a pane by prompting which profile to use.<br><br>Issue: [#1000] | 📝 |
| 2 | Theme marketplace | Marketplace for creation and distribution of themes.<br>Dependent on overall theming | 🦶 |
| 2 | Jump list | Show profiles from task bar (on right click)/start menu.<br><br>Issue: [#576]<br>Implementation: [#7515] | ✔️ |
| 2 | Open with multiple tabs | A setting that allows Windows Terminal to launch with a specific tab configuration (not using only command line arguments).<br><br>Issue: [#756] | ✔️ |
| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.<br><br>Issue: [#1060]<br>Implementation: [#6100] | ✔️ |
| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.<br><br>Issues: [#961], [#960], [#766] | ✔️ |
| 3 | Quake mode | Provide a quick launch terminal that appears and disappears when a hotkey is pressed.<br><br>Issue: [#653] | ✔️ |
| 3 | Settings migration infrastructure | Migrate people's settings without breaking them. Hand-in-hand with settings UI. | 🦶 |
| 3 | Pointer bindings | Provide settings that can be bound to the mouse.<br><br>Issue: [#1553] | 🦶 |
* 📝: The feature is currently in progress
* ✔️: The feature is complete and shipped in a Preview build
* 🦶: The feature is at risk of being punted to a future release cycle (beyond 2.0)
Feature Notes:
@ -91,6 +95,8 @@ Feature Notes:
[1.9]: https://github.com/microsoft/terminal/milestone/34
[1.10]: https://github.com/microsoft/terminal/milestone/35
[1.11]: https://github.com/microsoft/terminal/milestone/36
[1.12]: https://github.com/microsoft/terminal/milestone/38
[1.13]: https://github.com/microsoft/terminal/milestone/39
[2.0]: https://github.com/microsoft/terminal/milestone/22
[#1564]: https://github.com/microsoft/terminal/issues/1564
[#6720]: https://github.com/microsoft/terminal/pull/6720

View file

@ -13,6 +13,15 @@
<EventProvider Id="EventProvider_TerminalRemoting" Name="d6f04aad-629f-539a-77c1-73f5c3e4aa7b" />
<EventProvider Id="EventProvider_TerminalDirectX" Name="c93e739e-ae50-5a14-78e7-f171e947535d" />
<EventProvider Id="EventProvider_TerminalUIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
<!-- Console providers here -->
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Launcher" Name="770aa552-671a-5e97-579b-151709ec0dbd"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Host" Name="fe1ff234-1f09-50a8-d38d-c44fab43e818"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Server" Name="1A541C01-589A-496E-85A7-A9E02170166D"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.VirtualTerminal.Parser" Name="c9ba2a84-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.Render.VtEngine" Name="c9ba2a95-d3ca-5e19-2bd6-776a0910cb9d"/>
<EventProvider Id="EventProvider-Microsoft.Windows.Console.UIA" Name="e7ebce59-2161-572d-b263-2f16a6afb9e5"/>
<!-- Profile for General Terminal logging -->
<Profile Id="Terminal.Verbose.File" Name="Terminal" Description="Terminal" LoggingMode="File" DetailLevel="Verbose">
<Collectors>
<EventCollectorId Value="EventCollector_Terminal">
@ -32,5 +41,27 @@
<Profile Id="Terminal.Light.File" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="File" DetailLevel="Light" />
<Profile Id="Terminal.Verbose.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
<Profile Id="Terminal.Light.Memory" Name="Terminal" Description="Terminal" Base="Terminal.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
<!-- Profile for DefTerm logging. Includes some conhost logging. -->
<Profile Id="DefTerm.Verbose.File" Name="DefTerm" Description="DefTerm" LoggingMode="File" DetailLevel="Verbose">
<Collectors>
<EventCollectorId Value="EventCollector_Terminal">
<EventProviders>
<EventProviderId Value="EventProvider_TerminalControl" />
<EventProviderId Value="EventProvider_TerminalConnection" />
<EventProviderId Value="EventProvider_TerminalSettingsModel" />
<EventProviderId Value="EventProvider_TerminalApp" />
<EventProviderId Value="EventProvider_TerminalWin32Host" />
<EventProviderId Value="EventProvider_TerminalRemoting" />
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Launcher" />
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Host" />
<EventProviderId Value="EventProvider-Microsoft.Windows.Console.Server" />
</EventProviders>
</EventCollectorId>
</Collectors>
</Profile>
<Profile Id="DefTerm.Light.File" Name="DefTerm" Description="DefTerm" Base="DefTerm.Verbose.File" LoggingMode="File" DetailLevel="Light" />
<Profile Id="DefTerm.Verbose.Memory" Name="DefTerm" Description="DefTerm" Base="DefTerm.Verbose.File" LoggingMode="Memory" DetailLevel="Verbose" />
<Profile Id="DefTerm.Light.Memory" Name="DefTerm" Description="DefTerm" Base="DefTerm.Verbose.File" LoggingMode="Memory" DetailLevel="Light" />
</Profiles>
</WindowsPerformanceRecorder>
</WindowsPerformanceRecorder>

View file

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

View file

@ -97,6 +97,7 @@ namespace SettingsModelLocalTests
"confirmCloseAllTabs": true,
"largePasteWarning": true,
"multiLinePasteWarning": true,
"trimPaste": true,
"experimental.input.forceVT": false,
"experimental.rendering.forceFullRepaint": false,

View file

@ -977,7 +977,7 @@ void HwndTerminal::_StringPaste(const wchar_t* const pData) noexcept
CATCH_LOG();
}
COORD HwndTerminal::GetFontSize() const
COORD HwndTerminal::GetFontSize() const noexcept
{
return _actualFont.GetSize();
}

View file

@ -128,7 +128,7 @@ private:
void _SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept;
// Inherited via IControlAccessibilityInfo
COORD GetFontSize() const override;
COORD GetFontSize() const noexcept override;
RECT GetBounds() const noexcept override;
double GetScaleFactor() const noexcept override;
void ChangeViewport(const SMALL_RECT NewWindow) override;

View file

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

View file

@ -1597,7 +1597,10 @@ namespace winrt::TerminalApp::implementation
// the control here instead.
if (_startupState == StartupState::Initialized)
{
_GetActiveControl().Focus(FocusState::Programmatic);
if (const auto control = _GetActiveControl())
{
control.Focus(FocusState::Programmatic);
}
}
}
CATCH_LOG();
@ -1869,6 +1872,22 @@ namespace winrt::TerminalApp::implementation
}
}
if (_settings.GlobalSettings().TrimPaste())
{
std::wstring_view textView{ text };
const auto pos = textView.find_last_not_of(L"\t\n\v\f\r ");
if (pos == textView.npos)
{
// Text is all white space, nothing to paste
co_return;
}
else if (const auto toRemove = textView.size() - 1 - pos; toRemove > 0)
{
textView.remove_suffix(toRemove);
text = { textView };
}
}
bool warnMultiLine = _settings.GlobalSettings().WarnAboutMultiLinePaste();
if (warnMultiLine)
{

View file

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

View file

@ -372,6 +372,16 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// window is expecting it to be on the first layout.
else
{
#pragma warning(suppress : 26477 26485 26494 26482 26446) // We don't control TraceLoggingWrite
TraceLoggingWrite(
g_hTerminalConnectionProvider,
"ConPtyConnectedToDefterm",
TraceLoggingDescription("Event emitted when ConPTY connection is started, for a defterm session"),
TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
TraceLoggingWideString(_clientName.c_str(), "Client", "The attached client process"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), dimensions));
}

View file

@ -600,6 +600,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// not, but DX doesn't use that info at all.
// The Codepage is additionally not actually used by the DX engine at all.
_actualFont = { fontFace, 0, fontWeight.Weight, { 0, fontHeight }, CP_UTF8, false };
_actualFontFaceName = { fontFace };
_desiredFont = { _actualFont };
// Update the terminal core with its new Core settings
@ -742,6 +743,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto fontFace = _settings.FontFace();
const auto fontWeight = _settings.FontWeight();
_actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false };
_actualFontFaceName = { fontFace };
_desiredFont = { _actualFont };
auto lock = _terminal->LockForWriting();
@ -1020,7 +1022,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::Foundation::Size ControlCore::FontSize() const noexcept
{
const auto fontSize = GetFont().GetSize();
const auto fontSize = _actualFont.GetSize();
return {
::base::saturated_cast<float>(fontSize.X),
::base::saturated_cast<float>(fontSize.Y)
@ -1028,17 +1030,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
winrt::hstring ControlCore::FontFaceName() const noexcept
{
return winrt::hstring{ GetFont().GetFaceName() };
// This getter used to return _actualFont.GetFaceName(), however GetFaceName() returns a STL
// string and we need to return a WinRT string. This would require an additional allocation.
// This method is called 10/s by TSFInputControl at the time of writing.
return _actualFontFaceName;
}
uint16_t ControlCore::FontWeight() const noexcept
{
return static_cast<uint16_t>(GetFont().GetWeight());
return static_cast<uint16_t>(_actualFont.GetWeight());
}
til::size ControlCore::FontSizeInDips() const
{
const til::size fontSize{ GetFont().GetSize() };
const til::size fontSize{ _actualFont.GetSize() };
return fontSize.scale(til::math::rounding, 1.0f / ::base::saturated_cast<float>(_compositionScale));
}

View file

@ -195,6 +195,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
FontInfoDesired _desiredFont;
FontInfo _actualFont;
winrt::hstring _actualFontFaceName;
// storage location for the leading surrogate of a utf-16 surrogate pair
std::optional<wchar_t> _leadingSurrogate{ std::nullopt };

View file

@ -141,12 +141,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma endregion
#pragma region IControlAccessibilityInfo
COORD InteractivityAutomationPeer::GetFontSize() const
COORD InteractivityAutomationPeer::GetFontSize() const noexcept
{
return til::size{ til::math::rounding, _interactivity->Core().FontSize() };
}
RECT InteractivityAutomationPeer::GetBounds() const
RECT InteractivityAutomationPeer::GetBounds() const noexcept
{
return _controlBounds;
}
@ -159,12 +159,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return S_OK;
}
RECT InteractivityAutomationPeer::GetPadding() const
RECT InteractivityAutomationPeer::GetPadding() const noexcept
{
return _controlPadding;
}
double InteractivityAutomationPeer::GetScaleFactor() const
double InteractivityAutomationPeer::GetScaleFactor() const noexcept
{
return DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
}

View file

@ -62,10 +62,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#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 COORD GetFontSize() const noexcept override;
virtual RECT GetBounds() const noexcept override;
virtual RECT GetPadding() const noexcept override;
virtual double GetScaleFactor() const noexcept override;
virtual void ChangeViewport(SMALL_RECT NewWindow) override;
virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) override;
#pragma endregion

View file

@ -297,37 +297,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
if (!newAppearance.BackgroundImage().empty())
{
Windows::Foundation::Uri imageUri{ newAppearance.BackgroundImage() };
// 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>();
if (imageSource == nullptr ||
imageSource.UriSource() == nullptr ||
imageSource.UriSource().RawUri() != imageUri.RawUri())
{
// Note that BitmapImage handles the image load asynchronously,
// which is especially important since the image
// may well be both large and somewhere out on the
// internet.
Media::Imaging::BitmapImage image(imageUri);
BackgroundImage().Source(image);
}
// Apply stretch, opacity and alignment settings
BackgroundImage().Stretch(newAppearance.BackgroundImageStretchMode());
BackgroundImage().Opacity(newAppearance.BackgroundImageOpacity());
BackgroundImage().HorizontalAlignment(newAppearance.BackgroundImageHorizontalAlignment());
BackgroundImage().VerticalAlignment(newAppearance.BackgroundImageVerticalAlignment());
}
else
{
BackgroundImage().Source(nullptr);
}
_SetBackgroundImage(newAppearance);
// Update our control settings
const auto bg = newAppearance.DefaultBackground();
@ -412,6 +382,57 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
// Method Description:
// - Sets background image and applies its settings (stretch, opacity and alignment)
// - Checks path validity
// Arguments:
// - newAppearance
// Return Value:
// - <none>
void TermControl::_SetBackgroundImage(const IControlAppearance& newAppearance)
{
if (newAppearance.BackgroundImage().empty())
{
BackgroundImage().Source(nullptr);
return;
}
Windows::Foundation::Uri imageUri{ nullptr };
try
{
imageUri = Windows::Foundation::Uri{ newAppearance.BackgroundImage() };
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
BackgroundImage().Source(nullptr);
return;
}
// 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>();
if (imageSource == nullptr ||
imageSource.UriSource() == nullptr ||
imageSource.UriSource().RawUri() != imageUri.RawUri())
{
// Note that BitmapImage handles the image load asynchronously,
// which is especially important since the image
// may well be both large and somewhere out on the
// internet.
Media::Imaging::BitmapImage image(imageUri);
BackgroundImage().Source(image);
}
// Apply stretch, opacity and alignment settings
BackgroundImage().Stretch(newAppearance.BackgroundImageStretchMode());
BackgroundImage().Opacity(newAppearance.BackgroundImageOpacity());
BackgroundImage().HorizontalAlignment(newAppearance.BackgroundImageHorizontalAlignment());
BackgroundImage().VerticalAlignment(newAppearance.BackgroundImageVerticalAlignment());
}
// Method Description:
// - Set up each layer's brush used to display the control's background.
// - Respects the settings for acrylic, background image and opacity from

View file

@ -202,6 +202,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _UpdateSettingsFromUIThread(IControlSettings newSettings);
void _UpdateAppearanceFromUIThread(IControlAppearance newAppearance);
void _ApplyUISettings(const IControlSettings&);
void _SetBackgroundImage(const IControlAppearance& newAppearance);
void _InitializeBackgroundBrush();
void _BackgroundColorChangedHandler(const IInspectable& sender, const IInspectable& args);

View file

@ -3,6 +3,7 @@
#pragma once
#include "../../terminal/adapter/DispatchTypes.hpp"
#include "../../terminal/input/terminalInput.hpp"
#include "../../buffer/out/TextAttribute.hpp"
#include "../../types/inc/Viewport.hpp"
@ -47,16 +48,9 @@ namespace Microsoft::Terminal::Core
virtual bool SetDefaultForeground(const DWORD color) noexcept = 0;
virtual bool SetDefaultBackground(const DWORD color) noexcept = 0;
virtual bool EnableWin32InputMode(const bool win32InputMode) noexcept = 0;
virtual bool SetCursorKeysMode(const bool applicationMode) noexcept = 0;
virtual bool SetKeypadMode(const bool applicationMode) noexcept = 0;
virtual bool SetInputMode(const ::Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) noexcept = 0;
virtual bool SetScreenMode(const bool reverseMode) noexcept = 0;
virtual bool EnableVT200MouseMode(const bool enabled) noexcept = 0;
virtual bool EnableUTF8ExtendedMouseMode(const bool enabled) noexcept = 0;
virtual bool EnableSGRExtendedMouseMode(const bool enabled) noexcept = 0;
virtual bool EnableButtonEventMouseMode(const bool enabled) noexcept = 0;
virtual bool EnableAnyEventMouseMode(const bool enabled) noexcept = 0;
virtual bool EnableAlternateScrollMode(const bool enabled) noexcept = 0;
virtual bool EnableXtermBracketedPasteMode(const bool enabled) noexcept = 0;
virtual bool IsXtermBracketedPasteModeEnabled() const = 0;

View file

@ -112,16 +112,9 @@ public:
bool SetDefaultForeground(const COLORREF color) noexcept override;
bool SetDefaultBackground(const COLORREF color) noexcept override;
bool EnableWin32InputMode(const bool win32InputMode) noexcept override;
bool SetCursorKeysMode(const bool applicationMode) noexcept override;
bool SetKeypadMode(const bool applicationMode) noexcept override;
bool SetInputMode(const ::Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) noexcept override;
bool SetScreenMode(const bool reverseMode) noexcept override;
bool EnableVT200MouseMode(const bool enabled) noexcept override;
bool EnableUTF8ExtendedMouseMode(const bool enabled) noexcept override;
bool EnableSGRExtendedMouseMode(const bool enabled) noexcept override;
bool EnableButtonEventMouseMode(const bool enabled) noexcept override;
bool EnableAnyEventMouseMode(const bool enabled) noexcept override;
bool EnableAlternateScrollMode(const bool enabled) noexcept override;
bool EnableXtermBracketedPasteMode(const bool enabled) noexcept override;
bool IsXtermBracketedPasteModeEnabled() const noexcept override;

View file

@ -482,23 +482,13 @@ til::color Terminal::GetDefaultBackground() const noexcept
return _defaultBg;
}
bool Terminal::EnableWin32InputMode(const bool win32InputMode) noexcept
bool Terminal::SetInputMode(const TerminalInput::Mode mode, const bool enabled) noexcept
try
{
_terminalInput->ChangeWin32InputMode(win32InputMode);
return true;
}
bool Terminal::SetCursorKeysMode(const bool applicationMode) noexcept
{
_terminalInput->ChangeCursorKeysMode(applicationMode);
return true;
}
bool Terminal::SetKeypadMode(const bool applicationMode) noexcept
{
_terminalInput->ChangeKeypadMode(applicationMode);
_terminalInput->SetInputMode(mode, enabled);
return true;
}
CATCH_RETURN_FALSE()
bool Terminal::SetScreenMode(const bool reverseMode) noexcept
try
@ -511,42 +501,6 @@ try
}
CATCH_RETURN_FALSE()
bool Terminal::EnableVT200MouseMode(const bool enabled) noexcept
{
_terminalInput->EnableDefaultTracking(enabled);
return true;
}
bool Terminal::EnableUTF8ExtendedMouseMode(const bool enabled) noexcept
{
_terminalInput->SetUtf8ExtendedMode(enabled);
return true;
}
bool Terminal::EnableSGRExtendedMouseMode(const bool enabled) noexcept
{
_terminalInput->SetSGRExtendedMode(enabled);
return true;
}
bool Terminal::EnableButtonEventMouseMode(const bool enabled) noexcept
{
_terminalInput->EnableButtonEventTracking(enabled);
return true;
}
bool Terminal::EnableAnyEventMouseMode(const bool enabled) noexcept
{
_terminalInput->EnableAnyEventTracking(enabled);
return true;
}
bool Terminal::EnableAlternateScrollMode(const bool enabled) noexcept
{
_terminalInput->EnableAlternateScroll(enabled);
return true;
}
bool Terminal::EnableXtermBracketedPasteMode(const bool enabled) noexcept
{
_bracketedPasteMode = enabled;

View file

@ -321,9 +321,9 @@ CATCH_LOG_RETURN_FALSE()
// - applicationMode - set to true to enable Application Mode Input, false for Numeric Mode Input.
// Return Value:
// - True if handled successfully. False otherwise.
bool TerminalDispatch::SetKeypadMode(const bool fApplicationMode) noexcept
bool TerminalDispatch::SetKeypadMode(const bool applicationMode) noexcept
{
_terminalApi.SetKeypadMode(fApplicationMode);
_terminalApi.SetInputMode(TerminalInput::Mode::Keypad, applicationMode);
return true;
}
@ -334,7 +334,7 @@ bool TerminalDispatch::SetKeypadMode(const bool fApplicationMode) noexcept
// - True if handled successfully. False otherwise.
bool TerminalDispatch::SetCursorKeysMode(const bool applicationMode) noexcept
{
_terminalApi.SetCursorKeysMode(applicationMode);
_terminalApi.SetInputMode(TerminalInput::Mode::CursorKey, applicationMode);
return true;
}
@ -359,7 +359,7 @@ bool TerminalDispatch::SetScreenMode(const bool reverseMode) noexcept
// - True if handled successfully. False otherwise.
bool TerminalDispatch::EnableWin32InputMode(const bool win32Mode) noexcept
{
_terminalApi.EnableWin32InputMode(win32Mode);
_terminalApi.SetInputMode(TerminalInput::Mode::Win32, win32Mode);
return true;
}
@ -371,7 +371,7 @@ bool TerminalDispatch::EnableWin32InputMode(const bool win32Mode) noexcept
// True if handled successfully. False otherwise.
bool TerminalDispatch::EnableVT200MouseMode(const bool enabled) noexcept
{
_terminalApi.EnableVT200MouseMode(enabled);
_terminalApi.SetInputMode(TerminalInput::Mode::DefaultMouseTracking, enabled);
return true;
}
@ -384,7 +384,7 @@ bool TerminalDispatch::EnableVT200MouseMode(const bool enabled) noexcept
// True if handled successfully. False otherwise.
bool TerminalDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) noexcept
{
_terminalApi.EnableUTF8ExtendedMouseMode(enabled);
_terminalApi.SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, enabled);
return true;
}
@ -397,7 +397,7 @@ bool TerminalDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) noexcept
// True if handled successfully. False otherwise.
bool TerminalDispatch::EnableSGRExtendedMouseMode(const bool enabled) noexcept
{
_terminalApi.EnableSGRExtendedMouseMode(enabled);
_terminalApi.SetInputMode(TerminalInput::Mode::SgrMouseEncoding, enabled);
return true;
}
@ -409,7 +409,7 @@ bool TerminalDispatch::EnableSGRExtendedMouseMode(const bool enabled) noexcept
// True if handled successfully. False otherwise.
bool TerminalDispatch::EnableButtonEventMouseMode(const bool enabled) noexcept
{
_terminalApi.EnableButtonEventMouseMode(enabled);
_terminalApi.SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, enabled);
return true;
}
@ -422,7 +422,7 @@ bool TerminalDispatch::EnableButtonEventMouseMode(const bool enabled) noexcept
// True if handled successfully. False otherwise.
bool TerminalDispatch::EnableAnyEventMouseMode(const bool enabled) noexcept
{
_terminalApi.EnableAnyEventMouseMode(enabled);
_terminalApi.SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enabled);
return true;
}
@ -435,7 +435,7 @@ bool TerminalDispatch::EnableAnyEventMouseMode(const bool enabled) noexcept
// True if handled successfully. False otherwise.
bool TerminalDispatch::EnableAlternateScroll(const bool enabled) noexcept
{
_terminalApi.EnableAlternateScrollMode(enabled);
_terminalApi.SetInputMode(TerminalInput::Mode::AlternateScroll, enabled);
return true;
}

View file

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

View file

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

View file

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

View file

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

View file

@ -45,6 +45,11 @@
<ToggleSwitch IsOn="{x:Bind State.Globals.TrimBlockSelection, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Trim Paste -->
<local:SettingContainer x:Uid="Globals_TrimPaste">
<ToggleSwitch IsOn="{x:Bind State.Globals.TrimPaste, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Word Delimiters -->
<local:SettingContainer x:Uid="Globals_WordDelimiters">
<TextBox IsSpellCheckEnabled="False"

View file

@ -8,7 +8,6 @@
#include "Interaction.h"
#include "Rendering.h"
#include "Actions.h"
#include "ReadOnlyActions.h"
#include "Profiles.h"
#include "GlobalAppearance.h"
#include "ColorSchemes.h"
@ -293,21 +292,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (clickedItemTag == actionsTag)
{
if constexpr (Feature_EditableActionsPage::IsEnabled())
{
contentFrame().Navigate(xaml_typename<Editor::Actions>(), winrt::make<ActionsPageNavigationState>(_settingsClone));
}
else
{
auto actionsState{ winrt::make<ReadOnlyActionsPageNavigationState>(_settingsClone) };
actionsState.OpenJson([weakThis = get_weak()](auto&&, auto&& arg) {
if (auto self{ weakThis.get() })
{
self->_OpenJsonHandlers(nullptr, arg);
}
});
contentFrame().Navigate(xaml_typename<Editor::ReadOnlyActions>(), actionsState);
}
contentFrame().Navigate(xaml_typename<Editor::Actions>(), winrt::make<ActionsPageNavigationState>(_settingsClone));
}
else if (clickedItemTag == globalProfileTag)
{

View file

@ -77,9 +77,6 @@
<DependentUpon>Appearances.xaml</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="ReadOnlyActions.h">
<DependentUpon>ReadOnlyActions.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="Rendering.h">
<DependentUpon>Rendering.xaml</DependentUpon>
</ClInclude>
@ -121,9 +118,6 @@
<Page Include="Profiles.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="ReadOnlyActions.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="Appearances.xaml">
<SubType>Designer</SubType>
</Page>
@ -177,9 +171,6 @@
<DependentUpon>Appearances.xaml</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="ReadOnlyActions.cpp">
<DependentUpon>ReadOnlyActions.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="Rendering.cpp">
<DependentUpon>Rendering.xaml</DependentUpon>
</ClCompile>
@ -223,10 +214,6 @@
<DependentUpon>Interaction.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="ReadOnlyActions.idl">
<DependentUpon>ReadOnlyActions.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="Rendering.idl">
<DependentUpon>Rendering.xaml</DependentUpon>
<SubType>Code</SubType>

View file

@ -40,7 +40,6 @@
<Page Include="Actions.xaml" />
<Page Include="SettingContainerStyle.xaml" />
<Page Include="AddProfile.xaml" />
<Page Include="ReadOnlyActions.xaml" />
<Page Include="KeyChordListener.xaml" />
</ItemGroup>
</Project>

View file

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

View file

@ -1,64 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ReadOnlyActions.h"
#include "ReadOnlyActions.g.cpp"
#include "ReadOnlyActionsPageNavigationState.g.cpp"
#include "EnumEntry.h"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::Xaml::Navigation;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
ReadOnlyActions::ReadOnlyActions()
{
InitializeComponent();
_filteredActions = winrt::single_threaded_observable_vector<Command>();
}
void ReadOnlyActions::OnNavigatedTo(const NavigationEventArgs& e)
{
_State = e.Parameter().as<Editor::ReadOnlyActionsPageNavigationState>();
std::vector<Command> keyBindingList;
for (const auto& [_, command] : _State.Settings().GlobalSettings().ActionMap().NameMap())
{
// Filter out nested commands, and commands that aren't bound to a
// key. This page is currently just for displaying the actions that
// _are_ bound to keys.
if (command.HasNestedCommands() || !command.Keys())
{
continue;
}
keyBindingList.push_back(command);
}
std::sort(begin(keyBindingList), end(keyBindingList), CommandComparator{});
_filteredActions = single_threaded_observable_vector<Command>(std::move(keyBindingList));
}
Collections::IObservableVector<Command> ReadOnlyActions::FilteredActions()
{
return _filteredActions;
}
void ReadOnlyActions::_OpenSettingsClick(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
{
const CoreWindow window = CoreWindow::GetForCurrentThread();
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
const auto target = altPressed ? SettingsTarget::DefaultsFile : SettingsTarget::SettingsFile;
_State.RequestOpenJson(target);
}
}

View file

@ -1,57 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "ReadOnlyActions.g.h"
#include "ReadOnlyActionsPageNavigationState.g.h"
#include "Utils.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct CommandComparator
{
bool operator()(const Model::Command& lhs, const Model::Command& rhs) const
{
return lhs.Name() < rhs.Name();
}
};
struct ReadOnlyActionsPageNavigationState : ReadOnlyActionsPageNavigationStateT<ReadOnlyActionsPageNavigationState>
{
public:
ReadOnlyActionsPageNavigationState(const Model::CascadiaSettings& settings) :
_Settings{ settings } {}
void RequestOpenJson(const Model::SettingsTarget target)
{
_OpenJsonHandlers(nullptr, target);
}
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget);
};
struct ReadOnlyActions : public HasScrollViewer<ReadOnlyActions>, ReadOnlyActionsT<ReadOnlyActions>
{
public:
ReadOnlyActions();
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> FilteredActions();
WINRT_PROPERTY(Editor::ReadOnlyActionsPageNavigationState, State, nullptr);
private:
friend struct ReadOnlyActionsT<ReadOnlyActions>; // for Xaml to bind events
Windows::Foundation::Collections::IObservableVector<winrt::Microsoft::Terminal::Settings::Model::Command> _filteredActions{ nullptr };
void _OpenSettingsClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(ReadOnlyActions);
}

View file

@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "EnumEntry.idl";
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass ReadOnlyActionsPageNavigationState
{
Microsoft.Terminal.Settings.Model.CascadiaSettings Settings;
void RequestOpenJson(Microsoft.Terminal.Settings.Model.SettingsTarget target);
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.SettingsTarget> OpenJson;
};
[default_interface] runtimeclass ReadOnlyActions : Windows.UI.Xaml.Controls.Page
{
ReadOnlyActions();
ReadOnlyActionsPageNavigationState State { get; };
IObservableVector<Microsoft.Terminal.Settings.Model.Command> FilteredActions { get; };
}
}

View file

@ -1,222 +0,0 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<Page x:Class="Microsoft.Terminal.Settings.Editor.ReadOnlyActions"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="CommonResources.xaml" />
</ResourceDictionary.MergedDictionaries>
<!--
Template for actions. This is _heavily_ copied from the command
palette, with modifications:
* We don't need to use a HighlightedTextControl, because we're
not filtering this list
* We don't need the chevron for nested commands
* We're not displaying the icon
* We're binding directly to a Command, not a FilteredCommand
If we wanted to reuse the command palette's list more directly,
that's theoretically possible, but then it would need to be
lifted out of TerminalApp and either moved into the
TerminalSettingsEditor or moved to it's own project consumed by
both TSE and TerminalApp.
-->
<DataTemplate x:Key="GeneralItemTemplate"
x:DataType="SettingsModel:Command">
<!--
This HorizontalContentAlignment="Stretch" is important
to make sure it takes the entire width of the line
-->
<ListViewItem HorizontalContentAlignment="Stretch"
AutomationProperties.AcceleratorKey="{x:Bind KeyChordText, Mode=OneWay}"
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}">
<Grid HorizontalAlignment="Stretch"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<!-- command label -->
<ColumnDefinition Width="*" />
<!-- key chord -->
<ColumnDefinition Width="32" />
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
HorizontalAlignment="Left"
Text="{x:Bind Name, Mode=OneWay}" />
<!--
Inexplicably, we don't need to set the
AutomationProperties to Raw here, unlike in the
CommandPalette. We're not quite sure why.
-->
<Border Grid.Column="1"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{ThemeResource KeyChordBorderStyle}">
<TextBlock FontSize="12"
Style="{ThemeResource KeyChordTextBlockStyle}"
Text="{x:Bind KeyChordText, Mode=OneWay}" />
</Border>
</Grid>
</ListViewItem>
</DataTemplate>
<!-- These resources again, HEAVILY copied from the command palette -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<!-- TextBox colors ! -->
<SolidColorBrush x:Key="TextControlBackground"
Color="#333333" />
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlForeground"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlBorderBrush"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForeground"
Color="#B5B5B5" />
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
Color="#404040" />
<SolidColorBrush x:Key="TextControlForegroundPointerOver"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
Color="#FF4343" />
<SolidColorBrush x:Key="TextControlBackgroundFocused"
Color="#333333" />
<SolidColorBrush x:Key="TextControlForegroundFocused"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
Color="#404040" />
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
Color="#FF4343" />
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<!-- TextBox colors ! -->
<SolidColorBrush x:Key="TextControlBackground"
Color="#CCCCCC" />
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
Color="#636363" />
<SolidColorBrush x:Key="TextControlBorderBrush"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForeground"
Color="#636363" />
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
Color="#DADADA" />
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
Color="#FF4343" />
<SolidColorBrush x:Key="TextControlBackgroundFocused"
Color="#CCCCCC" />
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
Color="#636363" />
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
Color="#FFFFFF" />
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
Color="#FF4343" />
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="1" />
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border" />
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Page.Resources>
<ScrollViewer ViewChanging="ViewChanging">
<StackPanel Style="{StaticResource SettingsStackStyle}">
<TextBlock x:Uid="Globals_KeybindingsDisclaimer"
Style="{StaticResource DisclaimerStyle}" />
<!--
The Nav_OpenJSON resource just so happens to have a .Content
and .Tooltip that are _exactly_ what we're looking for here.
-->
<HyperlinkButton x:Uid="Nav_OpenJSON"
Click="_OpenSettingsClick" />
<!-- Keybindings -->
<!--
NOTE: Globals_Keybindings.Header is not defined, because that
would result in the page having "Keybindings" displayed twice, which
looks quite redundant
-->
<ContentPresenter x:Uid="Globals_Keybindings"
Margin="0">
<ListView HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowDrop="False"
CanReorderItems="False"
IsItemClickEnabled="False"
ItemTemplate="{StaticResource GeneralItemTemplate}"
ItemsSource="{x:Bind FilteredActions, Mode=OneWay}"
SelectionMode="None" />
</ContentPresenter>
</StackPanel>
</ScrollViewer>
</Page>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -247,6 +247,10 @@
<value>Remove trailing white-space in rectangular selection</value>
<comment>Header for a control to toggle whether a text selected with block selection should be trimmed of white spaces when copied to the clipboard, or not.</comment>
</data>
<data name="Globals_TrimPaste.Header" xml:space="preserve">
<value>Remove trailing white-space when pasting</value>
<comment>Header for a control to toggle whether pasted text should be trimmed of white spaces, or not.</comment>
</data>
<data name="Globals_KeybindingsDisclaimer.Text" xml:space="preserve">
<value>Below are the currently bound keys, which can be modified by editing the JSON settings file.</value>
<comment>A disclaimer located at the top of the actions page that presents the list of keybindings.</comment>

View file

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

View file

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

View file

@ -35,6 +35,7 @@ static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" };
static constexpr std::string_view CopyFormattingKey{ "copyFormatting" };
static constexpr std::string_view WarnAboutLargePasteKey{ "largePasteWarning" };
static constexpr std::string_view WarnAboutMultiLinePasteKey{ "multiLinePasteWarning" };
static constexpr std::string_view TrimPasteKey{ "trimPaste" };
static constexpr std::string_view LaunchModeKey{ "launchMode" };
static constexpr std::string_view ConfirmCloseAllKey{ "confirmCloseAllTabs" };
static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" };
@ -101,6 +102,7 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_CopyFormatting = _CopyFormatting;
globals->_WarnAboutLargePaste = _WarnAboutLargePaste;
globals->_WarnAboutMultiLinePaste = _WarnAboutMultiLinePaste;
globals->_TrimPaste = _TrimPaste;
globals->_InitialPosition = _InitialPosition;
globals->_CenterOnLaunch = _CenterOnLaunch;
globals->_LaunchMode = _LaunchMode;
@ -201,6 +203,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, CopyFormattingKey, _CopyFormatting);
JsonUtils::GetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
JsonUtils::GetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
JsonUtils::GetValueForKey(json, TrimPasteKey, _TrimPaste);
JsonUtils::GetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference);
JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode);
JsonUtils::GetValueForKey(json, LanguageKey, _Language);
@ -306,6 +309,7 @@ Json::Value GlobalAppSettings::ToJson() const
JsonUtils::SetValueForKey(json, CopyFormattingKey, _CopyFormatting);
JsonUtils::SetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
JsonUtils::SetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
JsonUtils::SetValueForKey(json, TrimPasteKey, _TrimPaste);
JsonUtils::SetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference);
JsonUtils::SetValueForKey(json, LaunchModeKey, _LaunchMode);
JsonUtils::SetValueForKey(json, LanguageKey, _Language);

View file

@ -77,6 +77,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, WarnAboutLargePaste, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, WarnAboutMultiLinePaste, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, TrimPaste, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::LaunchPosition, InitialPosition, nullptr, nullptr);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, CenterOnLaunch, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::FirstWindowPreference, FirstWindowPreference, FirstWindowPreference::DefaultProfile);

View file

@ -63,6 +63,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Microsoft.Terminal.Control.CopyFormat, CopyFormatting);
INHERITABLE_SETTING(Boolean, WarnAboutLargePaste);
INHERITABLE_SETTING(Boolean, WarnAboutMultiLinePaste);
INHERITABLE_SETTING(Boolean, TrimPaste);
INHERITABLE_SETTING(LaunchPosition, InitialPosition);
INHERITABLE_SETTING(Boolean, CenterOnLaunch);
INHERITABLE_SETTING(FirstWindowPreference, FirstWindowPreference);

View file

@ -14,7 +14,6 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="VcDevCmdGenerator.h" />
<ClInclude Include="VisualStudioGenerator.h" />
<ClInclude Include="DefaultTerminal.h">
<DependentUpon>DefaultTerminal.idl</DependentUpon>
@ -85,7 +84,6 @@
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="VcDevCmdGenerator.cpp" />
<ClCompile Include="VisualStudioGenerator.cpp" />
<ClCompile Include="DefaultTerminal.cpp">
<DependentUpon>DefaultTerminal.idl</DependentUpon>
@ -267,4 +265,4 @@
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
<Import Project="..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="Exists('..\..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />
</Project>
</Project>

View file

@ -46,9 +46,6 @@
<ClCompile Include="VsSetupConfiguration.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
<ClCompile Include="VcDevCmdGenerator.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@ -95,9 +92,6 @@
<ClInclude Include="VsSetupConfiguration.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="VcDevCmdGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="ActionArgs.idl" />
@ -129,4 +123,4 @@
<UniqueIdentifier>{81a6314f-aa5b-4533-a499-13bc3a5c4af0}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>

View file

@ -1,94 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "DynamicProfileUtils.h"
#include "VcDevCmdGenerator.h"
using namespace winrt::Microsoft::Terminal::Settings::Model;
void VcDevCmdGenerator::GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
{
try
{
const std::filesystem::path root{ GetVcCmdScriptDirectory(instance) };
if (!std::filesystem::exists(root))
{
return;
}
// x64 environments only installed on 64-bit machines.
#ifdef _WIN64
const auto vcvars64 = root / L"vcvars64.bat";
if (std::filesystem::exists(vcvars64))
{
auto profile = CreateProfile(instance, L"x64", vcvars64, hidden);
profiles.emplace_back(std::move(profile));
// Only the VC environment for the matching architecture should be shown by default.
hidden = true;
}
const auto vcvarsamd64_x86 = root / L"vcvarsamd64_x86.bat";
if (std::filesystem::exists(vcvarsamd64_x86))
{
auto profile = CreateProfile(instance, L"x64_x86", vcvarsamd64_x86, true);
profiles.emplace_back(std::move(profile));
}
const auto vcvarsx86_amd64 = root / L"vcvarsx86_amd64.bat";
if (std::filesystem::exists(vcvarsx86_amd64))
{
auto profile = CreateProfile(instance, L"x86_x64", vcvarsx86_amd64, true);
profiles.emplace_back(std::move(profile));
}
#endif // _WIN64
const auto vcvars32 = root / L"vcvars32.bat";
if (std::filesystem::exists(vcvars32))
{
auto profile = CreateProfile(instance, L"x86", vcvars32, hidden);
profiles.emplace_back(std::move(profile));
}
}
CATCH_LOG();
}
winrt::com_ptr<implementation::Profile> VcDevCmdGenerator::CreateProfile(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix, const std::filesystem::path& path, bool hidden) const
{
const auto seed = GetProfileGuidSeed(instance, path);
const winrt::guid profileGuid{ ::Microsoft::Console::Utils::CreateV5Uuid(TERMINAL_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(seed))) };
auto profile = winrt::make_self<implementation::Profile>(profileGuid);
profile->Name(winrt::hstring{ GetProfileName(instance, prefix) });
profile->Commandline(winrt::hstring{ GetProfileCommandLine(path) });
profile->StartingDirectory(winrt::hstring{ instance.GetInstallationPath() });
profile->Icon(winrt::hstring{ GetProfileIconPath() });
profile->Hidden(hidden);
return profile;
}
std::wstring VcDevCmdGenerator::GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance, const std::filesystem::path& path) const
{
return L"VsDevCmd" + instance.GetInstanceId() + path.native();
}
std::wstring VcDevCmdGenerator::GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix) const
{
std::wstring name{ prefix + L" Native Tools Command Prompt for VS " };
name.append(instance.GetProfileNameSuffix());
return name;
}
std::wstring VcDevCmdGenerator::GetProfileCommandLine(const std::filesystem::path& path) const
{
std::wstring commandLine{ L"cmd.exe /k \"" + path.native() + L"\"" };
return commandLine;
}
std::wstring VcDevCmdGenerator::GetVcCmdScriptDirectory(const VsSetupConfiguration::VsSetupInstance& instance) const
{
return instance.ResolvePath(L"VC\\Auxiliary\\Build\\");
}

View file

@ -1,40 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- VcDevCmdGenerator
Abstract:
- Dynamic profile generator for Visual C++ development environments.
Author(s):
- Heath Stewart - September 2021
--*/
#pragma once
#include "VisualStudioGenerator.h"
#include "VsSetupConfiguration.h"
namespace winrt::Microsoft::Terminal::Settings::Model
{
class VcDevCmdGenerator final : public VisualStudioGenerator::IVisualStudioProfileGenerator
{
public:
void GenerateProfiles(const VsSetupConfiguration::VsSetupInstance& instance, bool hidden, std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
private:
winrt::com_ptr<implementation::Profile> CreateProfile(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix, const std::filesystem::path& path, bool hidden) const;
std::wstring GetProfileIconPath() const
{
return L"ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png";
}
std::wstring GetProfileGuidSeed(const VsSetupConfiguration::VsSetupInstance& instance, const std::filesystem::path& path) const;
std::wstring GetProfileName(const VsSetupConfiguration::VsSetupInstance& instance, const std::wstring_view& prefix) const;
std::wstring GetProfileCommandLine(const std::filesystem::path& path) const;
std::wstring GetVcCmdScriptDirectory(const VsSetupConfiguration::VsSetupInstance& instance) const;
};
};

View file

@ -4,7 +4,6 @@
#include "pch.h"
#include "DynamicProfileUtils.h"
#include "VisualStudioGenerator.h"
#include "VcDevCmdGenerator.h"
#include "VsDevCmdGenerator.h"
#include "VsDevShellGenerator.h"
@ -20,7 +19,6 @@ void VisualStudioGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implemen
const auto instances = VsSetupConfiguration::QueryInstances();
VsDevCmdGenerator devCmdGenerator;
VcDevCmdGenerator vcCmdGenerator;
VsDevShellGenerator devShellGenerator;
// Instances are ordered from latest to oldest. Hide all but the profiles for the latest instance.
@ -28,9 +26,7 @@ void VisualStudioGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implemen
for (auto const& instance : instances)
{
devCmdGenerator.GenerateProfiles(instance, hidden, profiles);
vcCmdGenerator.GenerateProfiles(instance, hidden, profiles);
devShellGenerator.GenerateProfiles(instance, hidden, profiles);
hidden = true;
}
}

View file

@ -38,8 +38,15 @@ std::wstring VsDevCmdGenerator::GetProfileName(const VsSetupConfiguration::VsSet
std::wstring VsDevCmdGenerator::GetProfileCommandLine(const VsSetupConfiguration::VsSetupInstance& instance) const
{
std::wstring commandLine{ L"cmd.exe /k \"" + GetDevCmdScriptPath(instance) + L"\"" };
std::wstring commandLine;
commandLine.reserve(256);
commandLine.append(LR"(cmd.exe /k ")");
commandLine.append(GetDevCmdScriptPath(instance));
#if defined(_M_ARM64)
commandLine.append(LR"(" -arch=arm64 -host_arch=x64)");
#elif defined(_M_AMD64)
commandLine.append(LR"(" -arch=x64 -host_arch=x64)");
#endif
return commandLine;
}

View file

@ -42,10 +42,19 @@ std::wstring VsDevShellGenerator::GetProfileCommandLine(const VsSetupConfigurati
// The triple-quotes are a PowerShell path escape sequence that can safely be stored in a JSON object.
// The "SkipAutomaticLocation" parameter will prevent "Enter-VsDevShell" from automatically setting the shell path
// so the path in the profile will be used instead.
std::wstring commandLine{ L"powershell.exe -NoExit -Command \"& {" };
commandLine.append(L"Import-Module \"\"\"" + GetDevShellModulePath(instance) + L"\"\"\";");
commandLine.append(L"Enter-VsDevShell " + instance.GetInstanceId() + L" -SkipAutomaticLocation");
commandLine.append(L"}\"");
std::wstring commandLine;
commandLine.reserve(256);
commandLine.append(LR"(powershell.exe -NoExit -Command "&{Import-Module """)");
commandLine.append(GetDevShellModulePath(instance));
commandLine.append(LR"("""; Enter-VsDevShell )");
commandLine.append(instance.GetInstanceId());
#if defined(_M_ARM64)
commandLine.append(LR"( -SkipAutomaticLocation -DevCmdArguments """-arch=arm64 -host_arch=x64"""}")");
#elif defined(_M_AMD64)
commandLine.append(LR"( -SkipAutomaticLocation -DevCmdArguments """-arch=x64 -host_arch=x64"""}")");
#else
commandLine.append(LR"( -SkipAutomaticLocation}")");
#endif
return commandLine;
}

View file

@ -21,17 +21,11 @@ std::vector<VsSetupConfiguration::VsSetupInstance> VsSetupConfiguration::QueryIn
wil::com_ptr<IEnumSetupInstances> e;
THROW_IF_FAILED(pQuery->EnumInstances(&e));
ComPtrSetupInstance rgpInstance;
auto result = e->Next(1, &rgpInstance, nullptr);
while (S_OK == result)
for (ComPtrSetupInstance rgpInstance; S_OK == THROW_IF_FAILED(e->Next(1, &rgpInstance, nullptr));)
{
// wrap the COM pointers in a friendly interface
instances.emplace_back(VsSetupInstance{ pQuery, rgpInstance });
result = e->Next(1, &rgpInstance, nullptr);
instances.emplace_back(pQuery, std::move(rgpInstance));
}
THROW_IF_FAILED(result);
// Sort instances based on version and install date from latest to oldest.
std::sort(instances.begin(), instances.end(), [](const VsSetupInstance& a, const VsSetupInstance& b) {
auto const aVersion = a.GetComparableVersion();
@ -123,7 +117,7 @@ std::wstring VsSetupConfiguration::GetStringProperty(ISetupPropertyStore* pProps
}
wil::unique_variant var;
if (FAILED(pProps->GetValue(name.data(), &var)))
if (FAILED(pProps->GetValue(name.data(), &var)) || var.vt != VT_BSTR)
{
return std::wstring{};
}

View file

@ -56,12 +56,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model
return VsSetupConfiguration::GetInstallationVersion(inst.get());
}
unsigned long long GetComparableInstallDate() const
unsigned long long GetComparableInstallDate() const noexcept
{
return installDate;
}
unsigned long long GetComparableVersion() const
unsigned long long GetComparableVersion() const noexcept
{
return version;
}
@ -114,9 +114,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model
return profileNameSuffix;
}
private:
friend class VsSetupConfiguration;
VsSetupInstance(ComPtrSetupQuery pQuery, ComPtrSetupInstance pInstance) :
query(pQuery), // Copy and AddRef the query object.
inst(std::move(pInstance)), // Take ownership of the instance object.
@ -126,6 +123,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model
{
}
private:
ComPtrSetupQuery query;
ComPtrSetupInstance inst;

View file

@ -22,7 +22,7 @@
// Miscellaneous
"confirmCloseAllTabs": true,
"startOnUserLogin": false,
"startOnUserLogin": false,
"theme": "system",
"snapToGridOnResize": true,

View file

@ -12,6 +12,7 @@
"copyOnSelect": false,
"copyFormatting": true,
"trimBlockSelection": false,
"trimPaste": true,
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
// Tab UI
@ -24,7 +25,7 @@
// Miscellaneous
"confirmCloseAllTabs": true,
"startOnUserLogin": false,
"startOnUserLogin": false,
"theme": "system",
"snapToGridOnResize": true,
"disableAnimations": false,

View file

@ -86,7 +86,7 @@ AppHost::AppHost() noexcept :
_window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop());
_window->MakeWindow();
_windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& args) {
_GetWindowLayoutRequestedToken = _windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& args) {
// The peasants are running on separate threads, so they'll need to
// swap what context they are in to the ui thread to get the actual layout.
args.WindowLayoutJsonAsync(_GetWindowLayoutAsync());
@ -401,23 +401,42 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se
_HideNotificationIconRequested();
}
// We don't want to try to save layouts if we are about to close
_getWindowLayoutThrottler.reset();
_windowManager.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken);
// Remove ourself from the list of peasants so that we aren't included in
// any future requests. This will also mean we block until any existing
// event handler finishes.
_windowManager.SignalClose();
_window->Close();
}
LaunchPosition AppHost::_GetWindowLaunchPosition()
{
// Get the position of the current window. This includes the
// non-client already.
const auto window = _window->GetWindowRect();
const auto dpi = _window->GetCurrentDpi();
const auto nonClientArea = _window->GetNonClientFrame(dpi);
// The nonClientArea adjustment is negative, so subtract that out.
// This way we save the user-visible location of the terminal.
LaunchPosition pos{};
pos.X = window.left - nonClientArea.left;
pos.Y = window.top;
// If we started saving before closing, but didn't resume the event handler
// until after _window might be a nullptr.
if (!_window)
{
return pos;
}
try
{
// Get the position of the current window. This includes the
// non-client already.
const auto window = _window->GetWindowRect();
const auto dpi = _window->GetCurrentDpi();
const auto nonClientArea = _window->GetNonClientFrame(dpi);
// The nonClientArea adjustment is negative, so subtract that out.
// This way we save the user-visible location of the terminal.
pos.X = window.left - nonClientArea.left;
pos.Y = window.top;
}
CATCH_LOG();
return pos;
}
@ -723,10 +742,15 @@ winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> AppHost::_GetWindowL
{
winrt::apartment_context peasant_thread;
winrt::hstring layoutJson = L"";
// Use the main thread since we are accessing controls.
co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher());
const auto pos = _GetWindowLaunchPosition();
const auto layoutJson = _logic.GetWindowLayoutJson(pos);
try
{
const auto pos = _GetWindowLaunchPosition();
layoutJson = _logic.GetWindowLayoutJson(pos);
}
CATCH_LOG()
// go back to give the result to the peasant.
co_await peasant_thread;
@ -1323,14 +1347,23 @@ void AppHost::_WindowMoved()
const auto root{ _logic.GetRoot() };
// This is basically DismissAllPopups which is also in
// TerminalSettingsEditor/Utils.h
// There isn't a good place that's shared between these two files, but
// it's only 5 LOC so whatever.
const auto popups{ Media::VisualTreeHelper::GetOpenPopupsForXamlRoot(root.XamlRoot()) };
for (const auto& p : popups)
try
{
p.IsOpen(false);
// This is basically DismissAllPopups which is also in
// TerminalSettingsEditor/Utils.h
// There isn't a good place that's shared between these two files, but
// it's only 5 LOC so whatever.
const auto popups{ Media::VisualTreeHelper::GetOpenPopupsForXamlRoot(root.XamlRoot()) };
for (const auto& p : popups)
{
p.IsOpen(false);
}
}
catch (...)
{
// We purposely don't log here, because this is exceptionally noisy,
// especially on startup, when we're moving the window into place
// but might not have a real xamlRoot yet.
}
}
}

View file

@ -114,4 +114,5 @@ private:
winrt::event_token _NotificationIconPressedToken;
winrt::event_token _ShowNotificationIconContextMenuToken;
winrt::event_token _NotificationIconMenuItemSelectedToken;
winrt::event_token _GetWindowLayoutRequestedToken;
};

View file

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

View file

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

View file

@ -10,12 +10,6 @@
</alwaysDisabledBrandingTokens>
</feature>
<feature>
<name>Feature_EditableActionsPage</name>
<description>The Actions page in the settings UI should allow users to edit actions.</description>
<stage>AlwaysEnabled</stage>
</feature>
<feature>
<name>Feature_EditableUnfocusedAppearance</name>
<description>The unfocused appearance section in profiles in the SUI that allows users to create and edit unfocused appearances.</description>

View file

@ -274,8 +274,7 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept
const FontInfo& fontInfo = activeScreenInfo.GetCurrentFont();
consoleFontInfoEx.FontFamily = fontInfo.GetFamily();
consoleFontInfoEx.FontWeight = fontInfo.GetWeight();
RETURN_IF_FAILED(fontInfo.FillLegacyNameBuffer(gsl::make_span(consoleFontInfoEx.FaceName)));
fontInfo.FillLegacyNameBuffer(consoleFontInfoEx.FaceName);
return S_OK;
}
@ -387,7 +386,7 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept
RETURN_HR_IF(E_INVALIDARG, WI_IsAnyFlagSet(mode, ~(INPUT_MODES | PRIVATE_MODES)));
// ECHO on with LINE off is invalid.
RETURN_HR_IF(E_INVALIDARG, WI_IsFlagSet(mode, ENABLE_ECHO_INPUT) && WI_IsFlagClear(mode, ENABLE_LINE_INPUT));
RETURN_HR_IF_EXPECTED(E_INVALIDARG, WI_IsFlagSet(mode, ENABLE_ECHO_INPUT) && WI_IsFlagClear(mode, ENABLE_LINE_INPUT));
}
return S_OK;
@ -1248,55 +1247,6 @@ void ApiRoutines::GetConsoleDisplayModeImpl(ULONG& flags) noexcept
CATCH_RETURN();
}
// Routine Description:
// - A private API call for changing the cursor keys input mode between normal and application mode.
// The cursor keys are the arrows, plus Home and End.
// Parameters:
// - fApplicationMode - set to true to enable Application Mode Input, false for Numeric Mode Input.
// Return value:
// - True if handled successfully. False otherwise.
[[nodiscard]] NTSTATUS DoSrvPrivateSetCursorKeysMode(_In_ bool fApplicationMode)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (gci.pInputBuffer == nullptr)
{
return STATUS_UNSUCCESSFUL;
}
gci.pInputBuffer->GetTerminalInput().ChangeCursorKeysMode(fApplicationMode);
return STATUS_SUCCESS;
}
// Routine Description:
// - A private API call for changing the keypad input mode between numeric and application mode.
// This controls what the keys on the numpad translate to.
// Parameters:
// - fApplicationMode - set to true to enable Application Mode Input, false for Numeric Mode Input.
// Return value:
// - True if handled successfully. False otherwise.
[[nodiscard]] NTSTATUS DoSrvPrivateSetKeypadMode(_In_ bool fApplicationMode)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (gci.pInputBuffer == nullptr)
{
return STATUS_UNSUCCESSFUL;
}
gci.pInputBuffer->GetTerminalInput().ChangeKeypadMode(fApplicationMode);
return STATUS_SUCCESS;
}
// Function Description:
// - A private API call which enables/disables sending full input records
// encoded as a string of characters to the client application.
// Parameters:
// - win32InputMode - set to true to enable win32-input-mode, false to disable.
// Return value:
// - <none>
void DoSrvPrivateEnableWin32InputMode(const bool win32InputMode)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.pInputBuffer->GetTerminalInput().ChangeWin32InputMode(win32InputMode);
}
// Routine Description:
// - A private API call for changing the screen mode between normal and reverse.
// When in reverse screen mode, the background and foreground colors are switched.
@ -1525,78 +1475,6 @@ void DoSrvPrivateUseMainScreenBuffer(SCREEN_INFORMATION& screenInfo)
screenInfo.GetActiveBuffer().UseMainScreenBuffer();
}
// Routine Description:
// - A private API call for enabling VT200 style mouse mode.
// Parameters:
// - fEnable - true to enable default tracking mode, false to disable mouse mode.
// Return value:
// - None
void DoSrvPrivateEnableVT200MouseMode(const bool fEnable)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetActiveInputBuffer()->GetTerminalInput().EnableDefaultTracking(fEnable);
}
// Routine Description:
// - A private API call for enabling utf8 style mouse mode.
// Parameters:
// - fEnable - true to enable, false to disable.
// Return value:
// - None
void DoSrvPrivateEnableUTF8ExtendedMouseMode(const bool fEnable)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetActiveInputBuffer()->GetTerminalInput().SetUtf8ExtendedMode(fEnable);
}
// Routine Description:
// - A private API call for enabling SGR style mouse mode.
// Parameters:
// - fEnable - true to enable, false to disable.
// Return value:
// - None
void DoSrvPrivateEnableSGRExtendedMouseMode(const bool fEnable)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetActiveInputBuffer()->GetTerminalInput().SetSGRExtendedMode(fEnable);
}
// Routine Description:
// - A private API call for enabling button-event mouse mode.
// Parameters:
// - fEnable - true to enable button-event mode, false to disable mouse mode.
// Return value:
// - None
void DoSrvPrivateEnableButtonEventMouseMode(const bool fEnable)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetActiveInputBuffer()->GetTerminalInput().EnableButtonEventTracking(fEnable);
}
// Routine Description:
// - A private API call for enabling any-event mouse mode.
// Parameters:
// - fEnable - true to enable any-event mode, false to disable mouse mode.
// Return value:
// - None
void DoSrvPrivateEnableAnyEventMouseMode(const bool fEnable)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetActiveInputBuffer()->GetTerminalInput().EnableAnyEventTracking(fEnable);
}
// Routine Description:
// - A private API call for enabling alternate scroll mode
// Parameters:
// - fEnable - true to enable alternate scroll mode, false to disable.
// Return value:
// None
void DoSrvPrivateEnableAlternateScroll(const bool fEnable)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetActiveInputBuffer()->GetTerminalInput().EnableAlternateScroll(fEnable);
}
// Routine Description:
// - A private API call for performing a VT-style erase all operation on the buffer.
// See SCREEN_INFORMATION::VtEraseAll's description for details.

View file

@ -18,10 +18,6 @@ Revision History:
#include "../inc/conattrs.hpp"
class SCREEN_INFORMATION;
[[nodiscard]] NTSTATUS DoSrvPrivateSetCursorKeysMode(_In_ bool fApplicationMode);
[[nodiscard]] NTSTATUS DoSrvPrivateSetKeypadMode(_In_ bool fApplicationMode);
void DoSrvPrivateEnableWin32InputMode(const bool win32InputMode);
[[nodiscard]] NTSTATUS DoSrvPrivateSetScreenMode(const bool reverseMode);
[[nodiscard]] NTSTATUS DoSrvPrivateSetAutoWrapMode(const bool wrapAtEOL);
@ -35,13 +31,6 @@ void DoSrvPrivateAllowCursorBlinking(SCREEN_INFORMATION& screenInfo, const bool
[[nodiscard]] NTSTATUS DoSrvPrivateUseAlternateScreenBuffer(SCREEN_INFORMATION& screenInfo);
void DoSrvPrivateUseMainScreenBuffer(SCREEN_INFORMATION& screenInfo);
void DoSrvPrivateEnableVT200MouseMode(const bool fEnable);
void DoSrvPrivateEnableUTF8ExtendedMouseMode(const bool fEnable);
void DoSrvPrivateEnableSGRExtendedMouseMode(const bool fEnable);
void DoSrvPrivateEnableButtonEventMouseMode(const bool fEnable);
void DoSrvPrivateEnableAnyEventMouseMode(const bool fEnable);
void DoSrvPrivateEnableAlternateScroll(const bool fEnable);
[[nodiscard]] HRESULT DoSrvPrivateEraseAll(SCREEN_INFORMATION& screenInfo);
[[nodiscard]] HRESULT DoSrvPrivateClearBuffer(SCREEN_INFORMATION& screenInfo);

View file

@ -15,6 +15,7 @@
using namespace Microsoft::Console;
using Microsoft::Console::Interactivity::ServiceLocator;
using Microsoft::Console::VirtualTerminal::TerminalInput;
WriteBuffer::WriteBuffer(_In_ Microsoft::Console::IIoProvider& io) :
_io{ io },
@ -237,43 +238,28 @@ bool ConhostInternalGetSet::SetConsoleWindowInfo(const bool absolute, const SMAL
}
// Routine Description:
// - Connects the PrivateSetCursorKeysMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateSetCursorKeysMode is an internal-only "API" call that the vt commands can execute,
// - Sets the various terminal input modes.
// SetInputMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - fApplicationMode - set to true to enable Application Mode Input, false for Normal Mode.
// - mode - the input mode to change.
// - enabled - set to true to enable the mode, false to disable it.
// Return Value:
// - true if successful (see DoSrvPrivateSetCursorKeysMode). false otherwise.
bool ConhostInternalGetSet::PrivateSetCursorKeysMode(const bool fApplicationMode)
// - true if successful. false otherwise.
bool ConhostInternalGetSet::SetInputMode(const TerminalInput::Mode mode, const bool enabled)
{
return NT_SUCCESS(DoSrvPrivateSetCursorKeysMode(fApplicationMode));
}
auto& terminalInput = _io.GetActiveInputBuffer()->GetTerminalInput();
terminalInput.SetInputMode(mode, enabled);
// Routine Description:
// - Connects the PrivateSetKeypadMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateSetKeypadMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - fApplicationMode - set to true to enable Application Mode Input, false for Numeric Mode.
// Return Value:
// - true if successful (see DoSrvPrivateSetKeypadMode). false otherwise.
bool ConhostInternalGetSet::PrivateSetKeypadMode(const bool fApplicationMode)
{
return NT_SUCCESS(DoSrvPrivateSetKeypadMode(fApplicationMode));
}
// Routine Description:
// - Connects the PrivateEnableWin32InputMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEnableWin32InputMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - win32InputMode - set to true to enable win32-input-mode, false to disable.
// Return Value:
// - true always
bool ConhostInternalGetSet::PrivateEnableWin32InputMode(const bool win32InputMode)
{
DoSrvPrivateEnableWin32InputMode(win32InputMode);
return true;
// If we're a conpty, AND WE'RE IN VT INPUT MODE, always pass input mode requests
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input.
// The original comment said, "Once the conpty supports these types of input,
// this check can be removed. See GH#4911". Unfortunately, time has shown
// us that SSH 7.7 _also_ requests mouse input and that can have a user interface
// impact on the actual connected terminal. We can't remove this check,
// because SSH <=7.7 is out in the wild on all versions of Windows <=2004.
return !(IsConsolePty() && PrivateIsVtInputEnabled());
}
// Routine Description:
@ -289,7 +275,7 @@ bool ConhostInternalGetSet::PrivateSetAnsiMode(const bool ansiMode)
auto& stateMachine = _io.GetActiveOutputBuffer().GetStateMachine();
stateMachine.SetAnsiMode(ansiMode);
auto& terminalInput = _io.GetActiveInputBuffer()->GetTerminalInput();
terminalInput.ChangeAnsiMode(ansiMode);
terminalInput.SetInputMode(TerminalInput::Mode::Ansi, ansiMode);
return true;
}
@ -448,90 +434,6 @@ bool ConhostInternalGetSet::PrivateUseMainScreenBuffer()
return true;
}
// Routine Description:
// - Connects the PrivateEnableVT200MouseMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEnableVT200MouseMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - enabled - set to true to enable vt200 mouse mode, false to disable
// Return Value:
// - true if successful (see DoSrvPrivateEnableVT200MouseMode). false otherwise.
bool ConhostInternalGetSet::PrivateEnableVT200MouseMode(const bool enabled)
{
DoSrvPrivateEnableVT200MouseMode(enabled);
return true;
}
// Routine Description:
// - Connects the PrivateEnableUTF8ExtendedMouseMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEnableUTF8ExtendedMouseMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - enabled - set to true to enable utf8 extended mouse mode, false to disable
// Return Value:
// - true if successful (see DoSrvPrivateEnableUTF8ExtendedMouseMode). false otherwise.
bool ConhostInternalGetSet::PrivateEnableUTF8ExtendedMouseMode(const bool enabled)
{
DoSrvPrivateEnableUTF8ExtendedMouseMode(enabled);
return true;
}
// Routine Description:
// - Connects the PrivateEnableSGRExtendedMouseMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEnableSGRExtendedMouseMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - enabled - set to true to enable SGR extended mouse mode, false to disable
// Return Value:
// - true if successful (see DoSrvPrivateEnableSGRExtendedMouseMode). false otherwise.
bool ConhostInternalGetSet::PrivateEnableSGRExtendedMouseMode(const bool enabled)
{
DoSrvPrivateEnableSGRExtendedMouseMode(enabled);
return true;
}
// Routine Description:
// - Connects the PrivateEnableButtonEventMouseMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEnableButtonEventMouseMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - enabled - set to true to enable button-event mouse mode, false to disable
// Return Value:
// - true if successful (see DoSrvPrivateEnableButtonEventMouseMode). false otherwise.
bool ConhostInternalGetSet::PrivateEnableButtonEventMouseMode(const bool enabled)
{
DoSrvPrivateEnableButtonEventMouseMode(enabled);
return true;
}
// Routine Description:
// - Connects the PrivateEnableAnyEventMouseMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEnableAnyEventMouseMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - enabled - set to true to enable any-event mouse mode, false to disable
// Return Value:
// - true if successful (see DoSrvPrivateEnableAnyEventMouseMode). false otherwise.
bool ConhostInternalGetSet::PrivateEnableAnyEventMouseMode(const bool enabled)
{
DoSrvPrivateEnableAnyEventMouseMode(enabled);
return true;
}
// Routine Description:
// - Connects the PrivateEnableAlternateScroll call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEnableAlternateScroll is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on out public API surface.
// Arguments:
// - enabled - set to true to enable alternate scroll mode, false to disable
// Return Value:
// - true if successful (see DoSrvPrivateEnableAnyEventMouseMode). false otherwise.
bool ConhostInternalGetSet::PrivateEnableAlternateScroll(const bool enabled)
{
DoSrvPrivateEnableAlternateScroll(enabled);
return true;
}
// Routine Description:
// - Connects the PrivateEraseAll call directly into our Driver Message servicing call inside Conhost.exe
// PrivateEraseAll is an internal-only "API" call that the vt commands can execute,

View file

@ -72,9 +72,7 @@ public:
bool SetConsoleWindowInfo(bool const absolute,
const SMALL_RECT& window) override;
bool PrivateSetCursorKeysMode(const bool applicationMode) override;
bool PrivateSetKeypadMode(const bool applicationMode) override;
bool PrivateEnableWin32InputMode(const bool win32InputMode) override;
bool SetInputMode(const Microsoft::Console::VirtualTerminal::TerminalInput::Mode mode, const bool enabled) override;
bool PrivateSetAnsiMode(const bool ansiMode) override;
bool PrivateSetScreenMode(const bool reverseMode) override;
@ -97,12 +95,6 @@ public:
bool PrivateUseMainScreenBuffer() override;
bool PrivateEnableVT200MouseMode(const bool enabled) override;
bool PrivateEnableUTF8ExtendedMouseMode(const bool enabled) override;
bool PrivateEnableSGRExtendedMouseMode(const bool enabled) override;
bool PrivateEnableButtonEventMouseMode(const bool enabled) override;
bool PrivateEnableAnyEventMouseMode(const bool enabled) override;
bool PrivateEnableAlternateScroll(const bool enabled) override;
bool PrivateEraseAll() override;
bool PrivateClearBuffer() override;

View file

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

View file

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

View file

@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <guiddef.h>
#define memcmp(a, b, c) (!InlineIsEqualGUID(a, b))

View file

@ -255,6 +255,14 @@ void Selection::ExtendSelection(_In_ COORD coordBufferPos)
srNewSelection.Top = _coordSelectionAnchor.Y;
}
// This function is called on WM_MOUSEMOVE.
// Prevent triggering an invalidation just because the mouse moved
// in the same cell without changing the actual (visible) selection.
if (_srSelectionRect == srNewSelection)
{
return;
}
// call special update method to modify the displayed selection in-place
// NOTE: Using HideSelection, editing the rectangle, then ShowSelection will cause flicker.
//_PaintUpdateSelection(&srNewSelection);

View file

@ -28,6 +28,8 @@
#include "../inc/conint.h"
#include "../propslib/DelegationConfig.hpp"
#include "tracing.hpp"
#if TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED
#include "ITerminalHandoff.h"
#endif // TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED
@ -69,10 +71,20 @@ try
if (SUCCEEDED(DelegationConfig::s_GetDefaultConsoleId(delegationClsid)))
{
Globals.handoffConsoleClsid = delegationClsid;
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_FoundDelegationConsole",
TraceLoggingGuid(Globals.handoffConsoleClsid.value(), "ConsoleClsid"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
if (SUCCEEDED(DelegationConfig::s_GetDefaultTerminalId(delegationClsid)))
{
Globals.handoffTerminalClsid = delegationClsid;
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_FoundDelegationTerminal",
TraceLoggingGuid(Globals.handoffTerminalClsid.value(), "TerminalClsid"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}
@ -400,7 +412,16 @@ HRESULT ConsoleCreateIoThread(_In_ HANDLE Server,
[[maybe_unused]] PCONSOLE_API_MSG connectMessage)
try
{
// Create a telemetry instance here - this singleton is responsible for
// setting up the g_hConhostV2EventTraceProvider, which is otherwise not
// initialized in the defterm handoff at this point.
(void)Telemetry::Instance();
#if !TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_ReceiveHandoff_Disabled",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
#else // TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED
auto& g = ServiceLocator::LocateGlobals();
@ -418,9 +439,19 @@ try
if (!g.handoffTerminalClsid)
{
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_ReceiveHandoff_NoTerminal",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
return E_NOT_SET;
}
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_ReceiveHandoff",
TraceLoggingGuid(g.handoffTerminalClsid.value(), "TerminalClsid"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// Capture handle to the inbox process into a unique handle holder.
g.handoffInboxConsoleHandle.reset(hostProcessHandle);
@ -453,9 +484,19 @@ try
RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(outPipeTheirSide.addressof(), outPipeOurSide.addressof(), nullptr, 0));
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_ReceiveHandoff_OpenedPipes",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE, TRUE, static_cast<DWORD>(connectMessage->Descriptor.Process)) };
RETURN_LAST_ERROR_IF_NULL(clientProcess.get());
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_ReceiveHandoff_OpenedClient",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
wil::unique_handle refHandle;
RETURN_IF_NTSTATUS_FAILED(DeviceHandle::CreateClientHandle(refHandle.addressof(),
Server,
@ -466,7 +507,18 @@ try
::Microsoft::WRL::ComPtr<ITerminalHandoff> handoff;
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_PrepareToCreateDelegationTerminal",
TraceLoggingGuid(g.handoffTerminalClsid.value(), "TerminalClsid"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
RETURN_IF_FAILED(CoCreateInstance(g.handoffTerminalClsid.value(), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&handoff)));
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_CreatedDelegationTerminal",
TraceLoggingGuid(g.handoffTerminalClsid.value(), "TerminalClsid"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
RETURN_IF_FAILED(handoff->EstablishPtyHandoff(inPipeTheirSide.get(),
outPipeTheirSide.get(),
@ -475,6 +527,11 @@ try
serverProcess,
clientProcess.get()));
TraceLoggingWrite(g_hConhostV2EventTraceProvider,
"SrvInit_DelegateToTerminalSucceeded",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
inPipeTheirSide.reset();
outPipeTheirSide.reset();
signalPipeTheirSide.reset();

View file

@ -7,20 +7,11 @@
#include "../inc/FontInfoBase.hpp"
bool operator==(const FontInfoBase& a, const FontInfoBase& b)
{
return a._faceName == b._faceName &&
a._weight == b._weight &&
a._family == b._family &&
a._codePage == b._codePage &&
a._fDefaultRasterSetFromEngine == b._fDefaultRasterSetFromEngine;
}
FontInfoBase::FontInfoBase(const std::wstring_view faceName,
FontInfoBase::FontInfoBase(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const bool fSetDefaultRasterFont,
const unsigned int codePage) :
const unsigned int codePage) noexcept :
_faceName(faceName),
_family(family),
_weight(weight),
@ -30,20 +21,16 @@ FontInfoBase::FontInfoBase(const std::wstring_view faceName,
ValidateFont();
}
FontInfoBase::FontInfoBase(const FontInfoBase& fibFont) :
FontInfoBase(fibFont.GetFaceName(),
fibFont.GetFamily(),
fibFont.GetWeight(),
fibFont.WasDefaultRasterSetFromEngine(),
fibFont.GetCodePage())
bool FontInfoBase::operator==(const FontInfoBase& other) noexcept
{
return _faceName == other._faceName &&
_weight == other._weight &&
_family == other._family &&
_codePage == other._codePage &&
_fDefaultRasterSetFromEngine == other._fDefaultRasterSetFromEngine;
}
FontInfoBase::~FontInfoBase()
{
}
unsigned char FontInfoBase::GetFamily() const
unsigned char FontInfoBase::GetFamily() const noexcept
{
return _family;
}
@ -51,22 +38,22 @@ unsigned char FontInfoBase::GetFamily() const
// When the default raster font is forced set from the engine, this is how we differentiate it from a simple apply.
// Default raster font is internally represented as a blank face name and zeros for weight, family, and size. This is
// the hint for the engine to use whatever comes back from GetStockObject(OEM_FIXED_FONT) (at least in the GDI world).
bool FontInfoBase::WasDefaultRasterSetFromEngine() const
bool FontInfoBase::WasDefaultRasterSetFromEngine() const noexcept
{
return _fDefaultRasterSetFromEngine;
}
unsigned int FontInfoBase::GetWeight() const
unsigned int FontInfoBase::GetWeight() const noexcept
{
return _weight;
}
const std::wstring_view FontInfoBase::GetFaceName() const noexcept
const std::wstring& FontInfoBase::GetFaceName() const noexcept
{
return _faceName;
}
unsigned int FontInfoBase::GetCodePage() const
unsigned int FontInfoBase::GetCodePage() const noexcept
{
return _codePage;
}
@ -77,21 +64,18 @@ unsigned int FontInfoBase::GetCodePage() const
// Arguments:
// - buffer: the buffer into which to copy characters
// - size: the size of buffer
HRESULT FontInfoBase::FillLegacyNameBuffer(gsl::span<wchar_t> buffer) const
try
void FontInfoBase::FillLegacyNameBuffer(wchar_t (&buffer)[LF_FACESIZE]) const noexcept
{
auto toCopy = std::min<size_t>(buffer.size() - 1, _faceName.size());
auto last = std::copy(_faceName.cbegin(), _faceName.cbegin() + toCopy, buffer.begin());
std::fill(last, buffer.end(), L'\0');
return S_OK;
const auto toCopy = std::min(std::size(buffer) - 1, _faceName.size());
const auto last = std::copy_n(_faceName.data(), toCopy, &buffer[0]);
*last = L'\0';
}
CATCH_RETURN();
// NOTE: this method is intended to only be used from the engine itself to respond what font it has chosen.
void FontInfoBase::SetFromEngine(const std::wstring_view faceName,
void FontInfoBase::SetFromEngine(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const bool fSetDefaultRasterFont)
const bool fSetDefaultRasterFont) noexcept
{
_faceName = faceName;
_family = family;
@ -101,12 +85,12 @@ void FontInfoBase::SetFromEngine(const std::wstring_view faceName,
// Internally, default raster font is represented by empty facename, and zeros for weight, family, and size. Since
// FontInfoBase doesn't have sizing information, this helper checks everything else.
bool FontInfoBase::IsDefaultRasterFontNoSize() const
bool FontInfoBase::IsDefaultRasterFontNoSize() const noexcept
{
return (_weight == 0 && _family == 0 && _faceName.empty());
}
void FontInfoBase::ValidateFont()
void FontInfoBase::ValidateFont() noexcept
{
// If we were given a blank name, it meant raster fonts, which to us is always Terminal.
if (!IsDefaultRasterFontNoSize() && s_pFontDefaultList != nullptr)
@ -128,14 +112,14 @@ void FontInfoBase::ValidateFont()
}
}
bool FontInfoBase::IsTrueTypeFont() const
bool FontInfoBase::IsTrueTypeFont() const noexcept
{
return WI_IsFlagSet(_family, TMPF_TRUETYPE);
}
Microsoft::Console::Render::IFontDefaultList* FontInfoBase::s_pFontDefaultList;
void FontInfoBase::s_SetFontDefaultList(_In_ Microsoft::Console::Render::IFontDefaultList* const pFontDefaultList)
void FontInfoBase::s_SetFontDefaultList(_In_ Microsoft::Console::Render::IFontDefaultList* const pFontDefaultList) noexcept
{
s_pFontDefaultList = pFontDefaultList;
}

View file

@ -5,13 +5,29 @@
#include "../inc/FontInfoDesired.hpp"
bool operator==(const FontInfoDesired& a, const FontInfoDesired& b)
FontInfoDesired::FontInfoDesired(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const COORD coordSizeDesired,
const unsigned int codePage) noexcept :
FontInfoBase(faceName, family, weight, false, codePage),
_coordSizeDesired(coordSizeDesired)
{
return (static_cast<FontInfoBase>(a) == static_cast<FontInfoBase>(b) &&
a._coordSizeDesired == b._coordSizeDesired);
}
COORD FontInfoDesired::GetEngineSize() const
FontInfoDesired::FontInfoDesired(const FontInfo& fiFont) noexcept :
FontInfoBase(fiFont),
_coordSizeDesired(fiFont.GetUnscaledSize())
{
}
bool FontInfoDesired::operator==(const FontInfoDesired& other) noexcept
{
return FontInfoBase::operator==(other) &&
_coordSizeDesired == other._coordSizeDesired;
}
COORD FontInfoDesired::GetEngineSize() const noexcept
{
COORD coordSize = _coordSizeDesired;
if (IsTrueTypeFont())
@ -22,30 +38,14 @@ COORD FontInfoDesired::GetEngineSize() const
return coordSize;
}
FontInfoDesired::FontInfoDesired(const std::wstring_view faceName,
const unsigned char family,
const unsigned int weight,
const COORD coordSizeDesired,
const unsigned int codePage) :
FontInfoBase(faceName, family, weight, false, codePage),
_coordSizeDesired(coordSizeDesired)
{
}
FontInfoDesired::FontInfoDesired(const FontInfo& fiFont) :
FontInfoBase(fiFont),
_coordSizeDesired(fiFont.GetUnscaledSize())
{
}
// This helper determines if this object represents the default raster font. This can either be because internally we're
// using the empty facename and zeros for size, weight, and family, or it can be because we were given explicit
// dimensions from the engine that were the result of loading the default raster font. See GdiEngine::_GetProposedFont().
bool FontInfoDesired::IsDefaultRasterFont() const
bool FontInfoDesired::IsDefaultRasterFont() const noexcept
{
// Either the raster was set from the engine...
// OR the face name is empty with a size of 0x0 or 8x12.
return WasDefaultRasterSetFromEngine() || (GetFaceName().empty() &&
((_coordSizeDesired.X == 0 && _coordSizeDesired.Y == 0) ||
(_coordSizeDesired.X == 8 && _coordSizeDesired.Y == 12)));
(_coordSizeDesired == COORD{ 0, 0 } ||
_coordSizeDesired == COORD{ 8, 12 }));
}

View file

@ -5,19 +5,12 @@
#include "../inc/FontInfo.hpp"
bool operator==(const FontInfo& a, const FontInfo& b)
{
return (static_cast<FontInfoBase>(a) == static_cast<FontInfoBase>(b) &&
a._coordSize == b._coordSize &&
a._coordSizeUnscaled == b._coordSizeUnscaled);
}
FontInfo::FontInfo(const std::wstring_view faceName,
FontInfo::FontInfo(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const COORD coordSize,
const unsigned int codePage,
const bool fSetDefaultRasterFont /* = false */) :
const bool fSetDefaultRasterFont /* = false */) noexcept :
FontInfoBase(faceName, family, weight, fSetDefaultRasterFont, codePage),
_coordSize(coordSize),
_coordSizeUnscaled(coordSize),
@ -26,38 +19,36 @@ FontInfo::FontInfo(const std::wstring_view faceName,
ValidateFont();
}
FontInfo::FontInfo(const FontInfo& fiFont) :
FontInfoBase(fiFont),
_coordSize(fiFont.GetSize()),
_coordSizeUnscaled(fiFont.GetUnscaledSize())
bool FontInfo::operator==(const FontInfo& other) noexcept
{
return FontInfoBase::operator==(other) &&
_coordSize == other._coordSize &&
_coordSizeUnscaled == other._coordSizeUnscaled;
}
COORD FontInfo::GetUnscaledSize() const
COORD FontInfo::GetUnscaledSize() const noexcept
{
return _coordSizeUnscaled;
}
COORD FontInfo::GetSize() const
COORD FontInfo::GetSize() const noexcept
{
return _coordSize;
}
void FontInfo::SetFromEngine(const std::wstring_view faceName,
void FontInfo::SetFromEngine(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const bool fSetDefaultRasterFont,
const COORD coordSize,
const COORD coordSizeUnscaled)
const COORD coordSizeUnscaled) noexcept
{
FontInfoBase::SetFromEngine(faceName,
family,
weight,
fSetDefaultRasterFont);
_coordSize = coordSize;
_coordSizeUnscaled = coordSizeUnscaled;
_ValidateCoordSize();
}
@ -71,12 +62,12 @@ void FontInfo::SetFallback(const bool didFallback) noexcept
_didFallback = didFallback;
}
void FontInfo::ValidateFont()
void FontInfo::ValidateFont() noexcept
{
_ValidateCoordSize();
}
void FontInfo::_ValidateCoordSize()
void FontInfo::_ValidateCoordSize() noexcept
{
// a (0,0) font is okay for the default raster font, as we will eventually set the dimensions based on the font GDI
// passes back to us.

View file

@ -610,7 +610,7 @@ GdiEngine::~GdiEngine()
// NOTE: not using what GDI gave us because some fonts don't quite roundtrip (e.g. MS Gothic and VL Gothic)
lf.lfPitchAndFamily = (FIXED_PITCH | FF_MODERN);
RETURN_IF_FAILED(FontDesired.FillLegacyNameBuffer(gsl::make_span(lf.lfFaceName)));
FontDesired.FillLegacyNameBuffer(lf.lfFaceName);
// Create font.
hFont.reset(CreateFontIndirectW(&lf));

View file

@ -28,40 +28,31 @@ Author(s):
class FontInfo : public FontInfoBase
{
public:
FontInfo(const std::wstring_view faceName,
FontInfo(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const COORD coordSize,
const unsigned int codePage,
const bool fSetDefaultRasterFont = false);
const bool fSetDefaultRasterFont = false) noexcept;
FontInfo(const FontInfo& fiFont);
bool operator==(const FontInfo& other) noexcept;
COORD GetSize() const;
COORD GetUnscaledSize() const;
void SetFromEngine(const std::wstring_view faceName,
COORD GetSize() const noexcept;
COORD GetUnscaledSize() const noexcept;
void SetFromEngine(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const bool fSetDefaultRasterFont,
const COORD coordSize,
const COORD coordSizeUnscaled);
const COORD coordSizeUnscaled) noexcept;
bool GetFallback() const noexcept;
void SetFallback(const bool didFallback) noexcept;
void ValidateFont();
friend bool operator==(const FontInfo& a, const FontInfo& b);
void ValidateFont() noexcept;
private:
void _ValidateCoordSize();
void _ValidateCoordSize() noexcept;
COORD _coordSize;
COORD _coordSizeUnscaled;
bool _didFallback;
};
bool operator==(const FontInfo& a, const FontInfo& b);
// SET AND UNSET CONSOLE_OEMFONT_DISPLAY unless we can get rid of the stupid recoding in the conhost side.

View file

@ -26,40 +26,32 @@ static constexpr wchar_t DEFAULT_RASTER_FONT_FACENAME[]{ L"Terminal" };
class FontInfoBase
{
public:
FontInfoBase(const std::wstring_view faceName,
FontInfoBase(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const bool fSetDefaultRasterFont,
const unsigned int uiCodePage);
const unsigned int uiCodePage) noexcept;
FontInfoBase(const FontInfoBase& fibFont);
bool operator==(const FontInfoBase& other) noexcept;
~FontInfoBase();
unsigned char GetFamily() const;
unsigned int GetWeight() const;
const std::wstring_view GetFaceName() const noexcept;
unsigned int GetCodePage() const;
HRESULT FillLegacyNameBuffer(gsl::span<wchar_t> buffer) const;
bool IsTrueTypeFont() const;
void SetFromEngine(const std::wstring_view faceName,
unsigned char GetFamily() const noexcept;
unsigned int GetWeight() const noexcept;
const std::wstring& GetFaceName() const noexcept;
unsigned int GetCodePage() const noexcept;
void FillLegacyNameBuffer(wchar_t (&buffer)[LF_FACESIZE]) const noexcept;
bool IsTrueTypeFont() const noexcept;
void SetFromEngine(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const bool fSetDefaultRasterFont);
bool WasDefaultRasterSetFromEngine() const;
void ValidateFont();
const bool fSetDefaultRasterFont) noexcept;
bool WasDefaultRasterSetFromEngine() const noexcept;
void ValidateFont() noexcept;
static Microsoft::Console::Render::IFontDefaultList* s_pFontDefaultList;
static void s_SetFontDefaultList(_In_ Microsoft::Console::Render::IFontDefaultList* const pFontDefaultList);
friend bool operator==(const FontInfoBase& a, const FontInfoBase& b);
static void s_SetFontDefaultList(_In_ Microsoft::Console::Render::IFontDefaultList* const pFontDefaultList) noexcept;
protected:
bool IsDefaultRasterFontNoSize() const;
bool IsDefaultRasterFontNoSize() const noexcept;
private:
std::wstring _faceName;
@ -68,5 +60,3 @@ private:
unsigned int _codePage;
bool _fDefaultRasterSetFromEngine;
};
bool operator==(const FontInfoBase& a, const FontInfoBase& b);

View file

@ -24,21 +24,18 @@ Author(s):
class FontInfoDesired : public FontInfoBase
{
public:
FontInfoDesired(const std::wstring_view faceName,
FontInfoDesired(const std::wstring_view& faceName,
const unsigned char family,
const unsigned int weight,
const COORD coordSizeDesired,
const unsigned int uiCodePage);
const unsigned int uiCodePage) noexcept;
FontInfoDesired(const FontInfo& fiFont) noexcept;
FontInfoDesired(const FontInfo& fiFont);
bool operator==(const FontInfoDesired& other) noexcept;
COORD GetEngineSize() const;
bool IsDefaultRasterFont() const;
friend bool operator==(const FontInfoDesired& a, const FontInfoDesired& b);
COORD GetEngineSize() const noexcept;
bool IsDefaultRasterFont() const noexcept;
private:
COORD _coordSizeDesired;
};
bool operator==(const FontInfoDesired& a, const FontInfoDesired& b);

View file

@ -1167,15 +1167,7 @@ bool AdaptDispatch::ResetMode(const DispatchTypes::ModeParams param)
// - True if handled successfully. False otherwise.
bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode)
{
bool success = true;
success = _pConApi->PrivateSetKeypadMode(fApplicationMode);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::Keypad, fApplicationMode);
}
// Method Description:
@ -1187,15 +1179,7 @@ bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode)
// - True if handled successfully. False otherwise.
bool AdaptDispatch::EnableWin32InputMode(const bool win32InputMode)
{
bool success = true;
success = _pConApi->PrivateEnableWin32InputMode(win32InputMode);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::Win32, win32InputMode);
}
// - DECCKM - Sets the cursor keys input mode to either Application mode or Normal mode (true, false respectively)
@ -1205,15 +1189,7 @@ bool AdaptDispatch::EnableWin32InputMode(const bool win32InputMode)
// - True if handled successfully. False otherwise.
bool AdaptDispatch::SetCursorKeysMode(const bool applicationMode)
{
bool success = true;
success = _pConApi->PrivateSetCursorKeysMode(applicationMode);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::CursorKey, applicationMode);
}
// - att610 - Enables or disables the cursor blinking.
@ -2096,15 +2072,7 @@ bool AdaptDispatch::EnableDECCOLMSupport(const bool enabled) noexcept
// True if handled successfully. False otherwise.
bool AdaptDispatch::EnableVT200MouseMode(const bool enabled)
{
bool success = true;
success = _pConApi->PrivateEnableVT200MouseMode(enabled);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::DefaultMouseTracking, enabled);
}
//Routine Description:
@ -2116,15 +2084,7 @@ bool AdaptDispatch::EnableVT200MouseMode(const bool enabled)
// True if handled successfully. False otherwise.
bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled)
{
bool success = true;
success = _pConApi->PrivateEnableUTF8ExtendedMouseMode(enabled);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, enabled);
}
//Routine Description:
@ -2136,15 +2096,7 @@ bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled)
// True if handled successfully. False otherwise.
bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled)
{
bool success = true;
success = _pConApi->PrivateEnableSGRExtendedMouseMode(enabled);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::SgrMouseEncoding, enabled);
}
//Routine Description:
@ -2155,15 +2107,7 @@ bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled)
// True if handled successfully. False otherwise.
bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled)
{
bool success = true;
success = _pConApi->PrivateEnableButtonEventMouseMode(enabled);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, enabled);
}
//Routine Description:
@ -2175,15 +2119,7 @@ bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled)
// True if handled successfully. False otherwise.
bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled)
{
bool success = true;
success = _pConApi->PrivateEnableAnyEventMouseMode(enabled);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enabled);
}
//Routine Description:
@ -2195,15 +2131,7 @@ bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled)
// True if handled successfully. False otherwise.
bool AdaptDispatch::EnableAlternateScroll(const bool enabled)
{
bool success = true;
success = _pConApi->PrivateEnableAlternateScroll(enabled);
if (_ShouldPassThroughInputModeChange())
{
return false;
}
return success;
return _pConApi->SetInputMode(TerminalInput::Mode::AlternateScroll, enabled);
}
//Routine Description:
@ -2672,22 +2600,3 @@ void AdaptDispatch::_ReportDECSTBMSetting() const
response += L"r\033\\";
_WriteResponse(response);
}
// Routine Description:
// - Determines whether we should pass any sequence that manipulates
// TerminalInput's input generator through the PTY. It encapsulates
// a check for whether the PTY is in use.
// Return value:
// True if the request should be passed.
bool AdaptDispatch::_ShouldPassThroughInputModeChange() const
{
// If we're a conpty, AND WE'RE IN VT INPUT MODE, always pass input mode requests
// The VT Input mode check is to work around ssh.exe v7.7, which uses VT
// output, but not Input.
// The original comment said, "Once the conpty supports these types of input,
// this check can be removed. See GH#4911". Unfortunately, time has shown
// us that SSH 7.7 _also_ requests mouse input and that can have a user interface
// impact on the actual connected terminal. We can't remove this check,
// because SSH <=7.7 is out in the wild on all versions of Windows <=2004.
return _pConApi->IsConsolePty() && _pConApi->PrivateIsVtInputEnabled();
}

View file

@ -194,8 +194,6 @@ namespace Microsoft::Console::VirtualTerminal
void _ReportSGRSetting() const;
void _ReportDECSTBMSetting() const;
bool _ShouldPassThroughInputModeChange() const;
std::vector<bool> _tabStopColumns;
bool _initDefaultTabStops = true;

View file

@ -15,6 +15,7 @@ Author(s):
#pragma once
#include "../input/terminalInput.hpp"
#include "../../types/inc/IInputEvent.hpp"
#include "../../buffer/out/LineRendition.hpp"
#include "../../buffer/out/TextAttribute.hpp"
@ -46,9 +47,8 @@ namespace Microsoft::Console::VirtualTerminal
size_t& eventsWritten) = 0;
virtual bool SetConsoleWindowInfo(const bool absolute,
const SMALL_RECT& window) = 0;
virtual bool PrivateSetCursorKeysMode(const bool applicationMode) = 0;
virtual bool PrivateSetKeypadMode(const bool applicationMode) = 0;
virtual bool PrivateEnableWin32InputMode(const bool win32InputMode) = 0;
virtual bool SetInputMode(const TerminalInput::Mode mode, const bool enabled) = 0;
virtual bool PrivateSetAnsiMode(const bool ansiMode) = 0;
virtual bool PrivateSetScreenMode(const bool reverseMode) = 0;
@ -66,12 +66,6 @@ namespace Microsoft::Console::VirtualTerminal
virtual bool PrivateUseAlternateScreenBuffer() = 0;
virtual bool PrivateUseMainScreenBuffer() = 0;
virtual bool PrivateEnableVT200MouseMode(const bool enabled) = 0;
virtual bool PrivateEnableUTF8ExtendedMouseMode(const bool enabled) = 0;
virtual bool PrivateEnableSGRExtendedMouseMode(const bool enabled) = 0;
virtual bool PrivateEnableButtonEventMouseMode(const bool enabled) = 0;
virtual bool PrivateEnableAnyEventMouseMode(const bool enabled) = 0;
virtual bool PrivateEnableAlternateScroll(const bool enabled) = 0;
virtual bool PrivateEraseAll() = 0;
virtual bool PrivateClearBuffer() = 0;
virtual bool GetUserDefaultCursorStyle(CursorType& style) = 0;

View file

@ -298,7 +298,7 @@ public:
s_pwszInputExpected = L"\x0";
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
mouseInput->EnableDefaultTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::DefaultMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
@ -318,7 +318,7 @@ public:
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
}
mouseInput->EnableButtonEventTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -337,7 +337,7 @@ public:
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
}
mouseInput->EnableAnyEventTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -381,11 +381,11 @@ public:
s_pwszInputExpected = L"\x0";
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
mouseInput->SetUtf8ExtendedMode(true);
mouseInput->SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, true);
short MaxCoord = SHORT_MAX - 33;
mouseInput->EnableDefaultTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::DefaultMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -404,7 +404,7 @@ public:
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
}
mouseInput->EnableButtonEventTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -423,7 +423,7 @@ public:
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
}
mouseInput->EnableAnyEventTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -467,13 +467,13 @@ public:
s_pwszInputExpected = L"\x0";
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
mouseInput->SetSGRExtendedMode(true);
mouseInput->SetInputMode(TerminalInput::Mode::SgrMouseEncoding, true);
// SGR Mode should be able to handle any arbitrary coords.
// However, mouse moves are only handled in Any Event mode
fExpectedKeyHandled = uiButton != WM_MOUSEMOVE;
mouseInput->EnableDefaultTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::DefaultMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -487,7 +487,7 @@ public:
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
}
mouseInput->EnableButtonEventTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -506,7 +506,7 @@ public:
}
fExpectedKeyHandled = true;
mouseInput->EnableAnyEventTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
COORD Coord = s_rgTestCoords[i];
@ -550,7 +550,7 @@ public:
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
// Default Tracking, Default Encoding
mouseInput->EnableDefaultTracking(true);
mouseInput->SetInputMode(TerminalInput::Mode::DefaultMouseTracking, true);
for (int i = 0; i < s_iTestCoordsLength; i++)
{
@ -571,7 +571,7 @@ public:
}
// Default Tracking, UTF8 Encoding
mouseInput->SetUtf8ExtendedMode(true);
mouseInput->SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, true);
short MaxCoord = SHORT_MAX - 33;
for (int i = 0; i < s_iTestCoordsLength; i++)
{
@ -592,7 +592,7 @@ public:
}
// Default Tracking, SGR Encoding
mouseInput->SetSGRExtendedMode(true);
mouseInput->SetInputMode(TerminalInput::Mode::SgrMouseEncoding, true);
fExpectedKeyHandled = true; // SGR Mode should be able to handle any arbitrary coords.
for (int i = 0; i < s_iTestCoordsLength; i++)
{
@ -620,7 +620,7 @@ public:
Log::Comment(L"Enable alternate scroll mode in the alt screen buffer");
mouseInput->UseAlternateScreenBuffer();
mouseInput->EnableAlternateScroll(true);
mouseInput->SetInputMode(TerminalInput::Mode::AlternateScroll, true);
Log::Comment(L"Test mouse wheel scrolling up");
s_pwszInputExpected = L"\x1B[A";
@ -631,7 +631,7 @@ public:
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
Log::Comment(L"Enable cursor keys mode");
mouseInput->ChangeCursorKeysMode(true);
mouseInput->SetInputMode(TerminalInput::Mode::CursorKey, true);
Log::Comment(L"Test mouse wheel scrolling up");
s_pwszInputExpected = L"\x1BOA";
@ -643,12 +643,12 @@ public:
Log::Comment(L"Confirm no effect when scroll mode is disabled");
mouseInput->UseAlternateScreenBuffer();
mouseInput->EnableAlternateScroll(false);
mouseInput->SetInputMode(TerminalInput::Mode::AlternateScroll, false);
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
Log::Comment(L"Confirm no effect when using the main buffer");
mouseInput->UseMainScreenBuffer();
mouseInput->EnableAlternateScroll(true);
mouseInput->SetInputMode(TerminalInput::Mode::AlternateScroll, true);
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
}
};

View file

@ -112,35 +112,17 @@ public:
return _setConsoleWindowInfoResult;
}
bool PrivateSetCursorKeysMode(const bool applicationMode) override
bool SetInputMode(const TerminalInput::Mode mode, const bool enabled) override
{
Log::Comment(L"PrivateSetCursorKeysMode MOCK called...");
Log::Comment(L"SetInputMode MOCK called...");
if (_privateSetCursorKeysModeResult)
if (_setInputModeResult)
{
VERIFY_ARE_EQUAL(_cursorKeysApplicationMode, applicationMode);
VERIFY_ARE_EQUAL(_expectedInputMode, mode);
VERIFY_ARE_EQUAL(_expectedInputModeEnabled, enabled);
}
return _privateSetCursorKeysModeResult;
}
bool PrivateSetKeypadMode(const bool applicationMode) override
{
Log::Comment(L"PrivateSetKeypadMode MOCK called...");
if (_privateSetKeypadModeResult)
{
VERIFY_ARE_EQUAL(_keypadApplicationMode, applicationMode);
}
return _privateSetKeypadModeResult;
}
bool PrivateEnableWin32InputMode(const bool /*win32InputMode*/) override
{
Log::Comment(L"PrivateEnableWin32InputMode MOCK called...");
return true;
return _setInputModeResult;
}
bool PrivateSetAnsiMode(const bool ansiMode) override
@ -352,66 +334,6 @@ public:
return true;
}
bool PrivateEnableVT200MouseMode(const bool enabled) override
{
Log::Comment(L"PrivateEnableVT200MouseMode MOCK called...");
if (_privateEnableVT200MouseModeResult)
{
VERIFY_ARE_EQUAL(_expectedMouseEnabled, enabled);
}
return _privateEnableVT200MouseModeResult;
}
bool PrivateEnableUTF8ExtendedMouseMode(const bool enabled) override
{
Log::Comment(L"PrivateEnableUTF8ExtendedMouseMode MOCK called...");
if (_privateEnableUTF8ExtendedMouseModeResult)
{
VERIFY_ARE_EQUAL(_expectedMouseEnabled, enabled);
}
return _privateEnableUTF8ExtendedMouseModeResult;
}
bool PrivateEnableSGRExtendedMouseMode(const bool enabled) override
{
Log::Comment(L"PrivateEnableSGRExtendedMouseMode MOCK called...");
if (_privateEnableSGRExtendedMouseModeResult)
{
VERIFY_ARE_EQUAL(_expectedMouseEnabled, enabled);
}
return _privateEnableSGRExtendedMouseModeResult;
}
bool PrivateEnableButtonEventMouseMode(const bool enabled) override
{
Log::Comment(L"PrivateEnableButtonEventMouseMode MOCK called...");
if (_privateEnableButtonEventMouseModeResult)
{
VERIFY_ARE_EQUAL(_expectedMouseEnabled, enabled);
}
return _privateEnableButtonEventMouseModeResult;
}
bool PrivateEnableAnyEventMouseMode(const bool enabled) override
{
Log::Comment(L"PrivateEnableAnyEventMouseMode MOCK called...");
if (_privateEnableAnyEventMouseModeResult)
{
VERIFY_ARE_EQUAL(_expectedMouseEnabled, enabled);
}
return _privateEnableAnyEventMouseModeResult;
}
bool PrivateEnableAlternateScroll(const bool enabled) override
{
Log::Comment(L"PrivateEnableAlternateScroll MOCK called...");
if (_privateEnableAlternateScrollResult)
{
VERIFY_ARE_EQUAL(_expectedAlternateScrollEnabled, enabled);
}
return _privateEnableAlternateScrollResult;
}
bool PrivateEraseAll() override
{
Log::Comment(L"PrivateEraseAll MOCK called...");
@ -787,10 +709,9 @@ public:
COORD _expectedScreenBufferSize = { 0, 0 };
SMALL_RECT _expectedScreenBufferViewport{ 0, 0, 0, 0 };
bool _privateSetCursorKeysModeResult = false;
bool _privateSetKeypadModeResult = false;
bool _cursorKeysApplicationMode = false;
bool _keypadApplicationMode = false;
bool _setInputModeResult = false;
TerminalInput::Mode _expectedInputMode;
bool _expectedInputModeEnabled = false;
bool _privateSetAnsiModeResult = false;
bool _expectedAnsiMode = false;
bool _privateAllowCursorBlinkingResult = false;
@ -803,14 +724,6 @@ public:
bool _setConsoleTitleWResult = false;
std::wstring_view _expectedWindowTitle{};
bool _expectedMouseEnabled = false;
bool _expectedAlternateScrollEnabled = false;
bool _privateEnableVT200MouseModeResult = false;
bool _privateEnableUTF8ExtendedMouseModeResult = false;
bool _privateEnableSGRExtendedMouseModeResult = false;
bool _privateEnableButtonEventMouseModeResult = false;
bool _privateEnableAnyEventMouseModeResult = false;
bool _privateEnableAlternateScrollResult = false;
bool _setCursorStyleResult = false;
CursorType _expectedCursorStyle;
bool _setCursorColorResult = false;
@ -2100,15 +2013,17 @@ public:
// success cases
// set numeric mode = true
Log::Comment(L"Test 1: application mode = false");
_testGetSet->_privateSetCursorKeysModeResult = TRUE;
_testGetSet->_cursorKeysApplicationMode = false;
_testGetSet->_setInputModeResult = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::CursorKey;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->SetCursorKeysMode(false));
// set numeric mode = false
Log::Comment(L"Test 2: application mode = true");
_testGetSet->_privateSetCursorKeysModeResult = TRUE;
_testGetSet->_cursorKeysApplicationMode = true;
_testGetSet->_setInputModeResult = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::CursorKey;
_testGetSet->_expectedInputModeEnabled = true;
VERIFY_IS_TRUE(_pDispatch.get()->SetCursorKeysMode(true));
}
@ -2120,15 +2035,17 @@ public:
// success cases
// set numeric mode = true
Log::Comment(L"Test 1: application mode = false");
_testGetSet->_privateSetKeypadModeResult = TRUE;
_testGetSet->_keypadApplicationMode = false;
_testGetSet->_setInputModeResult = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::Keypad;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->SetKeypadMode(false));
// set numeric mode = false
Log::Comment(L"Test 2: application mode = true");
_testGetSet->_privateSetKeypadModeResult = TRUE;
_testGetSet->_keypadApplicationMode = true;
_testGetSet->_setInputModeResult = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::Keypad;
_testGetSet->_expectedInputModeEnabled = true;
VERIFY_IS_TRUE(_pDispatch.get()->SetKeypadMode(true));
}
@ -2316,45 +2233,51 @@ public:
Log::Comment(L"Starting test...");
Log::Comment(L"Test 1: Test Default Mouse Mode");
_testGetSet->_expectedMouseEnabled = true;
_testGetSet->_privateEnableVT200MouseModeResult = TRUE;
_testGetSet->_expectedInputModeEnabled = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::DefaultMouseTracking;
_testGetSet->_setInputModeResult = true;
VERIFY_IS_TRUE(_pDispatch.get()->EnableVT200MouseMode(true));
_testGetSet->_expectedMouseEnabled = false;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->EnableVT200MouseMode(false));
Log::Comment(L"Test 2: Test UTF-8 Extended Mouse Mode");
_testGetSet->_expectedMouseEnabled = true;
_testGetSet->_privateEnableUTF8ExtendedMouseModeResult = TRUE;
_testGetSet->_expectedInputModeEnabled = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::Utf8MouseEncoding;
_testGetSet->_setInputModeResult = true;
VERIFY_IS_TRUE(_pDispatch.get()->EnableUTF8ExtendedMouseMode(true));
_testGetSet->_expectedMouseEnabled = false;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->EnableUTF8ExtendedMouseMode(false));
Log::Comment(L"Test 3: Test SGR Extended Mouse Mode");
_testGetSet->_expectedMouseEnabled = true;
_testGetSet->_privateEnableSGRExtendedMouseModeResult = TRUE;
_testGetSet->_expectedInputModeEnabled = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::SgrMouseEncoding;
_testGetSet->_setInputModeResult = true;
VERIFY_IS_TRUE(_pDispatch.get()->EnableSGRExtendedMouseMode(true));
_testGetSet->_expectedMouseEnabled = false;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->EnableSGRExtendedMouseMode(false));
Log::Comment(L"Test 4: Test Button-Event Mouse Mode");
_testGetSet->_expectedMouseEnabled = true;
_testGetSet->_privateEnableButtonEventMouseModeResult = TRUE;
_testGetSet->_expectedInputModeEnabled = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::ButtonEventMouseTracking;
_testGetSet->_setInputModeResult = true;
VERIFY_IS_TRUE(_pDispatch.get()->EnableButtonEventMouseMode(true));
_testGetSet->_expectedMouseEnabled = false;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->EnableButtonEventMouseMode(false));
Log::Comment(L"Test 5: Test Any-Event Mouse Mode");
_testGetSet->_expectedMouseEnabled = true;
_testGetSet->_privateEnableAnyEventMouseModeResult = TRUE;
_testGetSet->_expectedInputModeEnabled = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::AnyEventMouseTracking;
_testGetSet->_setInputModeResult = true;
VERIFY_IS_TRUE(_pDispatch.get()->EnableAnyEventMouseMode(true));
_testGetSet->_expectedMouseEnabled = false;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->EnableAnyEventMouseMode(false));
Log::Comment(L"Test 6: Test Alt Scroll Mouse Mode");
_testGetSet->_expectedAlternateScrollEnabled = true;
_testGetSet->_privateEnableAlternateScrollResult = TRUE;
_testGetSet->_expectedInputModeEnabled = true;
_testGetSet->_expectedInputMode = TerminalInput::Mode::AlternateScroll;
_testGetSet->_setInputModeResult = true;
VERIFY_IS_TRUE(_pDispatch.get()->EnableAlternateScroll(true));
_testGetSet->_expectedAlternateScrollEnabled = false;
_testGetSet->_expectedInputModeEnabled = false;
VERIFY_IS_TRUE(_pDispatch.get()->EnableAlternateScroll(false));
}

View file

@ -285,13 +285,13 @@ static constexpr short _encodeDefaultCoordinate(const short sCoordinateValue) no
// - true, if we are tracking mouse input. False, otherwise
bool TerminalInput::IsTrackingMouseInput() const noexcept
{
return (_mouseInputState.trackingMode != TrackingMode::None);
return _inputMode.any(Mode::DefaultMouseTracking, Mode::ButtonEventMouseTracking, Mode::AnyEventMouseTracking);
}
// Routine Description:
// - Attempt to handle the given mouse coordinates and windows button as a VT-style mouse event.
// If the event should be transmitted in the selected mouse mode, then we'll try and
// encode the event according to the rules of the selected ExtendedMode, and insert those characters into the input buffer.
// encode the event according to the rules of the encoding mode, and insert those characters into the input buffer.
// Parameters:
// - position - The windows coordinates (top,left = 0,0) of the mouse event
// - button - the message to decode.
@ -339,7 +339,7 @@ bool TerminalInput::HandleMouse(const COORD position,
}
else
{
success = (_mouseInputState.trackingMode != TrackingMode::None);
success = IsTrackingMouseInput();
if (success)
{
// isHover is only true for WM_MOUSEMOVE events
@ -363,30 +363,23 @@ bool TerminalInput::HandleMouse(const COORD position,
// In AnyEvent, all coord change hovers are sent
const bool physicalButtonPressed = realButton != WM_LBUTTONUP;
success = (isButton && _mouseInputState.trackingMode != TrackingMode::None) ||
(isHover && _mouseInputState.trackingMode == TrackingMode::ButtonEvent && ((!sameCoord) && (physicalButtonPressed))) ||
(isHover && _mouseInputState.trackingMode == TrackingMode::AnyEvent && !sameCoord);
success = (isButton && IsTrackingMouseInput()) ||
(isHover && _inputMode.test(Mode::ButtonEventMouseTracking) && ((!sameCoord) && (physicalButtonPressed))) ||
(isHover && _inputMode.test(Mode::AnyEventMouseTracking) && !sameCoord);
if (success)
{
std::wstring sequence;
switch (_mouseInputState.extendedMode)
if (_inputMode.test(Mode::Utf8MouseEncoding))
{
case ExtendedMode::None:
sequence = _GenerateDefaultSequence(position,
realButton,
isHover,
modifierKeyState,
delta);
break;
case ExtendedMode::Utf8:
sequence = _GenerateUtf8Sequence(position,
realButton,
isHover,
modifierKeyState,
delta);
break;
case ExtendedMode::Sgr:
}
else if (_inputMode.test(Mode::SgrMouseEncoding))
{
// For SGR encoding, if no physical buttons were pressed,
// then we want to handle hovers with WM_MOUSEMOVE.
// However, if we're dragging (WM_MOUSEMOVE with a button pressed),
@ -397,13 +390,15 @@ bool TerminalInput::HandleMouse(const COORD position,
isHover,
modifierKeyState,
delta);
break;
case ExtendedMode::Urxvt:
default:
success = false;
break;
}
else
{
sequence = _GenerateDefaultSequence(position,
realButton,
isHover,
modifierKeyState,
delta);
}
success = !sequence.empty();
if (success)
@ -411,7 +406,7 @@ bool TerminalInput::HandleMouse(const COORD position,
_SendInputSequence(sequence);
success = true;
}
if (_mouseInputState.trackingMode == TrackingMode::ButtonEvent || _mouseInputState.trackingMode == TrackingMode::AnyEvent)
if (_inputMode.any(Mode::ButtonEventMouseTracking, Mode::AnyEventMouseTracking))
{
_mouseInputState.lastPos.X = position.X;
_mouseInputState.lastPos.Y = position.Y;
@ -547,10 +542,10 @@ std::wstring TerminalInput::_GenerateSGRSequence(const COORD position,
// - delta: The scroll wheel delta of the input event
// Return value:
// True iff the alternate buffer is active and alternate scroll mode is enabled and the event is a mouse wheel event.
bool TerminalInput::_ShouldSendAlternateScroll(const unsigned int button, const short delta) const noexcept
bool TerminalInput::_ShouldSendAlternateScroll(const unsigned int button, const short delta) const
{
return _mouseInputState.inAlternateBuffer &&
_mouseInputState.alternateScroll &&
_inputMode.test(Mode::AlternateScroll) &&
(button == WM_MOUSEWHEEL || button == WM_MOUSEHWHEEL) && delta != 0;
}
@ -560,15 +555,15 @@ bool TerminalInput::_ShouldSendAlternateScroll(const unsigned int button, const
// - delta: The scroll wheel delta of the input event
// Return value:
// True iff the input sequence was sent successfully.
bool TerminalInput::_SendAlternateScroll(const short delta) const noexcept
bool TerminalInput::_SendAlternateScroll(const short delta) const
{
if (delta > 0)
{
_SendInputSequence(_cursorApplicationMode ? ApplicationUpSequence : CursorUpSequence);
_SendInputSequence(_inputMode.test(Mode::CursorKey) ? ApplicationUpSequence : CursorUpSequence);
}
else
{
_SendInputSequence(_cursorApplicationMode ? ApplicationDownSequence : CursorDownSequence);
_SendInputSequence(_inputMode.test(Mode::CursorKey) ? ApplicationDownSequence : CursorDownSequence);
}
return true;
}

View file

@ -7,91 +7,6 @@
using namespace Microsoft::Console::VirtualTerminal;
// Routine Description:
// - Either enables or disables UTF-8 extended mode encoding. This *should* cause
// the coordinates of a mouse event to be encoded as a UTF-8 byte stream, however, because windows' input is
// typically UTF-16 encoded, it emits a UTF-16 stream.
// Does NOT enable or disable mouse mode by itself. This matches the behavior I found in Ubuntu terminals.
// Parameters:
// - enable - either enable or disable.
// Return value:
// <none>
void TerminalInput::SetUtf8ExtendedMode(const bool enable) noexcept
{
_mouseInputState.extendedMode = enable ? ExtendedMode::Utf8 : ExtendedMode::None;
}
// Routine Description:
// - Either enables or disables SGR extended mode encoding. This causes the
// coordinates of a mouse event to be emitted in a human readable format,
// eg, x,y=203,504 -> "^[[<B;203;504M". This way, applications don't need to worry about character encoding.
// Does NOT enable or disable mouse mode by itself. This matches the behavior I found in Ubuntu terminals.
// Parameters:
// - enable - either enable or disable.
// Return value:
// <none>
void TerminalInput::SetSGRExtendedMode(const bool enable) noexcept
{
_mouseInputState.extendedMode = enable ? ExtendedMode::Sgr : ExtendedMode::None;
}
// Routine Description:
// - Either enables or disables mouse mode handling. Leaves the extended mode alone,
// so if we disable then re-enable mouse mode without toggling an extended mode, the mode will persist.
// Parameters:
// - enable - either enable or disable.
// Return value:
// <none>
void TerminalInput::EnableDefaultTracking(const bool enable) noexcept
{
_mouseInputState.trackingMode = enable ? TrackingMode::Default : TrackingMode::None;
_mouseInputState.lastPos = { -1, -1 }; // Clear out the last saved mouse position & button.
_mouseInputState.lastButton = 0;
}
// Routine Description:
// - Either enables or disables ButtonEvent mouse handling. Button Event mode
// sends additional sequences when a button is pressed and the mouse changes character cells.
// Leaves the extended mode alone, so if we disable then re-enable mouse mode
// without toggling an extended mode, the mode will persist.
// Parameters:
// - enable - either enable or disable.
// Return value:
// <none>
void TerminalInput::EnableButtonEventTracking(const bool enable) noexcept
{
_mouseInputState.trackingMode = enable ? TrackingMode::ButtonEvent : TrackingMode::None;
_mouseInputState.lastPos = { -1, -1 }; // Clear out the last saved mouse position & button.
_mouseInputState.lastButton = 0;
}
// Routine Description:
// - Either enables or disables AnyEvent mouse handling. Any Event mode sends sequences
// for any and every mouse event, regardless if a button is pressed or not.
// Leaves the extended mode alone, so if we disable then re-enable mouse mode
// without toggling an extended mode, the mode will persist.
// Parameters:
// - enable - either enable or disable.
// Return value:
// <none>
void TerminalInput::EnableAnyEventTracking(const bool enable) noexcept
{
_mouseInputState.trackingMode = enable ? TrackingMode::AnyEvent : TrackingMode::None;
_mouseInputState.lastPos = { -1, -1 }; // Clear out the last saved mouse position & button.
_mouseInputState.lastButton = 0;
}
// Routine Description:
// - Enables alternate scroll mode. This sends Cursor Up/down sequences when in the alternate buffer
// Parameters:
// - enable - either enable or disable.
// Return value:
// <none>
void TerminalInput::EnableAlternateScroll(const bool enable) noexcept
{
_mouseInputState.alternateScroll = enable;
}
// Routine Description:
// - Notify the MouseInput handler that the screen buffer has been swapped to the alternate buffer
// Parameters:

View file

@ -250,25 +250,32 @@ const wchar_t* const CTRL_QUESTIONMARK_SEQUENCE = L"\x7F";
const wchar_t* const CTRL_ALT_SLASH_SEQUENCE = L"\x1b\x1f";
const wchar_t* const CTRL_ALT_QUESTIONMARK_SEQUENCE = L"\x1b\x7F";
void TerminalInput::ChangeAnsiMode(const bool ansiMode) noexcept
void TerminalInput::SetInputMode(const Mode mode, const bool enabled)
{
_ansiMode = ansiMode;
// If we're changing a tracking mode, we always clear other tracking modes first.
// We also clear out the last saved mouse position & button.
if (mode == Mode::DefaultMouseTracking || mode == Mode::ButtonEventMouseTracking || mode == Mode::AnyEventMouseTracking)
{
_inputMode.reset_all(Mode::DefaultMouseTracking, Mode::ButtonEventMouseTracking, Mode::AnyEventMouseTracking);
_mouseInputState.lastPos = { -1, -1 };
_mouseInputState.lastButton = 0;
}
// But if we're changing the encoding, we only clear out the other encoding modes
// when enabling a new encoding - not when disabling.
if ((mode == Mode::Utf8MouseEncoding || mode == Mode::SgrMouseEncoding) && enabled)
{
_inputMode.reset_all(Mode::Utf8MouseEncoding, Mode::SgrMouseEncoding);
}
_inputMode.set(mode, enabled);
}
void TerminalInput::ChangeKeypadMode(const bool applicationMode) noexcept
bool TerminalInput::GetInputMode(const Mode mode) const
{
_keypadApplicationMode = applicationMode;
return _inputMode.test(mode);
}
void TerminalInput::ChangeCursorKeysMode(const bool applicationMode) noexcept
{
_cursorApplicationMode = applicationMode;
}
void TerminalInput::ChangeWin32InputMode(const bool win32InputMode) noexcept
{
_win32InputMode = win32InputMode;
}
void TerminalInput::ForceDisableWin32InputMode(const bool win32InputMode) noexcept
{
_forceDisableWin32InputMode = win32InputMode;
@ -530,7 +537,7 @@ bool TerminalInput::HandleKey(const IInputEvent* const pInEvent)
// GH#4999 - If we're in win32-input mode, skip straight to doing that.
// Since this mode handles all types of key events, do nothing else.
// Only do this if win32-input-mode support isn't manually disabled.
if (_win32InputMode && !_forceDisableWin32InputMode)
if (_inputMode.test(Mode::Win32) && !_forceDisableWin32InputMode)
{
const auto seq = _GenerateWin32KeySequence(keyEvent);
_SendInputSequence(seq);
@ -655,7 +662,7 @@ bool TerminalInput::HandleKey(const IInputEvent* const pInEvent)
// Check any other key mappings (like those for the F1-F12 keys).
// These mappings will kick in no matter which modifiers are pressed and as such
// must be checked last, or otherwise we'd override more complex key combinations.
const auto mapping = _getKeyMapping(keyEvent, _ansiMode, _cursorApplicationMode, _keypadApplicationMode);
const auto mapping = _getKeyMapping(keyEvent, _inputMode.test(Mode::Ansi), _inputMode.test(Mode::CursorKey), _inputMode.test(Mode::Keypad));
if (_translateDefaultMapping(keyEvent, mapping, senderFunc))
{
return true;

View file

@ -34,11 +34,26 @@ namespace Microsoft::Console::VirtualTerminal
~TerminalInput() = default;
bool HandleKey(const IInputEvent* const pInEvent);
void ChangeAnsiMode(const bool ansiMode) noexcept;
void ChangeKeypadMode(const bool applicationMode) noexcept;
void ChangeCursorKeysMode(const bool applicationMode) noexcept;
void ChangeWin32InputMode(const bool win32InputMode) noexcept;
enum class Mode : size_t
{
Ansi,
Keypad,
CursorKey,
Win32,
Utf8MouseEncoding,
SgrMouseEncoding,
DefaultMouseTracking,
ButtonEventMouseTracking,
AnyEventMouseTracking,
AlternateScroll
};
void SetInputMode(const Mode mode, const bool enabled);
bool GetInputMode(const Mode mode) const;
void ForceDisableWin32InputMode(const bool win32InputMode) noexcept;
#pragma region MouseInput
@ -62,14 +77,6 @@ namespace Microsoft::Console::VirtualTerminal
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
void SetUtf8ExtendedMode(const bool enable) noexcept;
void SetSGRExtendedMode(const bool enable) noexcept;
void EnableDefaultTracking(const bool enable) noexcept;
void EnableButtonEventTracking(const bool enable) noexcept;
void EnableAnyEventTracking(const bool enable) noexcept;
void EnableAlternateScroll(const bool enable) noexcept;
void UseAlternateScreenBuffer() noexcept;
void UseMainScreenBuffer() noexcept;
#pragma endregion
@ -80,10 +87,7 @@ namespace Microsoft::Console::VirtualTerminal
// storage location for the leading surrogate of a utf-16 surrogate pair
std::optional<wchar_t> _leadingSurrogate;
bool _ansiMode{ true };
bool _keypadApplicationMode{ false };
bool _cursorApplicationMode{ false };
bool _win32InputMode{ false };
til::enumset<Mode> _inputMode{ Mode::Ansi };
bool _forceDisableWin32InputMode{ false };
void _SendChar(const wchar_t ch);
@ -94,27 +98,8 @@ namespace Microsoft::Console::VirtualTerminal
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
enum class ExtendedMode : unsigned int
{
None,
Utf8,
Sgr,
Urxvt
};
enum class TrackingMode : unsigned int
{
None,
Default,
ButtonEvent,
AnyEvent
};
struct MouseInputState
{
ExtendedMode extendedMode{ ExtendedMode::None };
TrackingMode trackingMode{ TrackingMode::None };
bool alternateScroll{ false };
bool inAlternateBuffer{ false };
COORD lastPos{ -1, -1 };
unsigned int lastButton{ 0 };
@ -142,8 +127,8 @@ namespace Microsoft::Console::VirtualTerminal
const short modifierKeyState,
const short delta);
bool _ShouldSendAlternateScroll(const unsigned int button, const short delta) const noexcept;
bool _SendAlternateScroll(const short delta) const noexcept;
bool _ShouldSendAlternateScroll(const unsigned int button, const short delta) const;
bool _SendAlternateScroll(const short delta) const;
static constexpr unsigned int s_GetPressedButton(const MouseButtonState state) noexcept;
#pragma endregion

View file

@ -1101,22 +1101,22 @@ bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view strin
std::wstring& content,
bool& queryClipboard) const noexcept
{
const size_t pos = string.find(';');
if (pos != std::wstring_view::npos)
const auto pos = string.find(L';');
if (pos == std::wstring_view::npos)
{
const std::wstring_view substr = string.substr(pos + 1);
if (substr == L"?")
{
queryClipboard = true;
return true;
}
else
{
return Base64::s_Decode(substr, content);
}
return false;
}
return false;
const auto substr = string.substr(pos + 1);
if (substr == L"?")
{
queryClipboard = true;
return true;
}
// Log_IfFailed has the following description: "Should be decorated WI_NOEXCEPT, but conflicts with forceinline."
#pragma warning(suppress : 26447) // The function is declared 'noexcept' but calls function 'Log_IfFailed()' which may throw exceptions (f.6).
return SUCCEEDED_LOG(Base64::Decode(substr, content));
}
// Method Description:

View file

@ -4,190 +4,155 @@
#include "precomp.h"
#include "base64.hpp"
#pragma warning(disable : 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
// I didn't want to handle out of memory errors. There's no reasonable mode of
// operation for this application without the ability to allocate memory anyways.
#pragma warning(disable : 26447) // The function is declared 'noexcept' but calls function '...' which may throw exceptions (f.6).
#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
#pragma warning(disable : 26482) // Only index into arrays using constant expressions (bounds.2).
using namespace Microsoft::Console::VirtualTerminal;
static const char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char padChar = '=';
// clang-format off
static constexpr uint8_t decodeTable[128] = {
255 /* NUL */, 255 /* SOH */, 255 /* STX */, 255 /* ETX */, 255 /* EOT */, 255 /* ENQ */, 255 /* ACK */, 255 /* BEL */, 255 /* BS */, 255 /* HT */, 255 /* LF */, 255 /* VT */, 255 /* FF */, 255 /* CR */, 255 /* SO */, 255 /* SI */,
255 /* DLE */, 255 /* DC1 */, 255 /* DC2 */, 255 /* DC3 */, 255 /* DC4 */, 255 /* NAK */, 255 /* SYN */, 255 /* ETB */, 255 /* CAN */, 255 /* EM */, 255 /* SUB */, 255 /* ESC */, 255 /* FS */, 255 /* GS */, 255 /* RS */, 255 /* US */,
255 /* SP */, 255 /* ! */, 255 /* " */, 255 /* # */, 255 /* $ */, 255 /* % */, 255 /* & */, 255 /* ' */, 255 /* ( */, 255 /* ) */, 255 /* * */, 62 /* + */, 255 /* , */, 62 /* - */, 255 /* . */, 63 /* / */,
52 /* 0 */, 53 /* 1 */, 54 /* 2 */, 55 /* 3 */, 56 /* 4 */, 57 /* 5 */, 58 /* 6 */, 59 /* 7 */, 60 /* 8 */, 61 /* 9 */, 255 /* : */, 255 /* ; */, 255 /* < */, 255 /* = */, 255 /* > */, 255 /* ? */,
255 /* @ */, 0 /* A */, 1 /* B */, 2 /* C */, 3 /* D */, 4 /* E */, 5 /* F */, 6 /* G */, 7 /* H */, 8 /* I */, 9 /* J */, 10 /* K */, 11 /* L */, 12 /* M */, 13 /* N */, 14 /* O */,
15 /* P */, 16 /* Q */, 17 /* R */, 18 /* S */, 19 /* T */, 20 /* U */, 21 /* V */, 22 /* W */, 23 /* X */, 24 /* Y */, 25 /* Z */, 255 /* [ */, 255 /* \ */, 255 /* ] */, 255 /* ^ */, 63 /* _ */,
255 /* ` */, 26 /* a */, 27 /* b */, 28 /* c */, 29 /* d */, 30 /* e */, 31 /* f */, 32 /* g */, 33 /* h */, 34 /* i */, 35 /* j */, 36 /* k */, 37 /* l */, 38 /* m */, 39 /* n */, 40 /* o */,
41 /* p */, 42 /* q */, 43 /* r */, 44 /* s */, 45 /* t */, 46 /* u */, 47 /* v */, 48 /* w */, 49 /* x */, 50 /* y */, 51 /* z */, 255 /* { */, 255 /* | */, 255 /* } */, 255 /* ~ */, 255 /* DEL */,
};
// clang-format on
#pragma warning(disable : 26446 26447 26482 26485 26493 26494)
// Routine Description:
// - Encode a string using base64. When there are not enough characters
// for one quantum, paddings are added.
// Arguments:
// - src - String to base64 encode.
// Return Value:
// - the encoded string.
std::wstring Base64::s_Encode(const std::wstring_view src) noexcept
// Decodes an UTF8 string encoded with RFC 4648 (Base64) and returns it as UTF16 in dst.
// It supports both variants of the RFC (base64 and base64url), but
// throws an error for non-alphabet characters, including newlines.
// * Throws an exception for all invalid base64 inputs.
// * Doesn't support whitespace and will throw an exception for such strings.
// * Doesn't validate the number of trailing "=". Those are basically ignored.
// Strings like "YQ===" will be accepted as valid input and simply result in "a".
HRESULT Base64::Decode(const std::wstring_view& src, std::wstring& dst) noexcept
{
std::wstring dst;
wchar_t input[3];
std::string result;
result.resize(((src.size() + 3) / 4) * 3);
const auto len = (src.size() + 2) / 3 * 4;
if (len == 0)
{
return dst;
}
dst.reserve(len);
// in and inEnd may be nullptr if src.empty().
// The remaining code in this function ensures not to read from in if src.empty().
#pragma warning(suppress : 26429) // Symbol 'in' is never tested for nullness, it can be marked as not_null (f.23).
auto in = src.data();
const auto inEnd = in + src.size();
// Sometimes in programming you have to ask yourself what the right offset for a pointer is.
// Is 4 enough? Certainly not. 6 on the other hand is just way too much. Clearly 5 is just right.
//
// In all seriousness however the offset is 5, because the batched loop reads 4 characters at a time,
// a base64 string can end with two "=" and the batched loop doesn't handle any such "=".
// Additionally the while() condition of the batched loop would make a lot more sense if it were using <=,
// but for reasons outlined below it needs to use < so we need to add 1 back again.
// We thus get -4-2+1 which is -5.
//
// There's a special reason we need to use < and not <= for the loop:
// In C++ it's undefined behavior to perform any pointer arithmetic that leads to unallocated memory,
// which is why we can't just write `inEnd - 6` as that might be UB if `src.size()` is less than 6.
// We thus would need write `inEnd - min(6, src.size())` in combination with `<=` for the batched loop.
// But if `src.size()` is actually less than 6 then `inEnd` is equal to the initial `in`, aka: an empty range.
// In such cases we'd enter the batched loop and read from `in` despite us not wanting to enter the loop.
// We can fix the issue by using < instead and adding +1 to the offset.
//
// Yes this works.
const auto inEndBatched = inEnd - std::min<size_t>(5, src.size());
auto iter = src.cbegin();
// Encode each three chars into one quantum (four chars).
while (iter < src.cend() - 2)
// outBeg and out may be nullptr if src.empty().
// The remaining code in this function ensures not to write to out if src.empty().
const auto outBeg = result.data();
#pragma warning(suppress : 26429) // Symbol 'out' is never tested for nullness, it can be marked as not_null (f.23).
auto out = outBeg;
// r is just a generic "remainder" we use to accumulate 4 base64 chars into 3 output bytes.
uint_fast32_t r = 0;
// error is treated as a boolean. If it's not 0 we had an invalid input character.
uint_fast16_t error = 0;
// Capturing r/error by reference produces less optimal assembly.
static constexpr auto accumulate = [](auto& r, auto& error, auto ch) {
// n will be in the range [0, 0x3f] for valid ch
// and exactly 0xff for invalid ch.
const auto n = decodeTable[ch & 0x7f];
// Both ch > 0x7f, as well as n > 0x7f are invalid values and count as an error.
// We can add the error state by checking if any bits ~0x7f are set (which is 0xff80).
error |= (ch | n) & 0xff80;
r = r << 6 | n;
};
// If src.empty() then `in == inEndBatched == nullptr` and this is skipped.
while (in < inEndBatched)
{
input[0] = *iter++;
input[1] = *iter++;
input[2] = *iter++;
dst.push_back(base64Chars[input[0] >> 2]);
dst.push_back(base64Chars[(input[0] & 0x03) << 4 | input[1] >> 4]);
dst.push_back(base64Chars[(input[1] & 0x0f) << 2 | input[2] >> 6]);
dst.push_back(base64Chars[(input[2] & 0x3f)]);
const auto ch0 = *in++;
const auto ch1 = *in++;
const auto ch2 = *in++;
const auto ch3 = *in++;
// Most other base64 libraries do something like this:
// const auto n0 = decodeTable[a];
// const auto n1 = decodeTable[b];
// const auto n2 = decodeTable[c];
// const auto n3 = decodeTable[d];
// *out++ = n0 << 2 | n1 >> 4;
// *out++ = (n1 & 0xf) << 4 | n2 >> 2;
// *out++ = (n2 & 0x3) << 6 | n3;
//
// But on all modern CPUs I tested (well even those 10 years old at this point) shifting base64
// characters into a single register (here: r) is faster than the traditional approach.
// I believe this is due to reducing the dependency of instructions on prior calculations.
accumulate(r, error, ch0);
accumulate(r, error, ch1);
accumulate(r, error, ch2);
accumulate(r, error, ch3);
*out++ = gsl::narrow_cast<char>(r >> 16);
*out++ = gsl::narrow_cast<char>(r >> 8);
*out++ = gsl::narrow_cast<char>(r >> 0);
}
// Here only zero, or one, or two chars are left. We may need to add paddings.
if (iter < src.cend())
{
input[0] = *iter++;
dst.push_back(base64Chars[input[0] >> 2]);
if (iter < src.cend()) // Two chars left.
uint_fast8_t ri = 0;
// If src.empty() then `in == inEnd == nullptr` and this is skipped.
for (; in < inEnd; ++in)
{
input[1] = *iter++;
dst.push_back(base64Chars[(input[0] & 0x03) << 4 | input[1] >> 4]);
dst.push_back(base64Chars[(input[1] & 0x0f) << 2]);
}
else // Only one char left.
{
dst.push_back(base64Chars[(input[0] & 0x03) << 4]);
dst.push_back(padChar);
}
dst.push_back(padChar);
}
return dst;
}
// Routine Description:
// - Decode a base64 string. This requires the base64 string is properly padded.
// Otherwise, false will be returned.
// Arguments:
// - src - String to decode.
// - dst - Destination to decode into.
// Return Value:
// - true if decoding successfully, otherwise false.
bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) noexcept
{
std::string mbStr;
int state = 0;
char tmp;
const auto len = src.size() / 4 * 3;
if (len == 0)
{
return false;
}
mbStr.reserve(len);
auto iter = src.cbegin();
while (iter < src.cend())
{
if (s_IsSpace(*iter)) // Skip whitespace anywhere.
{
iter++;
continue;
if (const auto ch = *in; ch != '=')
{
accumulate(r, error, ch);
ri++;
}
}
if (*iter == padChar)
switch (ri)
{
break;
}
auto pos = strchr(base64Chars, *iter);
if (!pos) // A non-base64 character found.
{
return false;
}
switch (state)
{
case 0:
tmp = (char)(pos - base64Chars) << 2;
state = 1;
break;
case 1:
tmp |= (char)(pos - base64Chars) >> 4;
mbStr += tmp;
tmp = (char)((pos - base64Chars) & 0x0f) << 4;
state = 2;
break;
case 2:
tmp |= (char)(pos - base64Chars) >> 2;
mbStr += tmp;
tmp = (char)((pos - base64Chars) & 0x03) << 6;
state = 3;
*out++ = gsl::narrow_cast<char>(r >> 4);
break;
case 3:
tmp |= pos - base64Chars;
mbStr += tmp;
state = 0;
break;
default:
break;
}
iter++;
}
if (iter < src.cend()) // Padding char is met.
{
iter++;
switch (state)
{
// Invalid when state is 0 or 1.
case 0:
case 1:
return false;
case 2:
// Skip any number of spaces.
while (iter < src.cend() && s_IsSpace(*iter))
{
iter++;
}
// Make sure there is another trailing padding character.
if (iter == src.cend() || *iter != padChar)
{
return false;
}
iter++; // Skip the padding character and fallthrough to "single trailing padding character" case.
[[fallthrough]];
case 3:
while (iter < src.cend())
{
if (!s_IsSpace(*iter))
{
return false;
}
iter++;
}
*out++ = gsl::narrow_cast<char>(r >> 10);
*out++ = gsl::narrow_cast<char>(r >> 2);
break;
case 4:
*out++ = gsl::narrow_cast<char>(r >> 16);
*out++ = gsl::narrow_cast<char>(r >> 8);
*out++ = gsl::narrow_cast<char>(r >> 0);
break;
default:
error |= ri;
break;
}
}
else if (state != 0) // When no padding, we must be in state 0.
if (error)
{
return false;
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
return SUCCEEDED(til::u8u16(mbStr, dst));
}
// Routine Description:
// - Check if parameter is a base64 whitespace. Only carriage return or line feed
// is valid whitespace.
// Arguments:
// - ch - Character to check.
// Return Value:
// - true iff ch is a carriage return or line feed.
constexpr bool Base64::s_IsSpace(const wchar_t ch) noexcept
{
return ch == L'\r' || ch == L'\n';
result.resize(out - outBeg);
return til::u8u16(result, dst);
}

View file

@ -16,10 +16,6 @@ namespace Microsoft::Console::VirtualTerminal
class Base64
{
public:
static std::wstring s_Encode(const std::wstring_view src) noexcept;
static bool s_Decode(const std::wstring_view src, std::wstring& dst) noexcept;
private:
static constexpr bool s_IsSpace(const wchar_t ch) noexcept;
static HRESULT Decode(const std::wstring_view& src, std::wstring& dst) noexcept;
};
}

View file

@ -3,7 +3,8 @@
#include "precomp.h"
#include "WexTestClass.h"
#include "../../inc/consoletaeftemplates.hpp"
#include <til/rand.h>
#include "base64.hpp"
@ -28,74 +29,90 @@ class Microsoft::Console::VirtualTerminal::Base64Test
{
TEST_CLASS(Base64Test);
TEST_METHOD(TestBase64Encode)
TEST_METHOD(DecodeFuzz)
{
VERIFY_ARE_EQUAL(L"Zm9v", Base64::s_Encode(L"foo"));
VERIFY_ARE_EQUAL(L"Zm9vYg==", Base64::s_Encode(L"foob"));
VERIFY_ARE_EQUAL(L"Zm9vYmE=", Base64::s_Encode(L"fooba"));
VERIFY_ARE_EQUAL(L"Zm9vYmFy", Base64::s_Encode(L"foobar"));
VERIFY_ARE_EQUAL(L"Zm9vYmFyDQo=", Base64::s_Encode(L"foobar\r\n"));
// NOTE: Modify testRounds to get the feeling of running a fuzz test on Base64::Decode.
static constexpr auto testRounds = 8;
pcg_engines::oneseq_dxsm_64_32 rng{ til::gen_random<uint64_t>() };
// Fills referenceData with random ASCII characters.
// We use ASCII as Base64::Decode uses til:u8u16 internally and I don't want to test that.
char referenceData[128];
{
uint32_t randomData[sizeof(referenceData) / sizeof(uint32_t)];
for (auto& i : randomData)
{
i = rng();
}
const std::string_view randomDataView{ reinterpret_cast<const char*>(randomData), sizeof(randomData) };
auto out = std::begin(referenceData);
for (const auto& ch : randomDataView)
{
*out++ = static_cast<char>(ch & 0x7f);
}
}
wchar_t wideReferenceData[std::size(referenceData)];
std::copy_n(std::begin(referenceData), std::size(referenceData), std::begin(wideReferenceData));
std::wstring encoded;
std::wstring decoded;
for (auto i = 0; i < testRounds; ++i)
{
const auto referenceLength = rng(static_cast<uint32_t>(std::size(referenceData)));
const std::wstring_view wideReference{ std::begin(wideReferenceData), referenceLength };
if (!referenceLength)
{
encoded.clear();
}
else
{
const auto reference = reinterpret_cast<const BYTE*>(std::begin(referenceData));
DWORD encodedLen;
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringW(reference, referenceLength, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, nullptr, &encodedLen));
// encodedLen is returned by CryptBinaryToStringW including the trailing null byte.
encoded.resize(encodedLen - 1);
THROW_IF_WIN32_BOOL_FALSE(CryptBinaryToStringW(reference, referenceLength, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, encoded.data(), &encodedLen));
}
// Test whether Decode() handles strings with and without trailing "=".
if (rng(2))
{
while (!encoded.empty() && encoded.back() == '=')
{
encoded.pop_back();
}
}
// Test whether Decode() handles null-pointer arguments correctly.
std::wstring_view encodedView{ encoded };
if (encodedView.empty() && rng(2))
{
encodedView = {};
}
Base64::Decode(encodedView, decoded);
VERIFY_ARE_EQUAL(wideReference, decoded);
}
}
TEST_METHOD(TestBase64Decode)
TEST_METHOD(DecodeUTF8)
{
std::wstring result;
bool success;
success = Base64::s_Decode(L"Zm9v", result);
VERIFY_ARE_EQUAL(true, success);
VERIFY_ARE_EQUAL(L"foo", result);
result = L"";
success = Base64::s_Decode(L"Zm9vYg==", result);
VERIFY_ARE_EQUAL(true, success);
VERIFY_ARE_EQUAL(L"foob", result);
result = L"";
success = Base64::s_Decode(L"Zm9vYmE=", result);
VERIFY_ARE_EQUAL(true, success);
VERIFY_ARE_EQUAL(L"fooba", result);
result = L"";
success = Base64::s_Decode(L"Zm9vYmFy", result);
VERIFY_ARE_EQUAL(true, success);
VERIFY_ARE_EQUAL(L"foobar", result);
result = L"";
success = Base64::s_Decode(L"Zm9vYmFyDQo=", result);
VERIFY_ARE_EQUAL(true, success);
VERIFY_ARE_EQUAL(L"foobar\r\n", result);
result = L"";
success = Base64::s_Decode(L"Zm9v\rYmFy", result);
VERIFY_ARE_EQUAL(true, success);
VERIFY_ARE_EQUAL(L"foobar", result);
result = L"";
success = Base64::s_Decode(L"Zm9v\r\nYmFy\n", result);
VERIFY_ARE_EQUAL(true, success);
VERIFY_ARE_EQUAL(L"foobar", result);
success = Base64::s_Decode(L"Z", result);
VERIFY_ARE_EQUAL(false, success);
success = Base64::s_Decode(L"Zm9vYg", result);
VERIFY_ARE_EQUAL(false, success);
success = Base64::s_Decode(L"Zm9vYg=", result);
VERIFY_ARE_EQUAL(false, success);
// U+306b U+307b U+3093 U+3054 U+6c49 U+8bed U+d55c U+ad6d
result = L"";
success = Base64::s_Decode(L"44Gr44G744KT44GU5rGJ6K+t7ZWc6rWt", result);
VERIFY_ARE_EQUAL(true, success);
Base64::Decode(L"44Gr44G744KT44GU5rGJ6K+t7ZWc6rWt", result);
VERIFY_ARE_EQUAL(L"にほんご汉语한국", result);
// U+d83d U+dc4d U+d83d U+dc4d U+d83c U+dffb U+d83d U+dc4d U+d83c U+dffc U+d83d
// U+dc4d U+d83c U+dffd U+d83d U+dc4d U+d83c U+dffe U+d83d U+dc4d U+d83c U+dfff
result = L"";
success = Base64::s_Decode(L"8J+RjfCfkY3wn4+78J+RjfCfj7zwn5GN8J+PvfCfkY3wn4++8J+RjfCfj78=", result);
VERIFY_ARE_EQUAL(true, success);
Base64::Decode(L"8J+RjfCfkY3wn4+78J+RjfCfj7zwn5GN8J+PvfCfkY3wn4++8J+RjfCfj78=", result);
VERIFY_ARE_EQUAL(L"👍👍🏻👍🏼👍🏽👍🏾👍🏿", result);
}
};

View file

@ -3255,7 +3255,7 @@ class StateMachineExternalTest final
pDispatch->_copyContent = L"UNCHANGED";
// Passing a non-base64 `Pd` param is illegal, won't change the content.
mach.ProcessString(L"\x1b]52;;foo\x07");
mach.ProcessString(L"\x1b]52;;???\x07");
VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent);
pDispatch->ClearState();

View file

@ -24,10 +24,10 @@ namespace Microsoft::Console::Types
public:
virtual ~IControlAccessibilityInfo() = 0;
virtual COORD GetFontSize() const = 0;
virtual RECT GetBounds() const = 0;
virtual RECT GetPadding() const = 0;
virtual double GetScaleFactor() const = 0;
virtual COORD GetFontSize() const noexcept = 0;
virtual RECT GetBounds() const noexcept = 0;
virtual RECT GetPadding() const noexcept = 0;
virtual double GetScaleFactor() const noexcept = 0;
virtual void ChangeViewport(const SMALL_RECT NewWindow) = 0;
virtual HRESULT GetHostUiaProvider(IRawElementProviderSimple** provider) = 0;
@ -40,4 +40,4 @@ namespace Microsoft::Console::Types
};
inline IControlAccessibilityInfo::~IControlAccessibilityInfo() {}
}
}

View file

@ -44,7 +44,7 @@ IFACEMETHODIMP TermControlUiaProvider::Navigate(_In_ NavigateDirection direction
return S_OK;
}
IFACEMETHODIMP TermControlUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect)
IFACEMETHODIMP TermControlUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect) noexcept
{
// TODO GitHub #1914: Re-attach Tracing to UIA Tree
//Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr);
@ -89,17 +89,17 @@ IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybe
return S_OK;
}
const COORD TermControlUiaProvider::GetFontSize() const
const COORD TermControlUiaProvider::GetFontSize() const noexcept
{
return _controlInfo->GetFontSize();
}
const RECT TermControlUiaProvider::GetPadding() const
const RECT TermControlUiaProvider::GetPadding() const noexcept
{
return _controlInfo->GetPadding();
}
const double TermControlUiaProvider::GetScaleFactor() const
const double TermControlUiaProvider::GetScaleFactor() const noexcept
{
return _controlInfo->GetScaleFactor();
}

View file

@ -36,12 +36,12 @@ namespace Microsoft::Terminal
IFACEMETHODIMP Navigate(_In_ NavigateDirection direction,
_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) noexcept override;
IFACEMETHODIMP get_HostRawElementProvider(IRawElementProviderSimple** ppProvider) noexcept override;
IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override;
IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) noexcept override;
IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) noexcept override;
const COORD GetFontSize() const;
const RECT GetPadding() const;
const double GetScaleFactor() const;
const COORD GetFontSize() const noexcept;
const RECT GetPadding() const noexcept;
const double GetScaleFactor() const noexcept;
void ChangeViewport(const SMALL_RECT NewWindow) override;
protected:

View file

@ -134,7 +134,7 @@ void TermControlUiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) con
screenPoint->y = includeOffsets(screenPoint->y, boundingRect.top, padding.top, scaleFactor);
}
const COORD TermControlUiaTextRange::_getScreenFontSize() const
const COORD TermControlUiaTextRange::_getScreenFontSize() const noexcept
{
// 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

View file

@ -57,6 +57,6 @@ namespace Microsoft::Terminal
protected:
void _TranslatePointToScreen(LPPOINT clientPoint) const override;
void _TranslatePointFromScreen(LPPOINT screenPoint) const override;
const COORD _getScreenFontSize() const override;
const COORD _getScreenFontSize() const noexcept override;
};
}

View file

@ -1313,7 +1313,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetChildren(_Outptr_result_maybenull_ SAFEARRAY
#pragma endregion
const COORD UiaTextRangeBase::_getScreenFontSize() const
const COORD UiaTextRangeBase::_getScreenFontSize() const noexcept
{
COORD coordRet = _pData->GetFontInfo().GetSize();

View file

@ -146,7 +146,7 @@ namespace Microsoft::Console::Types
RECT _getTerminalRect() const;
virtual const COORD _getScreenFontSize() const;
virtual const COORD _getScreenFontSize() const noexcept;
const unsigned int _getViewportHeight(const SMALL_RECT viewport) const noexcept;
const Viewport _getOptimizedBufferSize() const noexcept;