diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 6d09960c68..e93e898c01 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1758,6 +1758,7 @@ UPKEY upss uregex URegular +urxvt usebackq USECALLBACK USECOLOR diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 2a59887da8..0a5405c727 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2794,6 +2794,11 @@ "description": "When set to true, VT applications will be allowed to set the contents of the local clipboard using OSC 52 (Manipulate Selection Data).", "type": "boolean" }, + "compatibility.allowOSC777": { + "default": false, + "description": "When set to true, applications can send OSC 777 escape sequences to trigger desktop toast notifications with a custom title and body.", + "type": "boolean" + }, "unfocusedAppearance": { "$ref": "#/$defs/AppearanceConfig", "description": "Sets the appearance of the terminal when it is unfocused.", diff --git a/src/cascadia/TerminalApp/DesktopNotification.cpp b/src/cascadia/TerminalApp/DesktopNotification.cpp index d1e8a198c7..b43a13855c 100644 --- a/src/cascadia/TerminalApp/DesktopNotification.cpp +++ b/src/cascadia/TerminalApp/DesktopNotification.cpp @@ -91,7 +91,7 @@ namespace winrt::TerminalApp::implementation // When the user activates (clicks) the toast, fire the callback. if (activatedFunc) { - toast.Activated([activatedFunc](const auto& /*sender*/, const auto& /*eventArgs*/) { + toast.Activated([activatedFunc = std::move(activatedFunc)](const auto& /*sender*/, const auto& /*eventArgs*/) { activatedFunc(); }); } diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 74b6c73693..5bfed438aa 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -1353,7 +1353,11 @@ namespace winrt::TerminalApp::implementation // Use the Tab object's identity hash as a stable toast tag. // This survives tab reordering and cross-window moves. const auto tabHash = std::hash{}(*tab); +#ifdef _WIN64 const hstring tabTag{ fmt::format(FMT_COMPILE(L"wt-tab-{:016x}"), tabHash) }; +#else + const hstring tabTag{ fmt::format(FMT_COMPILE(L"wt-tab-{:08x}"), tabHash) }; +#endif const implementation::DesktopNotificationArgs args{ .Title = notificationTitle, diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 5e02da1eb7..2cd233dcdf 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -42,6 +42,7 @@ namespace winrt::TerminalApp::implementation _controlEvents._SetTaskbarProgress = _control.SetTaskbarProgress(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlSetTaskbarProgress }); _controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlReadOnlyChanged }); _controlEvents._FocusFollowMouseRequested = _control.FocusFollowMouseRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlFocusFollowMouseRequested }); + _controlEvents._ShowNotification = _control.ShowNotification(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlShowNotification }); } void TerminalPaneContent::_removeControlEvents() { @@ -170,6 +171,11 @@ namespace winrt::TerminalApp::implementation FocusRequested.raise(*this, nullptr); } + void TerminalPaneContent::_controlShowNotification(const IInspectable& /*sender*/, const ShowNotificationEventArgs& args) + { + NotificationRequested.raise(*this, winrt::make(args.Title(), args.Body())); + } + // Method Description: // - Called when our attached control is closed. Triggers listeners to our close // event, if we're a leaf pane. diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index f6277d73f7..868e1d3e9d 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -88,6 +88,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl::SetTaskbarProgress_revoker _SetTaskbarProgress; winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged; winrt::Microsoft::Terminal::Control::TermControl::FocusFollowMouseRequested_revoker _FocusFollowMouseRequested; + winrt::Microsoft::Terminal::Control::TermControl::ShowNotification_revoker _ShowNotification; } _controlEvents; void _setupControlEvents(); @@ -103,6 +104,7 @@ namespace winrt::TerminalApp::implementation void _controlSetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); void _controlReadOnlyChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); void _controlFocusFollowMouseRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _controlShowNotification(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Control::ShowNotificationEventArgs& args); void _closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); void _restartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 3eaa4dc346..3ca144bb4d 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -139,6 +139,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnSearchMissingCommand = [this](auto&& PH1, auto&& PH2) { _terminalSearchMissingCommand(std::forward(PH1), std::forward(PH2)); }; _terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand); + auto pfnShowNotification = [this](auto&& PH1, auto&& PH2) { _terminalShowNotification(std::forward(PH1), std::forward(PH2)); }; + _terminal->SetShowNotificationCallback(pfnShowNotification); + auto pfnClearQuickFix = [this] { ClearQuickFix(); }; _terminal->SetClearQuickFixCallback(pfnClearQuickFix); @@ -1692,6 +1695,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation SearchMissingCommand.raise(*this, make(hstring{ missingCommand }, bufferRow)); } + void ControlCore::_terminalShowNotification(std::wstring_view title, std::wstring_view body) + { + ShowNotification.raise(*this, make(hstring{ title }, hstring{ body })); + } + void ControlCore::OpenCWD() { const auto workingDirectory = WorkingDirectory(); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index d74473001a..1a03da9dbe 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -292,6 +292,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event OpenHyperlink; til::typed_event CompletionsChanged; til::typed_event SearchMissingCommand; + til::typed_event ShowNotification; til::typed_event<> RefreshQuickFixUI; til::typed_event WindowSizeChanged; @@ -333,6 +334,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const int velocity, const std::chrono::microseconds duration); void _terminalSearchMissingCommand(std::wstring_view missingCommand, const til::CoordType& bufferRow); + void _terminalShowNotification(std::wstring_view title, std::wstring_view body); void _terminalWindowSizeChanged(int32_t width, int32_t height); void _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 31bc24d593..d0ecdb7895 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -198,6 +198,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler RendererEnteredErrorState; event Windows.Foundation.TypedEventHandler ShowWindowChanged; event Windows.Foundation.TypedEventHandler SearchMissingCommand; + event Windows.Foundation.TypedEventHandler ShowNotification; event Windows.Foundation.TypedEventHandler RefreshQuickFixUI; event Windows.Foundation.TypedEventHandler WindowSizeChanged; diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 53f1245ef0..6dec6d734a 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -20,6 +20,7 @@ #include "CharSentEventArgs.g.h" #include "StringSentEventArgs.g.h" #include "SearchMissingCommandEventArgs.g.h" +#include "ShowNotificationEventArgs.g.h" #include "WindowSizeChangedEventArgs.g.h" namespace winrt::Microsoft::Terminal::Control::implementation @@ -252,6 +253,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::property BufferRow; }; + struct ShowNotificationEventArgs : public ShowNotificationEventArgsT + { + public: + ShowNotificationEventArgs(const winrt::hstring& title, const winrt::hstring& body) : + Title(title), + Body(body) {} + + til::property Title; + til::property Body; + }; + struct WindowSizeChangedEventArgs : public WindowSizeChangedEventArgsT { public: diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 5f34e84264..38dbebda6d 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -160,6 +160,12 @@ namespace Microsoft.Terminal.Control Int32 BufferRow { get; }; } + runtimeclass ShowNotificationEventArgs + { + String Title { get; }; + String Body { get; }; + } + runtimeclass WindowSizeChangedEventArgs { Int32 Width; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index a616bec79a..f549dc6258 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -328,6 +328,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged }); _revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested }); _revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand }); + _revokers.ShowNotification = _core.ShowNotification(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleShowNotification }); _revokers.WindowSizeChanged = _core.WindowSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWindowSizeChanged }); _revokers.WriteToClipboard = _core.WriteToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWriteToClipboard }); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 767596e95a..4011c76fc2 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -230,6 +230,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable); BUBBLED_FORWARDED_TYPED_EVENT(WriteToClipboard, IInspectable, Control::WriteToClipboardEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(ShowNotification, IInspectable, Control::ShowNotificationEventArgs); // clang-format on @@ -468,6 +469,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::CompletionsChanged_revoker CompletionsChanged; Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested; Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand; + Control::ControlCore::ShowNotification_revoker ShowNotification; Control::ControlCore::RefreshQuickFixUI_revoker RefreshQuickFixUI; Control::ControlCore::WindowSizeChanged_revoker WindowSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 54ded1a0fd..faf16be6c8 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -82,6 +82,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler CharSent; event Windows.Foundation.TypedEventHandler StringSent; event Windows.Foundation.TypedEventHandler SearchMissingCommand; + event Windows.Foundation.TypedEventHandler ShowNotification; Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; }; diff --git a/src/cascadia/TerminalCore/ICoreSettings.idl b/src/cascadia/TerminalCore/ICoreSettings.idl index 0f88e0c186..c2d074556f 100644 --- a/src/cascadia/TerminalCore/ICoreSettings.idl +++ b/src/cascadia/TerminalCore/ICoreSettings.idl @@ -124,6 +124,7 @@ namespace Microsoft.Terminal.Core Boolean AllowKittyKeyboardMode { get; }; Boolean AllowVtChecksumReport { get; }; Boolean AllowVtClipboardWrite { get; }; + Boolean AllowOscNotifications { get; }; Boolean TrimBlockSelection { get; }; Boolean DetectURLs { get; }; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 203f158531..0746066b75 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -267,6 +267,7 @@ void Terminal::SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettin auto features = til::enumset{}; features.set(ITermDispatch::OptionalFeature::ChecksumReport, settings.AllowVtChecksumReport()); features.set(ITermDispatch::OptionalFeature::ClipboardWrite, settings.AllowVtClipboardWrite()); + features.set(ITermDispatch::OptionalFeature::DesktopNotification, settings.AllowOscNotifications()); engine.Dispatch().SetOptionalFeatures(features); } @@ -1276,6 +1277,11 @@ void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::f _pfnSearchMissingCommand.swap(pfn); } +void Microsoft::Terminal::Core::Terminal::SetShowNotificationCallback(std::function pfn) noexcept +{ + _pfnShowNotification.swap(pfn); +} + void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function pfn) noexcept { _pfnClearQuickFix.swap(pfn); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index f3330544fc..8f4ca627a2 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -165,6 +165,8 @@ public: void SearchMissingCommand(const std::wstring_view command) override; + void ShowNotification(const std::wstring_view title, const std::wstring_view body) override; + #pragma endregion void ClearMark(); @@ -233,6 +235,7 @@ public: void SetPlayMidiNoteCallback(std::function pfn) noexcept; void CompletionsChangedCallback(std::function pfn) noexcept; void SetSearchMissingCommandCallback(std::function pfn) noexcept; + void SetShowNotificationCallback(std::function pfn) noexcept; void SetClearQuickFixCallback(std::function pfn) noexcept; void SetWindowSizeChangedCallback(std::function pfn) noexcept; void SetSearchHighlights(const std::vector& highlights) noexcept; @@ -341,6 +344,7 @@ private: std::function _pfnPlayMidiNote; std::function _pfnCompletionsChanged; std::function _pfnSearchMissingCommand; + std::function _pfnShowNotification; std::function _pfnClearQuickFix; std::function _pfnWindowSizeChanged; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 3030e3b8ba..2eb8b505ec 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -380,6 +380,14 @@ void Terminal::SearchMissingCommand(const std::wstring_view command) } } +void Terminal::ShowNotification(const std::wstring_view title, const std::wstring_view body) +{ + if (_pfnShowNotification) + { + _pfnShowNotification(title, body); + } +} + void Terminal::NotifyBufferRotation(const int delta) { // Update our selection, so it doesn't move as the buffer is cycled diff --git a/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp b/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp index e9106f6878..2b2fad9322 100644 --- a/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp @@ -352,6 +352,7 @@ namespace winrt::Microsoft::Terminal::Settings _AllowKittyKeyboardMode = profile.AllowKittyKeyboardMode(); _AllowVtChecksumReport = profile.AllowVtChecksumReport(); _AllowVtClipboardWrite = profile.AllowVtClipboardWrite(); + _AllowOscNotifications = profile.AllowOscNotifications(); _PathTranslationStyle = profile.PathTranslationStyle(); _DragDropDelimiter = profile.DragDropDelimiter(); } diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index fa403952e5..16d4d720f4 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -143,6 +143,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_profile, AllowKittyKeyboardMode); OBSERVABLE_PROJECTED_SETTING(_profile, AllowVtChecksumReport); OBSERVABLE_PROJECTED_SETTING(_profile, AllowVtClipboardWrite); + OBSERVABLE_PROJECTED_SETTING(_profile, AllowOscNotifications); OBSERVABLE_PROJECTED_SETTING(_profile, AnswerbackMessage); OBSERVABLE_PROJECTED_SETTING(_profile, RainbowSuggestions); OBSERVABLE_PROJECTED_SETTING(_profile, PathTranslationStyle); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index c2d99246d0..2d3c5c5d7b 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -142,5 +142,6 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle); OBSERVABLE_PROJECTED_PROFILE_SETTING(String, DragDropDelimiter); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowOscNotifications); } } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml index b8701d7132..e224ff88be 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Terminal.xaml @@ -81,6 +81,16 @@ Style="{StaticResource ToggleSwitchInExpanderStyle}" /> + + + + + Allow OSC 52 (Manipulate Selection Data) to write to the clipboard {Locked="OSC 52"}{Locked="Manipulate Selection Data"}Header for a control to toggle support for applications to change the contents of the Windows system clipboard. + + Allow OSC 777 (Desktop Notification) to show toast notifications + {Locked="OSC 777"}{Locked="Desktop Notification"}Header for a control to toggle support for applications to display desktop toast notifications via the OSC 777 escape sequence. + + + When enabled, applications can send the OSC 777 escape sequence to trigger a desktop notification with a custom title and body. + {Locked="OSC 777"}Help text for the OSC 777 desktop notification toggle. + Allow Windows Terminal to run in the background Header for a control to toggle support for Windows Terminal to run in the background. diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index e97e6774bf..08095670c9 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -108,6 +108,7 @@ Author(s): X(bool, AllowKittyKeyboardMode, "compatibility.kittyKeyboardMode", true) \ X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \ X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \ + X(bool, AllowOscNotifications, "compatibility.allowOSC777", false) \ X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \ X(hstring, DragDropDelimiter, "dragDropDelimiter", L" ") \ X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None) diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 874b37235a..3702003acd 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -93,6 +93,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(Boolean, AllowVtChecksumReport); INHERITABLE_PROFILE_SETTING(Boolean, AllowKeypadMode); INHERITABLE_PROFILE_SETTING(Boolean, AllowVtClipboardWrite); + INHERITABLE_PROFILE_SETTING(Boolean, AllowOscNotifications); INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle); INHERITABLE_PROFILE_SETTING(String, DragDropDelimiter); diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index cb4750d5e6..e54cb14a24 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -56,7 +56,8 @@ X(bool, RepositionCursorWithMouse, false) \ X(bool, RainbowSuggestions) \ X(bool, AllowVtChecksumReport) \ - X(bool, AllowVtClipboardWrite, true) + X(bool, AllowVtClipboardWrite, true) \ + X(bool, AllowOscNotifications, false) // --------------------------- Control Settings --------------------------- // All of these settings are defined in IControlSettings. diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index df07c620d6..7d0940d12f 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -459,3 +459,7 @@ void ConhostInternalGetSet::SearchMissingCommand(std::wstring_view /*missingComm { // Not implemented for conhost. } +void ConhostInternalGetSet::ShowNotification(std::wstring_view /*title*/, std::wstring_view /*body*/) +{ + // Not implemented for conhost. +} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 2591e43048..b872fcfa9b 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -74,6 +74,8 @@ public: void SearchMissingCommand(std::wstring_view missingCommand) override; + void ShowNotification(std::wstring_view title, std::wstring_view body) override; + private: Microsoft::Console::IIoProvider& _io; }; diff --git a/src/inc/til/string.h b/src/inc/til/string.h index 1afb7b9ad6..616f33bc0b 100644 --- a/src/inc/til/string.h +++ b/src/inc/til/string.h @@ -336,6 +336,18 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" return sentinel{}; } + typename iterator::value_type next() noexcept + { + const auto part = value(); + advance(); + return part; + } + + typename iterator::value_type remaining() noexcept + { + return { _it, _end }; + } + private: bool valid() const noexcept { diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 2a06541547..0e400ee6b3 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -29,6 +29,7 @@ public: { ChecksumReport, ClipboardWrite, + DesktopNotification, }; #pragma warning(push) @@ -162,6 +163,8 @@ public: virtual void DoVsCodeAction(const std::wstring_view string) = 0; virtual void DoWTAction(const std::wstring_view string) = 0; + virtual void DoUrxvtAction(const std::wstring_view string) = 0; + virtual StringHandler DefineSixelImage(const VTInt macroParameter, const DispatchTypes::SixelBackground backgroundSelect, const VTParameter backgroundColor) = 0; // SIXEL diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index cb67bd1f60..368111f44d 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -92,5 +92,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; virtual void SearchMissingCommand(const std::wstring_view command) = 0; + + virtual void ShowNotification(const std::wstring_view title, const std::wstring_view body) = 0; }; } diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 633156c51d..815a9a79a9 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3872,6 +3872,41 @@ void AdaptDispatch::DoWTAction(const std::wstring_view string) } } +// Method Description: +// - OSC 777 - Handles urxvt requests. Currently, the only supported request is for desktop notifications. +// The format is: OSC 777;notify;title;body ST +// Arguments: +// - string: contains the parameters that define the notification +void AdaptDispatch::DoUrxvtAction(const std::wstring_view string) +{ + if (!_optionalFeatures.test(OptionalFeature::DesktopNotification)) + { + return; + } + + const auto parts = Utils::SplitString(string, L';'); + + if (parts.size() < 1) + { + return; + } + + til::split_iterator split{ string, L';' }; + const auto action = split.next(); + if (action == L"notify") + { + // The body is everything after "notify;title;". We can't just use + // parts[2] because the body itself may contain semicolons. + const auto title = split.next(); + const auto body = split.remaining(); + _api.ShowNotification(title, body); + } + else + { + _api.UnknownSequence(); + } +} + // Method Description: // - SIXEL - Defines an image transmitted in sixel format via the returned // StringHandler function. diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 15e94867bf..1c1c714ee0 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -163,6 +163,8 @@ namespace Microsoft::Console::VirtualTerminal void DoWTAction(const std::wstring_view string) override; + void DoUrxvtAction(const std::wstring_view string) override; + StringHandler DefineSixelImage(const VTInt macroParameter, const DispatchTypes::SixelBackground backgroundSelect, const VTParameter backgroundColor) override; // SIXEL diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index 462740a02b..598aad0585 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -153,6 +153,8 @@ public: void DoWTAction(const std::wstring_view /*string*/) override {} + void DoUrxvtAction(const std::wstring_view /*string*/) override {} + StringHandler DefineSixelImage(const VTInt /*macroParameter*/, const DispatchTypes::SixelBackground /*backgroundSelect*/, const VTParameter /*backgroundColor*/) override { return nullptr; }; // SIXEL diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 596c9ed1a2..dcbe580b24 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -233,6 +233,11 @@ public: Log::Comment(L"SearchMissingCommand MOCK called..."); } + void ShowNotification(const std::wstring_view /*title*/, const std::wstring_view /*body*/) override + { + Log::Comment(L"ShowNotification MOCK called..."); + } + void PrepData() { PrepData(CursorDirection::UP); // if called like this, the cursor direction doesn't matter. diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 5ba404adb4..5673b716f6 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -909,6 +909,9 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s _dispatch->DoWTAction(string); break; } + case OscActionCodes::UrxvtAction: + _dispatch->DoUrxvtAction(string); + break; default: _dispatch->UnknownSequence(); break; diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index be6d054c5a..517bd2d80e 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -227,6 +227,7 @@ namespace Microsoft::Console::VirtualTerminal ResetHighlightColor = 117, FinalTermAction = 133, VsCodeAction = 633, + UrxvtAction = 777, ITerm2Action = 1337, WTAction = 9001, };