From e8d1933b9eb837d8a7a04bfcf4074c04b651d268 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 2 Mar 2026 05:53:01 -0600 Subject: [PATCH 01/11] jesus what --- doc/cascadia/profiles.schema.json | 53 ++++++ src/cascadia/TerminalApp/BasicPaneEvents.h | 1 + .../TerminalApp/DesktopNotification.cpp | 99 +++++++++++ .../TerminalApp/DesktopNotification.h | 52 ++++++ src/cascadia/TerminalApp/IPaneContent.idl | 6 + .../Resources/en-US/Resources.resw | 12 ++ src/cascadia/TerminalApp/Tab.cpp | 90 ++++++++++ src/cascadia/TerminalApp/Tab.h | 8 + .../TerminalApp/TabHeaderControl.xaml | 6 + src/cascadia/TerminalApp/TabManagement.cpp | 60 +++++++ .../TerminalApp/TerminalAppLib.vcxproj | 2 + .../TerminalAppLib.vcxproj.filters | 2 + src/cascadia/TerminalApp/TerminalPage.h | 2 + .../TerminalApp/TerminalPaneContent.cpp | 117 +++++++++++++ .../TerminalApp/TerminalPaneContent.h | 24 ++- src/cascadia/TerminalApp/TerminalTabStatus.h | 1 + .../TerminalApp/TerminalTabStatus.idl | 1 + src/cascadia/TerminalApp/pch.h | 3 + src/cascadia/TerminalControl/ControlCore.cpp | 22 +++ src/cascadia/TerminalControl/ControlCore.h | 4 + src/cascadia/TerminalControl/ControlCore.idl | 2 + .../TerminalControl/IControlSettings.idl | 21 +++ src/cascadia/TerminalControl/TermControl.cpp | 13 ++ src/cascadia/TerminalControl/TermControl.h | 7 + src/cascadia/TerminalControl/TermControl.idl | 3 + src/cascadia/TerminalCore/Terminal.cpp | 10 ++ src/cascadia/TerminalCore/Terminal.hpp | 6 +- src/cascadia/TerminalCore/TerminalApi.cpp | 20 ++- .../TerminalSettings.cpp | 4 + .../ProfileViewModel.cpp | 165 ++++++++++++++++++ .../TerminalSettingsEditor/ProfileViewModel.h | 21 +++ .../ProfileViewModel.idl | 20 +++ .../Profiles_Advanced.xaml | 53 ++++++ .../Resources/en-US/Resources.resw | 88 ++++++++++ .../TerminalSettingsModel/EnumMappings.cpp | 1 + .../TerminalSettingsModel/EnumMappings.h | 1 + .../TerminalSettingsModel/EnumMappings.idl | 1 + .../TerminalSettingsModel/MTSMSettings.h | 5 +- .../TerminalSettingsModel/Profile.idl | 4 + .../TerminalSettingsSerializationHelpers.h | 40 +++++ src/cascadia/inc/ControlProperties.h | 5 +- src/host/outputStream.cpp | 2 +- src/host/outputStream.hpp | 2 +- src/terminal/adapter/ITerminalApi.hpp | 11 +- src/terminal/adapter/adaptDispatch.cpp | 12 +- .../adapter/ut_adapter/adapterTest.cpp | 2 +- 46 files changed, 1068 insertions(+), 16 deletions(-) create mode 100644 src/cascadia/TerminalApp/DesktopNotification.cpp create mode 100644 src/cascadia/TerminalApp/DesktopNotification.h diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index b8e6c9ab401..45385fa4e67 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -76,6 +76,36 @@ } ] }, + "OutputNotificationStyle": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "taskbar", + "audible", + "tab", + "notification" + ] + } + }, + { + "type": "string", + "enum": [ + "taskbar", + "audible", + "tab", + "notification", + "all", + "none" + ] + } + ] + }, "BellSound": { "default": "", "description": "Sets the file location of the sound played when the application emits a BEL character. If the path is invalid no sound will be played. This property also accepts an array of sounds and the terminal will pick one at random.", @@ -94,6 +124,14 @@ } ] }, + "AutoDetectRunningCommand": { + "type": "string", + "enum": [ + "disabled", + "automatic", + "progress" + ] + }, "BuiltinSuggestionSource": { "type": "string", "anyOf": [ @@ -3174,6 +3212,21 @@ "mingw" ], "type": "string" + }, + "notifyOnInactiveOutput": { + "default": "none", + "description": "Controls how the terminal notifies you when a background pane produces output. Supported values include `taskbar`, `audible`, `tab`, and `notification`. Can be set to true (equivalent to `tab`), false/none (disabled), a single string, or an array of strings.", + "$ref": "#/$defs/OutputNotificationStyle" + }, + "notifyOnNextPrompt": { + "default": "none", + "description": "Controls how the terminal notifies you when a running command finishes and the shell returns to a prompt. Requires shell integration. Supported values include `taskbar`, `audible`, `tab`, and `notification`. Can be set to true (equivalent to `tab`), false/none (disabled), a single string, or an array of strings.", + "$ref": "#/$defs/OutputNotificationStyle" + }, + "autoDetectRunningCommand": { + "default": "disabled", + "description": "Controls automatic detection of running commands via shell integration marks. When set to `automatic`, an indeterminate progress indicator is shown on the tab while a command is executing. When set to `progress`, the terminal will also attempt to detect progress percentages from command output. Requires shell integration.", + "$ref": "#/$defs/AutoDetectRunningCommand" } } }, diff --git a/src/cascadia/TerminalApp/BasicPaneEvents.h b/src/cascadia/TerminalApp/BasicPaneEvents.h index e82ff0f49b4..8d4f8ab8102 100644 --- a/src/cascadia/TerminalApp/BasicPaneEvents.h +++ b/src/cascadia/TerminalApp/BasicPaneEvents.h @@ -10,6 +10,7 @@ namespace winrt::TerminalApp::implementation til::typed_event<> ConnectionStateChanged; til::typed_event CloseRequested; til::typed_event BellRequested; + til::typed_event NotificationRequested; til::typed_event TitleChanged; til::typed_event TabColorChanged; til::typed_event TaskbarProgressChanged; diff --git a/src/cascadia/TerminalApp/DesktopNotification.cpp b/src/cascadia/TerminalApp/DesktopNotification.cpp new file mode 100644 index 00000000000..1dcd6d2a2d0 --- /dev/null +++ b/src/cascadia/TerminalApp/DesktopNotification.cpp @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "DesktopNotification.h" + +using namespace winrt::Windows::UI::Notifications; +using namespace winrt::Windows::Data::Xml::Dom; + +namespace winrt::TerminalApp::implementation +{ + std::atomic DesktopNotification::_lastNotificationTime{ 0 }; + + bool DesktopNotification::ShouldSendNotification() + { + FILETIME ft{}; + GetSystemTimeAsFileTime(&ft); + const auto now = (static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + auto last = _lastNotificationTime.load(std::memory_order_relaxed); + + if (now - last < MinNotificationIntervalTicks) + { + return false; + } + + // Attempt to update; if another thread beat us, that's fine — we'll skip this one. + _lastNotificationTime.compare_exchange_strong(last, now, std::memory_order_relaxed); + return true; + } + + void DesktopNotification::SendNotification( + const DesktopNotificationArgs& args, + std::function activated) + { + try + { + if (!ShouldSendNotification()) + { + return; + } + + // Build the toast XML. We use a simple template with a title and body text. + // + // + // + // + // Title + // Message + // + // + // + auto toastXml = ToastNotificationManager::GetTemplateContent(ToastTemplateType::ToastText02); + auto textNodes = toastXml.GetElementsByTagName(L"text"); + + // First is the title + textNodes.Item(0).InnerText(args.Title); + // Second is the body + textNodes.Item(1).InnerText(args.Message); + + // Set launch args so we can identify which tab to activate. + auto toastElement = toastXml.DocumentElement(); + toastElement.SetAttribute(L"launch", fmt::format(FMT_COMPILE(L"tabIndex={}"), args.TabIndex)); + + // Set the scenario to "reminder" to ensure the toast shows even in DND, + // and the group/tag to allow replacement of repeated notifications. + toastElement.SetAttribute(L"scenario", L"default"); + + auto toast = ToastNotification{ toastXml }; + + // Set the tag and group to enable notification replacement. + // Using the tab index as a tag means repeated output from the same tab + // replaces the previous notification rather than stacking. + toast.Tag(fmt::format(FMT_COMPILE(L"wt-tab-{}"), args.TabIndex)); + toast.Group(L"WindowsTerminal"); + + // When the user activates (clicks) the toast, fire the callback. + if (activated) + { + const auto tabIndex = args.TabIndex; + toast.Activated([activated, tabIndex](const auto& /*sender*/, const auto& /*eventArgs*/) { + activated(tabIndex); + }); + } + + // For packaged apps, CreateToastNotifier() uses the package identity automatically. + // For unpackaged apps, we need to provide an AUMID, but that case is less common + // and toast notifications may not be supported without additional setup. + auto notifier = ToastNotificationManager::CreateToastNotifier(); + notifier.Show(toast); + } + catch (...) + { + // Toast notification is a best-effort feature. If it fails (e.g., notifications + // are disabled, or the app is unpackaged without proper AUMID setup), we silently + // ignore the error. + LOG_CAUGHT_EXCEPTION(); + } + } +} diff --git a/src/cascadia/TerminalApp/DesktopNotification.h b/src/cascadia/TerminalApp/DesktopNotification.h new file mode 100644 index 00000000000..55cb228ba6c --- /dev/null +++ b/src/cascadia/TerminalApp/DesktopNotification.h @@ -0,0 +1,52 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- DesktopNotification.h + +Module Description: +- Helper for sending Windows desktop toast notifications. Used by the + `OutputNotificationStyle::Notification` flag to surface activity + and prompt-return events to the user via the Windows notification center. + +--*/ + +#pragma once +#include "pch.h" + +namespace winrt::TerminalApp::implementation +{ + struct DesktopNotificationArgs + { + winrt::hstring Title; + winrt::hstring Message; + uint32_t TabIndex{ 0 }; + }; + + class DesktopNotification + { + public: + // Sends a toast notification with the given title and message. + // When the user clicks the toast, the `Activated` callback fires + // with the tabIndex that was passed in, so the caller can switch + // to the correct tab and summon the window. + // + // activated: A callback invoked on the background thread when the + // toast is clicked. The uint32_t parameter is the tab index. + static void SendNotification( + const DesktopNotificationArgs& args, + std::function activated); + + // Rate-limits toast notifications so we don't spam the user. + // Returns true if a notification is allowed, false if too recent. + static bool ShouldSendNotification(); + + private: + static std::atomic _lastNotificationTime; + + // Minimum interval between notifications, in 100ns ticks (FILETIME units). + // 5 seconds = 5 * 10,000,000 + static constexpr int64_t MinNotificationIntervalTicks = 50'000'000LL; + }; +} diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index f4c6ce13957..679205c8462 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -16,6 +16,11 @@ namespace TerminalApp Boolean FlashTaskbar { get; }; }; + runtimeclass NotificationEventArgs + { + Microsoft.Terminal.Control.OutputNotificationStyle Style { get; }; + }; + interface IPaneContent { Windows.UI.Xaml.FrameworkElement GetRoot(); @@ -41,6 +46,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler ConnectionStateChanged; event Windows.Foundation.TypedEventHandler BellRequested; + event Windows.Foundation.TypedEventHandler NotificationRequested; event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler TabColorChanged; event Windows.Foundation.TypedEventHandler TaskbarProgressChanged; diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 81ca7d51f83..3458d2dc350 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -923,4 +923,16 @@ An invalid regular expression was found. + + Windows Terminal + Title shown in desktop toast notifications for tab activity. + + + Tab "{0}" has new activity + {Locked="{0}"}Message shown in a desktop toast notification when a tab produces output. {0} is the tab title. + + + Tab "{0}" in {1} has new activity + {Locked="{0}"}{Locked="{1}"}Message shown in a desktop toast notification when a tab produces output. {0} is the tab title. {1} is the window name. + diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 4bbf58e50ad..ecd023789cf 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -123,6 +123,15 @@ namespace winrt::TerminalApp::implementation _bellIndicatorTimer.Stop(); } + // Method Description: + // - Called when the timer for the activity indicator in the tab header fires + // - Removes the activity indicator from the tab header + void Tab::_ActivityIndicatorTimerTick(const Windows::Foundation::IInspectable& /*sender*/, const Windows::Foundation::IInspectable& /*e*/) + { + ShowActivityIndicator(false); + _activityIndicatorTimer.Stop(); + } + // Method Description: // - Initializes a TabViewItem for this Tab instance. // Arguments: @@ -329,6 +338,11 @@ namespace winrt::TerminalApp::implementation { ShowBellIndicator(false); } + // When we gain focus, remove the activity indicator if it is active + if (_tabStatus.ActivityIndicator()) + { + ShowActivityIndicator(false); + } } } @@ -459,6 +473,29 @@ namespace winrt::TerminalApp::implementation _bellIndicatorTimer.Start(); } + void Tab::ShowActivityIndicator(const bool show) + { + ASSERT_UI_THREAD(); + + _tabStatus.ActivityIndicator(show); + } + + // Method Description: + // - Activates the timer for the activity indicator in the tab + // - Called if a notification was raised when the tab already has focus + void Tab::ActivateActivityIndicatorTimer() + { + ASSERT_UI_THREAD(); + + if (!_activityIndicatorTimer) + { + _activityIndicatorTimer.Interval(std::chrono::milliseconds(2000)); + _activityIndicatorTimer.Tick({ get_weak(), &Tab::_ActivityIndicatorTimerTick }); + } + + _activityIndicatorTimer.Start(); + } + // Method Description: // - Gets the title string of the last focused terminal control in our tree. // Returns the empty string if there is no such control. @@ -1161,6 +1198,54 @@ namespace winrt::TerminalApp::implementation } }); + events.NotificationRequested = content.NotificationRequested( + winrt::auto_revoke, + [dispatcher, weakThis](TerminalApp::IPaneContent sender, auto notifArgs) -> safe_void_coroutine { + const auto weakThisCopy = weakThis; + co_await wil::resume_foreground(dispatcher); + if (const auto tab{ weakThisCopy.get() }) + { + // For NotifyOnInactiveOutput, only show notifications if this sender + // is NOT the currently active pane content. For NotifyOnNextPrompt, + // the notification is always relevant. + const auto activeContent = tab->GetActiveContent(); + const auto isActivePaneContent = activeContent && activeContent == sender; + + const auto style = notifArgs.Style(); + + if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Taskbar)) + { + // Flash the taskbar button + tab->TabRaiseVisualBell.raise(); + } + + // Audible notification is handled in TerminalPaneContent already + + if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Tab)) + { + // Show the activity indicator in the tab header (distinct from bell) + // Only for inactive pane output, skip if this is the focused pane in a focused tab + if (!isActivePaneContent || tab->_focusState == WUX::FocusState::Unfocused) + { + tab->ShowActivityIndicator(true); + + if (tab->_focusState != WUX::FocusState::Unfocused) + { + tab->ActivateActivityIndicatorTimer(); + } + } + } + + if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Notification)) + { + // Request a desktop toast notification. + // TerminalPage subscribes to this event and handles sending the toast + // and processing its activation (summoning the window + switching tabs). + tab->TabToastNotificationRequested.raise(tab->Title(), tab->TabViewIndex()); + } + } + }); + if (const auto& terminal{ content.try_as() }) { events.RestartTerminalRequested = terminal.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &Tab::_bubbleRestartTerminalRequested }); @@ -1393,6 +1478,11 @@ namespace winrt::TerminalApp::implementation { tab->ShowBellIndicator(false); } + // Also remove the activity indicator + if (tab->_tabStatus.ActivityIndicator()) + { + tab->ShowActivityIndicator(false); + } } }); diff --git a/src/cascadia/TerminalApp/Tab.h b/src/cascadia/TerminalApp/Tab.h index 65d4e574ede..b4e5d22875e 100644 --- a/src/cascadia/TerminalApp/Tab.h +++ b/src/cascadia/TerminalApp/Tab.h @@ -48,6 +48,9 @@ namespace winrt::TerminalApp::implementation void ShowBellIndicator(const bool show); void ActivateBellIndicatorTimer(); + void ShowActivityIndicator(const bool show); + void ActivateActivityIndicatorTimer(); + float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; std::optional PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType, const float splitSize, @@ -121,6 +124,7 @@ namespace winrt::TerminalApp::implementation til::typed_event ActivePaneChanged; til::event> TabRaiseVisualBell; + til::event> TabToastNotificationRequested; til::typed_event TaskbarProgressChanged; // The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector. @@ -176,6 +180,7 @@ namespace winrt::TerminalApp::implementation struct ContentEventTokens { winrt::TerminalApp::IPaneContent::BellRequested_revoker BellRequested; + winrt::TerminalApp::IPaneContent::NotificationRequested_revoker NotificationRequested; winrt::TerminalApp::IPaneContent::TitleChanged_revoker TitleChanged; winrt::TerminalApp::IPaneContent::TabColorChanged_revoker TabColorChanged; winrt::TerminalApp::IPaneContent::TaskbarProgressChanged_revoker TaskbarProgressChanged; @@ -210,6 +215,9 @@ namespace winrt::TerminalApp::implementation SafeDispatcherTimer _bellIndicatorTimer; void _BellIndicatorTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e); + SafeDispatcherTimer _activityIndicatorTimer; + void _ActivityIndicatorTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e); + void _UpdateHeaderControlMaxWidth(); void _CreateContextMenu(); diff --git a/src/cascadia/TerminalApp/TabHeaderControl.xaml b/src/cascadia/TerminalApp/TabHeaderControl.xaml index d44d7a0c7d9..83fc04f7b86 100644 --- a/src/cascadia/TerminalApp/TabHeaderControl.xaml +++ b/src/cascadia/TerminalApp/TabHeaderControl.xaml @@ -32,6 +32,12 @@ FontSize="12" Glyph="" Visibility="{x:Bind TabStatus.BellIndicator, Mode=OneWay}" /> + @@ -150,6 +151,15 @@ namespace winrt::TerminalApp::implementation } }); + // When a tab requests a desktop toast notification (OutputNotificationStyle::Notification), + // send the toast and handle activation by summoning this window and switching to the tab. + newTabImpl->TabToastNotificationRequested([weakThis{ get_weak() }](const winrt::hstring& title, uint32_t tabIndex) { + if (const auto page{ weakThis.get() }) + { + page->_SendDesktopNotification(title, tabIndex); + } + }); + auto tabViewItem = newTabImpl->TabViewItem(); _tabView.TabItems().InsertAt(insertPosition, tabViewItem); @@ -1185,4 +1195,54 @@ namespace winrt::TerminalApp::implementation { return _tabs.Size() > 1; } + + // Method Description: + // - Sends a Windows desktop toast notification for a tab. When the user clicks + // the toast, summon this window and switch to the specified tab. + // Arguments: + // - tabTitle: The title of the tab to display in the notification. + // - tabIndex: The index of the tab to switch to when the toast is activated. + void TerminalPage::_SendDesktopNotification(const winrt::hstring& tabTitle, uint32_t tabIndex) + { + // Build the notification message. + // Use the window name if available for context, otherwise just use the tab title. + const auto windowName = _WindowProperties ? _WindowProperties.WindowNameForDisplay() : winrt::hstring{}; + winrt::hstring message; + if (!windowName.empty()) + { + message = RS_fmt(L"NotificationMessage_TabActivityInWindow", std::wstring_view{ tabTitle }, std::wstring_view{ windowName }); + } + else + { + message = RS_fmt(L"NotificationMessage_TabActivity", std::wstring_view{ tabTitle }); + } + + implementation::DesktopNotificationArgs args; + args.Title = RS_(L"NotificationTitle"); + args.Message = message; + args.TabIndex = tabIndex; + + // Capture a weak ref and the dispatcher so we can marshal back to the UI thread + // when the toast is activated. + auto weakThis = get_weak(); + auto dispatcher = Dispatcher(); + + implementation::DesktopNotification::SendNotification( + args, + [weakThis, dispatcher, tabIndex](uint32_t /*activatedTabIndex*/) -> void { + // The toast Activated callback fires on a background thread. + // We need to dispatch to the UI thread to summon the window and switch tabs. + [](auto weakThis, auto dispatcher, auto tabIndex) -> safe_void_coroutine { + co_await wil::resume_foreground(dispatcher); + if (const auto page{ weakThis.get() }) + { + // Summon this window (bring to foreground) + page->SummonWindowRequested.raise(nullptr, nullptr); + + // Switch to the tab that triggered the notification + page->_SelectTab(tabIndex); + } + }(weakThis, dispatcher, tabIndex); + }); + } } diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index fe141dfa64f..b929f5e9b64 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -173,6 +173,7 @@ TerminalPaneContent.idl + @@ -286,6 +287,7 @@ TerminalPaneContent.idl + diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters index 661065d9ac3..37581283584 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters @@ -30,6 +30,7 @@ fzf + @@ -58,6 +59,7 @@ fzf + diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 4b48cc0e9d9..6c12e4835e6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -570,6 +570,8 @@ namespace winrt::TerminalApp::implementation void _activePaneChanged(winrt::TerminalApp::Tab tab, Windows::Foundation::IInspectable args); safe_void_coroutine _doHandleSuggestions(Microsoft::Terminal::Settings::Model::SuggestionsArgs realArgs); + void _SendDesktopNotification(const winrt::hstring& tabTitle, uint32_t tabIndex); + #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp #define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action); diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index b7000d9c2d9..48df513fc29 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -10,6 +10,7 @@ #include "../../types/inc/utils.hpp" #include "BellEventArgs.g.cpp" +#include "NotificationEventArgs.g.cpp" #include "TerminalPaneContent.g.cpp" using namespace winrt::Windows::Foundation; @@ -34,8 +35,11 @@ namespace winrt::TerminalApp::implementation { _controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &TerminalPaneContent::_controlConnectionStateChangedHandler }); _controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlWarningBellHandler }); + _controlEvents._PromptReturned = _control.PromptReturned(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlPromptReturnedHandler }); + _controlEvents._CommandStarted = _control.CommandStarted(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlCommandStartedHandler }); _controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_closeTerminalRequestedHandler }); _controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_restartTerminalRequestedHandler }); + _controlEvents._OutputIdle = _control.OutputIdle(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlOutputIdleHandler }); _controlEvents._TitleChanged = _control.TitleChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTitleChanged }); _controlEvents._TabColorChanged = _control.TabColorChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTabColorChanged }); @@ -298,6 +302,119 @@ namespace winrt::TerminalApp::implementation } } + // Method Description: + // - Returns the taskbar state, accounting for auto-detected running command progress. + // VT-set progress (OSC 9;4) takes precedence over auto-detected progress. + uint64_t TerminalPaneContent::TaskbarState() + { + const auto vtState = _control.TaskbarState(); + // VT-set progress takes precedence over auto-detected progress + if (vtState != 0) + { + return vtState; + } + // Auto-detected indeterminate progress (between command start and prompt return) + if (_autoDetectActive) + { + return 3; // TaskbarState::Indeterminate + } + return 0; + } + + // Method Description: + // - Returns the taskbar progress, accounting for auto-detected running command progress. + uint64_t TerminalPaneContent::TaskbarProgress() + { + const auto vtState = _control.TaskbarState(); + // VT-set progress takes precedence + if (vtState != 0) + { + return _control.TaskbarProgress(); + } + return 0; + } + + // Method Description: + // - Raised when a shell integration prompt mark (133;A) is received, indicating + // the command has finished and we're back at a prompt. + // - Checks NotifyOnNextPrompt setting and raises NotificationRequested. + // - If autoDetectRunningCommand is enabled, clears the indeterminate progress ring. + void TerminalPaneContent::_controlPromptReturnedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) + { + if (_profile) + { + // Check NotifyOnNextPrompt setting and raise a notification + const auto notifyStyle = _profile.NotifyOnNextPrompt(); + if (static_cast(notifyStyle) != 0) + { + // Play audible notification if requested (handle here like BellStyle::Audible) + if (WI_IsFlagSet(notifyStyle, OutputNotificationStyle::Audible)) + { + const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); + PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); + } + + // Raise NotificationRequested so Tab can handle Taskbar/Tab/Notification flags + NotificationRequested.raise(*this, + *winrt::make_self(notifyStyle)); + } + + // If autoDetectRunningCommand is enabled, clear the progress ring + const auto autoDetect = _profile.AutoDetectRunningCommand(); + if (autoDetect != AutoDetectRunningCommand::Disabled && _autoDetectActive) + { + _autoDetectActive = false; + TaskbarProgressChanged.raise(*this, nullptr); + } + } + } + + // Method Description: + // - Raised when a shell integration command output mark (133;C) is received, + // indicating a command has started executing. + // - If autoDetectRunningCommand is enabled, shows an indeterminate progress ring. + void TerminalPaneContent::_controlCommandStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) + { + if (_profile) + { + const auto autoDetect = _profile.AutoDetectRunningCommand(); + if (autoDetect != AutoDetectRunningCommand::Disabled && !_autoDetectActive) + { + _autoDetectActive = true; + TaskbarProgressChanged.raise(*this, nullptr); + } + } + } + + // Method Description: + // - Raised when output stops being produced for ~100ms (debounced). + // Used to detect output in inactive panes for NotifyOnInactiveOutput. + // - Raises NotificationRequested so Tab can show activity indicators. + void TerminalPaneContent::_controlOutputIdleHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) + { + if (_profile) + { + const auto notifyStyle = _profile.NotifyOnInactiveOutput(); + if (static_cast(notifyStyle) != 0) + { + // Play audible notification if requested + if (WI_IsFlagSet(notifyStyle, OutputNotificationStyle::Audible)) + { + const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); + PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); + } + + // Raise NotificationRequested so Tab can handle Taskbar/Tab flags. + // Tab will check if this pane is the active pane and skip if so. + NotificationRequested.raise(*this, + *winrt::make_self(notifyStyle)); + } + } + } + safe_void_coroutine TerminalPaneContent::_playBellSound(winrt::Windows::Foundation::Uri uri) { auto weakThis{ get_weak() }; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 0e828cdc1b8..8b025587438 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -4,6 +4,7 @@ #pragma once #include "TerminalPaneContent.g.h" #include "BellEventArgs.g.h" +#include "NotificationEventArgs.g.h" #include "BasicPaneEvents.h" namespace winrt::TerminalApp::implementation @@ -19,6 +20,15 @@ namespace winrt::TerminalApp::implementation til::property FlashTaskbar; }; + struct NotificationEventArgs : public NotificationEventArgsT + { + public: + NotificationEventArgs(winrt::Microsoft::Terminal::Control::OutputNotificationStyle style) : + Style(style) {} + + til::property Style; + }; + struct TerminalPaneContent : TerminalPaneContentT, BasicPaneEvents { TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile, @@ -43,8 +53,8 @@ namespace winrt::TerminalApp::implementation } winrt::hstring Title() { return _control.Title(); } - uint64_t TaskbarState() { return _control.TaskbarState(); } - uint64_t TaskbarProgress() { return _control.TaskbarProgress(); } + uint64_t TaskbarState(); + uint64_t TaskbarProgress(); bool ReadOnly() { return _control.ReadOnly(); } winrt::hstring Icon() const; Windows::Foundation::IReference TabColor() const noexcept; @@ -66,11 +76,15 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; bool _bellPlayerCreated{ false }; + bool _autoDetectActive{ false }; struct ControlEventTokens { winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged; winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell; + winrt::Microsoft::Terminal::Control::TermControl::PromptReturned_revoker _PromptReturned; + winrt::Microsoft::Terminal::Control::TermControl::CommandStarted_revoker _CommandStarted; + winrt::Microsoft::Terminal::Control::TermControl::OutputIdle_revoker _OutputIdle; winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested; winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested; @@ -89,6 +103,12 @@ namespace winrt::TerminalApp::implementation safe_void_coroutine _controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); void _controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); + void _controlPromptReturnedHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); + void _controlCommandStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); + void _controlOutputIdleHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); void _controlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); void _controlTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); diff --git a/src/cascadia/TerminalApp/TerminalTabStatus.h b/src/cascadia/TerminalApp/TerminalTabStatus.h index 8d1d015193b..81166b89ae5 100644 --- a/src/cascadia/TerminalApp/TerminalTabStatus.h +++ b/src/cascadia/TerminalApp/TerminalTabStatus.h @@ -17,6 +17,7 @@ namespace winrt::TerminalApp::implementation WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingActive, PropertyChanged.raise); WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingIndeterminate, PropertyChanged.raise); WINRT_OBSERVABLE_PROPERTY(bool, BellIndicator, PropertyChanged.raise); + WINRT_OBSERVABLE_PROPERTY(bool, ActivityIndicator, PropertyChanged.raise); WINRT_OBSERVABLE_PROPERTY(bool, IsReadOnlyActive, PropertyChanged.raise); WINRT_OBSERVABLE_PROPERTY(uint32_t, ProgressValue, PropertyChanged.raise); WINRT_OBSERVABLE_PROPERTY(bool, IsInputBroadcastActive, PropertyChanged.raise); diff --git a/src/cascadia/TerminalApp/TerminalTabStatus.idl b/src/cascadia/TerminalApp/TerminalTabStatus.idl index d5664c76c6c..f91199a632b 100644 --- a/src/cascadia/TerminalApp/TerminalTabStatus.idl +++ b/src/cascadia/TerminalApp/TerminalTabStatus.idl @@ -12,6 +12,7 @@ namespace TerminalApp Boolean IsProgressRingActive { get; set; }; Boolean IsProgressRingIndeterminate { get; set; }; Boolean BellIndicator { get; set; }; + Boolean ActivityIndicator { get; set; }; UInt32 ProgressValue { get; set; }; Boolean IsReadOnlyActive { get; set; }; Boolean IsInputBroadcastActive { get; set; }; diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index ee36db25e32..ba0dcfda260 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -54,6 +54,9 @@ #include #include +#include +#include + #include #include #include diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 86747d0f8ff..87627d008a0 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -118,6 +118,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnWarningBell = [this] { _terminalWarningBell(); }; _terminal->SetWarningBellCallback(pfnWarningBell); + auto pfnPromptReturned = [this] { _terminalPromptReturned(); }; + _terminal->SetPromptReturnedCallback(pfnPromptReturned); + + auto pfnCommandStarted = [this] { _terminalCommandStarted(); }; + _terminal->SetCommandStartedCallback(pfnCommandStarted); + auto pfnTitleChanged = [this](auto&& PH1) { _terminalTitleChanged(std::forward(PH1)); }; _terminal->SetTitleChangedCallback(pfnTitleChanged); @@ -1593,6 +1599,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation WarningBell.raise(*this, nullptr); } + void ControlCore::_terminalPromptReturned() + { + // Since this can only ever be triggered by output from the connection, + // then the Terminal already has the write lock when calling this + // callback. + PromptReturned.raise(*this, nullptr); + } + + void ControlCore::_terminalCommandStarted() + { + // Since this can only ever be triggered by output from the connection, + // then the Terminal already has the write lock when calling this + // callback. + CommandStarted.raise(*this, nullptr); + } + // Method Description: // - Called for the Terminal's TitleChanged callback. This will re-raise // a new winrt TypedEvent that can be listened to. diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 690f6fb465b..f7ab24259f6 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -276,6 +276,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event TitleChanged; til::typed_event WriteToClipboard; til::typed_event<> WarningBell; + til::typed_event<> PromptReturned; + til::typed_event<> CommandStarted; til::typed_event<> TabColorChanged; til::typed_event<> BackgroundColorChanged; til::typed_event ScrollPositionChanged; @@ -324,6 +326,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation #pragma region TerminalCoreCallbacks void _terminalWarningBell(); + void _terminalPromptReturned(); + void _terminalCommandStarted(); void _terminalTitleChanged(std::wstring_view wstr); void _terminalScrollPositionChanged(const int viewTop, const int viewHeight, diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index c09fdf2d1aa..6f1b7666bee 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -191,6 +191,8 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler WriteToClipboard; event Windows.Foundation.TypedEventHandler WarningBell; + event Windows.Foundation.TypedEventHandler PromptReturned; + event Windows.Foundation.TypedEventHandler CommandStarted; event Windows.Foundation.TypedEventHandler TabColorChanged; event Windows.Foundation.TypedEventHandler BackgroundColorChanged; event Windows.Foundation.TypedEventHandler TaskbarProgressChanged; diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index 19f0d70eca5..4dc8ec754eb 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -29,6 +29,23 @@ namespace Microsoft.Terminal.Control MinGW, }; + [flags] + enum OutputNotificationStyle + { + Taskbar = 0x1, + Audible = 0x2, + Tab = 0x4, + Notification = 0x8, + All = 0xffffffff + }; + + enum AutoDetectRunningCommand + { + Disabled, + Automatic, + Progress + }; + // Class Description: // TerminalSettings encapsulates all settings that control the // TermControl's behavior. In these settings there is both the entirety @@ -77,6 +94,10 @@ namespace Microsoft.Terminal.Control PathTranslationStyle PathTranslationStyle { get; }; + OutputNotificationStyle NotifyOnInactiveOutput { get; }; + OutputNotificationStyle NotifyOnNextPrompt { get; }; + AutoDetectRunningCommand AutoDetectRunningCommand { get; }; + // NOTE! When adding something here, make sure to update ControlProperties.h too! }; } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index dfbc7623c02..ea6cd620cc1 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -393,6 +393,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // attached content before we set up the throttled func, and that'll A/V _revokers.coreScrollPositionChanged = _core.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged }); _revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell }); + _revokers.PromptReturned = _core.PromptReturned(winrt::auto_revoke, { get_weak(), &TermControl::_corePromptReturned }); + _revokers.CommandStarted = _core.CommandStarted(winrt::auto_revoke, { get_weak(), &TermControl::_coreCommandStarted }); static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000)); _autoScrollTimer.Interval(AutoScrollUpdateInterval); @@ -3699,6 +3701,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation _playWarningBell->Run(); } + void TermControl::_corePromptReturned(const IInspectable& /*sender*/, const IInspectable& /*args*/) + { + PromptReturned.raise(*this, nullptr); + } + + void TermControl::_coreCommandStarted(const IInspectable& /*sender*/, const IInspectable& /*args*/) + { + CommandStarted.raise(*this, nullptr); + } + hstring TermControl::ReadEntireBuffer() const { return _core.ReadEntireBuffer(); @@ -3820,6 +3832,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_coreOutputIdle(const IInspectable& /*sender*/, const IInspectable& /*args*/) { _refreshSearch(); + OutputIdle.raise(*this, nullptr); } void TermControl::OwningHwnd(uint64_t owner) diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 3db3cec6730..291cb94f0f7 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -212,6 +212,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event FocusFollowMouseRequested; til::typed_event Initialized; til::typed_event<> WarningBell; + til::typed_event<> PromptReturned; + til::typed_event<> CommandStarted; + til::typed_event<> OutputIdle; til::typed_event KeySent; til::typed_event CharSent; til::typed_event StringSent; @@ -425,6 +428,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args); void _coreWarningBell(const IInspectable& sender, const IInspectable& args); + void _corePromptReturned(const IInspectable& sender, const IInspectable& args); + void _coreCommandStarted(const IInspectable& sender, const IInspectable& args); void _coreOutputIdle(const IInspectable& sender, const IInspectable& args); winrt::Windows::Foundation::Point _toPosInDips(const Core::Point terminalCellPos); @@ -450,6 +455,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged; Control::ControlCore::WarningBell_revoker WarningBell; + Control::ControlCore::PromptReturned_revoker PromptReturned; + Control::ControlCore::CommandStarted_revoker CommandStarted; Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState; Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged; Control::ControlCore::FontSizeChanged_revoker FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index fb994786321..05038aa4373 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -68,6 +68,9 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler RaiseNotice; event Windows.Foundation.TypedEventHandler WarningBell; + event Windows.Foundation.TypedEventHandler PromptReturned; + event Windows.Foundation.TypedEventHandler CommandStarted; + event Windows.Foundation.TypedEventHandler OutputIdle; event Windows.Foundation.TypedEventHandler HidePointerCursor; event Windows.Foundation.TypedEventHandler RestorePointerCursor; event Windows.Foundation.TypedEventHandler TabColorChanged; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index e43180e9aaf..653d2fa0afd 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1265,6 +1265,16 @@ void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function _pfnClearQuickFix.swap(pfn); } +void Terminal::SetPromptReturnedCallback(std::function pfn) noexcept +{ + _pfnPromptReturned.swap(pfn); +} + +void Terminal::SetCommandStartedCallback(std::function pfn) noexcept +{ + _pfnCommandStarted.swap(pfn); +} + // Method Description: // - Stores the search highlighted regions in the terminal void Terminal::SetSearchHighlights(const std::vector& highlights) noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index e69b42e373b..09473c820cd 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -156,7 +156,7 @@ class Microsoft::Terminal::Core::Terminal final : bool IsVtInputEnabled() const noexcept override; void NotifyBufferRotation(const int delta) override; - void NotifyShellIntegrationMark() override; + void NotifyShellIntegrationMark(ShellIntegrationMark mark) override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; @@ -232,6 +232,8 @@ class Microsoft::Terminal::Core::Terminal final : void SetSearchMissingCommandCallback(std::function pfn) noexcept; void SetClearQuickFixCallback(std::function pfn) noexcept; void SetWindowSizeChangedCallback(std::function pfn) noexcept; + void SetPromptReturnedCallback(std::function pfn) noexcept; + void SetCommandStartedCallback(std::function pfn) noexcept; void SetSearchHighlights(const std::vector& highlights) noexcept; void SetSearchHighlightFocused(size_t focusedIdx) noexcept; void ScrollToSearchHighlight(til::CoordType searchScrollOffset); @@ -340,6 +342,8 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnSearchMissingCommand; std::function _pfnClearQuickFix; std::function _pfnWindowSizeChanged; + std::function _pfnPromptReturned; + std::function _pfnCommandStarted; RenderSettings _renderSettings; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index e87dc765f24..6968f910d0b 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -400,8 +400,26 @@ void Terminal::NotifyBufferRotation(const int delta) } } -void Terminal::NotifyShellIntegrationMark() +void Terminal::NotifyShellIntegrationMark(ShellIntegrationMark mark) { // Notify the scrollbar that marks have been added so it can refresh the mark indicators _NotifyScrollEvent(); + + switch (mark) + { + case ShellIntegrationMark::Prompt: + if (_pfnPromptReturned) + { + _pfnPromptReturned(); + } + break; + case ShellIntegrationMark::Output: + if (_pfnCommandStarted) + { + _pfnCommandStarted(); + } + break; + default: + break; + } } diff --git a/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp b/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp index 71f8695e2a9..72d22d93ef7 100644 --- a/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp @@ -353,6 +353,10 @@ namespace winrt::Microsoft::Terminal::Settings _AllowVtChecksumReport = profile.AllowVtChecksumReport(); _AllowVtClipboardWrite = profile.AllowVtClipboardWrite(); _PathTranslationStyle = profile.PathTranslationStyle(); + + _NotifyOnInactiveOutput = profile.NotifyOnInactiveOutput(); + _NotifyOnNextPrompt = profile.NotifyOnNextPrompt(); + _AutoDetectRunningCommand = profile.AutoDetectRunningCommand(); } // Method Description: diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index 661d077b7e8..64328902f8c 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -40,6 +40,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER(CloseOnExitMode, CloseOnExitMode, winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode, L"Profile_CloseOnExit", L"Content"); INITIALIZE_BINDABLE_ENUM_SETTING(ScrollState, ScrollbarState, winrt::Microsoft::Terminal::Control::ScrollbarState, L"Profile_ScrollbarVisibility", L"Content"); INITIALIZE_BINDABLE_ENUM_SETTING(PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle, L"Profile_PathTranslationStyle", L"Content"); + INITIALIZE_BINDABLE_ENUM_SETTING(AutoDetectRunningCommand, AutoDetectRunningCommand, winrt::Microsoft::Terminal::Control::AutoDetectRunningCommand, L"Profile_AutoDetectRunningCommand", L"Content"); _InitializeCurrentBellSounds(); @@ -640,6 +641,170 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation BellStyle(currentStyle); } + // ===================== NotifyOnInactiveOutput ===================== + + hstring ProfileViewModel::NotifyOnInactiveOutputPreview() const + { + using Ons = Control::OutputNotificationStyle; + const auto style = NotifyOnInactiveOutput(); + if (WI_AreAllFlagsSet(style, Ons::Taskbar | Ons::Audible | Ons::Tab | Ons::Notification)) + { + return RS_(L"Profile_OutputNotificationStyleAll/Content"); + } + else if (style == static_cast(0)) + { + return RS_(L"Profile_OutputNotificationStyleNone/Content"); + } + + std::vector resultList; + resultList.reserve(4); + if (WI_IsFlagSet(style, Ons::Taskbar)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleTaskbar/Content")); + } + if (WI_IsFlagSet(style, Ons::Audible)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleAudible/Content")); + } + if (WI_IsFlagSet(style, Ons::Tab)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleTab/Content")); + } + if (WI_IsFlagSet(style, Ons::Notification)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleNotification/Content")); + } + + hstring result{}; + for (auto&& entry : resultList) + { + if (result.empty()) + { + result = entry; + } + else + { + result = result + L", " + entry; + } + } + return result; + } + + bool ProfileViewModel::IsNotifyOnInactiveOutputFlagSet(const uint32_t flag) + { + return (WI_EnumValue(NotifyOnInactiveOutput()) & flag) == flag; + } + + void ProfileViewModel::SetNotifyOnInactiveOutputTaskbar(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnInactiveOutput(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Taskbar, winrt::unbox_value(on)); + NotifyOnInactiveOutput(currentStyle); + } + + void ProfileViewModel::SetNotifyOnInactiveOutputAudible(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnInactiveOutput(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Audible, winrt::unbox_value(on)); + NotifyOnInactiveOutput(currentStyle); + } + + void ProfileViewModel::SetNotifyOnInactiveOutputTab(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnInactiveOutput(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Tab, winrt::unbox_value(on)); + NotifyOnInactiveOutput(currentStyle); + } + + void ProfileViewModel::SetNotifyOnInactiveOutputNotification(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnInactiveOutput(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Notification, winrt::unbox_value(on)); + NotifyOnInactiveOutput(currentStyle); + } + + // ===================== NotifyOnNextPrompt ===================== + + hstring ProfileViewModel::NotifyOnNextPromptPreview() const + { + using Ons = Control::OutputNotificationStyle; + const auto style = NotifyOnNextPrompt(); + if (WI_AreAllFlagsSet(style, Ons::Taskbar | Ons::Audible | Ons::Tab | Ons::Notification)) + { + return RS_(L"Profile_OutputNotificationStyleAll/Content"); + } + else if (style == static_cast(0)) + { + return RS_(L"Profile_OutputNotificationStyleNone/Content"); + } + + std::vector resultList; + resultList.reserve(4); + if (WI_IsFlagSet(style, Ons::Taskbar)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleTaskbar/Content")); + } + if (WI_IsFlagSet(style, Ons::Audible)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleAudible/Content")); + } + if (WI_IsFlagSet(style, Ons::Tab)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleTab/Content")); + } + if (WI_IsFlagSet(style, Ons::Notification)) + { + resultList.emplace_back(RS_(L"Profile_OutputNotificationStyleNotification/Content")); + } + + hstring result{}; + for (auto&& entry : resultList) + { + if (result.empty()) + { + result = entry; + } + else + { + result = result + L", " + entry; + } + } + return result; + } + + bool ProfileViewModel::IsNotifyOnNextPromptFlagSet(const uint32_t flag) + { + return (WI_EnumValue(NotifyOnNextPrompt()) & flag) == flag; + } + + void ProfileViewModel::SetNotifyOnNextPromptTaskbar(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnNextPrompt(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Taskbar, winrt::unbox_value(on)); + NotifyOnNextPrompt(currentStyle); + } + + void ProfileViewModel::SetNotifyOnNextPromptAudible(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnNextPrompt(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Audible, winrt::unbox_value(on)); + NotifyOnNextPrompt(currentStyle); + } + + void ProfileViewModel::SetNotifyOnNextPromptTab(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnNextPrompt(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Tab, winrt::unbox_value(on)); + NotifyOnNextPrompt(currentStyle); + } + + void ProfileViewModel::SetNotifyOnNextPromptNotification(winrt::Windows::Foundation::IReference on) + { + auto currentStyle = NotifyOnNextPrompt(); + WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Notification, winrt::unbox_value(on)); + NotifyOnNextPrompt(currentStyle); + } + // Method Description: // - Construct _CurrentBellSounds by importing the _inherited_ value from the model // - Adds a PropertyChanged handler to each BellSoundViewModel to propagate changes to the model diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index f120ed74673..09443ac67bd 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -47,6 +47,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void SetBellStyleWindow(winrt::Windows::Foundation::IReference on); void SetBellStyleTaskbar(winrt::Windows::Foundation::IReference on); + // notify on inactive output bits + hstring NotifyOnInactiveOutputPreview() const; + bool IsNotifyOnInactiveOutputFlagSet(const uint32_t flag); + void SetNotifyOnInactiveOutputTaskbar(winrt::Windows::Foundation::IReference on); + void SetNotifyOnInactiveOutputAudible(winrt::Windows::Foundation::IReference on); + void SetNotifyOnInactiveOutputTab(winrt::Windows::Foundation::IReference on); + void SetNotifyOnInactiveOutputNotification(winrt::Windows::Foundation::IReference on); + + // notify on next prompt bits + hstring NotifyOnNextPromptPreview() const; + bool IsNotifyOnNextPromptFlagSet(const uint32_t flag); + void SetNotifyOnNextPromptTaskbar(winrt::Windows::Foundation::IReference on); + void SetNotifyOnNextPromptAudible(winrt::Windows::Foundation::IReference on); + void SetNotifyOnNextPromptTab(winrt::Windows::Foundation::IReference on); + void SetNotifyOnNextPromptNotification(winrt::Windows::Foundation::IReference on); + hstring BellSoundPreview(); void RequestAddBellSound(hstring path); void RequestDeleteBellSound(const Editor::BellSoundViewModel& vm); @@ -146,12 +162,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation OBSERVABLE_PROJECTED_SETTING(_profile, RainbowSuggestions); OBSERVABLE_PROJECTED_SETTING(_profile, PathTranslationStyle); + OBSERVABLE_PROJECTED_SETTING(_profile, NotifyOnInactiveOutput); + OBSERVABLE_PROJECTED_SETTING(_profile, NotifyOnNextPrompt); + OBSERVABLE_PROJECTED_SETTING(_profile, AutoDetectRunningCommand); + WINRT_PROPERTY(bool, IsBaseLayer, false); WINRT_PROPERTY(bool, FocusDeleteButton, false); GETSET_BINDABLE_ENUM_SETTING(AntiAliasingMode, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode); GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit); GETSET_BINDABLE_ENUM_SETTING(ScrollState, Microsoft::Terminal::Control::ScrollbarState, ScrollState); GETSET_BINDABLE_ENUM_SETTING(PathTranslationStyle, Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle); + GETSET_BINDABLE_ENUM_SETTING(AutoDetectRunningCommand, Microsoft::Terminal::Control::AutoDetectRunningCommand, AutoDetectRunningCommand); private: Model::Profile _profile; diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index 1fb13853a64..7298ec00085 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -50,6 +50,23 @@ namespace Microsoft.Terminal.Settings.Editor void SetBellStyleWindow(Windows.Foundation.IReference on); void SetBellStyleTaskbar(Windows.Foundation.IReference on); + String NotifyOnInactiveOutputPreview { get; }; + Boolean IsNotifyOnInactiveOutputFlagSet(UInt32 flag); + void SetNotifyOnInactiveOutputTaskbar(Windows.Foundation.IReference on); + void SetNotifyOnInactiveOutputAudible(Windows.Foundation.IReference on); + void SetNotifyOnInactiveOutputTab(Windows.Foundation.IReference on); + void SetNotifyOnInactiveOutputNotification(Windows.Foundation.IReference on); + + String NotifyOnNextPromptPreview { get; }; + Boolean IsNotifyOnNextPromptFlagSet(UInt32 flag); + void SetNotifyOnNextPromptTaskbar(Windows.Foundation.IReference on); + void SetNotifyOnNextPromptAudible(Windows.Foundation.IReference on); + void SetNotifyOnNextPromptTab(Windows.Foundation.IReference on); + void SetNotifyOnNextPromptNotification(Windows.Foundation.IReference on); + + IInspectable CurrentAutoDetectRunningCommand; + Windows.Foundation.Collections.IObservableVector AutoDetectRunningCommandList { get; }; + String BellSoundPreview { get; }; Windows.Foundation.Collections.IObservableVector CurrentBellSounds { get; }; void RequestAddBellSound(String path); @@ -140,5 +157,8 @@ namespace Microsoft.Terminal.Settings.Editor OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RainbowSuggestions); OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle); OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnInactiveOutput); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnNextPrompt); + OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.AutoDetectRunningCommand, AutoDetectRunningCommand); } } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml index 3f243c22bca..06d635f528d 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -196,6 +196,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Type to filter icons Placeholder text for a text box to filter and select an icon. + + + Flash taskbar + An option to choose from for a notification style setting. When selected, the taskbar is flashed. + + + Audible + An option to choose from for a notification style setting. When selected, an audible cue is played. + + + Tab indicator + An option to choose from for a notification style setting. When selected, an activity indicator is shown on the tab. + + + Desktop notification + An option to choose from for a notification style setting. When selected, a desktop toast notification is sent. + + + All + An option label for notification style. Shown when all notification styles are enabled. + + + None + An option label for notification style. Shown when no notification styles are enabled. + + + + Notify on inactive output + Name for a control to select how the app notifies the user when an inactive pane produces output. + + + Notify on inactive output + Header for a control to select how the app notifies the user when an inactive pane produces output. + + + Controls how you are notified when a background pane produces new output. + A description for what the "notify on inactive output" setting does. + + + + Notify on next prompt + Name for a control to select how the app notifies the user when a command finishes and the shell returns to a prompt. + + + Notify on next prompt + Header for a control to select how the app notifies the user when a command finishes and the shell returns to a prompt. + + + Controls how you are notified when a running command finishes and the shell returns to a prompt. Requires shell integration. + A description for what the "notify on next prompt" setting does. + + + + Auto-detect running command + Name for a control to select how the app detects and indicates a running command. + + + Auto-detect running command + Header for a control to select how the app detects and indicates a running command. + + + Controls whether and how the terminal automatically shows progress while a command is running. Requires shell integration. + A description for what the "auto-detect running command" setting does. + + + Disabled + An option to choose from for the "auto-detect running command" setting. When selected, no auto-detection occurs. + + + Automatic + An option to choose from for the "auto-detect running command" setting. When selected, an indeterminate progress ring is shown while a command is running. + + + Progress + An option to choose from for the "auto-detect running command" setting. When selected, the terminal attempts to detect progress percentage from command output. + + + Disabled + An option to choose from for the "auto-detect running command" setting. When selected, no auto-detection occurs. + + + Automatic + An option to choose from for the "auto-detect running command" setting. When selected, an indeterminate progress ring is shown while a command is running. + + + Progress + An option to choose from for the "auto-detect running command" setting. When selected, the terminal attempts to detect progress percentage from command output. + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index de1bf5185da..d4e45adb969 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -53,6 +53,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::IntenseStyle, IntenseTextStyle); DEFINE_ENUM_MAP(Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors); DEFINE_ENUM_MAP(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle); + DEFINE_ENUM_MAP(Microsoft::Terminal::Control::AutoDetectRunningCommand, AutoDetectRunningCommand); // Actions DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::ResizeDirection, ResizeDirection); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index 160c9a11b1f..545ac6adca8 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -51,6 +51,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap IntenseTextStyle(); static winrt::Windows::Foundation::Collections::IMap AdjustIndistinguishableColors(); static winrt::Windows::Foundation::Collections::IMap PathTranslationStyle(); + static winrt::Windows::Foundation::Collections::IMap AutoDetectRunningCommand(); // Actions static winrt::Windows::Foundation::Collections::IMap ResizeDirection(); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index 128260a507a..4749ab7c778 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -33,6 +33,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap FontWeight { get; }; static Windows.Foundation.Collections.IMap IntenseTextStyle { get; }; static Windows.Foundation.Collections.IMap PathTranslationStyle { get; }; + static Windows.Foundation.Collections.IMap AutoDetectRunningCommand { get; }; // Actions static Windows.Foundation.Collections.IMap ResizeDirection { get; }; diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 4f99bda187a..ede64d2fc00 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -108,7 +108,10 @@ Author(s): X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \ X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \ X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \ - X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None) + X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None) \ + X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnInactiveOutput, "notifyOnInactiveOutput", Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ + X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, "notifyOnNextPrompt", Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ + X(Microsoft::Terminal::Control::AutoDetectRunningCommand, AutoDetectRunningCommand, "autoDetectRunningCommand", Microsoft::Terminal::Control::AutoDetectRunningCommand::Disabled) // Intentionally omitted Profile settings: // * Name diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 65de99b9135..844a8c0a641 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -94,5 +94,9 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(Boolean, AllowVtClipboardWrite); INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle); + + INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnInactiveOutput); + INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnNextPrompt); + INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.AutoDetectRunningCommand, AutoDetectRunningCommand); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 9c071945a29..d7d291f2acb 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -119,6 +119,46 @@ JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle) } }; +JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Control::OutputNotificationStyle) +{ + static constexpr std::array mappings = { + pair_type{ "none", AllClear }, + pair_type{ "taskbar", ValueType::Taskbar }, + pair_type{ "audible", ValueType::Audible }, + pair_type{ "tab", ValueType::Tab }, + pair_type{ "notification", ValueType::Notification }, + pair_type{ "all", AllSet }, + }; + + auto FromJson(const Json::Value& json) + { + if (json.isBool()) + { + return json.asBool() ? ValueType::Tab : AllClear; + } + return BaseFlagMapper::FromJson(json); + } + + bool CanConvert(const Json::Value& json) + { + return BaseFlagMapper::CanConvert(json) || json.isBool(); + } + + Json::Value ToJson(const ::winrt::Microsoft::Terminal::Control::OutputNotificationStyle& style) + { + return BaseFlagMapper::ToJson(style); + } +}; + +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::AutoDetectRunningCommand) +{ + JSON_MAPPINGS(3) = { + pair_type{ "disabled", ValueType::Disabled }, + pair_type{ "automatic", ValueType::Automatic }, + pair_type{ "progress", ValueType::Progress }, + }; +}; + JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::ConvergedAlignment) { // reduce repetition diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index 91ae711786c..1f2e96fed41 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -87,4 +87,7 @@ X(bool, ShowMarks, false) \ X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \ X(bool, RightClickContextMenu, false) \ - X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None) + X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None) \ + X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnInactiveOutput, winrt::Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ + X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, winrt::Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ + X(winrt::Microsoft::Terminal::Control::AutoDetectRunningCommand, AutoDetectRunningCommand, winrt::Microsoft::Terminal::Control::AutoDetectRunningCommand::Disabled) diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 4f359dba13c..7c66ef9059b 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -424,7 +424,7 @@ void ConhostInternalGetSet::NotifyBufferRotation(const int) { } -void ConhostInternalGetSet::NotifyShellIntegrationMark() +void ConhostInternalGetSet::NotifyShellIntegrationMark(ShellIntegrationMark /*mark*/) { // Not implemented for conhost - shell integration marks are a Terminal app feature. } diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index c80df20ffea..d035aa1c296 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -66,7 +66,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool IsVtInputEnabled() const override; void NotifyBufferRotation(const int delta) override; - void NotifyShellIntegrationMark() override; + void NotifyShellIntegrationMark(ShellIntegrationMark mark) override; void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index fb613c4e121..804ad1c804c 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -85,7 +85,16 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ResizeWindow(const til::CoordType width, const til::CoordType height) = 0; virtual void NotifyBufferRotation(const int delta) = 0; - virtual void NotifyShellIntegrationMark() = 0; + + enum class ShellIntegrationMark + { + Prompt, + Command, + Output, + CommandFinished, + Other + }; + virtual void NotifyShellIntegrationMark(ShellIntegrationMark mark) = 0; virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index d49bbfad8fa..f5b45b5b32d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3583,7 +3583,7 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string) else if (subParam == 12) { _pages.ActivePage().Buffer().StartCommand(); - _api.NotifyShellIntegrationMark(); + _api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::Command); } } @@ -3614,7 +3614,7 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string) if (action == L"SetMark") { _pages.ActivePage().Buffer().StartPrompt(); - _api.NotifyShellIntegrationMark(); + _api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::Prompt); } } @@ -3648,19 +3648,19 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string) case L'A': // FTCS_PROMPT { _pages.ActivePage().Buffer().StartPrompt(); - _api.NotifyShellIntegrationMark(); + _api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::Prompt); break; } case L'B': // FTCS_COMMAND_START { _pages.ActivePage().Buffer().StartCommand(); - _api.NotifyShellIntegrationMark(); + _api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::Command); break; } case L'C': // FTCS_COMMAND_EXECUTED { _pages.ActivePage().Buffer().StartOutput(); - _api.NotifyShellIntegrationMark(); + _api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::Output); break; } case L'D': // FTCS_COMMAND_FINISHED @@ -3681,7 +3681,7 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string) } _pages.ActivePage().Buffer().EndCurrentCommand(error); - _api.NotifyShellIntegrationMark(); + _api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::CommandFinished); break; } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 9edc155e999..1db1b6c0c84 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -207,7 +207,7 @@ class TestGetSet final : public ITerminalApi Log::Comment(L"NotifyBufferRotation MOCK called..."); } - void NotifyShellIntegrationMark() override + void NotifyShellIntegrationMark(ShellIntegrationMark /*mark*/) override { Log::Comment(L"NotifyShellIntegrationMark MOCK called..."); } From 19cfe09bda28fac410fb93d1e5754d2dfe012132 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 2 Mar 2026 10:10:10 -0600 Subject: [PATCH 02/11] fuck man --- .../TerminalApp/AppCommandlineArgs.cpp | 10 +++ .../TerminalApp/DesktopNotification.cpp | 11 +++- src/cascadia/TerminalApp/IPaneContent.idl | 1 + src/cascadia/TerminalApp/Tab.cpp | 35 ++++++---- .../TerminalApp/TerminalPaneContent.cpp | 66 +++++++++---------- .../TerminalApp/TerminalPaneContent.h | 6 +- .../TerminalApp/TerminalPaneContent.idl | 1 + 7 files changed, 79 insertions(+), 51 deletions(-) diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index c41eea5431c..73ca1f7cd72 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -1068,6 +1068,16 @@ int AppCommandlineArgs::ParseArgs(winrt::array_view args) return 0; } + // Toast notification activations launch a new instance with "__fromToast" + // as command-line args. We handle toast clicks in-process via the + // ToastNotification::Activated event, so there's nothing for this + // (possibly handed-off) commandline to do. Return non-zero with no exit + // message so that _dispatchCommandline silently ignores it. + if (args.size() == 2 && args[1] == L"__fromToast") + { + return 1; + } + auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args); for (auto& cmdBlob : commands) diff --git a/src/cascadia/TerminalApp/DesktopNotification.cpp b/src/cascadia/TerminalApp/DesktopNotification.cpp index 1dcd6d2a2d0..573dc5a461f 100644 --- a/src/cascadia/TerminalApp/DesktopNotification.cpp +++ b/src/cascadia/TerminalApp/DesktopNotification.cpp @@ -41,7 +41,7 @@ namespace winrt::TerminalApp::implementation // Build the toast XML. We use a simple template with a title and body text. // - // + // // // // Title @@ -57,9 +57,14 @@ namespace winrt::TerminalApp::implementation // Second is the body textNodes.Item(1).InnerText(args.Message); - // Set launch args so we can identify which tab to activate. auto toastElement = toastXml.DocumentElement(); - toastElement.SetAttribute(L"launch", fmt::format(FMT_COMPILE(L"tabIndex={}"), args.TabIndex)); + + // When a toast is clicked, Windows launches a new instance of the app + // with the "launch" attribute as command-line arguments. We handle + // toast activation in-process via the Activated event below, so the + // new instance should do nothing. "__fromToast" is recognized by + // AppCommandlineArgs::ParseArgs as a no-op sentinel. + toastElement.SetAttribute(L"launch", L"__fromToast"); // Set the scenario to "reminder" to ensure the toast shows even in DND, // and the group/tag to allow replacement of repeated notifications. diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl index 679205c8462..17719ad24fb 100644 --- a/src/cascadia/TerminalApp/IPaneContent.idl +++ b/src/cascadia/TerminalApp/IPaneContent.idl @@ -19,6 +19,7 @@ namespace TerminalApp runtimeclass NotificationEventArgs { Microsoft.Terminal.Control.OutputNotificationStyle Style { get; }; + Boolean OnlyWhenInactive { get; }; }; interface IPaneContent diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index ecd023789cf..6b34b3f3d56 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -1205,12 +1205,18 @@ namespace winrt::TerminalApp::implementation co_await wil::resume_foreground(dispatcher); if (const auto tab{ weakThisCopy.get() }) { - // For NotifyOnInactiveOutput, only show notifications if this sender - // is NOT the currently active pane content. For NotifyOnNextPrompt, - // the notification is always relevant. + // For NotifyOnInactiveOutput (OnlyWhenInactive=true), suppress + // ALL notification styles when the sender is the active pane. + // For NotifyOnNextPrompt (OnlyWhenInactive=false), always fire. const auto activeContent = tab->GetActiveContent(); const auto isActivePaneContent = activeContent && activeContent == sender; + if (notifArgs.OnlyWhenInactive() && isActivePaneContent && + tab->_focusState != WUX::FocusState::Unfocused) + { + co_return; + } + const auto style = notifArgs.Style(); if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Taskbar)) @@ -1219,20 +1225,23 @@ namespace winrt::TerminalApp::implementation tab->TabRaiseVisualBell.raise(); } - // Audible notification is handled in TerminalPaneContent already + if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Audible)) + { + // Play the notification sound via the pane's bell infrastructure + // (respects BellSound profile setting, uses MediaPlayer, etc.) + if (const auto termContent{ sender.try_as() }) + { + termContent.PlayNotificationSound(); + } + } if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Tab)) { - // Show the activity indicator in the tab header (distinct from bell) - // Only for inactive pane output, skip if this is the focused pane in a focused tab - if (!isActivePaneContent || tab->_focusState == WUX::FocusState::Unfocused) - { - tab->ShowActivityIndicator(true); + tab->ShowActivityIndicator(true); - if (tab->_focusState != WUX::FocusState::Unfocused) - { - tab->ActivateActivityIndicatorTimer(); - } + if (tab->_focusState != WUX::FocusState::Unfocused) + { + tab->ActivateActivityIndicatorTimer(); } } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 48df513fc29..079b159594c 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -265,6 +265,30 @@ namespace winrt::TerminalApp::implementation // has the 'visual' flag set // Arguments: // - + // Method Description: + // - Plays the notification sound using the profile's BellSound setting if + // configured, otherwise falls back to the system "Critical Stop" sound. + // Reused by the warning bell handler, NotifyOnNextPrompt, and + // NotifyOnInactiveOutput (called from Tab after the active-pane check). + void TerminalPaneContent::PlayNotificationSound() + { + if (_profile) + { + auto sounds{ _profile.BellSound() }; + if (sounds && sounds.Size() > 0) + { + winrt::hstring soundPath{ sounds.GetAt(rand() % sounds.Size()).Resolved() }; + winrt::Windows::Foundation::Uri uri{ soundPath }; + _playBellSound(uri); + } + else + { + const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); + PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); + } + } + } + void TerminalPaneContent::_controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) { @@ -275,19 +299,7 @@ namespace winrt::TerminalApp::implementation { if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible)) { - // Audible is set, play the sound - auto sounds{ _profile.BellSound() }; - if (sounds && sounds.Size() > 0) - { - winrt::hstring soundPath{ sounds.GetAt(rand() % sounds.Size()).Resolved() }; - winrt::Windows::Foundation::Uri uri{ soundPath }; - _playBellSound(uri); - } - else - { - const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); - PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); - } + PlayNotificationSound(); } if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window)) @@ -344,20 +356,14 @@ namespace winrt::TerminalApp::implementation { if (_profile) { - // Check NotifyOnNextPrompt setting and raise a notification + // Check NotifyOnNextPrompt setting and raise a notification. + // Pass OnlyWhenInactive=true so Tab suppresses notifications when + // this pane is active in a focused tab. const auto notifyStyle = _profile.NotifyOnNextPrompt(); if (static_cast(notifyStyle) != 0) { - // Play audible notification if requested (handle here like BellStyle::Audible) - if (WI_IsFlagSet(notifyStyle, OutputNotificationStyle::Audible)) - { - const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); - PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); - } - - // Raise NotificationRequested so Tab can handle Taskbar/Tab/Notification flags NotificationRequested.raise(*this, - *winrt::make_self(notifyStyle)); + *winrt::make_self(notifyStyle, true)); } // If autoDetectRunningCommand is enabled, clear the progress ring @@ -400,17 +406,11 @@ namespace winrt::TerminalApp::implementation const auto notifyStyle = _profile.NotifyOnInactiveOutput(); if (static_cast(notifyStyle) != 0) { - // Play audible notification if requested - if (WI_IsFlagSet(notifyStyle, OutputNotificationStyle::Audible)) - { - const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); - PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); - } - - // Raise NotificationRequested so Tab can handle Taskbar/Tab flags. - // Tab will check if this pane is the active pane and skip if so. + // Raise NotificationRequested so Tab can handle Taskbar/Tab/Audible flags. + // Pass OnlyWhenInactive=true so Tab skips notifications for the active pane. + // Note: Audible is handled by Tab (not here) so it can be gated on active state. NotificationRequested.raise(*this, - *winrt::make_self(notifyStyle)); + *winrt::make_self(notifyStyle, true)); } } } diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 8b025587438..9cf38172ccc 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -23,10 +23,11 @@ namespace winrt::TerminalApp::implementation struct NotificationEventArgs : public NotificationEventArgsT { public: - NotificationEventArgs(winrt::Microsoft::Terminal::Control::OutputNotificationStyle style) : - Style(style) {} + NotificationEventArgs(winrt::Microsoft::Terminal::Control::OutputNotificationStyle style, bool onlyWhenInactive = false) : + Style(style), OnlyWhenInactive(onlyWhenInactive) {} til::property Style; + til::property OnlyWhenInactive; }; struct TerminalPaneContent : TerminalPaneContentT, BasicPaneEvents @@ -46,6 +47,7 @@ namespace winrt::TerminalApp::implementation void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); void MarkAsDefterm(); + void PlayNotificationSound(); winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const { diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl index 53dcf4c3379..36a1612c181 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.idl +++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl @@ -11,6 +11,7 @@ namespace TerminalApp Microsoft.Terminal.Control.TermControl GetTermControl(); void MarkAsDefterm(); + void PlayNotificationSound(); Microsoft.Terminal.Settings.Model.Profile GetProfile(); From cd8ff95c1a036d24fb399f8c24b4475d64d6c5a6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 2 Mar 2026 13:44:22 -0600 Subject: [PATCH 03/11] notify when we automark progress --- src/cascadia/TerminalApp/Tab.cpp | 1 + src/cascadia/TerminalApp/TerminalPaneContent.h | 2 +- src/cascadia/TerminalCore/Terminal.cpp | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 6b34b3f3d56..2c55fba4df9 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -1105,6 +1105,7 @@ namespace winrt::TerminalApp::implementation dispatcher, til::throttled_func_options{ .delay = std::chrono::milliseconds{ 200 }, + .leading = true, .trailing = true, }, [weakThis]() { diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 9cf38172ccc..8c89ca0b385 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -78,7 +78,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr }; bool _bellPlayerCreated{ false }; - bool _autoDetectActive{ false }; + std::atomic _autoDetectActive{ false }; struct ControlEventTokens { diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 653d2fa0afd..6a1073a6d6a 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -750,6 +750,12 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s // This changed the scrollbar marks - raise a notification to update them _NotifyScrollEvent(); + + } + // regardless, start notify that we started command output + if (_pfnCommandStarted) + { + _pfnCommandStarted(); } } } From d449c86a33f4cb53246f4a5eea4b25fd12691b25 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 2 Mar 2026 14:03:42 -0600 Subject: [PATCH 04/11] don't pling 900 times on startup --- src/cascadia/TerminalControl/ControlCore.cpp | 29 +++++++++++++++++++- src/cascadia/TerminalControl/ControlCore.h | 3 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 87627d008a0..7883bd4d5b1 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1596,6 +1596,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Since this can only ever be triggered by output from the connection, // then the Terminal already has the write lock when calling this // callback. + if (_restoring) + { + return; + } WarningBell.raise(*this, nullptr); } @@ -1604,6 +1608,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Since this can only ever be triggered by output from the connection, // then the Terminal already has the write lock when calling this // callback. + if (_restoring) + { + return; + } PromptReturned.raise(*this, nullptr); } @@ -1612,6 +1620,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Since this can only ever be triggered by output from the connection, // then the Terminal already has the write lock when calling this // callback. + if (_restoring) + { + return; + } CommandStarted.raise(*this, nullptr); } @@ -1672,6 +1684,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_terminalTaskbarProgressChanged() { + if (_restoring) + { + return; + } TaskbarProgressChanged.raise(*this, nullptr); } @@ -1689,6 +1705,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - duration - How long the note should be sustained (in microseconds). void ControlCore::_terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) { + if (_restoring) + { + return; + } // The UI thread might try to acquire the console lock from time to time. // --> Unlock it, so the UI doesn't hang while we're busy. const auto suspension = _terminal->SuspendLock(); @@ -1861,8 +1881,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation _terminal->SerializeMainBuffer(handle); } - void ControlCore::RestoreFromPath(const wchar_t* path) const + void ControlCore::RestoreFromPath(const wchar_t* path) { + // Suppress notifications (bells, prompt-returned, command-started, etc.) + // while we replay persisted buffer content. Without this, restoring a + // session fires the same events that live output would, producing + // unwanted audible bells, tab activity indicators, and taskbar flashes. + _restoring = true; + const auto restoreComplete = wil::scope_exit([&] { _restoring = false; }); + wil::unique_handle file{ CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) }; // This block of code exists temporarily to fix buffer dumps that were diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index f7ab24259f6..c48d67ccc31 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -154,7 +154,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void Close(); void PersistTo(HANDLE handle) const; - void RestoreFromPath(const wchar_t* path) const; + void RestoreFromPath(const wchar_t* path); void ClearQuickFix(); @@ -424,6 +424,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::optional _lastHoveredCell; uint16_t _lastHoveredId{ 0 }; std::atomic _initializedTerminal{ false }; + std::atomic _restoring{ false }; bool _isReadOnly{ false }; bool _closing{ false }; From 8a132d6b770ec0e1a19892c4176a209ee55dd8b4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 3 Mar 2026 05:18:22 -0600 Subject: [PATCH 05/11] cleaner --- .../TerminalApp/AppCommandlineArgs.cpp | 10 --------- .../WindowsTerminal/WindowEmperor.cpp | 21 +++++++++++++++++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 73ca1f7cd72..c41eea5431c 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -1068,16 +1068,6 @@ int AppCommandlineArgs::ParseArgs(winrt::array_view args) return 0; } - // Toast notification activations launch a new instance with "__fromToast" - // as command-line args. We handle toast clicks in-process via the - // ToastNotification::Activated event, so there's nothing for this - // (possibly handed-off) commandline to do. Return non-zero with no exit - // message so that _dispatchCommandline silently ignores it. - if (args.size() == 2 && args[1] == L"__fromToast") - { - return 1; - } - auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args); for (auto& cmdBlob : commands) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 07e96fcc2e5..b6b0c18baa1 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -353,6 +353,25 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) #endif } + // Toast notification activations launch a new unelevated instance of the + // app with "__fromToast" as the sole command-line argument. This happens + // when the originating Terminal window is elevated — Windows cannot + // COM-activate an elevated process from a toast click, so it starts a new + // unelevated process instead. That process must not restore persisted + // layouts, create windows, or do anything else — just exit immediately. + // + // We check this BEFORE the mutex handoff because the elevated instance + // owns the mutex and UIPI blocks WM_COPYDATA from our unelevated process, + // which would cause acquireMutexOrAttemptHandoff to spin for ~30s. + const auto args = commandlineToArgArray(GetCommandLineW()); + { + if (args.size() == 2 && args[1] == L"__fromToast") + { + TerminateProcess(GetCurrentProcess(), 0); + __assume(false); + } + } + // Windows Terminal is a single-instance application. Either acquire ownership // over the mutex, or hand off the command line to the existing instance. const auto mutex = acquireMutexOrAttemptHandoff(windowClassName.c_str(), nCmdShow); @@ -406,8 +425,6 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) } } - const auto args = commandlineToArgArray(GetCommandLineW()); - if (args.size() == 2 && args[1] == L"-Embedding") { // We were launched for ConPTY handoff. We have no windows and also don't want to exit. From 267d37974761ae3fad34738d44ca852fce2ba854 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 3 Mar 2026 05:43:55 -0600 Subject: [PATCH 06/11] cleaner notification text --- src/cascadia/TerminalApp/TabManagement.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index a81dd99e04c..6f9a9439dca 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -1206,7 +1206,9 @@ namespace winrt::TerminalApp::implementation { // Build the notification message. // Use the window name if available for context, otherwise just use the tab title. - const auto windowName = _WindowProperties ? _WindowProperties.WindowNameForDisplay() : winrt::hstring{}; + // Use the raw WindowName (not WindowNameForDisplay) so we don't include + // the "" placeholder in the notification body. + const auto windowName = _WindowProperties ? _WindowProperties.WindowName() : winrt::hstring{}; winrt::hstring message; if (!windowName.empty()) { From 20dcfd098bdb42b5d9361ab974f63411986d1ac9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 3 Mar 2026 06:39:58 -0600 Subject: [PATCH 07/11] better names --- src/cascadia/TerminalApp/TerminalPaneContent.cpp | 12 ++++++------ src/cascadia/TerminalApp/TerminalPaneContent.h | 12 ++++++------ src/cascadia/TerminalControl/ControlCore.cpp | 16 ++++++++-------- src/cascadia/TerminalControl/ControlCore.h | 8 ++++---- src/cascadia/TerminalControl/ControlCore.idl | 4 ++-- src/cascadia/TerminalControl/TermControl.cpp | 12 ++++++------ src/cascadia/TerminalControl/TermControl.h | 12 ++++++------ src/cascadia/TerminalControl/TermControl.idl | 4 ++-- src/cascadia/TerminalCore/Terminal.cpp | 12 ++++++------ src/cascadia/TerminalCore/Terminal.hpp | 8 ++++---- src/cascadia/TerminalCore/TerminalApi.cpp | 8 ++++---- 11 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index 079b159594c..ba8c931064a 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -35,8 +35,8 @@ namespace winrt::TerminalApp::implementation { _controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &TerminalPaneContent::_controlConnectionStateChangedHandler }); _controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlWarningBellHandler }); - _controlEvents._PromptReturned = _control.PromptReturned(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlPromptReturnedHandler }); - _controlEvents._CommandStarted = _control.CommandStarted(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlCommandStartedHandler }); + _controlEvents._PromptStarted = _control.PromptStarted(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlPromptStartedHandler }); + _controlEvents._OutputStarted = _control.OutputStarted(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlOutputStartedHandler }); _controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_closeTerminalRequestedHandler }); _controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_restartTerminalRequestedHandler }); _controlEvents._OutputIdle = _control.OutputIdle(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlOutputIdleHandler }); @@ -351,8 +351,8 @@ namespace winrt::TerminalApp::implementation // the command has finished and we're back at a prompt. // - Checks NotifyOnNextPrompt setting and raises NotificationRequested. // - If autoDetectRunningCommand is enabled, clears the indeterminate progress ring. - void TerminalPaneContent::_controlPromptReturnedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) + void TerminalPaneContent::_controlPromptStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) { if (_profile) { @@ -380,8 +380,8 @@ namespace winrt::TerminalApp::implementation // - Raised when a shell integration command output mark (133;C) is received, // indicating a command has started executing. // - If autoDetectRunningCommand is enabled, shows an indeterminate progress ring. - void TerminalPaneContent::_controlCommandStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) + void TerminalPaneContent::_controlOutputStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*eventArgs*/) { if (_profile) { diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h index 8c89ca0b385..7d4b67b51ec 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.h +++ b/src/cascadia/TerminalApp/TerminalPaneContent.h @@ -84,8 +84,8 @@ namespace winrt::TerminalApp::implementation { winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged; winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell; - winrt::Microsoft::Terminal::Control::TermControl::PromptReturned_revoker _PromptReturned; - winrt::Microsoft::Terminal::Control::TermControl::CommandStarted_revoker _CommandStarted; + winrt::Microsoft::Terminal::Control::TermControl::PromptStarted_revoker _PromptStarted; + winrt::Microsoft::Terminal::Control::TermControl::OutputStarted_revoker _OutputStarted; winrt::Microsoft::Terminal::Control::TermControl::OutputIdle_revoker _OutputIdle; winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested; winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested; @@ -105,10 +105,10 @@ namespace winrt::TerminalApp::implementation safe_void_coroutine _controlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/); void _controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); - void _controlPromptReturnedHandler(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& e); - void _controlCommandStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& e); + void _controlPromptStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); + void _controlOutputStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); void _controlOutputIdleHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); void _controlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 7883bd4d5b1..31b3d8981b1 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -118,11 +118,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnWarningBell = [this] { _terminalWarningBell(); }; _terminal->SetWarningBellCallback(pfnWarningBell); - auto pfnPromptReturned = [this] { _terminalPromptReturned(); }; - _terminal->SetPromptReturnedCallback(pfnPromptReturned); + auto pfnPromptStarted = [this] { _terminalPromptStarted(); }; + _terminal->SetPromptStartedCallback(pfnPromptStarted); - auto pfnCommandStarted = [this] { _terminalCommandStarted(); }; - _terminal->SetCommandStartedCallback(pfnCommandStarted); + auto pfnOutputStarted = [this] { _terminalOutputStarted(); }; + _terminal->SetOutputStartedCallback(pfnOutputStarted); auto pfnTitleChanged = [this](auto&& PH1) { _terminalTitleChanged(std::forward(PH1)); }; _terminal->SetTitleChangedCallback(pfnTitleChanged); @@ -1603,7 +1603,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation WarningBell.raise(*this, nullptr); } - void ControlCore::_terminalPromptReturned() + void ControlCore::_terminalPromptStarted() { // Since this can only ever be triggered by output from the connection, // then the Terminal already has the write lock when calling this @@ -1612,10 +1612,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return; } - PromptReturned.raise(*this, nullptr); + PromptStarted.raise(*this, nullptr); } - void ControlCore::_terminalCommandStarted() + void ControlCore::_terminalOutputStarted() { // Since this can only ever be triggered by output from the connection, // then the Terminal already has the write lock when calling this @@ -1624,7 +1624,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return; } - CommandStarted.raise(*this, nullptr); + OutputStarted.raise(*this, nullptr); } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index c48d67ccc31..071f9dd4f89 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -276,8 +276,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event TitleChanged; til::typed_event WriteToClipboard; til::typed_event<> WarningBell; - til::typed_event<> PromptReturned; - til::typed_event<> CommandStarted; + til::typed_event<> PromptStarted; + til::typed_event<> OutputStarted; til::typed_event<> TabColorChanged; til::typed_event<> BackgroundColorChanged; til::typed_event ScrollPositionChanged; @@ -326,8 +326,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation #pragma region TerminalCoreCallbacks void _terminalWarningBell(); - void _terminalPromptReturned(); - void _terminalCommandStarted(); + void _terminalPromptStarted(); + void _terminalOutputStarted(); void _terminalTitleChanged(std::wstring_view wstr); void _terminalScrollPositionChanged(const int viewTop, const int viewHeight, diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 6f1b7666bee..9b09a6d7d6a 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -191,8 +191,8 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler WriteToClipboard; event Windows.Foundation.TypedEventHandler WarningBell; - event Windows.Foundation.TypedEventHandler PromptReturned; - event Windows.Foundation.TypedEventHandler CommandStarted; + event Windows.Foundation.TypedEventHandler PromptStarted; + event Windows.Foundation.TypedEventHandler OutputStarted; event Windows.Foundation.TypedEventHandler TabColorChanged; event Windows.Foundation.TypedEventHandler BackgroundColorChanged; event Windows.Foundation.TypedEventHandler TaskbarProgressChanged; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index ea6cd620cc1..2f8a9f45305 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -393,8 +393,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // attached content before we set up the throttled func, and that'll A/V _revokers.coreScrollPositionChanged = _core.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged }); _revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell }); - _revokers.PromptReturned = _core.PromptReturned(winrt::auto_revoke, { get_weak(), &TermControl::_corePromptReturned }); - _revokers.CommandStarted = _core.CommandStarted(winrt::auto_revoke, { get_weak(), &TermControl::_coreCommandStarted }); + _revokers.PromptStarted = _core.PromptStarted(winrt::auto_revoke, { get_weak(), &TermControl::_corePromptStarted }); + _revokers.OutputStarted = _core.OutputStarted(winrt::auto_revoke, { get_weak(), &TermControl::_coreOutputStarted }); static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000)); _autoScrollTimer.Interval(AutoScrollUpdateInterval); @@ -3701,14 +3701,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation _playWarningBell->Run(); } - void TermControl::_corePromptReturned(const IInspectable& /*sender*/, const IInspectable& /*args*/) + void TermControl::_corePromptStarted(const IInspectable& /*sender*/, const IInspectable& /*args*/) { - PromptReturned.raise(*this, nullptr); + PromptStarted.raise(*this, nullptr); } - void TermControl::_coreCommandStarted(const IInspectable& /*sender*/, const IInspectable& /*args*/) + void TermControl::_coreOutputStarted(const IInspectable& /*sender*/, const IInspectable& /*args*/) { - CommandStarted.raise(*this, nullptr); + OutputStarted.raise(*this, nullptr); } hstring TermControl::ReadEntireBuffer() const diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 291cb94f0f7..4c18b64fb1f 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -212,8 +212,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event FocusFollowMouseRequested; til::typed_event Initialized; til::typed_event<> WarningBell; - til::typed_event<> PromptReturned; - til::typed_event<> CommandStarted; + til::typed_event<> PromptStarted; + til::typed_event<> OutputStarted; til::typed_event<> OutputIdle; til::typed_event KeySent; til::typed_event CharSent; @@ -428,8 +428,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args); void _coreWarningBell(const IInspectable& sender, const IInspectable& args); - void _corePromptReturned(const IInspectable& sender, const IInspectable& args); - void _coreCommandStarted(const IInspectable& sender, const IInspectable& args); + void _corePromptStarted(const IInspectable& sender, const IInspectable& args); + void _coreOutputStarted(const IInspectable& sender, const IInspectable& args); void _coreOutputIdle(const IInspectable& sender, const IInspectable& args); winrt::Windows::Foundation::Point _toPosInDips(const Core::Point terminalCellPos); @@ -455,8 +455,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged; Control::ControlCore::WarningBell_revoker WarningBell; - Control::ControlCore::PromptReturned_revoker PromptReturned; - Control::ControlCore::CommandStarted_revoker CommandStarted; + Control::ControlCore::PromptStarted_revoker PromptStarted; + Control::ControlCore::OutputStarted_revoker OutputStarted; Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState; Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged; Control::ControlCore::FontSizeChanged_revoker FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 05038aa4373..15d714791e1 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -68,8 +68,8 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler RaiseNotice; event Windows.Foundation.TypedEventHandler WarningBell; - event Windows.Foundation.TypedEventHandler PromptReturned; - event Windows.Foundation.TypedEventHandler CommandStarted; + event Windows.Foundation.TypedEventHandler PromptStarted; + event Windows.Foundation.TypedEventHandler OutputStarted; event Windows.Foundation.TypedEventHandler OutputIdle; event Windows.Foundation.TypedEventHandler HidePointerCursor; event Windows.Foundation.TypedEventHandler RestorePointerCursor; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 6a1073a6d6a..3fbf9adc2d7 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -753,9 +753,9 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s } // regardless, start notify that we started command output - if (_pfnCommandStarted) + if (_pfnOutputStarted) { - _pfnCommandStarted(); + _pfnOutputStarted(); } } } @@ -1271,14 +1271,14 @@ void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function _pfnClearQuickFix.swap(pfn); } -void Terminal::SetPromptReturnedCallback(std::function pfn) noexcept +void Terminal::SetPromptStartedCallback(std::function pfn) noexcept { - _pfnPromptReturned.swap(pfn); + _pfnPromptStarted.swap(pfn); } -void Terminal::SetCommandStartedCallback(std::function pfn) noexcept +void Terminal::SetOutputStartedCallback(std::function pfn) noexcept { - _pfnCommandStarted.swap(pfn); + _pfnOutputStarted.swap(pfn); } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 09473c820cd..013f89ec8a1 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -232,8 +232,8 @@ class Microsoft::Terminal::Core::Terminal final : void SetSearchMissingCommandCallback(std::function pfn) noexcept; void SetClearQuickFixCallback(std::function pfn) noexcept; void SetWindowSizeChangedCallback(std::function pfn) noexcept; - void SetPromptReturnedCallback(std::function pfn) noexcept; - void SetCommandStartedCallback(std::function pfn) noexcept; + void SetPromptStartedCallback(std::function pfn) noexcept; + void SetOutputStartedCallback(std::function pfn) noexcept; void SetSearchHighlights(const std::vector& highlights) noexcept; void SetSearchHighlightFocused(size_t focusedIdx) noexcept; void ScrollToSearchHighlight(til::CoordType searchScrollOffset); @@ -342,8 +342,8 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnSearchMissingCommand; std::function _pfnClearQuickFix; std::function _pfnWindowSizeChanged; - std::function _pfnPromptReturned; - std::function _pfnCommandStarted; + std::function _pfnPromptStarted; + std::function _pfnOutputStarted; RenderSettings _renderSettings; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 6968f910d0b..fc9e5e32407 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -408,15 +408,15 @@ void Terminal::NotifyShellIntegrationMark(ShellIntegrationMark mark) switch (mark) { case ShellIntegrationMark::Prompt: - if (_pfnPromptReturned) + if (_pfnPromptStarted) { - _pfnPromptReturned(); + _pfnPromptStarted(); } break; case ShellIntegrationMark::Output: - if (_pfnCommandStarted) + if (_pfnOutputStarted) { - _pfnCommandStarted(); + _pfnOutputStarted(); } break; default: From 756a1d4d6df4cb1ca7e4c65b4fadadfc2d554852 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 4 Mar 2026 16:39:53 -0600 Subject: [PATCH 08/11] that's... not right --- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index b6b0c18baa1..c57fe7a5c4d 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -354,15 +354,13 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) } // Toast notification activations launch a new unelevated instance of the - // app with "__fromToast" as the sole command-line argument. This happens - // when the originating Terminal window is elevated — Windows cannot - // COM-activate an elevated process from a toast click, so it starts a new - // unelevated process instead. That process must not restore persisted - // layouts, create windows, or do anything else — just exit immediately. + // app with "__fromToast" as the sole command-line argument. It will always + // start a new instance of our exe. // - // We check this BEFORE the mutex handoff because the elevated instance - // owns the mutex and UIPI blocks WM_COPYDATA from our unelevated process, - // which would cause acquireMutexOrAttemptHandoff to spin for ~30s. + // However, we're also able to just handle the .Activated event on the toast + // itself, so we don't care about this process we're spawning. So before we + // do _anything_ else, if we were created for a toast, just immediately + // bail. const auto args = commandlineToArgArray(GetCommandLineW()); { if (args.size() == 2 && args[1] == L"__fromToast") From ad12c5b8a331bbbcf801c5a35f58e3bf608f7295 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 5 Mar 2026 06:52:39 -0600 Subject: [PATCH 09/11] spel --- src/cascadia/TerminalApp/Tab.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 2c55fba4df9..b8f64d44d02 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -1201,7 +1201,7 @@ namespace winrt::TerminalApp::implementation events.NotificationRequested = content.NotificationRequested( winrt::auto_revoke, - [dispatcher, weakThis](TerminalApp::IPaneContent sender, auto notifArgs) -> safe_void_coroutine { + [dispatcher, weakThis](TerminalApp::IPaneContent sender, auto notificationArgs) -> safe_void_coroutine { const auto weakThisCopy = weakThis; co_await wil::resume_foreground(dispatcher); if (const auto tab{ weakThisCopy.get() }) @@ -1212,15 +1212,15 @@ namespace winrt::TerminalApp::implementation const auto activeContent = tab->GetActiveContent(); const auto isActivePaneContent = activeContent && activeContent == sender; - if (notifArgs.OnlyWhenInactive() && isActivePaneContent && + if (notificationArgs.OnlyWhenInactive() && isActivePaneContent && tab->_focusState != WUX::FocusState::Unfocused) { co_return; } - const auto style = notifArgs.Style(); + const auto style = notificationArgs.Style(); - if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Taskbar)) + if (WI_IsFlagSet(style, OutputNotificationStyle::Taskbar)) { // Flash the taskbar button tab->TabRaiseVisualBell.raise(); From 7be3c7b9cb43db33eee38a3a73b3ce98d32c604a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 5 Mar 2026 09:10:21 -0600 Subject: [PATCH 10/11] format --- src/cascadia/TerminalCore/Terminal.cpp | 1 - .../TerminalSettingsModel/IInheritable.h | 4 +- .../TerminalSettingsModel/MTSMSettings.h | 64 +++++++++---------- .../WindowsTerminal/WindowEmperor.cpp | 2 +- src/cascadia/inc/ControlProperties.h | 60 ++++++++--------- tools/OpenConsole.psm1 | 7 ++ tools/runformat.cmd | 2 +- 7 files changed, 73 insertions(+), 67 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 3fbf9adc2d7..6ca87e44383 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -750,7 +750,6 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s // This changed the scrollbar marks - raise a notification to update them _NotifyScrollEvent(); - } // regardless, start notify that we started command output if (_pfnOutputStarted) diff --git a/src/cascadia/TerminalSettingsModel/IInheritable.h b/src/cascadia/TerminalSettingsModel/IInheritable.h index aae47f61db5..f25e3e81bb0 100644 --- a/src/cascadia/TerminalSettingsModel/IInheritable.h +++ b/src/cascadia/TerminalSettingsModel/IInheritable.h @@ -136,7 +136,7 @@ private: \ return std::nullopt; \ } \ \ - auto _get##name##OverrideSourceImpl()->decltype(get_strong()) \ + auto _get##name##OverrideSourceImpl() -> decltype(get_strong()) \ { \ /*we have a value*/ \ if (_##name) \ @@ -159,7 +159,7 @@ private: \ } \ \ auto _get##name##OverrideSourceAndValueImpl() \ - ->std::pair \ + -> std::pair \ { \ /*we have a value*/ \ if (_##name) \ diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index ede64d2fc00..d761177d1ce 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -79,38 +79,38 @@ Author(s): // * TerminalSettings.cpp: TerminalSettings::_ApplyProfileSettings // * IControlSettings.idl or ICoreSettings.idl // * ControlProperties.h -#define MTSM_PROFILE_SETTINGS(X) \ - X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \ - X(bool, SnapOnInput, "snapOnInput", true) \ - X(bool, AltGrAliasing, "altGrAliasing", true) \ - X(hstring, AnswerbackMessage, "answerbackMessage") \ - X(hstring, Commandline, "commandline", L"%SystemRoot%\\System32\\cmd.exe") \ - X(Microsoft::Terminal::Control::ScrollbarState, ScrollState, "scrollbarState", Microsoft::Terminal::Control::ScrollbarState::Visible) \ - X(Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, "antialiasingMode", Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ - X(hstring, StartingDirectory, "startingDirectory") \ - X(IMediaResource, Icon, "icon", implementation::MediaResource::FromString(L"\uE756")) \ - X(bool, SuppressApplicationTitle, "suppressApplicationTitle", false) \ - X(guid, ConnectionType, "connectionType") \ - X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic) \ - X(hstring, TabTitle, "tabTitle") \ - X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \ - X(IEnvironmentVariableMap, EnvironmentVariables, "environment", nullptr) \ - X(bool, RightClickContextMenu, "rightClickContextMenu", false) \ - X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ - X(bool, Elevate, "elevate", false) \ - X(bool, AutoMarkPrompts, "autoMarkPrompts", true) \ - X(bool, ShowMarks, "showMarksOnScrollbar", false) \ - X(bool, RepositionCursorWithMouse, "experimental.repositionCursorWithMouse", false) \ - X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true) \ - X(bool, RainbowSuggestions, "experimental.rainbowSuggestions", false) \ - X(bool, ForceVTInput, "compatibility.input.forceVT", false) \ - X(bool, AllowKittyKeyboardMode, "compatibility.kittyKeyboardMode", true) \ - X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \ - X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \ - X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \ - X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None) \ - X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnInactiveOutput, "notifyOnInactiveOutput", Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ - X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, "notifyOnNextPrompt", Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ +#define MTSM_PROFILE_SETTINGS(X) \ + X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \ + X(bool, SnapOnInput, "snapOnInput", true) \ + X(bool, AltGrAliasing, "altGrAliasing", true) \ + X(hstring, AnswerbackMessage, "answerbackMessage") \ + X(hstring, Commandline, "commandline", L"%SystemRoot%\\System32\\cmd.exe") \ + X(Microsoft::Terminal::Control::ScrollbarState, ScrollState, "scrollbarState", Microsoft::Terminal::Control::ScrollbarState::Visible) \ + X(Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, "antialiasingMode", Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ + X(hstring, StartingDirectory, "startingDirectory") \ + X(IMediaResource, Icon, "icon", implementation::MediaResource::FromString(L"\uE756")) \ + X(bool, SuppressApplicationTitle, "suppressApplicationTitle", false) \ + X(guid, ConnectionType, "connectionType") \ + X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic) \ + X(hstring, TabTitle, "tabTitle") \ + X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \ + X(IEnvironmentVariableMap, EnvironmentVariables, "environment", nullptr) \ + X(bool, RightClickContextMenu, "rightClickContextMenu", false) \ + X(Windows::Foundation::Collections::IVector, BellSound, "bellSound", nullptr) \ + X(bool, Elevate, "elevate", false) \ + X(bool, AutoMarkPrompts, "autoMarkPrompts", true) \ + X(bool, ShowMarks, "showMarksOnScrollbar", false) \ + X(bool, RepositionCursorWithMouse, "experimental.repositionCursorWithMouse", false) \ + X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true) \ + X(bool, RainbowSuggestions, "experimental.rainbowSuggestions", false) \ + X(bool, ForceVTInput, "compatibility.input.forceVT", false) \ + X(bool, AllowKittyKeyboardMode, "compatibility.kittyKeyboardMode", true) \ + X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \ + X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \ + X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \ + X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None) \ + X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnInactiveOutput, "notifyOnInactiveOutput", Microsoft::Terminal::Control::OutputNotificationStyle{ 0 }) \ + X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, "notifyOnNextPrompt", Microsoft::Terminal::Control::OutputNotificationStyle{ 0 }) \ X(Microsoft::Terminal::Control::AutoDetectRunningCommand, AutoDetectRunningCommand, "autoDetectRunningCommand", Microsoft::Terminal::Control::AutoDetectRunningCommand::Disabled) // Intentionally omitted Profile settings: diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index c57fe7a5c4d..ab95bda12e6 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -360,7 +360,7 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) // However, we're also able to just handle the .Activated event on the toast // itself, so we don't care about this process we're spawning. So before we // do _anything_ else, if we were created for a toast, just immediately - // bail. + // bail. const auto args = commandlineToArgArray(GetCommandLineW()); { if (args.size() == 2 && args[1] == L"__fromToast") diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index 1f2e96fed41..9ed02f4e095 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -60,34 +60,34 @@ // --------------------------- Control Settings --------------------------- // All of these settings are defined in IControlSettings. -#define CONTROL_SETTINGS(X) \ - X(winrt::guid, SessionId) \ - X(bool, EnableUnfocusedAcrylic, false) \ - X(winrt::hstring, Padding, DEFAULT_PADDING) \ - X(winrt::hstring, FontFace, L"Consolas") \ - X(float, FontSize, DEFAULT_FONT_SIZE) \ - X(winrt::Windows::UI::Text::FontWeight, FontWeight) \ - X(IFontFeatureMap, FontFeatures) \ - X(IFontAxesMap, FontAxes) \ - X(bool, EnableBuiltinGlyphs, true) \ - X(bool, EnableColorGlyphs, true) \ - X(winrt::hstring, CellWidth) \ - X(winrt::hstring, CellHeight) \ - X(winrt::hstring, Commandline) \ - X(winrt::hstring, StartingDirectory) \ - X(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible) \ - X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ - X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI) \ - X(bool, DisablePartialInvalidation, false) \ - X(bool, SoftwareRendering, false) \ - X(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement) \ - X(winrt::Microsoft::Terminal::Control::AmbiguousWidth, AmbiguousWidth, winrt::Microsoft::Terminal::Control::AmbiguousWidth::Narrow) \ - X(winrt::Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope::Default) \ - X(bool, UseBackgroundImageForWindow, false) \ - X(bool, ShowMarks, false) \ - X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \ - X(bool, RightClickContextMenu, false) \ - X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None) \ - X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnInactiveOutput, winrt::Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ - X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, winrt::Microsoft::Terminal::Control::OutputNotificationStyle{0}) \ +#define CONTROL_SETTINGS(X) \ + X(winrt::guid, SessionId) \ + X(bool, EnableUnfocusedAcrylic, false) \ + X(winrt::hstring, Padding, DEFAULT_PADDING) \ + X(winrt::hstring, FontFace, L"Consolas") \ + X(float, FontSize, DEFAULT_FONT_SIZE) \ + X(winrt::Windows::UI::Text::FontWeight, FontWeight) \ + X(IFontFeatureMap, FontFeatures) \ + X(IFontAxesMap, FontAxes) \ + X(bool, EnableBuiltinGlyphs, true) \ + X(bool, EnableColorGlyphs, true) \ + X(winrt::hstring, CellWidth) \ + X(winrt::hstring, CellHeight) \ + X(winrt::hstring, Commandline) \ + X(winrt::hstring, StartingDirectory) \ + X(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible) \ + X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ + X(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI) \ + X(bool, DisablePartialInvalidation, false) \ + X(bool, SoftwareRendering, false) \ + X(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement) \ + X(winrt::Microsoft::Terminal::Control::AmbiguousWidth, AmbiguousWidth, winrt::Microsoft::Terminal::Control::AmbiguousWidth::Narrow) \ + X(winrt::Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope::Default) \ + X(bool, UseBackgroundImageForWindow, false) \ + X(bool, ShowMarks, false) \ + X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, 0) \ + X(bool, RightClickContextMenu, false) \ + X(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None) \ + X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnInactiveOutput, winrt::Microsoft::Terminal::Control::OutputNotificationStyle{ 0 }) \ + X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, winrt::Microsoft::Terminal::Control::OutputNotificationStyle{ 0 }) \ X(winrt::Microsoft::Terminal::Control::AutoDetectRunningCommand, AutoDetectRunningCommand, winrt::Microsoft::Terminal::Control::AutoDetectRunningCommand::Disabled) diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index b9c80ce4579..12078e862ac 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -417,8 +417,15 @@ function Invoke-CodeFormat() { ) $clangFormatPath = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -latest -find "**\x64\bin\clang-format.exe" + If ([String]::IsNullOrEmpty($clangFormatPath)) { + # try again with prerelease versions of Visual Studio, + # and just take the first + $clangFormatPath = & 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe' -prerelease -find "**\clang-format.exe" | Select-Object -First 1 + } + If ([String]::IsNullOrEmpty($clangFormatPath)) { Write-Error "No Visual Studio-supplied version of clang-format could be found." + return -1 } $root = Find-OpenConsoleRoot diff --git a/tools/runformat.cmd b/tools/runformat.cmd index d9c1d56c73f..9c83fdd004f 100644 --- a/tools/runformat.cmd +++ b/tools/runformat.cmd @@ -2,4 +2,4 @@ rem run clang-format on c++ files -powershell -noprofile "import-module %OPENCON_TOOLS%\openconsole.psm1; Invoke-CodeFormat" +pwsh -noprofile "import-module %OPENCON_TOOLS%\openconsole.psm1; Invoke-CodeFormat" From 728e81f7dab0991754554f7e0033e75ba095171b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 5 Mar 2026 09:28:21 -0600 Subject: [PATCH 11/11] spel and format? --- src/cascadia/TerminalApp/TabManagement.cpp | 2 +- src/cascadia/TerminalApp/TerminalPaneContent.cpp | 2 +- src/cascadia/TerminalSettingsModel/IInheritable.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 6f9a9439dca..aa0a7a3545e 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -1205,7 +1205,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_SendDesktopNotification(const winrt::hstring& tabTitle, uint32_t tabIndex) { // Build the notification message. - // Use the window name if available for context, otherwise just use the tab title. + // Use the window name if available for context; otherwise just use the tab title. // Use the raw WindowName (not WindowNameForDisplay) so we don't include // the "" placeholder in the notification body. const auto windowName = _WindowProperties ? _WindowProperties.WindowName() : winrt::hstring{}; diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp index ba8c931064a..3abf0436e90 100644 --- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp +++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp @@ -267,7 +267,7 @@ namespace winrt::TerminalApp::implementation // - // Method Description: // - Plays the notification sound using the profile's BellSound setting if - // configured, otherwise falls back to the system "Critical Stop" sound. + // configured; otherwise falls back to the system "Critical Stop" sound. // Reused by the warning bell handler, NotifyOnNextPrompt, and // NotifyOnInactiveOutput (called from Tab after the active-pane check). void TerminalPaneContent::PlayNotificationSound() diff --git a/src/cascadia/TerminalSettingsModel/IInheritable.h b/src/cascadia/TerminalSettingsModel/IInheritable.h index f25e3e81bb0..aae47f61db5 100644 --- a/src/cascadia/TerminalSettingsModel/IInheritable.h +++ b/src/cascadia/TerminalSettingsModel/IInheritable.h @@ -136,7 +136,7 @@ private: \ return std::nullopt; \ } \ \ - auto _get##name##OverrideSourceImpl() -> decltype(get_strong()) \ + auto _get##name##OverrideSourceImpl()->decltype(get_strong()) \ { \ /*we have a value*/ \ if (_##name) \ @@ -159,7 +159,7 @@ private: \ } \ \ auto _get##name##OverrideSourceAndValueImpl() \ - -> std::pair \ + ->std::pair \ { \ /*we have a value*/ \ if (_##name) \