From 4fffd4f7687e3b1813ebb0d6038595eb25ebbd07 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 27 Apr 2021 21:13:59 -0700 Subject: [PATCH 1/8] mouse mode --- src/host/VtIo.cpp | 1 + src/host/getset.cpp | 10 ++++++++++ src/host/inputBuffer.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/host/inputBuffer.hpp | 6 ++++++ 4 files changed, 54 insertions(+) diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index 17789de025f..f7ac039bc5c 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -213,6 +213,7 @@ bool VtIo::IsUsingVt() const { g.pRender->AddRenderEngine(_pVtRenderEngine.get()); g.getConsoleInformation().GetActiveOutputBuffer().SetTerminalConnection(_pVtRenderEngine.get()); + g.getConsoleInformation().GetActiveInputBuffer()->SetTerminalConnection(_pVtRenderEngine.get()); } CATCH_RETURN(); } diff --git a/src/host/getset.cpp b/src/host/getset.cpp index cbf3868897a..09ed98c66cb 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -374,6 +374,16 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept // ECHO on with LINE off is invalid. RETURN_HR_IF(E_INVALIDARG, WI_IsFlagSet(mode, ENABLE_ECHO_INPUT) && WI_IsFlagClear(mode, ENABLE_LINE_INPUT)); } + if (WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT)) + { + gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(true); + gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(true); + } + else + { + gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(true); + gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(true); + } return S_OK; } diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index 9b8001723a3..b5c0d93a9f6 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -15,6 +15,7 @@ using Microsoft::Console::Interactivity::ServiceLocator; using Microsoft::Console::VirtualTerminal::TerminalInput; +using namespace Microsoft::Console; // Routine Description: // - This method creates an input buffer. @@ -25,6 +26,7 @@ using Microsoft::Console::VirtualTerminal::TerminalInput; InputBuffer::InputBuffer() : InputMode{ INPUT_BUFFER_DEFAULT_INPUT_MODE }, WaitQueue{}, + _pTtyConnection(nullptr), _termInput(std::bind(&InputBuffer::_HandleTerminalInputCallback, this, std::placeholders::_1)) { // The _termInput's constructor takes a reference to this object's _HandleTerminalInputCallback. @@ -218,6 +220,41 @@ void InputBuffer::FlushAllButKeys() _storage.erase(newEnd, _storage.end()); } +void InputBuffer::SetTerminalConnection(_In_ ITerminalOutputConnection* const pTtyConnection) +{ + this->_pTtyConnection = pTtyConnection; +} + +void InputBuffer::PassThroughEnableButtonEventMouseMode(bool enable) +{ + if (_pTtyConnection) + { + if (enable) + { + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1002h")); + } + else + { + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1002l")); + } + } +} + +void InputBuffer::PassThroughEnableSGRExtendedMouseMode(bool enable) +{ + if (_pTtyConnection) + { + if (enable) + { + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1006h")); + } + else + { + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1006l")); + } + } +} + // Routine Description: // - This routine reads from the input buffer. // - It can convert returned data to through the currently set Input CP, it can optionally return a wait condition diff --git a/src/host/inputBuffer.hpp b/src/host/inputBuffer.hpp index 401e956c53e..97fe8c3617d 100644 --- a/src/host/inputBuffer.hpp +++ b/src/host/inputBuffer.hpp @@ -26,6 +26,8 @@ Revision History: #include "../server/ObjectHeader.h" #include "../terminal/input/terminalInput.hpp" +#include "../inc/ITerminalOutputConnection.hpp" + #include class InputBuffer final : public ConsoleObjectHeader @@ -75,12 +77,16 @@ class InputBuffer final : public ConsoleObjectHeader bool IsInVirtualTerminalInputMode() const; Microsoft::Console::VirtualTerminal::TerminalInput& GetTerminalInput(); + void SetTerminalConnection(_In_ Microsoft::Console::ITerminalOutputConnection* const pTtyConnection); + void PassThroughEnableButtonEventMouseMode(bool enable); + void PassThroughEnableSGRExtendedMouseMode(bool enable); private: std::deque> _storage; std::unique_ptr _readPartialByteSequence; std::unique_ptr _writePartialByteSequence; Microsoft::Console::VirtualTerminal::TerminalInput _termInput; + Microsoft::Console::ITerminalOutputConnection* _pTtyConnection; // This flag is used in _HandleTerminalInputCallback // If the InputBuffer leads to a _HandleTerminalInputCallback call, From 565aba21bbf5be68d40beb8a1872faf1b343638e Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 28 Apr 2021 05:21:12 -0700 Subject: [PATCH 2/8] check prev mode --- src/host/getset.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 09ed98c66cb..1701cfc200d 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -357,6 +357,17 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept WI_ClearFlag(gci.Flags, CONSOLE_USE_PRIVATE_FLAGS); } + if (WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) && !WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT)) + { + gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(true); + gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(true); + } + else if (!WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) && WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT)) + { + gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(false); + gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(false); + } + context.InputMode = mode; WI_ClearAllFlags(context.InputMode, PRIVATE_MODES); @@ -374,16 +385,6 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept // ECHO on with LINE off is invalid. RETURN_HR_IF(E_INVALIDARG, WI_IsFlagSet(mode, ENABLE_ECHO_INPUT) && WI_IsFlagClear(mode, ENABLE_LINE_INPUT)); } - if (WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT)) - { - gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(true); - gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(true); - } - else - { - gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(true); - gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(true); - } return S_OK; } From 2f5a7c971e4b73eba8262f84eda5f666b4463949 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 28 Apr 2021 20:37:10 -0700 Subject: [PATCH 3/8] combine functions, correct VT --- src/host/getset.cpp | 12 ++++-------- src/host/inputBuffer.cpp | 19 +++---------------- src/host/inputBuffer.hpp | 3 +-- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 1701cfc200d..5ddcacf1e93 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -357,15 +357,11 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept WI_ClearFlag(gci.Flags, CONSOLE_USE_PRIVATE_FLAGS); } - if (WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) && !WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT)) + const auto oldMouseMode{ WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT) }; + const auto newMouseMode{ WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) }; + if (oldMouseMode != newMouseMode) { - gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(true); - gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(true); - } - else if (!WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) && WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT)) - { - gci.GetActiveInputBuffer()->PassThroughEnableButtonEventMouseMode(false); - gci.GetActiveInputBuffer()->PassThroughEnableSGRExtendedMouseMode(false); + gci.GetActiveInputBuffer()->PassThroughWin32MouseRequest(newMouseMode); } context.InputMode = mode; diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index b5c0d93a9f6..8fcc5150d80 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -225,31 +225,18 @@ void InputBuffer::SetTerminalConnection(_In_ ITerminalOutputConnection* const pT this->_pTtyConnection = pTtyConnection; } -void InputBuffer::PassThroughEnableButtonEventMouseMode(bool enable) -{ - if (_pTtyConnection) - { - if (enable) - { - LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1002h")); - } - else - { - LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1002l")); - } - } -} - -void InputBuffer::PassThroughEnableSGRExtendedMouseMode(bool enable) +void InputBuffer::PassThroughWin32MouseRequest(bool enable) { if (_pTtyConnection) { if (enable) { + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1003h")); LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1006h")); } else { + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1003l")); LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1006l")); } } diff --git a/src/host/inputBuffer.hpp b/src/host/inputBuffer.hpp index 97fe8c3617d..fabdadee173 100644 --- a/src/host/inputBuffer.hpp +++ b/src/host/inputBuffer.hpp @@ -78,8 +78,7 @@ class InputBuffer final : public ConsoleObjectHeader bool IsInVirtualTerminalInputMode() const; Microsoft::Console::VirtualTerminal::TerminalInput& GetTerminalInput(); void SetTerminalConnection(_In_ Microsoft::Console::ITerminalOutputConnection* const pTtyConnection); - void PassThroughEnableButtonEventMouseMode(bool enable); - void PassThroughEnableSGRExtendedMouseMode(bool enable); + void PassThroughWin32MouseRequest(bool enable); private: std::deque> _storage; From bce6310a34625d51758c193d858b39b5e0bdc135 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Sun, 2 May 2021 19:52:33 -0700 Subject: [PATCH 4/8] check quick edit mode --- src/host/getset.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 5ddcacf1e93..5dc4b919018 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -334,6 +334,8 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept LockConsole(); auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); + const auto oldQuickEditMode{ WI_IsFlagSet(gci.Flags, ENABLE_QUICK_EDIT_MODE) }; + if (WI_IsAnyFlagSet(mode, PRIVATE_MODES)) { WI_SetFlag(gci.Flags, CONSOLE_USE_PRIVATE_FLAGS); @@ -359,9 +361,27 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept const auto oldMouseMode{ WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT) }; const auto newMouseMode{ WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) }; - if (oldMouseMode != newMouseMode) + const auto newQuickEditMode{ WI_IsFlagSet(gci.Flags, ENABLE_QUICK_EDIT_MODE) }; + + // Whether we ask the terminal for mouse input is decided by 2 flags, ENABLE_MOUSE_INPUT + // and ENABLE_QUICK_EDIT_MODE + // If ENABLE_MOUSE_INPUT is set and ENABLE_QUICK_EDIT_MODE is not set, then we need + // to ask the terminal to send us mouse events - in all other cases, terminal should + // stop sending us mouse events + if ((oldMouseMode != newMouseMode) || + (oldQuickEditMode != newQuickEditMode)) { - gci.GetActiveInputBuffer()->PassThroughWin32MouseRequest(newMouseMode); + // At least one of ENABLE_MOUSE_INPUT or ENABLE_QUICK_EDIT_MODE changed, but we + // only want to send the VT if the end result (whether we want mouse input or not) + // changed, so check for that + if ((newMouseMode && !newQuickEditMode)) + { + gci.GetActiveInputBuffer()->PassThroughWin32MouseRequest(true); + } + else if (oldMouseMode && !oldQuickEditMode) + { + gci.GetActiveInputBuffer()->PassThroughWin32MouseRequest(false); + } } context.InputMode = mode; From b76cc01b60fd47131c79cbf86ef616f927490d21 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 4 May 2021 05:21:05 -0700 Subject: [PATCH 5/8] less verbose --- src/host/getset.cpp | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 5dc4b919018..80b186bf2b4 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -359,29 +359,15 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept WI_ClearFlag(gci.Flags, CONSOLE_USE_PRIVATE_FLAGS); } - const auto oldMouseMode{ WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT) }; - const auto newMouseMode{ WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) }; const auto newQuickEditMode{ WI_IsFlagSet(gci.Flags, ENABLE_QUICK_EDIT_MODE) }; - // Whether we ask the terminal for mouse input is decided by 2 flags, ENABLE_MOUSE_INPUT - // and ENABLE_QUICK_EDIT_MODE - // If ENABLE_MOUSE_INPUT is set and ENABLE_QUICK_EDIT_MODE is not set, then we need - // to ask the terminal to send us mouse events - in all other cases, terminal should - // stop sending us mouse events - if ((oldMouseMode != newMouseMode) || - (oldQuickEditMode != newQuickEditMode)) + // Mouse input should be received when mouse mode is on and quick edit mode is off + const auto oldMouseMode{ !oldQuickEditMode && WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT) }; + const auto newMouseMode{ !newQuickEditMode && WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) }; + + if (oldMouseMode != newMouseMode) { - // At least one of ENABLE_MOUSE_INPUT or ENABLE_QUICK_EDIT_MODE changed, but we - // only want to send the VT if the end result (whether we want mouse input or not) - // changed, so check for that - if ((newMouseMode && !newQuickEditMode)) - { - gci.GetActiveInputBuffer()->PassThroughWin32MouseRequest(true); - } - else if (oldMouseMode && !oldQuickEditMode) - { - gci.GetActiveInputBuffer()->PassThroughWin32MouseRequest(false); - } + gci.GetActiveInputBuffer()->PassThroughWin32MouseRequest(newMouseMode); } context.InputMode = mode; From 518576961d519b6f793e82badca651b5e990821f Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Tue, 4 May 2021 19:44:46 -0700 Subject: [PATCH 6/8] console quick edit mode --- src/host/getset.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 80b186bf2b4..a85f06ba827 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -334,7 +334,7 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept LockConsole(); auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); - const auto oldQuickEditMode{ WI_IsFlagSet(gci.Flags, ENABLE_QUICK_EDIT_MODE) }; + const auto oldQuickEditMode{ WI_IsFlagSet(gci.Flags, CONSOLE_QUICK_EDIT_MODE) }; if (WI_IsAnyFlagSet(mode, PRIVATE_MODES)) { @@ -359,7 +359,7 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept WI_ClearFlag(gci.Flags, CONSOLE_USE_PRIVATE_FLAGS); } - const auto newQuickEditMode{ WI_IsFlagSet(gci.Flags, ENABLE_QUICK_EDIT_MODE) }; + const auto newQuickEditMode{ WI_IsFlagSet(gci.Flags, CONSOLE_QUICK_EDIT_MODE) }; // Mouse input should be received when mouse mode is on and quick edit mode is off const auto oldMouseMode{ !oldQuickEditMode && WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT) }; From 32e5c6aa25d7b90e5900f62adb70a392b4f89ea7 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Wed, 5 May 2021 20:22:31 -0700 Subject: [PATCH 7/8] merge VT requests --- src/host/inputBuffer.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/host/inputBuffer.cpp b/src/host/inputBuffer.cpp index 8fcc5150d80..0c855a894af 100644 --- a/src/host/inputBuffer.cpp +++ b/src/host/inputBuffer.cpp @@ -231,13 +231,11 @@ void InputBuffer::PassThroughWin32MouseRequest(bool enable) { if (enable) { - LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1003h")); - LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1006h")); + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1003;1006h")); } else { - LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1003l")); - LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1006l")); + LOG_IF_FAILED(_pTtyConnection->WriteTerminalW(L"\x1b[?1003;1006l")); } } } From 22f1714765ccb9c2c680590293ec1e9623373d39 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Thu, 6 May 2021 05:57:02 -0700 Subject: [PATCH 8/8] comment to GH 9970 --- src/host/getset.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index a85f06ba827..0b2ea53528c 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -362,6 +362,8 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept const auto newQuickEditMode{ WI_IsFlagSet(gci.Flags, CONSOLE_QUICK_EDIT_MODE) }; // Mouse input should be received when mouse mode is on and quick edit mode is off + // (for more information regarding the quirks of mouse mode and why/how it relates + // to quick edit mode, see GH#9970) const auto oldMouseMode{ !oldQuickEditMode && WI_IsFlagSet(context.InputMode, ENABLE_MOUSE_INPUT) }; const auto newMouseMode{ !newQuickEditMode && WI_IsFlagSet(mode, ENABLE_MOUSE_INPUT) };