mirror of
https://github.com/microsoft/terminal.git
synced 2026-09-25 00:15:10 +00:00
Plumb Focus events through VT Input (#12900)
Further builds on #12799. #12799 assumes that the connection is prepared to receive FocusIn/FocusOut events as input. For ConPTY we can be relatively sure of that, but that's not _technically_ correct. In the hypothetical world where the connection is not a ConPTY connection, then the other side might not be expecting those sequences. This remedies the issue by * ConPTY will always request focus event mode (from the terminal) when it starts up * when a client tries to disable focus events in conpty, conpty is gonna note that internally, but never transmit that to the hosting terminal, to leave the terminal in focus event mode. * `TerminalDispatch` and `ControlCore` are hooked up now to only send focus events when the Terminal is in focus event mode (which will be always for conpty) * At this point, it was like, 4LOC in `terminalInput.cpp` to add support for focus events to conhost as well. ## checklist * [x] closes #11682 * This combined with #12515 will finally close out #2988 as well, but we can do that manually. * [x] I work here * [ ] There aren't tests for this. There probably should be.
This commit is contained in:
@@ -1708,13 +1708,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - This is related to work done for GH#2988.
|
||||
void ControlCore::GotFocus()
|
||||
{
|
||||
_connection.WriteInput(L"\x1b[I");
|
||||
_terminal->FocusChanged(true);
|
||||
}
|
||||
|
||||
// See GotFocus.
|
||||
void ControlCore::LostFocus()
|
||||
{
|
||||
_connection.WriteInput(L"\x1b[O");
|
||||
_terminal->FocusChanged(false);
|
||||
}
|
||||
|
||||
bool ControlCore::_isBackgroundTransparent()
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace Microsoft::Terminal::Core
|
||||
|
||||
virtual void TrySnapOnInput() = 0;
|
||||
|
||||
virtual void FocusChanged(const bool focused) = 0;
|
||||
|
||||
protected:
|
||||
ITerminalInput() = default;
|
||||
};
|
||||
|
||||
@@ -746,6 +746,19 @@ bool Terminal::SendCharEvent(const wchar_t ch, const WORD scanCode, const Contro
|
||||
return handledDown || handledUp;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Tell the terminal input that we gained or lost focus. If the client
|
||||
// requested focus events, this will send a message to them.
|
||||
// - ConPTY ALWAYS wants focus events.
|
||||
// Arguments:
|
||||
// - focused: true if we're focused, false otherwise.
|
||||
// Return Value:
|
||||
// - none
|
||||
void Terminal::FocusChanged(const bool focused) noexcept
|
||||
{
|
||||
_terminalInput->HandleFocus(focused);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Invalidates the regions described in the given pattern tree for the rendering purposes
|
||||
// Arguments:
|
||||
|
||||
@@ -156,6 +156,8 @@ public:
|
||||
bool IsTrackingMouseInput() const noexcept;
|
||||
bool ShouldSendAlternateScroll(const unsigned int uiButton, const int32_t delta) const noexcept;
|
||||
|
||||
void FocusChanged(const bool focused) noexcept override;
|
||||
|
||||
std::wstring GetHyperlinkAtPosition(const COORD position);
|
||||
uint16_t GetHyperlinkIdAtPosition(const COORD position);
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> GetHyperlinkIntervalFromPosition(const COORD position);
|
||||
|
||||
@@ -383,7 +383,6 @@ bool TerminalDispatch::EnableButtonEventMouseMode(const bool enabled)
|
||||
|
||||
//Routine Description:
|
||||
// Enable Any Event mode - send all mouse events to the input.
|
||||
|
||||
//Arguments:
|
||||
// - enabled - true to enable, false to disable.
|
||||
// Return value:
|
||||
@@ -394,6 +393,20 @@ bool TerminalDispatch::EnableAnyEventMouseMode(const bool enabled)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enables/disables focus event mode. A client may enable this if they want to
|
||||
// receive focus events.
|
||||
// - ConPTY always enables this mode and never disables it. For more details, see GH#12900.
|
||||
// Arguments:
|
||||
// - enabled - true to enable, false to disable.
|
||||
// Return Value:
|
||||
// - True if handled successfully. False otherwise.
|
||||
bool TerminalDispatch::EnableFocusEventMode(const bool enabled)
|
||||
{
|
||||
_terminalApi.SetInputMode(TerminalInput::Mode::FocusEvent, enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Routine Description:
|
||||
// Enable Alternate Scroll Mode - When in the Alt Buffer, send CUP and CUD on
|
||||
// scroll up/down events instead of the usual sequences
|
||||
@@ -631,6 +644,9 @@ bool TerminalDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param,
|
||||
case DispatchTypes::ModeParams::SGR_EXTENDED_MODE:
|
||||
success = EnableSGRExtendedMouseMode(enable);
|
||||
break;
|
||||
case DispatchTypes::ModeParams::FOCUS_EVENT_MODE:
|
||||
success = EnableFocusEventMode(enable);
|
||||
break;
|
||||
case DispatchTypes::ModeParams::ALTERNATE_SCROLL:
|
||||
success = EnableAlternateScroll(enable);
|
||||
break;
|
||||
|
||||
@@ -74,6 +74,7 @@ public:
|
||||
bool EnableSGRExtendedMouseMode(const bool enabled) override; // ?1006
|
||||
bool EnableButtonEventMouseMode(const bool enabled) override; // ?1002
|
||||
bool EnableAnyEventMouseMode(const bool enabled) override; // ?1003
|
||||
bool EnableFocusEventMode(const bool enabled) override; // ?1004
|
||||
bool EnableAlternateScroll(const bool enabled) override; // ?1007
|
||||
bool EnableXtermBracketedPasteMode(const bool enabled) override; // ?2004
|
||||
|
||||
|
||||
@@ -642,6 +642,7 @@ void InputBuffer::_WriteBuffer(_Inout_ std::deque<std::unique_ptr<IInputEvent>>&
|
||||
inEvents.pop_front();
|
||||
if (vtInputMode)
|
||||
{
|
||||
// GH#11682: TerminalInput::HandleKey can handle both KeyEvents and Focus events seamlessly
|
||||
const bool handled = _termInput.HandleKey(inEvent.get());
|
||||
if (handled)
|
||||
{
|
||||
|
||||
@@ -434,6 +434,11 @@ using namespace Microsoft::Console::Render;
|
||||
return _Write("\x1b[?9001h");
|
||||
}
|
||||
|
||||
[[nodiscard]] HRESULT VtEngine::_RequestFocusEventMode() noexcept
|
||||
{
|
||||
return _Write("\x1b[?1004h");
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Send a sequence to the connected terminal to switch to the alternate or main screen buffer.
|
||||
// Arguments:
|
||||
|
||||
@@ -524,6 +524,7 @@ void VtEngine::SetTerminalCursorTextPosition(const COORD cursor) noexcept
|
||||
HRESULT VtEngine::RequestWin32Input() noexcept
|
||||
{
|
||||
RETURN_IF_FAILED(_RequestWin32Input());
|
||||
RETURN_IF_FAILED(_RequestFocusEventMode());
|
||||
RETURN_IF_FAILED(_Flush());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,8 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT _RequestWin32Input() noexcept;
|
||||
[[nodiscard]] HRESULT _SwitchScreenBuffer(const bool useAltBuffer) noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _RequestFocusEventMode() noexcept;
|
||||
|
||||
[[nodiscard]] virtual HRESULT _MoveCursor(const COORD coord) noexcept = 0;
|
||||
[[nodiscard]] HRESULT _RgbUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept;
|
||||
[[nodiscard]] HRESULT _16ColorUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept;
|
||||
|
||||
@@ -375,6 +375,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
|
||||
VT200_MOUSE_MODE = DECPrivateMode(1000),
|
||||
BUTTON_EVENT_MOUSE_MODE = DECPrivateMode(1002),
|
||||
ANY_EVENT_MOUSE_MODE = DECPrivateMode(1003),
|
||||
FOCUS_EVENT_MODE = DECPrivateMode(1004),
|
||||
UTF8_EXTENDED_MODE = DECPrivateMode(1005),
|
||||
SGR_EXTENDED_MODE = DECPrivateMode(1006),
|
||||
ALTERNATE_SCROLL = DECPrivateMode(1007),
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
virtual bool EnableSGRExtendedMouseMode(const bool enabled) = 0; // ?1006
|
||||
virtual bool EnableButtonEventMouseMode(const bool enabled) = 0; // ?1002
|
||||
virtual bool EnableAnyEventMouseMode(const bool enabled) = 0; // ?1003
|
||||
virtual bool EnableFocusEventMode(const bool enabled) = 0; // ?1004
|
||||
virtual bool EnableAlternateScroll(const bool enabled) = 0; // ?1007
|
||||
virtual bool EnableXtermBracketedPasteMode(const bool enabled) = 0; // ?2004
|
||||
virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) = 0; // OSCColorTable
|
||||
|
||||
@@ -175,7 +175,6 @@ bool InteractDispatch::IsVtInputEnabled() const
|
||||
// which will end up here. This will update the console's internal tracker if
|
||||
// it's focused or not, as to match the end-terminal's state.
|
||||
// - Used to call ConsoleControl(ConsoleSetForeground,...).
|
||||
// - Full support for this sequence is tracked in GH#11682.
|
||||
// Arguments:
|
||||
// - focused: if the terminal is now focused
|
||||
// Return Value:
|
||||
@@ -234,11 +233,9 @@ bool InteractDispatch::FocusChanged(const bool focused) const
|
||||
|
||||
WI_UpdateFlag(gci.Flags, CONSOLE_HAS_FOCUS, shouldActuallyFocus);
|
||||
gci.ProcessHandleList.ModifyConsoleProcessFocus(shouldActuallyFocus);
|
||||
gci.pInputBuffer->Write(std::make_unique<FocusEvent>(focused));
|
||||
}
|
||||
// Does nothing outside of ConPTY. If there's a real HWND, then the HWND is solely in charge.
|
||||
|
||||
// Theoretically, this could be propagated as a focus event as well, to the
|
||||
// input buffer. That should be considered when implementing GH#11682.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1025,7 +1025,24 @@ bool AdaptDispatch::_SetInputMode(const TerminalInput::Mode mode, const bool ena
|
||||
// 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->IsVtInputEnabled());
|
||||
|
||||
// GH#12799 - If the app requested that we disable focus events, DON'T pass
|
||||
// that through. ConPTY would _always_ like to know about focus events.
|
||||
|
||||
return !_pConApi->IsConsolePty() ||
|
||||
!_pConApi->IsVtInputEnabled() ||
|
||||
(!enable && mode == TerminalInput::Mode::FocusEvent);
|
||||
|
||||
// Another way of writing the above statement is:
|
||||
//
|
||||
// const bool inConpty = _pConApi->IsConsolePty();
|
||||
// const bool shouldPassthrough = inConpty && _pConApi->IsVtInputEnabled();
|
||||
// const bool disabledFocusEvents = inConpty && (!enable && mode == TerminalInput::Mode::FocusEvent);
|
||||
// return !shouldPassthrough || disabledFocusEvents;
|
||||
//
|
||||
// It's like a "filter" left to right. Due to the early return via
|
||||
// !IsConsolePty, once you're at the !enable part, IsConsolePty can only be
|
||||
// true anymore.
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -1084,6 +1101,9 @@ bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, con
|
||||
case DispatchTypes::ModeParams::SGR_EXTENDED_MODE:
|
||||
success = EnableSGRExtendedMouseMode(enable);
|
||||
break;
|
||||
case DispatchTypes::ModeParams::FOCUS_EVENT_MODE:
|
||||
success = EnableFocusEventMode(enable);
|
||||
break;
|
||||
case DispatchTypes::ModeParams::ALTERNATE_SCROLL:
|
||||
success = EnableAlternateScroll(enable);
|
||||
break;
|
||||
@@ -2092,7 +2112,6 @@ bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled)
|
||||
|
||||
//Routine Description:
|
||||
// Enable Any Event mode - send all mouse events to the input.
|
||||
|
||||
//Arguments:
|
||||
// - enabled - true to enable, false to disable.
|
||||
// Return value:
|
||||
@@ -2102,6 +2121,21 @@ bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled)
|
||||
return _SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enabled);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enables/disables focus event mode. A client may enable this if they want to
|
||||
// receive focus events.
|
||||
// - ConPTY always enables this mode and never disables it. Internally, we'll
|
||||
// always set this mode, but conpty will never request this to be disabled by
|
||||
// the hosting terminal.
|
||||
// Arguments:
|
||||
// - enabled - true to enable, false to disable.
|
||||
// Return Value:
|
||||
// - True if handled successfully. False otherwise.
|
||||
bool AdaptDispatch::EnableFocusEventMode(const bool enabled)
|
||||
{
|
||||
return _SetInputMode(TerminalInput::Mode::FocusEvent, enabled);
|
||||
}
|
||||
|
||||
//Routine Description:
|
||||
// Enable Alternate Scroll Mode - When in the Alt Buffer, send CUP and CUD on
|
||||
// scroll up/down events instead of the usual sequences
|
||||
|
||||
@@ -107,6 +107,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
bool EnableSGRExtendedMouseMode(const bool enabled) override; // ?1006
|
||||
bool EnableButtonEventMouseMode(const bool enabled) override; // ?1002
|
||||
bool EnableAnyEventMouseMode(const bool enabled) override; // ?1003
|
||||
bool EnableFocusEventMode(const bool enabled) override; // ?1004
|
||||
bool EnableAlternateScroll(const bool enabled) override; // ?1007
|
||||
bool EnableXtermBracketedPasteMode(const bool enabled) noexcept override; // ?2004
|
||||
bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
bool EnableSGRExtendedMouseMode(const bool /*enabled*/) override { return false; } // ?1006
|
||||
bool EnableButtonEventMouseMode(const bool /*enabled*/) override { return false; } // ?1002
|
||||
bool EnableAnyEventMouseMode(const bool /*enabled*/) override { return false; } // ?1003
|
||||
bool EnableFocusEventMode(const bool /*enabled*/) override { return false; } // ?1004
|
||||
bool EnableAlternateScroll(const bool /*enabled*/) override { return false; } // ?1007
|
||||
bool EnableXtermBracketedPasteMode(const bool /*enabled*/) override { return false; } // ?2004
|
||||
bool SetColorTableEntry(const size_t /*tableIndex*/, const DWORD /*color*/) override { return false; } // OSCColorTable
|
||||
|
||||
@@ -522,6 +522,14 @@ bool TerminalInput::HandleKey(const IInputEvent* const pInEvent)
|
||||
return false;
|
||||
}
|
||||
|
||||
// GH#11682: If this was a focus event, we can handle this. Steal the
|
||||
// focused state, and return true if we're actually in focus event mode.
|
||||
if (pInEvent->EventType() == InputEventType::FocusEvent)
|
||||
{
|
||||
const auto& focusEvent = *static_cast<const FocusEvent* const>(pInEvent);
|
||||
return HandleFocus(focusEvent.GetFocus());
|
||||
}
|
||||
|
||||
// On key presses, prepare to translate to VT compatible sequences
|
||||
if (pInEvent->EventType() != InputEventType::KeyEvent)
|
||||
{
|
||||
@@ -674,6 +682,16 @@ bool TerminalInput::HandleKey(const IInputEvent* const pInEvent)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TerminalInput::HandleFocus(const bool focused) noexcept
|
||||
{
|
||||
const bool enabled{ _inputMode.test(Mode::FocusEvent) };
|
||||
if (enabled)
|
||||
{
|
||||
_SendInputSequence(focused ? L"\x1b[I" : L"\x1b[O");
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sends the given character to the shell.
|
||||
// - Surrogate pairs are being aggregated by this function before being sent.
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
~TerminalInput() = default;
|
||||
|
||||
bool HandleKey(const IInputEvent* const pInEvent);
|
||||
bool HandleFocus(const bool focused) noexcept;
|
||||
|
||||
enum class Mode : size_t
|
||||
{
|
||||
@@ -49,6 +50,8 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
ButtonEventMouseTracking,
|
||||
AnyEventMouseTracking,
|
||||
|
||||
FocusEvent,
|
||||
|
||||
AlternateScroll
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user