diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt
index 81bfde277..8d751a187 100644
--- a/.github/actions/spelling/excludes.txt
+++ b/.github/actions/spelling/excludes.txt
@@ -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$
diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt
index dccd6d740..c18d880e4 100644
--- a/.github/actions/spelling/expect/expect.txt
+++ b/.github/actions/spelling/expect/expect.txt
@@ -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
diff --git a/doc/terminal-v2-roadmap.md b/doc/terminal-v2-roadmap.md
index 34fe686ee..9db0aa652 100644
--- a/doc/terminal-v2-roadmap.md
+++ b/doc/terminal-v2-roadmap.md
@@ -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
@@ -32,8 +32,8 @@ Below is the schedule for when milestones will be included in release builds of
| 2021-07-14 | [1.10] in Windows Terminal Preview [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 [1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
| 2021-10-20 | [1.12] in Windows Terminal Preview [1.11] in Windows Terminal | [Windows Terminal Preview 1.12 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-12-release/) |
-| 2021-11-30 | 2.0 RC in Windows Terminal Preview 2.0 RC in Windows Terminal | |
-| 2021-12-31 | [2.0] in Windows Terminal Preview [2.0] in Windows Terminal | |
+| | 2.0 RC in Windows Terminal Preview 2.0 RC in Windows Terminal | |
+| | [2.0] in Windows Terminal Preview [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.
Issue: [#1564] Specs: [#6720], [#6904] Implementation: [#7283], [#7370], [#8048] |
-| 0 | Command palette | A popup menu to list possible actions and commands.
Issues: [#5400], [#2046] Spec: [#2193] 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.
Issue: [#1256], [#5000] 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.
Issue: [#574] Implementation: [#7251] |
-| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal
Issue: [#492] Spec: [#2080], [#7414] |
-| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme
Issue: [#3327] Spec: [#5772] |
-| 1 | Open profile elevated | Configure profiles to always open elevated (if Terminal was run unelevated)
Issue: [#5000], [#632] Spec: [#8455] |
-| 1 | Open tab in existing window | Open new tabs in existing Terminal windows
Issue: [#5000], [#4472] Spec: [#8135] |
-| 1 | Traditional opacity | Have a transparent background without the acrylic blur.
Issue: [#603] **Current State**: Blocked on WinUI 3.0 |
-| 2 | SnapOnOutput, scroll lock | Pause output or scrolling on click.
Issue: [#980] Spec: [#2529] Implementation: [#6062] |
-| 2 | Infinite scrollback | Have an infinite history for the text buffer.
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.
Issue: [#1000] |
-| 2 | Theme marketplace | Marketplace for creation and distribution of themes. Dependent on overall theming |
-| 2 | Jump list | Show profiles from task bar (on right click)/start menu.
Issue: [#576] 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).
Issue: [#756] |
-| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.
Issue: [#1060] Implementation: [#6100] |
-| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.
Issues: [#961], [#960], [#766] |
-| 3 | Quake mode | Provide a quick launch terminal that appears and disappears when a hotkey is pressed.
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.
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.
Issue: [#1564] Specs: [#6720], [#6904] Implementation: [#7283], [#7370], [#8048] | βοΈ |
+| 0 | Command palette | A popup menu to list possible actions and commands.
Issues: [#5400], [#2046] Spec: [#2193] 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.
Issue: [#1256], [#5000] 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.
Issue: [#574] Implementation: [#7251] | βοΈ |
+| 1 | Default terminal | If a command-line application is spawned, it should open in Windows Terminal (if installed) or your preferred terminal
Issue: [#492] Spec: [#2080], [#7414] | βοΈ |
+| 1 | Overall theme support | Tab coloring, title bar coloring, pane border coloring, pane border width, definition of what makes a theme
Issue: [#3327] Spec: [#5772] | π¦Ά |
+| 1 | Open profile elevated | Configure profiles to always open elevated (if Terminal was run unelevated)
Issue: [#5000], [#632] Spec: [#8455] | π |
+| 1 | Open tab in existing window | Open new tabs in existing Terminal windows
Issue: [#5000], [#4472] Spec: [#8135] | βοΈ |
+| 1 | Traditional opacity | Have a transparent background without the acrylic blur.
Issue: [#603] | βοΈ |
+| 2 | SnapOnOutput, scroll lock | Pause output or scrolling on click.
Issue: [#980] Spec: [#2529] Implementation: [#6062] | βοΈ |
+| 2 | Infinite scrollback | Have an infinite history for the text buffer.
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.
Issue: [#1000] | π |
+| 2 | Theme marketplace | Marketplace for creation and distribution of themes. Dependent on overall theming | π¦Ά |
+| 2 | Jump list | Show profiles from task bar (on right click)/start menu.
Issue: [#576] 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).
Issue: [#756] | βοΈ |
+| 3 | Open in Windows Terminal | Functionality to right click on a file or folder and select Open in Windows Terminal.
Issue: [#1060] Implementation: [#6100] | βοΈ |
+| 3 | Session restoration | Launch Windows Terminal and the previous session is restored with the proper tab and pane configuration and starting directories.
Issues: [#961], [#960], [#766] | βοΈ |
+| 3 | Quake mode | Provide a quick launch terminal that appears and disappears when a hotkey is pressed.
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.
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:
diff --git a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp
index ea603c942..c20a9a853 100644
--- a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp
+++ b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp
@@ -7,7 +7,9 @@
#include "../TerminalSettingsModel/CascadiaSettings.h"
#include "JsonTestClass.h"
#include "TestUtils.h"
+
#include
+#include
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 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());
+ }
}
diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml
index ef16f6a7a..6c0705437 100644
--- a/src/cascadia/TerminalApp/TerminalPage.xaml
+++ b/src/cascadia/TerminalApp/TerminalPage.xaml
@@ -15,6 +15,7 @@
+
@@ -22,8 +23,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -115,48 +158,6 @@
PreviewKeyDown="_KeyDownHandler"
Visibility="Collapsed" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 4103;4201;4312;4467;%(DisableSpecificWarnings)
+ 4103;4201;4312;4467;5105;%(DisableSpecificWarnings)_WINDOWS;EXTERNAL_BUILD;%(PreprocessorDefinitions)trueprecomp.h
@@ -117,6 +117,7 @@
falsefalsestdcpp17
+ stdc17/utf-8 %(AdditionalOptions)GuardFast
diff --git a/src/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props
index 4587ab3ea..2022dbc7f 100644
--- a/src/cppwinrt.build.pre.props
+++ b/src/cppwinrt.build.pre.props
@@ -75,7 +75,7 @@
truetrue%(AdditionalOptions) /bigobj /Zc:twoPhase-
- 5104;5105;28204;%(DisableSpecificWarnings)
+ 5104;28204;%(DisableSpecificWarnings)$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
diff --git a/src/host/getset.cpp b/src/host/getset.cpp
index c4e32b015..5ee114e6d 100644
--- a/src/host/getset.cpp
+++ b/src/host/getset.cpp
@@ -1248,55 +1248,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:
-// -
-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 +1476,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.
diff --git a/src/host/getset.h b/src/host/getset.h
index e4c539580..5a084d206 100644
--- a/src/host/getset.h
+++ b/src/host/getset.h
@@ -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);
diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp
index bc15c1cdf..c6c0f9868 100644
--- a/src/host/outputStream.cpp
+++ b/src/host/outputStream.cpp
@@ -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,
diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp
index 9df19446f..51300f3a1 100644
--- a/src/host/outputStream.hpp
+++ b/src/host/outputStream.hpp
@@ -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;
diff --git a/src/host/proxy/Host.Proxy.vcxproj b/src/host/proxy/Host.Proxy.vcxproj
index 45fc697f2..ed7283eba 100644
--- a/src/host/proxy/Host.Proxy.vcxproj
+++ b/src/host/proxy/Host.Proxy.vcxproj
@@ -11,7 +11,7 @@
-
REGISTER_PROXY_DLL;WIN32;%(PreprocessorDefinitions)NotUsing
+ Default
+ false
+ false
+ nodefaultlib_shim.h;%(ForcedIncludeFiles)OpenConsoleProxy.def
+
+
+ true
+ DllMain
-
\ No newline at end of file
+
diff --git a/src/host/proxy/Host.Proxy.vcxproj.filters b/src/host/proxy/Host.Proxy.vcxproj.filters
index df6bcbd10..1cdf7ca25 100644
--- a/src/host/proxy/Host.Proxy.vcxproj.filters
+++ b/src/host/proxy/Host.Proxy.vcxproj.filters
@@ -41,6 +41,9 @@
Header Files
+
+ Header Files
+
diff --git a/src/host/proxy/nodefaultlib_shim.h b/src/host/proxy/nodefaultlib_shim.h
new file mode 100644
index 000000000..4face4e49
--- /dev/null
+++ b/src/host/proxy/nodefaultlib_shim.h
@@ -0,0 +1,8 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#pragma once
+
+#include
+
+#define memcmp(a, b, c) (!InlineIsEqualGUID(a, b))
diff --git a/src/host/selection.cpp b/src/host/selection.cpp
index 7814bfae9..90bb5668f 100644
--- a/src/host/selection.cpp
+++ b/src/host/selection.cpp
@@ -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);
diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp
index 9bf49eeee..49a3f71a9 100644
--- a/src/terminal/adapter/adaptDispatch.cpp
+++ b/src/terminal/adapter/adaptDispatch.cpp
@@ -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();
-}
diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp
index b77345e51..da53109df 100644
--- a/src/terminal/adapter/adaptDispatch.hpp
+++ b/src/terminal/adapter/adaptDispatch.hpp
@@ -194,8 +194,6 @@ namespace Microsoft::Console::VirtualTerminal
void _ReportSGRSetting() const;
void _ReportDECSTBMSetting() const;
- bool _ShouldPassThroughInputModeChange() const;
-
std::vector _tabStopColumns;
bool _initDefaultTabStops = true;
diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp
index 7576d3801..db61551e3 100644
--- a/src/terminal/adapter/conGetSet.hpp
+++ b/src/terminal/adapter/conGetSet.hpp
@@ -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;
diff --git a/src/terminal/adapter/ut_adapter/MouseInputTest.cpp b/src/terminal/adapter/ut_adapter/MouseInputTest.cpp
index b16288f2b..6841c2782 100644
--- a/src/terminal/adapter/ut_adapter/MouseInputTest.cpp
+++ b/src/terminal/adapter/ut_adapter/MouseInputTest.cpp
@@ -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, {}));
}
};
diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp
index 876011422..b775f4c7a 100644
--- a/src/terminal/adapter/ut_adapter/adapterTest.cpp
+++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp
@@ -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));
}
diff --git a/src/terminal/input/mouseInput.cpp b/src/terminal/input/mouseInput.cpp
index ec02b2513..59c4ec910 100644
--- a/src/terminal/input/mouseInput.cpp
+++ b/src/terminal/input/mouseInput.cpp
@@ -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;
}
diff --git a/src/terminal/input/mouseInputState.cpp b/src/terminal/input/mouseInputState.cpp
index b1c2509de..bf94794c5 100644
--- a/src/terminal/input/mouseInputState.cpp
+++ b/src/terminal/input/mouseInputState.cpp
@@ -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:
-//
-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 -> "^[[
-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:
-//
-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:
-//
-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:
-//
-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:
-//
-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:
diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp
index 7430345b4..2d66699de 100644
--- a/src/terminal/input/terminalInput.cpp
+++ b/src/terminal/input/terminalInput.cpp
@@ -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;
diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp
index 0405ecca3..cb01937f9 100644
--- a/src/terminal/input/terminalInput.hpp
+++ b/src/terminal/input/terminalInput.hpp
@@ -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 _leadingSurrogate;
- bool _ansiMode{ true };
- bool _keypadApplicationMode{ false };
- bool _cursorApplicationMode{ false };
- bool _win32InputMode{ false };
+ til::enumset _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
diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp
index bc9299d0d..948ffaa4e 100644
--- a/src/terminal/parser/OutputStateMachineEngine.cpp
+++ b/src/terminal/parser/OutputStateMachineEngine.cpp
@@ -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:
diff --git a/src/terminal/parser/base64.cpp b/src/terminal/parser/base64.cpp
index 48e22833e..83a850282 100644
--- a/src/terminal/parser/base64.cpp
+++ b/src/terminal/parser/base64.cpp
@@ -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(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(r >> 16);
+ *out++ = gsl::narrow_cast(r >> 8);
+ *out++ = gsl::narrow_cast(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(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(r >> 10);
+ *out++ = gsl::narrow_cast(r >> 2);
+ break;
+ case 4:
+ *out++ = gsl::narrow_cast(r >> 16);
+ *out++ = gsl::narrow_cast(r >> 8);
+ *out++ = gsl::narrow_cast(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);
}
diff --git a/src/terminal/parser/base64.hpp b/src/terminal/parser/base64.hpp
index 976bcc520..da8f22ec5 100644
--- a/src/terminal/parser/base64.hpp
+++ b/src/terminal/parser/base64.hpp
@@ -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;
};
}
diff --git a/src/terminal/parser/ut_parser/Base64Test.cpp b/src/terminal/parser/ut_parser/Base64Test.cpp
index e3d9e5f29..5374a303a 100644
--- a/src/terminal/parser/ut_parser/Base64Test.cpp
+++ b/src/terminal/parser/ut_parser/Base64Test.cpp
@@ -3,7 +3,8 @@
#include "precomp.h"
#include "WexTestClass.h"
-#include "../../inc/consoletaeftemplates.hpp"
+
+#include
#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() };
+
+ // 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(randomData), sizeof(randomData) };
+ auto out = std::begin(referenceData);
+
+ for (const auto& ch : randomDataView)
+ {
+ *out++ = static_cast(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(std::size(referenceData)));
+ const std::wstring_view wideReference{ std::begin(wideReferenceData), referenceLength };
+
+ if (!referenceLength)
+ {
+ encoded.clear();
+ }
+ else
+ {
+ const auto reference = reinterpret_cast(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);
}
};
diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp
index c5b964731..f9471f47f 100644
--- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp
+++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp
@@ -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();