diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 32cf032606..8183ba064e 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -2518,6 +2518,7 @@ std::pair, std::shared_ptr> Pane::_Split(SplitDirect _profile = nullptr; _control = { nullptr }; _firstChild->_isDefTermSession = _isDefTermSession; + _firstChild->_broadcastEnabled = _broadcastEnabled; } _splitState = actualSplitType; @@ -3173,6 +3174,9 @@ void Pane::EnableBroadcast(bool enabled) if (_IsLeaf()) { _broadcastEnabled = enabled; + _control.CursorVisibility(enabled ? + CursorDisplayState::Shown : + CursorDisplayState::Default); UpdateVisuals(); } else diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index b96111994d..97b92a7d58 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4698,6 +4698,21 @@ namespace winrt::TerminalApp::implementation // the settings, change active panes, etc. _activated = activated; _updateThemeColors(); + + if (const auto& tab{ _GetFocusedTabImpl() }) + { + if (tab->TabStatus().IsInputBroadcastActive()) + { + tab->GetRootPane()->WalkTree([activated](const auto& p) { + if (const auto& control{ p->GetTerminalControl() }) + { + control.CursorVisibility(activated ? + Microsoft::Terminal::Control::CursorDisplayState::Shown : + Microsoft::Terminal::Control::CursorDisplayState::Default); + } + }); + } + } } winrt::fire_and_forget TerminalPage::_ControlCompletionsChangedHandler(const IInspectable sender, diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 6582d680a7..1fdbde88d7 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1051,8 +1051,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // _cursorTimer doesn't exist, and it would never turn on the // cursor. To mitigate, we'll initialize the cursor's 'on' state // with `_focused` here. - _core.CursorOn(_focused || DisplayCursorWhileBlurred); - if (DisplayCursorWhileBlurred) + _core.CursorOn(_focused || _displayCursorWhileBlurred()); + if (_displayCursorWhileBlurred()) { _cursorTimer->Start(); } @@ -1968,7 +1968,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TSFInputControl().NotifyFocusLeave(); } - if (_cursorTimer && !DisplayCursorWhileBlurred) + if (_cursorTimer && !_displayCursorWhileBlurred()) { _cursorTimer->Stop(); _core.CursorOn(false); @@ -3660,4 +3660,45 @@ namespace winrt::Microsoft::Terminal::Control::implementation SelectionContextMenu().Hide(); _core.ContextMenuSelectOutput(); } + + // Should the text cursor be displayed, even when the control isn't focused? + // n.b. "blur" is the opposite of "focus". + bool TermControl::_displayCursorWhileBlurred() const noexcept + { + return CursorVisibility() == Control::CursorDisplayState::Shown; + } + Control::CursorDisplayState TermControl::CursorVisibility() const noexcept + { + return _cursorVisibility; + } + void TermControl::CursorVisibility(Control::CursorDisplayState cursorVisibility) + { + _cursorVisibility = cursorVisibility; + if (!_initializedTerminal) + { + return; + } + + if (_displayCursorWhileBlurred()) + { + // If we should be ALWAYS displaying the cursor, turn it on and start blinking. + _core.CursorOn(true); + if (_cursorTimer.has_value()) + { + _cursorTimer->Start(); + } + } + else + { + // Otherwise, if we're unfocused, then turn the cursor off and stop + // blinking. (if we're focused, then we're already doing the right + // thing) + const auto focused = FocusState() != FocusState::Unfocused; + if (!focused && _cursorTimer.has_value()) + { + _cursorTimer->Stop(); + } + _core.CursorOn(focused); + } + } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index f2f76971ad..441ca5f576 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -160,6 +160,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation TerminalConnection::ITerminalConnection Connection(); void Connection(const TerminalConnection::ITerminalConnection& connection); + Control::CursorDisplayState CursorVisibility() const noexcept; + void CursorVisibility(Control::CursorDisplayState cursorVisibility); + // -------------------------------- WinRT Events --------------------------------- // clang-format off WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); @@ -194,9 +197,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr); - public: - til::property DisplayCursorWhileBlurred{ false }; - private: friend struct TermControlT; // friend our parent so it can bind private event handlers @@ -258,6 +258,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation Windows::Foundation::Collections::IObservableVector _originalSelectedPrimaryElements{ nullptr }; Windows::Foundation::Collections::IObservableVector _originalSelectedSecondaryElements{ nullptr }; + Control::CursorDisplayState _cursorVisibility{ Control::CursorDisplayState::Default }; + inline bool _IsClosing() const noexcept { #ifndef NDEBUG @@ -376,6 +378,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _SelectCommandHandler(const IInspectable& sender, const IInspectable& args); void _SelectOutputHandler(const IInspectable& sender, const IInspectable& args); + bool _displayCursorWhileBlurred() const noexcept; struct Revokers { diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index f01a11104e..6672769008 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -12,6 +12,12 @@ import "ControlCore.idl"; namespace Microsoft.Terminal.Control { + enum CursorDisplayState + { + Default, + Shown + }; + [default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IDirectKeyListener, IMouseWheelListener, @@ -121,7 +127,7 @@ namespace Microsoft.Terminal.Control // opacity set by the settings should call this instead. Double BackgroundOpacity { get; }; - Boolean DisplayCursorWhileBlurred; + CursorDisplayState CursorVisibility; Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp index d045bc3cf2..aa6585e6a1 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp @@ -41,7 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _previewControl = Control::TermControl(settings, settings, *_previewConnection); _previewControl.IsEnabled(false); _previewControl.AllowFocusWhenDisabled(false); - _previewControl.DisplayCursorWhileBlurred(true); + _previewControl.CursorVisibility(Microsoft::Terminal::Control::CursorDisplayState::Shown); ControlPreview().Child(_previewControl); } diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index f9fbe21662..cdd5d6e78d 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -437,6 +437,7 @@ { "command": { "action": "swapPane", "direction": "previousInOrder"} }, { "command": { "action": "swapPane", "direction": "nextInOrder"} }, { "command": { "action": "swapPane", "direction": "first" } }, + { "command": "toggleBroadcastInput" }, { "command": "togglePaneZoom" }, { "command": "toggleSplitOrientation" }, { "command": "toggleReadOnlyMode" },