PRE-MERGE #20014 Add settings for "notifying" on "activity"

This commit is contained in:
Carlos Zamora
2026-06-18 17:47:35 -07:00
49 changed files with 977 additions and 147 deletions

View File

@@ -2906,6 +2906,85 @@
"description": "Sets the sound played when the application emits a BEL. When set to an array, the terminal will pick one of those sounds at random.",
"$ref": "#/$defs/BellSound"
},
"notifyOnActivity": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string",
"enum": [
"taskbar",
"audible",
"tab",
"notification"
]
}
},
{
"type": "string",
"enum": [
"taskbar",
"audible",
"tab",
"notification",
"all",
"none"
]
}
],
"description": "Controls how you are notified when an inactive tab produces new output."
},
"notifyOnNextPrompt": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "array",
"items": {
"type": "string",
"enum": [
"taskbar",
"audible",
"tab",
"notification"
]
}
},
{
"type": "string",
"enum": [
"taskbar",
"audible",
"tab",
"notification",
"all",
"none"
]
}
],
"description": "Controls how you are notified when a new shell prompt is detected. Requires shell integration."
},
"notifyOnActivityThreshold": {
"type": "integer",
"minimum": 0,
"default": 5,
"description": "Minimum number of seconds between consecutive activity notifications for this profile. Use this to suppress repeated notifications from chatty processes. The first notification after the pane has been silent always fires; subsequent notifications within this window are suppressed. Use 0 to always notify."
},
"notifyOnNextPromptThreshold": {
"type": "integer",
"minimum": 0,
"default": 5,
"description": "Suppress the next-prompt notification unless the just-finished command ran for at least this many seconds. Requires shell integration. Use 0 to always notify."
},
"autoDetectRunningCommand": {
"default": false,
"description": "Automatically detect when a command is running and show a progress indicator in the tab and taskbar.",
"type": "boolean"
},
"closeOnExit": {
"default": "automatic",
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"automatic\" (behave as \"graceful\" only for processes launched by terminal, behave as \"always\" otherwise)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",

View File

@@ -231,6 +231,12 @@
Glyph=""
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).BellIndicator, Mode=OneWay}" />
<FontIcon Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="8"
Glyph="&#xF127;"
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).ActivityIndicator, Mode=OneWay}" />
<FontIcon Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"

View File

@@ -21,6 +21,8 @@ namespace TerminalApp
{
String Title { get; };
String Body { get; };
Microsoft.Terminal.Control.OutputNotificationStyle Style { get; };
Boolean AlwaysNotify { get; };
};
interface IPaneContent
@@ -31,7 +33,7 @@ namespace TerminalApp
Windows.Foundation.Size MinimumSize { get; };
String Title { get; };
UInt64 TaskbarState { get; };
Microsoft.Terminal.Control.TaskbarState TaskbarState { get; };
UInt64 TaskbarProgress { get; };
Boolean ReadOnly { get; };
String Icon { get; };

View File

@@ -33,7 +33,7 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const;
winrt::hstring Title() { return _filePath; }
uint64_t TaskbarState() { return 0; }
winrt::Microsoft::Terminal::Control::TaskbarState TaskbarState() { return winrt::Microsoft::Terminal::Control::TaskbarState::Clear; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;

View File

@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const;
winrt::hstring Title() { return L"Scratchpad"; }
uint64_t TaskbarState() { return 0; }
winrt::Microsoft::Terminal::Control::TaskbarState TaskbarState() { return winrt::Microsoft::Terminal::Control::TaskbarState::Clear; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;

View File

@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(const BuildStartupKind kind) const;
winrt::hstring Title() { return RS_(L"SettingsTab"); }
uint64_t TaskbarState() { return 0; }
winrt::Microsoft::Terminal::Control::TaskbarState TaskbarState() { return winrt::Microsoft::Terminal::Control::TaskbarState::Clear; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;

View File

@@ -24,7 +24,7 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const;
winrt::hstring Title() { return RS_(L"SnippetPaneTitle/Text"); }
uint64_t TaskbarState() { return 0; }
winrt::Microsoft::Terminal::Control::TaskbarState TaskbarState() { return winrt::Microsoft::Terminal::Control::TaskbarState::Clear; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;

View File

@@ -123,6 +123,12 @@ namespace winrt::TerminalApp::implementation
_bellIndicatorTimer.Stop();
}
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 +335,10 @@ namespace winrt::TerminalApp::implementation
{
ShowBellIndicator(false);
}
if (_tabStatus.ActivityIndicator())
{
ShowActivityIndicator(false);
}
}
}
@@ -459,6 +469,26 @@ namespace winrt::TerminalApp::implementation
_bellIndicatorTimer.Start();
}
void Tab::ShowActivityIndicator(const bool show)
{
ASSERT_UI_THREAD();
_tabStatus.ActivityIndicator(show);
}
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.
@@ -1181,8 +1211,45 @@ namespace winrt::TerminalApp::implementation
co_await wil::resume_foreground(dispatcher);
if (const auto tab{ weakThisCopy.get() })
{
const auto title = notifArgs.Title().empty() ? tab->Title() : notifArgs.Title();
tab->TabToastNotificationRequested.raise(title, notifArgs.Body(), sender);
const auto activeContent = tab->GetActiveContent();
const auto isActivePaneContent = activeContent && activeContent == sender;
if (!notifArgs.AlwaysNotify() && isActivePaneContent &&
tab->_focusState != WUX::FocusState::Unfocused)
{
co_return;
}
const auto style = notifArgs.Style();
if (WI_IsFlagSet(style, OutputNotificationStyle::Taskbar))
{
tab->TabRaiseVisualBell.raise();
}
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Audible))
{
if (const auto termContent{ sender.try_as<TerminalApp::TerminalPaneContent>() })
{
termContent.PlayNotificationSound();
}
}
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Tab))
{
tab->ShowActivityIndicator(true);
if (tab->_focusState != WUX::FocusState::Unfocused)
{
tab->ActivateActivityIndicatorTimer();
}
}
if (WI_IsFlagSet(style, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::Notification))
{
const auto title = notifArgs.Title().empty() ? tab->Title() : notifArgs.Title();
tab->TabToastNotificationRequested.raise(title, notifArgs.Body(), sender);
}
}
});
@@ -1242,11 +1309,10 @@ namespace winrt::TerminalApp::implementation
const auto taskbarState = state.State();
// The progress of the control changed, but not necessarily the progress of the tab.
// Set the tab's progress ring to the active pane's progress
if (taskbarState > 0)
if (taskbarState != winrt::Microsoft::Terminal::Control::TaskbarState::Clear)
{
if (taskbarState == 3)
if (taskbarState == winrt::Microsoft::Terminal::Control::TaskbarState::Indeterminate)
{
// 3 is the indeterminate state, set the progress ring as such
_tabStatus.IsProgressRingIndeterminate(true);
}
else
@@ -1422,6 +1488,10 @@ namespace winrt::TerminalApp::implementation
{
tab->ShowBellIndicator(false);
}
if (tab->_tabStatus.ActivityIndicator())
{
tab->ShowActivityIndicator(false);
}
}
});

View File

@@ -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<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
const float splitSize,
@@ -215,6 +218,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();

View File

@@ -32,6 +32,12 @@
FontSize="12"
Glyph="&#xEA8F;"
Visibility="{x:Bind TabStatus.BellIndicator, Mode=OneWay}" />
<FontIcon x:Name="HeaderActivityIndicator"
Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="8"
Glyph="&#xF127;"
Visibility="{x:Bind TabStatus.ActivityIndicator, Mode=OneWay}" />
<FontIcon x:Name="HeaderZoomIcon"
Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"

View File

@@ -1416,8 +1416,8 @@ namespace winrt::TerminalApp::implementation
{
// The toast Activated callback runs on a background thread.
// Marshal to the UI thread for tab focus and window summon.
page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakPage{ page->get_weak() }, weakTab, weakContent]() {
if (const auto p{ weakPage.get() })
page->Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis, weakTab, weakContent]() {
if (const auto p{ weakThis.get() })
{
if (const auto t{ weakTab.get() })
{

View File

@@ -9,27 +9,28 @@ namespace winrt::TerminalApp::implementation
{
// Default to unset, 0%.
TaskbarState::TaskbarState() :
TaskbarState(0, 0) {};
TaskbarState(winrt::Microsoft::Terminal::Control::TaskbarState::Clear, 0) {};
TaskbarState::TaskbarState(const uint64_t dispatchTypesState, const uint64_t progressParam) :
_State{ dispatchTypesState },
TaskbarState::TaskbarState(const winrt::Microsoft::Terminal::Control::TaskbarState state, const uint64_t progressParam) :
_State{ state },
_Progress{ progressParam } {}
uint64_t TaskbarState::Priority() const
{
// This seemingly nonsensical ordering is from
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist3-setprogressstate#how-the-taskbar-button-chooses-the-progress-indicator-for-a-group
using TbState = winrt::Microsoft::Terminal::Control::TaskbarState;
switch (_State)
{
case 0: // Clear = 0,
case TbState::Clear:
return 5;
case 1: // Set = 1,
case TbState::Set:
return 3;
case 2: // Error = 2,
case TbState::Error:
return 1;
case 3: // Indeterminate = 3,
case TbState::Indeterminate:
return 4;
case 4: // Paused = 4
case TbState::Paused:
return 2;
}
// Here, return 6, to definitely be greater than all the other valid values.

View File

@@ -16,13 +16,13 @@ namespace winrt::TerminalApp::implementation
{
public:
TaskbarState();
TaskbarState(const uint64_t dispatchTypesState, const uint64_t progress);
TaskbarState(const winrt::Microsoft::Terminal::Control::TaskbarState state, const uint64_t progress);
static int ComparePriority(const winrt::TerminalApp::TaskbarState& lhs, const winrt::TerminalApp::TaskbarState& rhs);
uint64_t Priority() const;
WINRT_PROPERTY(uint64_t, State, 0);
WINRT_PROPERTY(winrt::Microsoft::Terminal::Control::TaskbarState, State, winrt::Microsoft::Terminal::Control::TaskbarState::Clear);
WINRT_PROPERTY(uint64_t, Progress, 0);
};
}

View File

@@ -6,9 +6,9 @@ namespace TerminalApp
[default_interface] runtimeclass TaskbarState
{
TaskbarState();
TaskbarState(UInt64 dispatchTypesState, UInt64 progress);
TaskbarState(Microsoft.Terminal.Control.TaskbarState state, UInt64 progress);
UInt64 State{ get; };
Microsoft.Terminal.Control.TaskbarState State{ get; };
UInt64 Progress{ get; };
UInt64 Priority { get; };
}

View File

@@ -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,6 +35,7 @@ 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._PromptStarted = _control.PromptStarted(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlPromptStartedHandler });
_controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_closeTerminalRequestedHandler });
_controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_restartTerminalRequestedHandler });
@@ -42,6 +44,8 @@ namespace winrt::TerminalApp::implementation
_controlEvents._SetTaskbarProgress = _control.SetTaskbarProgress(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlSetTaskbarProgress });
_controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlReadOnlyChanged });
_controlEvents._FocusFollowMouseRequested = _control.FocusFollowMouseRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlFocusFollowMouseRequested });
_controlEvents._OutputStarted = _control.OutputStarted(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlOutputStartedHandler });
_controlEvents._OutputBurstEnded = _control.OutputIdle(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlOutputBurstEndedHandler });
_controlEvents._ShowNotification = _control.ShowNotification(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlShowNotification });
}
void TerminalPaneContent::_removeControlEvents()
@@ -162,6 +166,16 @@ namespace winrt::TerminalApp::implementation
{
TaskbarProgressChanged.raise(*this, nullptr);
}
winrt::Microsoft::Terminal::Control::TaskbarState TerminalPaneContent::TaskbarState()
{
return _control.TaskbarState();
}
uint64_t TerminalPaneContent::TaskbarProgress()
{
return _control.TaskbarProgress();
}
void TerminalPaneContent::_controlReadOnlyChanged(const IInspectable&, const IInspectable&)
{
ReadOnlyChanged.raise(*this, nullptr);
@@ -173,7 +187,7 @@ namespace winrt::TerminalApp::implementation
void TerminalPaneContent::_controlShowNotification(const IInspectable& /*sender*/, const ShowNotificationEventArgs& args)
{
NotificationRequested.raise(*this, winrt::make<implementation::NotificationEventArgs>(args.Title(), args.Body()));
NotificationRequested.raise(*this, winrt::make<implementation::NotificationEventArgs>(OutputNotificationStyle::Notification, true /*alwaysNotify*/, args.Title(), args.Body()));
}
// Method Description:
@@ -255,6 +269,26 @@ namespace winrt::TerminalApp::implementation
// has the 'visual' flag set
// Arguments:
// - <unused>
void TerminalPaneContent::PlayNotificationSound()
{
if (_profile)
{
auto sounds{ _profile.BellSound() };
if (sounds && sounds.Size() > 0)
{
// Sound paths are resolved and validated by CascadiaSettings
// before we reach this point.
auto soundPath{ sounds.GetAt(rand() % sounds.Size()).Resolved() };
PlaySoundW(soundPath.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_SENTRY | SND_NODEFAULT);
}
else
{
const auto soundAlias = reinterpret_cast<LPCWSTR>(SND_ALIAS_SYSTEMHAND);
PlaySoundW(soundAlias, nullptr, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
}
}
}
void TerminalPaneContent::_controlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
{
@@ -266,19 +300,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)
{
// Sound paths are resolved and validated by CascadiaSettings
// before we reach this point.
auto soundPath{ sounds.GetAt(rand() % sounds.Size()).Resolved() };
PlaySoundW(soundPath.c_str(), nullptr, SND_FILENAME | SND_ASYNC | SND_SENTRY | SND_NODEFAULT);
}
else
{
const auto soundAlias = reinterpret_cast<LPCWSTR>(SND_ALIAS_SYSTEMHAND);
PlaySoundW(soundAlias, nullptr, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
}
PlayNotificationSound();
}
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
@@ -295,6 +317,67 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPaneContent::_controlPromptStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
{
if (_profile)
{
const auto notifyStyle = _profile.NotifyOnNextPrompt();
if (notifyStyle != OutputNotificationStyle::None)
{
if (const auto thresholdInSeconds = _profile.NotifyOnNextPromptThreshold(); thresholdInSeconds > 0)
{
if (_lastOutputStartedAt == 0)
{
return;
}
if (const auto elapsedMs = GetTickCount64() - _lastOutputStartedAt; elapsedMs < (static_cast<uint64_t>(thresholdInSeconds) * 1000))
{
_lastOutputStartedAt = 0;
return;
}
}
_lastOutputStartedAt = 0;
NotificationRequested.raise(*this,
*winrt::make_self<TerminalApp::implementation::NotificationEventArgs>(notifyStyle, false));
}
}
}
void TerminalPaneContent::_controlOutputStartedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
{
_lastOutputStartedAt = GetTickCount64();
}
// The underlying TermControl::OutputIdle event is fired on the trailing
// edge of a 100ms-debounced output burst (see ControlCore::Initialize).
// When "notifyOnActivity" is enabled, we get one event per burst of
// output, which naturally coalesces a stream of output into a single notification.
void TerminalPaneContent::_controlOutputBurstEndedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
{
if (_profile)
{
const auto notifyStyle = _profile.NotifyOnActivity();
if (notifyStyle != OutputNotificationStyle::None)
{
const auto now = GetTickCount64();
const auto thresholdSeconds = _profile.NotifyOnActivityThreshold();
if (thresholdSeconds > 0 &&
_lastActivityNotificationAt != 0 &&
(now - _lastActivityNotificationAt) < (static_cast<uint64_t>(thresholdSeconds) * 1000))
{
return;
}
_lastActivityNotificationAt = now;
NotificationRequested.raise(*this,
*winrt::make_self<TerminalApp::implementation::NotificationEventArgs>(notifyStyle, false));
}
}
}
void TerminalPaneContent::_closeTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{

View File

@@ -24,9 +24,11 @@ namespace winrt::TerminalApp::implementation
struct NotificationEventArgs : public NotificationEventArgsT<NotificationEventArgs>
{
public:
NotificationEventArgs(const winrt::hstring& title = {}, const winrt::hstring& body = {}) :
Title(title), Body(body) {}
NotificationEventArgs(winrt::Microsoft::Terminal::Control::OutputNotificationStyle style, bool alwaysNotify = true, const winrt::hstring& title = {}, const winrt::hstring& body = {}) :
Style(style), AlwaysNotify(alwaysNotify), Title(title), Body(body) {}
til::property<winrt::Microsoft::Terminal::Control::OutputNotificationStyle> Style;
til::property<bool> AlwaysNotify;
til::property<winrt::hstring> Title;
til::property<winrt::hstring> Body;
};
@@ -48,6 +50,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
{
@@ -55,8 +58,8 @@ namespace winrt::TerminalApp::implementation
}
winrt::hstring Title() { return _control.Title(); }
uint64_t TaskbarState() { return _control.TaskbarState(); }
uint64_t TaskbarProgress() { return _control.TaskbarProgress(); }
winrt::Microsoft::Terminal::Control::TaskbarState TaskbarState();
uint64_t TaskbarProgress();
bool ReadOnly() { return _control.ReadOnly(); }
winrt::hstring Icon() const;
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept;
@@ -76,10 +79,18 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<TerminalSettingsCache> _cache{};
bool _isDefTermSession{ false };
// Tracks the GetTickCount64() for NotifyOnActivityThreshold
// and NotifyOnNextPromptThreshold respectively.
uint64_t _lastActivityNotificationAt{ 0 };
uint64_t _lastOutputStartedAt{ 0 };
struct ControlEventTokens
{
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
winrt::Microsoft::Terminal::Control::TermControl::PromptStarted_revoker _PromptStarted;
winrt::Microsoft::Terminal::Control::TermControl::OutputStarted_revoker _OutputStarted;
winrt::Microsoft::Terminal::Control::TermControl::OutputIdle_revoker _OutputBurstEnded;
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
@@ -97,6 +108,9 @@ 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 _controlPromptStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& eventArgs);
void _controlOutputStartedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& eventArgs);
void _controlOutputBurstEndedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& eventArgs);
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);

View File

@@ -11,6 +11,7 @@ namespace TerminalApp
Microsoft.Terminal.Control.TermControl GetTermControl();
void MarkAsDefterm();
void PlayNotificationSound();
Microsoft.Terminal.Settings.Model.Profile GetProfile();

View File

@@ -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);

View File

@@ -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; };

View File

@@ -139,6 +139,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnSearchMissingCommand = [this](auto&& PH1, auto&& PH2) { _terminalSearchMissingCommand(std::forward<decltype(PH1)>(PH1), std::forward<decltype(PH2)>(PH2)); };
_terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand);
auto pfnPromptStarted = [this] { _terminalPromptStarted(); };
_terminal->SetPromptStartedCallback(pfnPromptStarted);
auto pfnOutputStarted = [this] { _terminalOutputStarted(); };
_terminal->SetOutputStartedCallback(pfnOutputStarted);
auto pfnShowNotification = [this](auto&& PH1, auto&& PH2) { _terminalShowNotification(std::forward<decltype(PH1)>(PH1), std::forward<decltype(PH2)>(PH2)); };
_terminal->SetShowNotificationCallback(pfnShowNotification);
@@ -916,6 +922,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_hasUnfocusedAppearance = static_cast<bool>(newAppearance);
_unfocusedAppearance = _hasUnfocusedAppearance ? newAppearance : settings;
// Cache the auto-detect setting in an atomic so the off-thread output/prompt
// callbacks can read it without synchronizing with _settings. If the effective
// taskbar state changes (because a command is currently active and the setting
// toggled), notify listeners.
const auto nowEnabled = _settings.AutoDetectRunningCommand();
const auto wasEnabled = _autoDetectCommandActivity.exchange(nowEnabled, std::memory_order_relaxed);
if (wasEnabled != nowEnabled && _commandActive.load(std::memory_order_relaxed))
{
TaskbarProgressChanged.raise(*this, nullptr);
}
const auto lock = _terminal->LockForWriting();
_builtinGlyphs = _settings.EnableBuiltinGlyphs();
@@ -1551,10 +1568,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Gets the internal taskbar state value
// Return Value:
// - The taskbar state of this control
const size_t ControlCore::TaskbarState() const noexcept
const Control::TaskbarState ControlCore::TaskbarState() const noexcept
{
const auto lock = _terminal->LockForReading();
return _terminal->GetTaskbarState();
const auto vtState = static_cast<Control::TaskbarState>(_terminal->GetTaskbarState());
if (vtState == Control::TaskbarState::Clear &&
_autoDetectCommandActivity.load(std::memory_order_relaxed) &&
_commandActive.load(std::memory_order_relaxed))
{
return Control::TaskbarState::Indeterminate;
}
return vtState;
}
// Method Description:
@@ -1603,6 +1627,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WarningBell.raise(*this, nullptr);
}
void ControlCore::_terminalPromptStarted()
{
if (_commandActive.exchange(false, std::memory_order_relaxed) &&
_autoDetectCommandActivity.load(std::memory_order_relaxed))
{
TaskbarProgressChanged.raise(*this, nullptr);
}
PromptStarted.raise(*this, nullptr);
}
void ControlCore::_terminalOutputStarted()
{
if (!_commandActive.exchange(true, std::memory_order_relaxed) &&
_autoDetectCommandActivity.load(std::memory_order_relaxed))
{
TaskbarProgressChanged.raise(*this, nullptr);
}
OutputStarted.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.

View File

@@ -161,7 +161,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void OpenCWD();
#pragma region ICoreState
const size_t TaskbarState() const noexcept;
const Control::TaskbarState TaskbarState() const noexcept;
const size_t TaskbarProgress() const noexcept;
hstring Title();
@@ -275,6 +275,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::TitleChangedEventArgs> TitleChanged;
til::typed_event<IInspectable, Control::WriteToClipboardEventArgs> WriteToClipboard;
til::typed_event<> WarningBell;
til::typed_event<> PromptStarted;
til::typed_event<> OutputStarted;
til::typed_event<> TabColorChanged;
til::typed_event<> BackgroundColorChanged;
til::typed_event<IInspectable, Control::ScrollPositionChangedArgs> ScrollPositionChanged;
@@ -324,6 +326,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma region TerminalCoreCallbacks
void _terminalWarningBell();
void _terminalPromptStarted();
void _terminalOutputStarted();
void _terminalTitleChanged(std::wstring_view wstr);
void _terminalScrollPositionChanged(const int viewTop,
const int viewHeight,
@@ -422,6 +426,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::optional<til::point> _lastHoveredCell;
uint16_t _lastHoveredId{ 0 };
std::atomic<bool> _initializedTerminal{ false };
std::atomic<bool> _autoDetectCommandActivity{ false };
std::atomic<bool> _commandActive{ false };
bool _isReadOnly{ false };
bool _closing{ false };

View File

@@ -192,6 +192,8 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, TitleChangedEventArgs> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, WriteToClipboardEventArgs> WriteToClipboard;
event Windows.Foundation.TypedEventHandler<Object, Object> WarningBell;
event Windows.Foundation.TypedEventHandler<Object, Object> PromptStarted;
event Windows.Foundation.TypedEventHandler<Object, Object> OutputStarted;
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> BackgroundColorChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> TaskbarProgressChanged;

View File

@@ -29,6 +29,30 @@ namespace Microsoft.Terminal.Control
MinGW,
};
[flags]
enum OutputNotificationStyle
{
None = 0,
Taskbar = 0x1,
Audible = 0x2,
Tab = 0x4,
Notification = 0x8,
All = 0xffffffff
};
// Mirrors Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState
// (which is shared with conhost and lives outside the WinRT projection).
// Values must remain numerically identical so the conversion at the
// ControlCore boundary is a 1:1 cast.
enum TaskbarState
{
Clear = 0,
Set = 1,
Error = 2,
Indeterminate = 3,
Paused = 4,
};
// Class Description:
// TerminalSettings encapsulates all settings that control the
// TermControl's behavior. In these settings there is both the entirety
@@ -78,6 +102,12 @@ namespace Microsoft.Terminal.Control
PathTranslationStyle PathTranslationStyle { get; };
String DragDropDelimiter { get; };
OutputNotificationStyle NotifyOnActivity { get; };
OutputNotificationStyle NotifyOnNextPrompt { get; };
Int32 NotifyOnActivityThreshold { get; };
Int32 NotifyOnNextPromptThreshold { get; };
Boolean AutoDetectRunningCommand { get; };
// NOTE! When adding something here, make sure to update ControlProperties.h too!
};
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "IControlSettings.idl";
namespace Microsoft.Terminal.Control
{
enum MarkCategory
@@ -31,7 +33,7 @@ namespace Microsoft.Terminal.Control
interface ICoreState
{
String Title { get; };
UInt64 TaskbarState { get; };
Microsoft.Terminal.Control.TaskbarState TaskbarState { get; };
UInt64 TaskbarProgress { get; };
String WorkingDirectory { get; };

View File

@@ -394,6 +394,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.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<int>(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
@@ -3357,7 +3359,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Gets the internal taskbar state value
// Return Value:
// - The taskbar state of this control
const uint64_t TermControl::TaskbarState() const noexcept
const Control::TaskbarState TermControl::TaskbarState() const noexcept
{
return _core.TaskbarState();
}
@@ -3727,6 +3729,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_playWarningBell->Run();
}
void TermControl::_corePromptStarted(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
PromptStarted.raise(*this, nullptr);
}
void TermControl::_coreOutputStarted(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
OutputStarted.raise(*this, nullptr);
}
hstring TermControl::ReadEntireBuffer() const
{
return _core.ReadEntireBuffer();
@@ -3844,6 +3856,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)

View File

@@ -86,7 +86,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode);
#pragma region ICoreState
const uint64_t TaskbarState() const noexcept;
const Control::TaskbarState TaskbarState() const noexcept;
const uint64_t TaskbarProgress() const noexcept;
hstring Title();
@@ -211,6 +211,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, IInspectable> FocusFollowMouseRequested;
til::typed_event<Control::TermControl, Windows::UI::Xaml::RoutedEventArgs> Initialized;
til::typed_event<> WarningBell;
til::typed_event<> PromptStarted;
til::typed_event<> OutputStarted;
til::typed_event<> OutputIdle;
til::typed_event<IInspectable, Control::KeySentEventArgs> KeySent;
til::typed_event<IInspectable, Control::CharSentEventArgs> CharSent;
til::typed_event<IInspectable, Control::StringSentEventArgs> 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 _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);
@@ -450,6 +455,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged;
Control::ControlCore::WarningBell_revoker WarningBell;
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;

View File

@@ -69,6 +69,9 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
event Windows.Foundation.TypedEventHandler<Object, NoticeEventArgs> RaiseNotice;
event Windows.Foundation.TypedEventHandler<Object, Object> WarningBell;
event Windows.Foundation.TypedEventHandler<Object, Object> PromptStarted;
event Windows.Foundation.TypedEventHandler<Object, Object> OutputStarted;
event Windows.Foundation.TypedEventHandler<Object, Object> OutputIdle;
event Windows.Foundation.TypedEventHandler<Object, Object> HidePointerCursor;
event Windows.Foundation.TypedEventHandler<Object, Object> RestorePointerCursor;
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;

View File

@@ -768,6 +768,11 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s
// This changed the scrollbar marks - raise a notification to update them
_NotifyScrollEvent();
}
// regardless, notify that we started command output
if (_pfnOutputStarted)
{
_pfnOutputStarted();
}
}
}
@@ -1253,7 +1258,7 @@ const std::optional<til::color> Terminal::GetTabColor() const
// - Gets the internal taskbar state value
// Return Value:
// - The taskbar state
const size_t Microsoft::Terminal::Core::Terminal::GetTaskbarState() const noexcept
const Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState Microsoft::Terminal::Core::Terminal::GetTaskbarState() const noexcept
{
return _taskbarState;
}
@@ -1287,6 +1292,16 @@ void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function
_pfnClearQuickFix.swap(pfn);
}
void Terminal::SetPromptStartedCallback(std::function<void()> pfn) noexcept
{
_pfnPromptStarted.swap(pfn);
}
void Terminal::SetOutputStartedCallback(std::function<void()> pfn) noexcept
{
_pfnOutputStarted.swap(pfn);
}
// Method Description:
// - Stores the search highlighted regions in the terminal
void Terminal::SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept

View File

@@ -159,7 +159,7 @@ public:
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;
@@ -238,6 +238,8 @@ public:
void SetShowNotificationCallback(std::function<void(std::wstring_view, std::wstring_view)> pfn) noexcept;
void SetClearQuickFixCallback(std::function<void()> pfn) noexcept;
void SetWindowSizeChangedCallback(std::function<void(int32_t, int32_t)> pfn) noexcept;
void SetPromptStartedCallback(std::function<void()> pfn) noexcept;
void SetOutputStartedCallback(std::function<void()> pfn) noexcept;
void SetSearchHighlights(const std::vector<til::point_span>& highlights) noexcept;
void SetSearchHighlightFocused(size_t focusedIdx) noexcept;
void ScrollToSearchHighlight(til::CoordType searchScrollOffset);
@@ -246,7 +248,7 @@ public:
const std::optional<til::color> GetTabColor() const;
const size_t GetTaskbarState() const noexcept;
const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState GetTaskbarState() const noexcept;
const size_t GetTaskbarProgress() const noexcept;
void ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode);
@@ -347,6 +349,8 @@ private:
std::function<void(std::wstring_view, std::wstring_view)> _pfnShowNotification;
std::function<void()> _pfnClearQuickFix;
std::function<void(int32_t, int32_t)> _pfnWindowSizeChanged;
std::function<void()> _pfnPromptStarted;
std::function<void()> _pfnOutputStarted;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
@@ -367,6 +371,9 @@ private:
til::enumset<Mode> _systemMode{ Mode::AutoWrap };
::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState _taskbarState{ ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState::Clear };
size_t _taskbarProgress = 0;
bool _focused = false;
bool _snapOnInput = true;
bool _altGrAliasing = true;
@@ -375,9 +382,6 @@ private:
bool _autoMarkPrompts = false;
bool _rainbowSuggestions = false;
size_t _taskbarState = 0;
size_t _taskbarProgress = 0;
size_t _hyperlinkPatternId = 0;
std::wstring _answerbackMessage;

View File

@@ -173,7 +173,7 @@ void Terminal::SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::D
{
_assertLocked();
_taskbarState = static_cast<size_t>(state);
_taskbarState = state;
switch (state)
{
@@ -424,8 +424,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 (_pfnPromptStarted)
{
_pfnPromptStarted();
}
break;
case ShellIntegrationMark::Output:
if (_pfnOutputStarted)
{
_pfnOutputStarted();
}
break;
default:
break;
}
}

View File

@@ -371,6 +371,11 @@ namespace winrt::Microsoft::Terminal::Settings
_AllowOscNotifications = profile.AllowOscNotifications();
_PathTranslationStyle = profile.PathTranslationStyle();
_DragDropDelimiter = profile.DragDropDelimiter();
_NotifyOnActivity = profile.NotifyOnActivity();
_NotifyOnNextPrompt = profile.NotifyOnNextPrompt();
_NotifyOnActivityThreshold = profile.NotifyOnActivityThreshold();
_NotifyOnNextPromptThreshold = profile.NotifyOnNextPromptThreshold();
_AutoDetectRunningCommand = profile.AutoDetectRunningCommand();
}
// Method Description:

View File

@@ -107,6 +107,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
_NotifyChanges(L"CurrentPathTranslationStyle");
}
else if (viewModelProperty == L"NotifyOnActivity")
{
_NotifyChanges(L"IsNotifyOnActivityFlagSet", L"NotifyOnActivityPreview");
}
else if (viewModelProperty == L"NotifyOnNextPrompt")
{
_NotifyChanges(L"IsNotifyOnNextPromptFlagSet", L"NotifyOnNextPromptPreview");
}
else if (viewModelProperty == L"Padding")
{
_parsedPadding = StringToXamlThickness(_profile.Padding());
@@ -419,7 +427,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// Compile-time tripwire. PROFILE_INHERITABLE_SETTINGS now generates both the
// property accessors and the dispatch above, so those two can no longer drift.
#define PROFILE_COUNT(target, name) +1
static_assert(0 PROFILE_INHERITABLE_SETTINGS(PROFILE_COUNT) == 34,
static_assert(0 PROFILE_INHERITABLE_SETTINGS(PROFILE_COUNT) == 39,
"The set of inheritable profile settings changed. Update this count, then make "
"sure the new/removed setting is also reflected in ProfileViewModel.idl and in the "
"XAML reset buttons.");
@@ -636,6 +644,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
return iconPath.empty() || iconPath == IconPicker::HideIconValue;
}
#pragma region BellStyle
hstring ProfileViewModel::BellStylePreview() const
{
const auto bellStyle = BellStyle();
@@ -715,6 +724,147 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WI_UpdateFlag(currentStyle, Model::BellStyle::Notification, winrt::unbox_value<bool>(on));
BellStyle(currentStyle);
}
#pragma endregion
#pragma region NotifyOnActivity
hstring ProfileViewModel::NotifyOnActivityPreview() const
{
using Ons = Control::OutputNotificationStyle;
const auto style = NotifyOnActivity();
if (WI_AreAllFlagsSet(style, Ons::Taskbar | Ons::Audible | Ons::Tab | Ons::Notification))
{
return RS_(L"Profile_OutputNotificationStyleAll/Content");
}
else if (style == Ons::None)
{
return RS_(L"Profile_OutputNotificationStyleNone/Content");
}
std::wstring result;
const auto appendIfFlagSet = [&](Ons flag, std::wstring_view resource) {
// WI_IsFlagSet requires a compile-time constant flag; `flag` is a runtime parameter here.
if ((WI_EnumValue(style) & WI_EnumValue(flag)) != 0)
{
if (!result.empty())
{
result.append(L", ");
}
result.append(resource);
}
};
appendIfFlagSet(Ons::Taskbar, RS_(L"Profile_OutputNotificationStyleTaskbar/Content"));
appendIfFlagSet(Ons::Audible, RS_(L"Profile_OutputNotificationStyleAudible/Content"));
appendIfFlagSet(Ons::Tab, RS_(L"Profile_OutputNotificationStyleTab/Content"));
appendIfFlagSet(Ons::Notification, RS_(L"Profile_OutputNotificationStyleNotification/Content"));
return hstring{ result };
}
bool ProfileViewModel::IsNotifyOnActivityFlagSet(const uint32_t flag)
{
return (WI_EnumValue(NotifyOnActivity()) & flag) == flag;
}
void ProfileViewModel::SetNotifyOnActivityTaskbar(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnActivity();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Taskbar, winrt::unbox_value<bool>(on));
NotifyOnActivity(currentStyle);
}
void ProfileViewModel::SetNotifyOnActivityAudible(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnActivity();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Audible, winrt::unbox_value<bool>(on));
NotifyOnActivity(currentStyle);
}
void ProfileViewModel::SetNotifyOnActivityTab(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnActivity();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Tab, winrt::unbox_value<bool>(on));
NotifyOnActivity(currentStyle);
}
void ProfileViewModel::SetNotifyOnActivityNotification(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnActivity();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Notification, winrt::unbox_value<bool>(on));
NotifyOnActivity(currentStyle);
}
#pragma endregion
#pragma region 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 == Ons::None)
{
return RS_(L"Profile_OutputNotificationStyleNone/Content");
}
std::wstring result;
const auto appendIfFlagSet = [&](Ons flag, std::wstring_view resource) {
// WI_IsFlagSet requires a compile-time constant flag; `flag` is a runtime parameter here.
if ((WI_EnumValue(style) & WI_EnumValue(flag)) != 0)
{
if (!result.empty())
{
result.append(L", ");
}
result.append(resource);
}
};
appendIfFlagSet(Ons::Taskbar, RS_(L"Profile_OutputNotificationStyleTaskbar/Content"));
appendIfFlagSet(Ons::Audible, RS_(L"Profile_OutputNotificationStyleAudible/Content"));
appendIfFlagSet(Ons::Tab, RS_(L"Profile_OutputNotificationStyleTab/Content"));
appendIfFlagSet(Ons::Notification, RS_(L"Profile_OutputNotificationStyleNotification/Content"));
return hstring{ result };
}
bool ProfileViewModel::IsNotifyOnNextPromptFlagSet(const uint32_t flag)
{
return (WI_EnumValue(NotifyOnNextPrompt()) & flag) == flag;
}
void ProfileViewModel::SetNotifyOnNextPromptTaskbar(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnNextPrompt();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Taskbar, winrt::unbox_value<bool>(on));
NotifyOnNextPrompt(currentStyle);
}
void ProfileViewModel::SetNotifyOnNextPromptAudible(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnNextPrompt();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Audible, winrt::unbox_value<bool>(on));
NotifyOnNextPrompt(currentStyle);
}
void ProfileViewModel::SetNotifyOnNextPromptTab(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnNextPrompt();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Tab, winrt::unbox_value<bool>(on));
NotifyOnNextPrompt(currentStyle);
}
void ProfileViewModel::SetNotifyOnNextPromptNotification(winrt::Windows::Foundation::IReference<bool> on)
{
auto currentStyle = NotifyOnNextPrompt();
WI_UpdateFlag(currentStyle, Control::OutputNotificationStyle::Notification, winrt::unbox_value<bool>(on));
NotifyOnNextPrompt(currentStyle);
}
#pragma endregion
#pragma region BellSound
// Method Description:
// - Construct _CurrentBellSounds by importing the _inherited_ value from the model
@@ -853,6 +1003,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_NotifyChanges(L"CurrentBellSounds");
}
}
#pragma endregion
void ProfileViewModel::_RefreshDefaultAppearanceViewModel()
{

View File

@@ -49,6 +49,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void SetBellStyleTaskbar(winrt::Windows::Foundation::IReference<bool> on);
void SetBellStyleNotification(winrt::Windows::Foundation::IReference<bool> on);
// notify on activity bits
hstring NotifyOnActivityPreview() const;
bool IsNotifyOnActivityFlagSet(const uint32_t flag);
void SetNotifyOnActivityTaskbar(winrt::Windows::Foundation::IReference<bool> on);
void SetNotifyOnActivityAudible(winrt::Windows::Foundation::IReference<bool> on);
void SetNotifyOnActivityTab(winrt::Windows::Foundation::IReference<bool> on);
void SetNotifyOnActivityNotification(winrt::Windows::Foundation::IReference<bool> on);
// notify on next prompt bits
hstring NotifyOnNextPromptPreview() const;
bool IsNotifyOnNextPromptFlagSet(const uint32_t flag);
void SetNotifyOnNextPromptTaskbar(winrt::Windows::Foundation::IReference<bool> on);
void SetNotifyOnNextPromptAudible(winrt::Windows::Foundation::IReference<bool> on);
void SetNotifyOnNextPromptTab(winrt::Windows::Foundation::IReference<bool> on);
void SetNotifyOnNextPromptNotification(winrt::Windows::Foundation::IReference<bool> on);
hstring BellSoundPreview();
void RequestAddBellSound(hstring path);
void RequestDeleteBellSound(const Editor::BellSoundViewModel& vm);
@@ -159,7 +175,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
X(_profile, AnswerbackMessage) \
X(_profile, RainbowSuggestions) \
X(_profile, PathTranslationStyle) \
X(_profile, DragDropDelimiter)
X(_profile, DragDropDelimiter) \
X(_profile, NotifyOnActivity) \
X(_profile, NotifyOnNextPrompt) \
X(_profile, NotifyOnActivityThreshold) \
X(_profile, NotifyOnNextPromptThreshold) \
X(_profile, AutoDetectRunningCommand)
// Generate the projected property accessors from the list above.
#define PROFILE_GEN_PROJECTED(target, name) OBSERVABLE_PROJECTED_SETTING(target, name);

View File

@@ -52,6 +52,20 @@ namespace Microsoft.Terminal.Settings.Editor
void SetBellStyleTaskbar(Windows.Foundation.IReference<Boolean> on);
void SetBellStyleNotification(Windows.Foundation.IReference<Boolean> on);
String NotifyOnActivityPreview { get; };
Boolean IsNotifyOnActivityFlagSet(UInt32 flag);
void SetNotifyOnActivityTaskbar(Windows.Foundation.IReference<Boolean> on);
void SetNotifyOnActivityAudible(Windows.Foundation.IReference<Boolean> on);
void SetNotifyOnActivityTab(Windows.Foundation.IReference<Boolean> on);
void SetNotifyOnActivityNotification(Windows.Foundation.IReference<Boolean> on);
String NotifyOnNextPromptPreview { get; };
Boolean IsNotifyOnNextPromptFlagSet(UInt32 flag);
void SetNotifyOnNextPromptTaskbar(Windows.Foundation.IReference<Boolean> on);
void SetNotifyOnNextPromptAudible(Windows.Foundation.IReference<Boolean> on);
void SetNotifyOnNextPromptTab(Windows.Foundation.IReference<Boolean> on);
void SetNotifyOnNextPromptNotification(Windows.Foundation.IReference<Boolean> on);
String BellSoundPreview { get; };
Windows.Foundation.Collections.IObservableVector<BellSoundViewModel> CurrentBellSounds { get; };
void RequestAddBellSound(String path);
@@ -145,5 +159,10 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, DragDropDelimiter);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowOscNotifications);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnActivity);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnNextPrompt);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Int32, NotifyOnActivityThreshold);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Int32, NotifyOnNextPromptThreshold);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AutoDetectRunningCommand);
}
}

View File

@@ -221,6 +221,104 @@
</local:SettingsExpander.ItemsHeader>
</local:SettingsExpander>
<!-- Notify On Activity -->
<local:SettingsExpander x:Name="NotifyOnActivity"
x:Uid="Profile_NotifyOnActivity">
<local:SettingsExpander.Content>
<StackPanel Style="{StaticResource SettingControlStackStyle}">
<TextBlock VerticalAlignment="Center"
Style="{StaticResource SettingContainerCurrentValueTextBlockStyle}"
Text="{x:Bind Profile.NotifyOnActivityPreview, Mode=OneWay}" />
<local:SettingResetButton SettingName="NotifyOnActivity"
Target="{x:Bind Profile}" />
</StackPanel>
</local:SettingsExpander.Content>
<local:SettingsExpander.ItemsHeader>
<ContentPresenter Padding="16,12,16,16">
<StackPanel>
<CheckBox x:Uid="Profile_OutputNotificationStyleTaskbar"
IsChecked="{x:Bind Profile.IsNotifyOnActivityFlagSet(1), BindBack=Profile.SetNotifyOnActivityTaskbar, Mode=TwoWay}" />
<CheckBox x:Uid="Profile_OutputNotificationStyleAudible"
IsChecked="{x:Bind Profile.IsNotifyOnActivityFlagSet(2), BindBack=Profile.SetNotifyOnActivityAudible, Mode=TwoWay}" />
<CheckBox x:Uid="Profile_OutputNotificationStyleTab"
IsChecked="{x:Bind Profile.IsNotifyOnActivityFlagSet(4), BindBack=Profile.SetNotifyOnActivityTab, Mode=TwoWay}" />
<CheckBox x:Uid="Profile_OutputNotificationStyleNotification"
IsChecked="{x:Bind Profile.IsNotifyOnActivityFlagSet(8), BindBack=Profile.SetNotifyOnActivityNotification, Mode=TwoWay}" />
</StackPanel>
</ContentPresenter>
</local:SettingsExpander.ItemsHeader>
</local:SettingsExpander>
<!-- Notify On Activity Minimum Duration -->
<local:SettingsCard x:Name="NotifyOnActivityThreshold"
x:Uid="Profile_NotifyOnActivityThreshold">
<StackPanel Style="{StaticResource SettingControlStackStyle}">
<muxc:NumberBox AutomationProperties.Name="{Binding ElementName=NotifyOnActivityThreshold, Path=Header}"
LargeChange="10"
Minimum="0"
SmallChange="1"
Style="{StaticResource NumberBoxSettingStyle}"
Value="{x:Bind Profile.NotifyOnActivityThreshold, Mode=TwoWay}" />
<local:SettingResetButton SettingName="NotifyOnActivityThreshold"
Target="{x:Bind Profile}" />
</StackPanel>
</local:SettingsCard>
<!-- Notify On Next Prompt -->
<local:SettingsExpander x:Name="NotifyOnNextPrompt"
x:Uid="Profile_NotifyOnNextPrompt">
<local:SettingsExpander.Content>
<StackPanel Style="{StaticResource SettingControlStackStyle}">
<TextBlock VerticalAlignment="Center"
Style="{StaticResource SettingContainerCurrentValueTextBlockStyle}"
Text="{x:Bind Profile.NotifyOnNextPromptPreview, Mode=OneWay}" />
<local:SettingResetButton SettingName="NotifyOnNextPrompt"
Target="{x:Bind Profile}" />
</StackPanel>
</local:SettingsExpander.Content>
<local:SettingsExpander.ItemsHeader>
<ContentPresenter Padding="16,12,16,16">
<StackPanel>
<CheckBox x:Uid="Profile_OutputNotificationStyleTaskbar"
IsChecked="{x:Bind Profile.IsNotifyOnNextPromptFlagSet(1), BindBack=Profile.SetNotifyOnNextPromptTaskbar, Mode=TwoWay}" />
<CheckBox x:Uid="Profile_OutputNotificationStyleAudible"
IsChecked="{x:Bind Profile.IsNotifyOnNextPromptFlagSet(2), BindBack=Profile.SetNotifyOnNextPromptAudible, Mode=TwoWay}" />
<CheckBox x:Uid="Profile_OutputNotificationStyleTab"
IsChecked="{x:Bind Profile.IsNotifyOnNextPromptFlagSet(4), BindBack=Profile.SetNotifyOnNextPromptTab, Mode=TwoWay}" />
<CheckBox x:Uid="Profile_OutputNotificationStyleNotification"
IsChecked="{x:Bind Profile.IsNotifyOnNextPromptFlagSet(8), BindBack=Profile.SetNotifyOnNextPromptNotification, Mode=TwoWay}" />
</StackPanel>
</ContentPresenter>
</local:SettingsExpander.ItemsHeader>
</local:SettingsExpander>
<!-- Notify On Next Prompt Minimum Duration -->
<local:SettingsCard x:Name="NotifyOnNextPromptThreshold"
x:Uid="Profile_NotifyOnNextPromptThreshold">
<StackPanel Style="{StaticResource SettingControlStackStyle}">
<muxc:NumberBox AutomationProperties.Name="{Binding ElementName=NotifyOnNextPromptThreshold, Path=Header}"
LargeChange="10"
Minimum="0"
SmallChange="1"
Style="{StaticResource NumberBoxSettingStyle}"
Value="{x:Bind Profile.NotifyOnNextPromptThreshold, Mode=TwoWay}" />
<local:SettingResetButton SettingName="NotifyOnNextPromptThreshold"
Target="{x:Bind Profile}" />
</StackPanel>
</local:SettingsCard>
<!-- Auto Detect Running Command -->
<local:SettingsCard x:Name="AutoDetectRunningCommand"
x:Uid="Profile_AutoDetectRunningCommand">
<StackPanel Style="{StaticResource SettingControlStackStyle}">
<ToggleSwitch VerticalAlignment="Center"
IsOn="{x:Bind Profile.AutoDetectRunningCommand, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
<local:SettingResetButton SettingName="AutoDetectRunningCommand"
Target="{x:Bind Profile}" />
</StackPanel>
</local:SettingsCard>
<!-- RightClickContextMenu -->
<local:SettingsCard x:Name="RightClickContextMenu"
x:Uid="Profile_RightClickContextMenu">

View File

@@ -1554,6 +1554,70 @@
<value>Flash window</value>
<comment>An option to choose from for the "bell style" setting. When selected, a visual notification is used to notify the user. In this case, the window is flashed.</comment>
</data>
<data name="Profile_OutputNotificationStyleNone.Content" xml:space="preserve">
<value>None</value>
<comment>An option to choose from for the notification style setting. When selected, no notification is sent.</comment>
</data>
<data name="Profile_OutputNotificationStyleAll.Content" xml:space="preserve">
<value>All</value>
<comment>An option to choose from for the notification style setting. When selected, all notification methods are used.</comment>
</data>
<data name="Profile_OutputNotificationStyleTaskbar.Content" xml:space="preserve">
<value>Flash taskbar</value>
<comment>An option to choose from for the notification style setting. Flashes the taskbar icon.</comment>
</data>
<data name="Profile_OutputNotificationStyleAudible.Content" xml:space="preserve">
<value>Play sound</value>
<comment>An option to choose from for the notification style setting. Plays an audible notification sound.</comment>
</data>
<data name="Profile_OutputNotificationStyleTab.Content" xml:space="preserve">
<value>Show tab indicator</value>
<comment>An option to choose from for the notification style setting. Shows an activity indicator on the tab.</comment>
</data>
<data name="Profile_OutputNotificationStyleNotification.Content" xml:space="preserve">
<value>Desktop notification</value>
<comment>An option to choose from for the notification style setting. Sends a Windows desktop notification (toast).</comment>
</data>
<data name="Profile_NotifyOnActivity.Header" xml:space="preserve">
<value>Notify on activity</value>
<comment>Header for a control to set the notification style when a background tab has new activity (output).</comment>
</data>
<data name="Profile_NotifyOnActivity.Description" xml:space="preserve">
<value>Choose how to be notified when a background tab produces new output.</value>
<comment>Help text for the notify on activity setting.</comment>
</data>
<data name="Profile_NotifyOnActivityThreshold.Header" xml:space="preserve">
<value>Minimum seconds between activity notifications</value>
<comment>Header for a numeric control that sets the minimum number of seconds between consecutive "notify on activity" notifications. Useful for chatty programs.</comment>
</data>
<data name="Profile_NotifyOnActivityThreshold.Description" xml:space="preserve">
<value>Suppress repeated activity notifications within this many seconds of the previous one. Set to 0 to always notify.</value>
<comment>Help text for the minimum duration threshold applied to "notify on activity" notifications.</comment>
</data>
<data name="Profile_NotifyOnNextPrompt.Header" xml:space="preserve">
<value>Notify on next prompt</value>
<comment>Header for a control to set the notification style when a new shell prompt is detected (requires shell integration).</comment>
</data>
<data name="Profile_NotifyOnNextPrompt.Description" xml:space="preserve">
<value>Choose how to be notified when a command finishes and a new prompt appears. Requires shell integration.</value>
<comment>Help text for the notify on next prompt setting.</comment>
</data>
<data name="Profile_NotifyOnNextPromptThreshold.Header" xml:space="preserve">
<value>Minimum duration for the next prompt notification</value>
<comment>Header for a numeric control that sets the minimum number of seconds a command must run before its completion triggers a "notify on next prompt" notification.</comment>
</data>
<data name="Profile_NotifyOnNextPromptThreshold.Description" xml:space="preserve">
<value>Only notify when the command ran for at least this many seconds. Requires shell integration. Set to 0 to always notify.</value>
<comment>Help text for the minimum duration threshold applied to "notify on next prompt" notifications.</comment>
</data>
<data name="Profile_AutoDetectRunningCommand.Header" xml:space="preserve">
<value>Auto-detect running command</value>
<comment>Header for a control to configure automatic detection of a running command's progress state.</comment>
</data>
<data name="Profile_AutoDetectRunningCommand.Description" xml:space="preserve">
<value>Automatically show a progress indicator when a command is running. Requires shell integration.</value>
<comment>Help text for the auto-detect running command setting.</comment>
</data>
<data name="Profile_BellStyleNotification.Content" xml:space="preserve">
<value>Desktop notification</value>
<comment>An option to choose from for the "bell style" setting. When selected, a Windows desktop notification (toast) is sent to notify the user.</comment>

View File

@@ -80,38 +80,43 @@ 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<IMediaResource>, 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, AllowOscNotifications, "compatibility.allowOSC777", false) \
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \
X(hstring, DragDropDelimiter, "dragDropDelimiter", L" ") \
X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None)
#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<IMediaResource>, 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, AllowOscNotifications, "compatibility.allowOSC777", false) \
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \
X(hstring, DragDropDelimiter, "dragDropDelimiter", L" ") \
X(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, "pathTranslationStyle", Microsoft::Terminal::Control::PathTranslationStyle::None) \
X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnActivity, "notifyOnActivity", Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, "notifyOnNextPrompt", Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(int32_t, NotifyOnActivityThreshold, "notifyOnActivityThreshold", 5) \
X(int32_t, NotifyOnNextPromptThreshold, "notifyOnNextPromptThreshold", 5) \
X(bool, AutoDetectRunningCommand, "autoDetectRunningCommand", false)
// Intentionally omitted Profile settings:
// * Name

View File

@@ -97,5 +97,10 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.PathTranslationStyle, PathTranslationStyle);
INHERITABLE_PROFILE_SETTING(String, DragDropDelimiter);
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnActivity);
INHERITABLE_PROFILE_SETTING(Microsoft.Terminal.Control.OutputNotificationStyle, NotifyOnNextPrompt);
INHERITABLE_PROFILE_SETTING(Int32, NotifyOnActivityThreshold);
INHERITABLE_PROFILE_SETTING(Int32, NotifyOnNextPromptThreshold);
INHERITABLE_PROFILE_SETTING(Boolean, AutoDetectRunningCommand);
}
}

View File

@@ -139,6 +139,37 @@ JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::BellStyle)
}
};
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Control::OutputNotificationStyle)
{
static constexpr std::array<pair_type, 6> 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::Settings::Model::ConvergedAlignment)
{
// reduce repetition

View File

@@ -265,66 +265,68 @@ void TerminalCoreUnitTests::TerminalApiTest::SetTaskbarProgress()
auto& stateMachine = *(term._stateMachine);
using TaskbarState = ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState;
// Initial values for taskbar state and progress should be 0
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(0));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Clear);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(0));
// Set some values for taskbar state and progress through state machine
stateMachine.ProcessString(L"\x1b]9;4;1;50\x1b\\");
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(1));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Set);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(50));
// Reset to 0
stateMachine.ProcessString(L"\x1b]9;4;0;0\x1b\\");
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(0));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Clear);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(0));
// Set an out of bounds value for state
stateMachine.ProcessString(L"\x1b]9;4;5;50\x1b\\");
// Nothing should have changed (dispatch should have returned false)
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(0));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Clear);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(0));
// Set an out of bounds value for progress
stateMachine.ProcessString(L"\x1b]9;4;1;999\x1b\\");
// Progress should have been clamped to 100
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(1));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Set);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(100));
// Don't specify any params
stateMachine.ProcessString(L"\x1b]9;4\x1b\\");
// State and progress should both be reset to 0
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(0));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Clear);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(0));
// Specify additional params
stateMachine.ProcessString(L"\x1b]9;4;1;80;123\x1b\\");
// Additional params should be ignored, state and progress still set normally
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(1));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Set);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(80));
// Edge cases + trailing semicolon testing
stateMachine.ProcessString(L"\x1b]9;4;2;\x1b\\");
// String should be processed correctly despite the trailing semicolon,
// taskbar progress should remain unchanged from previous value
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(2));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Error);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(80));
stateMachine.ProcessString(L"\x1b]9;4;3;75\x1b\\");
// Given progress value should be ignored because this is the indeterminate state,
// so the progress value should remain unchanged
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(3));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Indeterminate);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(80));
stateMachine.ProcessString(L"\x1b]9;4;0;50\x1b\\");
// Taskbar progress should be 0 (the given value should be ignored)
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(0));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Clear);
VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow<size_t>(0));
stateMachine.ProcessString(L"\x1b]9;4;2;\x1b\\");
// String should be processed correctly despite the trailing semicolon,
// taskbar progress should be set to a 'minimum', non-zero value
VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow<size_t>(2));
VERIFY_ARE_EQUAL(term.GetTaskbarState(), TaskbarState::Error);
VERIFY_IS_GREATER_THAN(term.GetTaskbarProgress(), gsl::narrow<size_t>(0));
}

View File

@@ -106,7 +106,7 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable&
if (_windowLogic)
{
const auto state = _windowLogic.TaskbarState();
_window->SetTaskbarProgress(gsl::narrow_cast<size_t>(state.State()),
_window->SetTaskbarProgress(state.State(),
gsl::narrow_cast<size_t>(state.Progress()));
}
}

View File

@@ -930,27 +930,28 @@ void IslandWindow::FlashTaskbar()
// Arguments:
// - state: indicates the progress state
// - progress: indicates the progress value
void IslandWindow::SetTaskbarProgress(const size_t state, const size_t progress)
void IslandWindow::SetTaskbarProgress(const winrt::Microsoft::Terminal::Control::TaskbarState state, const size_t progress)
{
if (_taskbar)
{
using TbState = winrt::Microsoft::Terminal::Control::TaskbarState;
switch (state)
{
case 0:
case TbState::Clear:
// removes the taskbar progress indicator
_taskbar->SetProgressState(_window.get(), TBPF_NOPROGRESS);
break;
case 1:
case TbState::Set:
// sets the progress value to value given by the 'progress' parameter
_taskbar->SetProgressState(_window.get(), TBPF_NORMAL);
_taskbar->SetProgressValue(_window.get(), progress, 100);
break;
case 2:
case TbState::Error:
// sets the progress indicator to an error state
_taskbar->SetProgressState(_window.get(), TBPF_ERROR);
_taskbar->SetProgressValue(_window.get(), progress, 100);
break;
case 3:
case TbState::Indeterminate:
// sets the progress indicator to an indeterminate state.
// FIRST, set the progress to "no progress". That'll clear out any
// progress value from the previous state. Otherwise, a transition
@@ -959,7 +960,7 @@ void IslandWindow::SetTaskbarProgress(const size_t state, const size_t progress)
_taskbar->SetProgressState(_window.get(), TBPF_NOPROGRESS);
_taskbar->SetProgressState(_window.get(), TBPF_INDETERMINATE);
break;
case 4:
case TbState::Paused:
// sets the progress indicator to a pause state
_taskbar->SetProgressState(_window.get(), TBPF_PAUSED);
_taskbar->SetProgressValue(_window.get(), progress, 100);

View File

@@ -53,7 +53,7 @@ public:
virtual void SetShowTabsFullscreen(const bool newShowTabsFullscreen);
void FlashTaskbar();
void SetTaskbarProgress(const size_t state, const size_t progress);
void SetTaskbarProgress(const winrt::Microsoft::Terminal::Control::TaskbarState state, const size_t progress);
void SummonWindow(winrt::TerminalApp::SummonWindowBehavior args);

View File

@@ -61,32 +61,37 @@
// --------------------------- 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) \
#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, NotifyOnActivity, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(winrt::Microsoft::Terminal::Control::OutputNotificationStyle, NotifyOnNextPrompt, winrt::Microsoft::Terminal::Control::OutputNotificationStyle::None) \
X(int32_t, NotifyOnActivityThreshold, 5) \
X(int32_t, NotifyOnNextPromptThreshold, 5) \
X(bool, AutoDetectRunningCommand, false) \
X(winrt::hstring, DragDropDelimiter, L" ")

View File

@@ -446,7 +446,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.
}

View File

@@ -68,7 +68,7 @@ public:
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;

View File

@@ -87,7 +87,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;

View File

@@ -3638,7 +3638,7 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
else if (subParam == 12)
{
_pages.ActivePage().Buffer().StartCommand();
_api.NotifyShellIntegrationMark();
_api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::Command);
}
else
{
@@ -3673,7 +3673,7 @@ void AdaptDispatch::DoITerm2Action(const std::wstring_view string)
if (action == L"SetMark")
{
_pages.ActivePage().Buffer().StartPrompt();
_api.NotifyShellIntegrationMark();
_api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::Prompt);
}
else
{
@@ -3711,19 +3711,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
@@ -3744,7 +3744,7 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
}
_pages.ActivePage().Buffer().EndCurrentCommand(error);
_api.NotifyShellIntegrationMark();
_api.NotifyShellIntegrationMark(ITerminalApi::ShellIntegrationMark::CommandFinished);
break;
}

View File

@@ -216,7 +216,7 @@ public:
Log::Comment(L"NotifyBufferRotation MOCK called...");
}
void NotifyShellIntegrationMark() override
void NotifyShellIntegrationMark(ShellIntegrationMark /*mark*/) override
{
Log::Comment(L"NotifyShellIntegrationMark MOCK called...");
}