diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml
index a12620b8db8..b6a46ce3a97 100644
--- a/src/cascadia/TerminalApp/App.xaml
+++ b/src/cascadia/TerminalApp/App.xaml
@@ -42,11 +42,15 @@ the MIT License. See LICENSE in the project root for license information. -->
+
+
+
+
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index c7b75fc605e..8d074778154 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -167,6 +167,17 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
+ void TerminalPage::_HandleToggleInputBroadcast(const IInspectable& /*sender*/,
+ const ActionEventArgs& args)
+ {
+ if (const auto activeTab{ _GetFocusedTabImpl() })
+ {
+ activeTab->ToggleInputBroadcast();
+ }
+
+ args.Handled(true);
+ }
+
void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
diff --git a/src/cascadia/TerminalApp/CommandPalette.xaml b/src/cascadia/TerminalApp/CommandPalette.xaml
index 56bac30e6be..a54e554547e 100644
--- a/src/cascadia/TerminalApp/CommandPalette.xaml
+++ b/src/cascadia/TerminalApp/CommandPalette.xaml
@@ -246,6 +246,13 @@ the MIT License. See LICENSE in the project root for license information. -->
FontSize="12"
Margin="0,0,8,0"/>
+
+
diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp
index f7d681564a5..9340c8c3e0b 100644
--- a/src/cascadia/TerminalApp/Pane.cpp
+++ b/src/cascadia/TerminalApp/Pane.cpp
@@ -33,6 +33,8 @@ static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Wi
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr };
+winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_readOnlyBorderBrush = { nullptr };
+winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_broadcastBorderBrush = { nullptr };
Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocused) :
_control{ control },
@@ -44,11 +46,12 @@ Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocus
_connectionStateChangedToken = _control.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
_warningBellToken = _control.WarningBell({ this, &Pane::_ControlWarningBellHandler });
+ _readOnlyChangedToken = _control.ReadOnlyChanged({ this, &Pane::_ControlReadOnlyChangedHandler });
// On the first Pane's creation, lookup resources we'll use to theme the
// Pane, including the brushed to use for the focused/unfocused border
// color.
- if (s_focusedBorderBrush == nullptr || s_unfocusedBorderBrush == nullptr)
+ if (s_focusedBorderBrush == nullptr || s_unfocusedBorderBrush == nullptr || s_readOnlyBorderBrush == nullptr || s_broadcastBorderBrush == nullptr)
{
_SetupResources();
}
@@ -586,7 +589,7 @@ bool Pane::_HasFocusedChild() const noexcept
// -
void Pane::UpdateVisuals()
{
- _border.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush);
+ _border.BorderBrush(_ComputeBorderColor());
}
// Method Description:
@@ -698,6 +701,7 @@ void Pane::_CloseChild(const bool closeFirst)
// Add our new event handler before revoking the old one.
_connectionStateChangedToken = _control.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
_warningBellToken = _control.WarningBell({ this, &Pane::_ControlWarningBellHandler });
+ _readOnlyChangedToken = _control.ReadOnlyChanged({ this, &Pane::_ControlReadOnlyChangedHandler });
// Revoke the old event handlers. Remove both the handlers for the panes
// themselves closing, and remove their handlers for their controls
@@ -709,6 +713,8 @@ void Pane::_CloseChild(const bool closeFirst)
remainingChild->_control.ConnectionStateChanged(remainingChild->_connectionStateChangedToken);
closedChild->_control.WarningBell(closedChild->_warningBellToken);
remainingChild->_control.WarningBell(remainingChild->_warningBellToken);
+ closedChild->_control.ReadOnlyChanged(closedChild->_readOnlyChangedToken);
+ remainingChild->_control.ReadOnlyChanged(remainingChild->_readOnlyChangedToken);
// If either of our children was focused, we want to take that focus from
// them.
@@ -1440,6 +1446,8 @@ std::pair, std::shared_ptr> Pane::_Split(SplitState
_connectionStateChangedToken.value = 0;
_control.WarningBell(_warningBellToken);
_warningBellToken.value = 0;
+ _control.ReadOnlyChanged(_readOnlyChangedToken);
+ _readOnlyChangedToken.value = 0;
// Remove our old GotFocus handler from the control. We don't what the
// control telling us that it's now focused, we want it telling its new
@@ -2022,6 +2030,34 @@ void Pane::_SetupResources()
// will eat focus.
s_unfocusedBorderBrush = SolidColorBrush{ Colors::Black() };
}
+
+ const auto readOnlyColorKey = winrt::box_value(L"ReadOnlyPaneBorderColor");
+ if (res.HasKey(readOnlyColorKey))
+ {
+ winrt::Windows::Foundation::IInspectable obj = res.Lookup(readOnlyColorKey);
+ s_readOnlyBorderBrush = obj.try_as();
+ }
+ else
+ {
+ // DON'T use Transparent here - if it's "Transparent", then it won't
+ // be able to hittest for clicks, and then clicking on the border
+ // will eat focus.
+ s_readOnlyBorderBrush = SolidColorBrush{ Colors::Black() };
+ }
+
+ const auto broadcastColorKey = winrt::box_value(L"BroadcastPaneBorderColor");
+ if (res.HasKey(broadcastColorKey))
+ {
+ winrt::Windows::Foundation::IInspectable obj = res.Lookup(broadcastColorKey);
+ s_broadcastBorderBrush = obj.try_as();
+ }
+ else
+ {
+ // DON'T use Transparent here - if it's "Transparent", then it won't
+ // be able to hittest for clicks, and then clicking on the border
+ // will eat focus.
+ s_broadcastBorderBrush = SolidColorBrush{ Colors::Black() };
+ }
}
int Pane::GetLeafPaneCount() const noexcept
@@ -2101,6 +2137,78 @@ bool Pane::ContainsReadOnly() const
return _IsLeaf() ? _control.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly());
}
+void Pane::EnableBroadcast(bool enabled)
+{
+ if (_IsLeaf())
+ {
+ _broadcastEnabled = enabled;
+ UpdateVisuals();
+ }
+ else
+ {
+ _firstChild->EnableBroadcast(enabled);
+ _secondChild->EnableBroadcast(enabled);
+ }
+}
+
+
+void Pane::BroadcastKey(const winrt::Microsoft::Terminal::TerminalControl::TermControl& sourceControl, const WORD vkey, const WORD scanCode, const DWORD modifiers, const bool keyDown)
+{
+ if (_IsLeaf())
+ {
+ if (_control != sourceControl && !_control.ReadOnly())
+ {
+ _control.TrySendKeyEvent(vkey, scanCode, modifiers, keyDown);
+ }
+ }
+ else
+ {
+ _firstChild->BroadcastKey(sourceControl, vkey, scanCode, modifiers, keyDown);
+ _secondChild->BroadcastKey(sourceControl, vkey, scanCode, modifiers, keyDown);
+ }
+}
+
+void Pane::BroadcastChar(const winrt::Microsoft::Terminal::TerminalControl::TermControl& sourceControl, const wchar_t character, const WORD scanCode, const DWORD modifiers)
+{
+ if (_IsLeaf())
+ {
+ if (_control != sourceControl && !_control.ReadOnly())
+ {
+ _control.TrySendChar(character, scanCode, modifiers);
+ }
+ }
+ else
+ {
+ _firstChild->BroadcastChar(sourceControl, character, scanCode, modifiers);
+ _secondChild->BroadcastChar(sourceControl, character, scanCode, modifiers);
+ }
+}
+
+void Pane::_ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*e*/)
+{
+ UpdateVisuals();
+}
+
+winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor()
+{
+ if (_lastActive)
+ {
+ return s_focusedBorderBrush;
+ }
+
+ if (_control && _control.ReadOnly())
+ {
+ return s_readOnlyBorderBrush;
+ }
+
+ if (_broadcastEnabled)
+ {
+ return s_broadcastBorderBrush;
+ }
+
+ return s_unfocusedBorderBrush;
+}
+
DEFINE_EVENT(Pane, GotFocus, _GotFocusHandlers, winrt::delegate>);
DEFINE_EVENT(Pane, LostFocus, _LostFocusHandlers, winrt::delegate>);
DEFINE_EVENT(Pane, PaneRaiseBell, _PaneRaiseBellHandlers, winrt::Windows::Foundation::EventHandler);
diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h
index 0aa41958e1f..758412cd47c 100644
--- a/src/cascadia/TerminalApp/Pane.h
+++ b/src/cascadia/TerminalApp/Pane.h
@@ -83,6 +83,10 @@ class Pane : public std::enable_shared_from_this
bool ContainsReadOnly() const;
+ void EnableBroadcast(bool enabled);
+ void BroadcastKey(const winrt::Microsoft::Terminal::TerminalControl::TermControl& sourceControl, const WORD vkey, const WORD scanCode, const DWORD modifiers, const bool keyDown);
+ void BroadcastChar(const winrt::Microsoft::Terminal::TerminalControl::TermControl& sourceControl, const wchar_t vkey, const WORD scanCode, const DWORD modifiers);
+
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler);
DECLARE_EVENT(GotFocus, _GotFocusHandlers, winrt::delegate>);
DECLARE_EVENT(LostFocus, _LostFocusHandlers, winrt::delegate>);
@@ -98,6 +102,8 @@ class Pane : public std::enable_shared_from_this
winrt::Microsoft::Terminal::TerminalControl::TermControl _control{ nullptr };
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush;
+ static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_readOnlyBorderBrush;
+ static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_broadcastBorderBrush;
std::shared_ptr _firstChild{ nullptr };
std::shared_ptr _secondChild{ nullptr };
@@ -112,6 +118,7 @@ class Pane : public std::enable_shared_from_this
winrt::event_token _firstClosedToken{ 0 };
winrt::event_token _secondClosedToken{ 0 };
winrt::event_token _warningBellToken{ 0 };
+ winrt::event_token _readOnlyChangedToken{ 0 };
winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker;
@@ -123,6 +130,7 @@ class Pane : public std::enable_shared_from_this
std::atomic _isClosing{ false };
bool _zoomed{ false };
+ bool _broadcastEnabled{ false };
bool _IsLeaf() const noexcept;
bool _HasFocusedChild() const noexcept;
@@ -137,6 +145,7 @@ class Pane : public std::enable_shared_from_this
void _ApplySplitDefinitions();
void _SetupEntranceAnimation();
void _UpdateBorders();
+ winrt::Windows::UI::Xaml::Media::SolidColorBrush _ComputeBorderColor();
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
bool _NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
@@ -153,6 +162,8 @@ class Pane : public std::enable_shared_from_this
void _ControlLostFocusHandler(winrt::Windows::Foundation::IInspectable const& sender,
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
+ void _ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e);
+
std::pair _CalcChildrenSizes(const float fullSize) const;
SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const;
SnapSizeResult _CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp
index 90eede9d057..3a09c719cbe 100644
--- a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp
+++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp
@@ -265,6 +265,11 @@ namespace winrt::TerminalApp::implementation
_NewWindowHandlers(*this, eventArgs);
break;
}
+ case ShortcutAction::ToggleInputBroadcast:
+ {
+ _ToggleInputBroadcastHandlers(*this, eventArgs);
+ break;
+ }
default:
return false;
}
diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.h b/src/cascadia/TerminalApp/ShortcutActionDispatch.h
index bdcf59e7a17..aba0d110770 100644
--- a/src/cascadia/TerminalApp/ShortcutActionDispatch.h
+++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.h
@@ -67,6 +67,7 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(FindMatch, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(TogglePaneReadOnly, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
TYPED_EVENT(NewWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
+ TYPED_EVENT(ToggleInputBroadcast, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs);
// clang-format on
private:
diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.idl b/src/cascadia/TerminalApp/ShortcutActionDispatch.idl
index 4a8b366b945..c628e7b171e 100644
--- a/src/cascadia/TerminalApp/ShortcutActionDispatch.idl
+++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.idl
@@ -53,5 +53,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler FindMatch;
event Windows.Foundation.TypedEventHandler TogglePaneReadOnly;
event Windows.Foundation.TypedEventHandler NewWindow;
+ event Windows.Foundation.TypedEventHandler ToggleInputBroadcast;
}
}
diff --git a/src/cascadia/TerminalApp/TabHeaderControl.xaml b/src/cascadia/TerminalApp/TabHeaderControl.xaml
index 8b9741b07ab..ddf9f02aa7c 100644
--- a/src/cascadia/TerminalApp/TabHeaderControl.xaml
+++ b/src/cascadia/TerminalApp/TabHeaderControl.xaml
@@ -61,6 +61,12 @@ the MIT License. See LICENSE in the project root for license information. -->
Glyph=""
FontSize="12"
Margin="0,0,8,0"/>
+
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 42c771a229b..4b527933aea 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -1158,6 +1158,7 @@ namespace winrt::TerminalApp::implementation
_actionDispatch->FindMatch({ this, &TerminalPage::_HandleFindMatch });
_actionDispatch->TogglePaneReadOnly({ this, &TerminalPage::_HandleTogglePaneReadOnly });
_actionDispatch->NewWindow({ this, &TerminalPage::_HandleNewWindow });
+ _actionDispatch->ToggleInputBroadcast({ this, &TerminalPage::_HandleToggleInputBroadcast });
}
// Method Description:
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index d26e025516b..add18077780 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -320,6 +320,7 @@ namespace winrt::TerminalApp::implementation
void _HandleFindMatch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleTogglePaneReadOnly(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
void _HandleNewWindow(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
+ void _HandleToggleInputBroadcast(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
// Make sure to hook new actions up in _RegisterActionCallbacks!
#pragma endregion
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index 115833893dd..642823e82eb 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -413,7 +413,6 @@ namespace winrt::TerminalApp::implementation
++_nextPaneId;
}
_activePane = first;
- _AttachEventHandlersToControl(control);
// Add a event handlers to the new panes' GotFocus event. When the pane
// gains focus, we'll mark it as the new active pane.
@@ -629,6 +628,32 @@ namespace winrt::TerminalApp::implementation
}
}
});
+
+ control.KeySent([weakThis](auto&& sender, auto&& e) {
+ if (const auto tab{ weakThis.get() })
+ {
+ if (tab->_tabStatus.IsInputBroadcastActive())
+ {
+ if (const auto termControl{ sender.try_as() })
+ {
+ tab->_rootPane->BroadcastKey(termControl, e.VKey(), e.ScanCode(), e.Modifiers(), e.KeyDown());
+ }
+ }
+ }
+ });
+
+ control.CharSent([weakThis](auto&& sender, auto&& e) {
+ if (const auto tab{ weakThis.get() })
+ {
+ if (tab->_tabStatus.IsInputBroadcastActive())
+ {
+ if (const auto termControl{ sender.try_as() })
+ {
+ tab->_rootPane->BroadcastChar(termControl, e.Character(), e.ScanCode(), e.Modifiers());
+ }
+ }
+ }
+ });
}
// Method Description:
@@ -1156,6 +1181,14 @@ namespace winrt::TerminalApp::implementation
}
}
+ // Method Description:
+ // - Toggle read-only mode on the active pane
+ void TerminalTab::ToggleInputBroadcast()
+ {
+ _tabStatus.IsInputBroadcastActive(!_tabStatus.IsInputBroadcastActive());
+ _rootPane->EnableBroadcast(_tabStatus.IsInputBroadcastActive());
+ }
+
// Method Description:
// - Calculates if the tab is read-only.
// The tab is considered read-only if one of the panes is read-only.
diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h
index f8fb560ee60..5e9f65d96ec 100644
--- a/src/cascadia/TerminalApp/TerminalTab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -78,6 +78,8 @@ namespace winrt::TerminalApp::implementation
int GetLeafPaneCount() const noexcept;
void TogglePaneReadOnly();
+ void ToggleInputBroadcast();
+
std::shared_ptr GetActivePane() const;
winrt::TerminalApp::TerminalTabStatus TabStatus()
diff --git a/src/cascadia/TerminalApp/TerminalTabStatus.h b/src/cascadia/TerminalApp/TerminalTabStatus.h
index 64b75cda5d5..0c5dd96e053 100644
--- a/src/cascadia/TerminalApp/TerminalTabStatus.h
+++ b/src/cascadia/TerminalApp/TerminalTabStatus.h
@@ -18,6 +18,7 @@ namespace winrt::TerminalApp::implementation
OBSERVABLE_GETSET_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(bool, IsReadOnlyActive, _PropertyChangedHandlers);
+ OBSERVABLE_GETSET_PROPERTY(bool, IsInputBroadcastActive, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
};
}
diff --git a/src/cascadia/TerminalApp/TerminalTabStatus.idl b/src/cascadia/TerminalApp/TerminalTabStatus.idl
index 11d659f745f..57f4e05fa1a 100644
--- a/src/cascadia/TerminalApp/TerminalTabStatus.idl
+++ b/src/cascadia/TerminalApp/TerminalTabStatus.idl
@@ -13,5 +13,6 @@ namespace TerminalApp
Boolean BellIndicator { get; set; };
UInt32 ProgressValue { get; set; };
Boolean IsReadOnlyActive { get; set; };
+ Boolean IsInputBroadcastActive { get; set; };
}
}
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 041f9393802..26c24852ed5 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -909,10 +909,21 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
{
modifiers |= ControlKeyStates::EnhancedKey;
}
- const bool handled = _terminal->SendCharEvent(ch, scanCode, modifiers);
+
+ const bool handled = TrySendChar(ch, scanCode, modifiers.Value());
+ auto charSentArgs = winrt::make(ch, scanCode, modifiers.Value());
+ _charSentHandlers(*this, charSentArgs);
e.Handled(handled);
}
+ // Method Description:
+ // - Sends character to terminal
+ bool TermControl::TrySendChar(const wchar_t character, const WORD scanCode, const DWORD modifiers)
+ {
+ ControlKeyStates keyStates{ modifiers };
+ return _terminal->SendCharEvent(character, scanCode, keyStates);
+ }
+
// Method Description:
// - Manually handles key events for certain keys that can't be passed to us
// normally. Namely, the keys we're concerned with are F7 down and Alt up.
@@ -1111,6 +1122,25 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
}
+ // Method Description:
+ // - Invokes TrySendKeyEvent and triggers KeySent event
+ // Arguments:
+ // - vkey: The vkey of the key pressed.
+ // - scanCode: The scan code of the key pressed.
+ // - keyStates: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
+ // - keyDown: If true, the key was pressed, otherwise the key was released.
+ bool TermControl::_TrySendKeyEvent(const WORD vkey,
+ const WORD scanCode,
+ const ControlKeyStates keyStates,
+ const bool keyDown)
+ {
+ const auto modifiers = keyStates.Value();
+ const auto result = TrySendKeyEvent(vkey, scanCode, modifiers, keyDown);
+ auto keySentArgs = winrt::make(vkey, scanCode, modifiers, keyDown);
+ _keySentHandlers(*this, std::move(keySentArgs));
+ return result;
+ }
+
// Method Description:
// - Send this particular key event to the terminal.
// See Terminal::SendKeyEvent for more information.
@@ -1121,10 +1151,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - scanCode: The scan code of the key pressed.
// - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
// - keyDown: If true, the key was pressed, otherwise the key was released.
- bool TermControl::_TrySendKeyEvent(const WORD vkey,
- const WORD scanCode,
- const ControlKeyStates modifiers,
- const bool keyDown)
+ bool TermControl::TrySendKeyEvent(const WORD vkey,
+ const WORD scanCode,
+ const DWORD modifiers,
+ const bool keyDown)
{
// When there is a selection active, escape should clear it and NOT flow through
// to the terminal. With any other keypress, it should clear the selection AND
@@ -1169,7 +1199,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// If the terminal translated the key, mark the event as handled.
// This will prevent the system from trying to get the character out
// of it and sending us a CharacterReceived event.
- const auto handled = vkey ? _terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown) : true;
+ ControlKeyStates keyStates{ modifiers };
+ const auto handled = vkey ? _terminal->SendKeyEvent(vkey, scanCode, keyStates, keyDown) : true;
if (_cursorTimer.has_value())
{
@@ -3383,5 +3414,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs);
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, SetTaskbarProgress, _setTaskbarProgressHandlers, TerminalControl::TermControl, IInspectable);
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, RaiseNotice, _raiseNoticeHandlers, TerminalControl::TermControl, TerminalControl::NoticeEventArgs);
+ DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, KeySent, _keySentHandlers, TerminalControl::TermControl, TerminalControl::KeySentEventArgs);
+ DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, CharSent, _charSentHandlers, TerminalControl::TermControl, TerminalControl::CharSentEventArgs);
// clang-format on
}
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index 4a85142002b..ecb0f06109e 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -8,6 +8,8 @@
#include "PasteFromClipboardEventArgs.g.h"
#include "OpenHyperlinkEventArgs.g.h"
#include "NoticeEventArgs.g.h"
+#include "KeySentEventArgs.g.h"
+#include "CharSentEventArgs.g.h"
#include
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
@@ -98,6 +100,47 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const hstring _message;
};
+ struct KeySentEventArgs :
+ public KeySentEventArgsT
+ {
+ public:
+ KeySentEventArgs(const WORD vkey, const WORD scanCode, const DWORD modifiers, const bool keyDown) :
+ _vkey(vkey),
+ _scanCode(scanCode),
+ _modifiers(modifiers),
+ _keyDown(keyDown) {}
+
+ WORD VKey() { return _vkey; };
+ WORD ScanCode() { return _scanCode; };
+ DWORD Modifiers() { return _modifiers; };
+ bool KeyDown() { return _keyDown; };
+
+ private:
+ const WORD _vkey;
+ const WORD _scanCode;
+ const DWORD _modifiers;
+ const bool _keyDown;
+ };
+
+ struct CharSentEventArgs :
+ public CharSentEventArgsT
+ {
+ public:
+ CharSentEventArgs(const wchar_t character, const WORD scanCode, const DWORD modifiers) :
+ _character(character),
+ _scanCode(scanCode),
+ _modifiers(modifiers) {}
+
+ wchar_t Character() { return _character; };
+ WORD ScanCode() { return _scanCode; };
+ DWORD Modifiers() { return _modifiers; };
+
+ private:
+ const wchar_t _character;
+ const WORD _scanCode;
+ const DWORD _modifiers;
+ };
+
struct TermControl : TermControlT
{
TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection);
@@ -169,6 +212,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
bool ReadOnly() const noexcept;
void ToggleReadOnly();
+ bool TrySendKeyEvent(const WORD vkey, const WORD scanCode, const DWORD modifiers, const bool keyDown);
+ bool TrySendChar(const wchar_t character, const WORD scanCode, const DWORD modifiers);
+
// clang-format off
// -------------------------------- WinRT Events ---------------------------------
DECLARE_EVENT(TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs);
@@ -180,6 +226,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTaskbarProgress, _setTaskbarProgressHandlers, TerminalControl::TermControl, IInspectable);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RaiseNotice, _raiseNoticeHandlers, TerminalControl::TermControl, TerminalControl::NoticeEventArgs);
+ DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(KeySent, _keySentHandlers, TerminalControl::TermControl, TerminalControl::KeySentEventArgs);
+ DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(CharSent, _charSentHandlers, TerminalControl::TermControl, TerminalControl::CharSentEventArgs);
TYPED_EVENT(WarningBell, IInspectable, IInspectable);
TYPED_EVENT(ConnectionStateChanged, TerminalControl::TermControl, IInspectable);
diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl
index 4470c0555d6..e6d31266486 100644
--- a/src/cascadia/TerminalControl/TermControl.idl
+++ b/src/cascadia/TerminalControl/TermControl.idl
@@ -59,6 +59,21 @@ namespace Microsoft.Terminal.TerminalControl
String Message { get; };
}
+ runtimeclass KeySentEventArgs
+ {
+ UInt16 VKey { get; };
+ UInt16 ScanCode { get; };
+ UInt32 Modifiers { get; };
+ Boolean KeyDown { get; };
+ }
+
+ runtimeclass CharSentEventArgs
+ {
+ Char Character { get; };
+ UInt16 ScanCode { get; };
+ UInt32 Modifiers { get; };
+ }
+
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IDirectKeyListener, IMouseWheelListener
{
TermControl(Microsoft.Terminal.TerminalControl.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
@@ -79,6 +94,8 @@ namespace Microsoft.Terminal.TerminalControl
event Windows.Foundation.TypedEventHandler