Compare commits

...

31 Commits

Author SHA1 Message Date
Mike Griese
d28c0be8c1 Trying like 80 different things for chrswan but none of these actually worked 2024-02-15 11:11:26 -06:00
Mike Griese
70d905b658 add velocity, and a setting per-control for this 2024-02-08 09:29:13 -06:00
Mike Griese
9a6ded2e39 comments, disable with velocity 2024-02-08 06:11:01 -06:00
Mike Griese
eac27dbe25 spel 2024-02-07 13:58:34 -06:00
Mike Griese
d3a98b3754 Merge branch 'dev/migrie/fhl/7718-notifications' of https://github.com/microsoft/terminal into dev/migrie/fhl/7718-notifications 2024-02-07 13:08:26 -06:00
Mike Griese
d234049640 Revert "we do not want this"
This reverts commit 4ab628d62f.
2024-02-07 13:03:26 -06:00
Mike Griese
4ab628d62f we do not want this 2024-02-07 13:02:09 -06:00
Mike Griese
8309901fc9 We do want this 2024-02-07 13:01:33 -06:00
Mike Griese
015c5e8b93 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/7718-notifications-reboot 2024-02-07 09:19:58 -06:00
Mike Griese
1726176c85 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/7718-notifications-reboot 2023-08-28 08:53:09 -05:00
Mike Griese
3b02c96bd5 summon didn't work but the rest did 2023-08-24 17:00:56 -05:00
Mike Griese
dc448b4781 Merge branch 'dev/migrie/fhl/7718-notifications' into dev/migrie/fhl/7718-notifications-reboot 2023-08-24 16:51:35 -05:00
Mike Griese
75ea5f3aab Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/7718-notifications
# Conflicts:
#	src/cascadia/TerminalControl/ControlCore.h
#	src/cascadia/TerminalControl/ControlCore.idl
#	src/terminal/adapter/ITerminalApi.hpp
2023-03-03 12:05:48 -06:00
Mike Griese
6ac5137ba8 unpackaged and elevated hate him 2022-12-01 12:45:55 -06:00
Mike Griese
7b524b0d31 simple nits from review 2022-12-01 09:53:53 -06:00
Mike Griese
654416cdc1 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/7718-notifications 2022-11-30 15:52:02 -06:00
Dustin L. Howett
cae6f04cfb Migrate spelling-0.0.21 changes from main 2022-11-28 14:14:48 -06:00
Mike Griese
9208222884 I knew I forgot runformat 2022-11-28 14:14:48 -06:00
Mike Griese
8b67ed7779 more more austinmode 2022-11-28 13:56:14 -06:00
Mike Griese
c4f623aaf0 derp 2022-11-28 10:06:45 -06:00
Mike Griese
3d83cc348a austinmode 2022-11-22 15:51:15 -06:00
Mike Griese
8e170eb643 oops 2022-11-22 11:47:19 -06:00
Mike Griese
0f339d2498 revert some dead code 2022-11-22 08:44:36 -06:00
Mike Griese
054f173995 cleanup 2022-11-22 08:41:49 -06:00
Mike Griese
1fd87fecdf only send when inactive 2022-11-22 06:43:32 -06:00
Mike Griese
e9b2e5184a Plumbing is always the most work 2022-11-21 16:43:20 -06:00
Mike Griese
f0f75dcdd0 Actually parse parameters from the notification 2022-11-21 14:59:24 -06:00
Mike Griese
006da6a549 As a test, hook this up to BELs 2022-11-21 14:36:18 -06:00
Mike Griese
67d854821f Merge branch 'main' into dev/migrie/fhl/7718-notifications 2022-11-21 13:22:46 -06:00
Mike Griese
65bc163da4 stash, I never finished this before my kid was born 2022-10-31 16:02:00 -05:00
Mike Griese
ce375fa7f3 send notifications, and get callbacks (in an entirely new instance. Huh.) 2022-09-21 06:54:23 -05:00
45 changed files with 478 additions and 3 deletions

View File

@@ -117,6 +117,7 @@ uiatextrange
UIs
und
unregister
urxvt
versioned
vsdevcmd
walkthrough

View File

@@ -461,4 +461,35 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
co_await winrt::resume_background();
_monarch.RequestSendContent(args);
}
// Attempt to summon an existing window. This static function does NOT
// pre-register as the monarch. This is used for activations from a
// notification, where this process should NEVER become its own window.
bool WindowManager::SummonForNotification(const uint64_t windowId)
{
auto monarch = create_instance<Remoting::IMonarch>(Monarch_clsid,
CLSCTX_LOCAL_SERVER);
if (monarch == nullptr)
{
return false;
}
SummonWindowSelectionArgs args{};
args.WindowID(windowId);
// Summon the window...
// * On its current desktop
// * Without a dropdown
// * On the monitor it is already on
// * Do not toggle, just make visible.
const Remoting::SummonWindowBehavior summonArgs{};
summonArgs.MoveToCurrentDesktop(false);
summonArgs.DropdownDuration(0);
summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace);
summonArgs.ToggleVisibility(false);
args.SummonBehavior(summonArgs);
monarch.SummonWindow(args);
return true;
}
}

View File

@@ -47,6 +47,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference<Windows::Foundation::Rect> windowBounds);
winrt::fire_and_forget RequestSendContent(Remoting::RequestReceiveContentArgs args);
static bool SummonForNotification(const uint64_t windowId);
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);

View File

@@ -29,6 +29,8 @@ namespace Microsoft.Terminal.Remoting
void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference<Windows.Foundation.Rect> bounds);
void RequestSendContent(RequestReceiveContentArgs args);
static Boolean SummonForNotification(UInt64 windowId);
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;

View File

@@ -114,4 +114,16 @@ namespace winrt::TerminalApp::implementation
AddOtherProvider(winrt::Microsoft::Terminal::Settings::Editor::XamlMetaDataProvider{});
}
}
void App::OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs const& args)
{
if (args != nullptr &&
args.Kind() == Windows::ApplicationModel::Activation::ActivationKind::ToastNotification)
{
if (const auto& toastArgs{ args.try_as<Windows::ApplicationModel::Activation::ToastNotificationActivatedEventArgs>() })
{
Logic().DoTheThing();
}
}
}
}

View File

@@ -15,6 +15,7 @@ namespace winrt::TerminalApp::implementation
App();
void OnLaunched(const Windows::ApplicationModel::Activation::LaunchActivatedEventArgs&);
void Initialize();
void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs const& args);
TerminalApp::AppLogic Logic();

View File

@@ -74,6 +74,8 @@ namespace winrt::TerminalApp::implementation
TerminalApp::ParseCommandlineResult GetParseCommandlineMessage(array_view<const winrt::hstring> args);
void DoTheThing() { DebugBreak(); }
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs);
private:

View File

@@ -57,5 +57,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, SettingsLoadEventArgs> SettingsChanged;
void DoTheThing();
}
}

View File

@@ -14,6 +14,7 @@
#include <inc/WindowingBehavior.h>
#include <LibraryResources.h>
#include <WtExeUtils.h>
#include <TerminalCore/ControlKeyStates.hpp>
#include <til/latch.h>
@@ -45,6 +46,9 @@ using namespace ::Microsoft::Console;
using namespace ::Microsoft::Terminal::Core;
using namespace std::chrono_literals;
using namespace winrt::Windows::UI::Notifications;
using namespace winrt::Windows::Data::Xml::Dom;
#define HOOKUP_ACTION(action) _actionDispatch->action({ this, &TerminalPage::_Handle##action });
namespace winrt
@@ -1671,6 +1675,7 @@ namespace winrt::TerminalApp::implementation
{
term.CompletionsChanged({ get_weak(), &TerminalPage::_ControlCompletionsChangedHandler });
}
winrt::weak_ref<TermControl> weakTerm{ term };
term.ContextMenu().Opening([weak = get_weak(), weakTerm](auto&& sender, auto&& /*args*/) {
if (const auto& page{ weak.get() })
@@ -1684,6 +1689,7 @@ namespace winrt::TerminalApp::implementation
page->_PopulateContextMenu(weakTerm.get(), sender.try_as<MUX::Controls::CommandBarFlyout>(), true);
}
});
term.SendNotification({ get_weak(), &TerminalPage::_SendNotificationHandler });
}
// Method Description:
@@ -2958,6 +2964,124 @@ namespace winrt::TerminalApp::implementation
_ShowWindowChangedHandlers(*this, args);
}
// Method Description:
// - Handler for a control's SendNotification event. `args` will contain the
// title and body of the notification requested by the client application.
// - This will only actually send a notification when the sender is
// - in an inactive window OR
// - in an inactive tab.
winrt::fire_and_forget TerminalPage::_SendNotificationHandler(const IInspectable sender,
const Microsoft::Terminal::Control::SendNotificationArgs args)
{
// This never works as expected when we're an elevated instance. The
// notification will end up launching an unelevated instance to handle
// it, and there's no good way to get back to the elevated one.
// Possibly revisit after GH #13276.
//
// We're using CanDragDrop, because TODO! I bet this works with UAC disabled
if (!CanDragDrop())
{
co_return;
}
auto weakThis = get_weak();
co_await resume_foreground(Dispatcher());
auto page{ weakThis.get() };
if (page)
{
// If the window is inactive, we always want to send the notification.
//
// Otherwise, we only want to send the notification for panes in inactive tabs.
if (_activated)
{
auto foundControl = false;
if (const auto activeTab{ _GetFocusedTabImpl() })
{
activeTab->GetRootPane()->WalkTree([&](auto&& pane) {
if (const auto& term{ pane->GetTerminalControl() })
{
if (term == sender)
{
foundControl = true;
return;
}
}
});
}
// The control that sent this is in the active tab. We
// should only send the notification if the window was
// inactive.
if (foundControl)
{
co_return;
}
}
_sendNotification(args.Title(), args.Body());
}
}
// Actually write the payload to a XML doc and load it into a ToastNotification.
void TerminalPage::_sendNotification(const std::wstring_view title,
const std::wstring_view body)
{
// ToastNotificationManager::CreateToastNotifier doesn't work in
// unpackaged scenarios without an AUMID. We probably don't have one if
// we're unpackaged. Unpackaged isn't a wholly supported scenario
// anyways, so let's just bail.
if (!IsPackaged())
{
return;
}
static winrt::hstring xmlTemplate{ L"\
<toast launch=\"foo\">\
<visual>\
<binding template=\"ToastGeneric\">\
<text></text>\
<text></text>\
</binding>\
</visual>\
</toast>" };
XmlDocument doc;
doc.LoadXml(xmlTemplate);
// Populate with text and values
// auto payload{ fmt::format(L"window={}&tabIndex=0", WindowProperties().WindowId()) };
// doc.DocumentElement().SetAttribute(L"launch", payload);
// doc.SelectSingleNode(L"//toast").Attributes().SetNamedItem(payload);
doc.SelectSingleNode(L"//text[1]").InnerText(title);
doc.SelectSingleNode(L"//text[2]").InnerText(body);
// Construct the notification
ToastNotification notification{ doc };
// lazy-init
if (!_toastNotifier)
{
_toastNotifier = ToastNotificationManager::CreateToastNotifier();
}
if (_toastNotifier)
{
_toastActivatedRevoker = notification.Activated(winrt::auto_revoke, [this](auto&&, auto&&) {
TraceLoggingWrite(g_hTerminalAppProvider, "ActivatedFromToast", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
// Request a summon of this window to the foreground
_SummonWindowRequestedHandlers(*this, nullptr);
// args.Handled(true);
});
// And show it!
_toastNotifier.Show(notification);
TraceLoggingWrite(g_hTerminalAppProvider, "SendToastNotification", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
}
// Method Description:
// - Paste text from the Windows Clipboard to the focused terminal
void TerminalPage::_PasteText()

View File

@@ -272,6 +272,8 @@ namespace winrt::TerminalApp::implementation
TerminalApp::ContentManager _manager{ nullptr };
winrt::Windows::UI::Notifications::ToastNotification::Activated_revoker _toastActivatedRevoker;
struct StashedDragData
{
winrt::com_ptr<winrt::TerminalApp::implementation::TabBase> draggedTab{ nullptr };
@@ -287,6 +289,9 @@ namespace winrt::TerminalApp::implementation
__declspec(noinline) SuggestionsControl _loadSuggestionsElementSlowPath();
bool _suggestionsControlIs(winrt::Windows::UI::Xaml::Visibility visibility);
// todo! maybe move to TerminalWindow
winrt::Windows::UI::Notifications::ToastNotifier _toastNotifier{ nullptr };
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
void _ShowAboutDialog();
@@ -543,6 +548,9 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender);
winrt::com_ptr<TerminalTab> _senderOrFocusedTab(const IInspectable& sender);
winrt::fire_and_forget _SendNotificationHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SendNotificationArgs args);
void _sendNotification(const std::wstring_view title, const std::wstring_view body);
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);

View File

@@ -27,6 +27,8 @@
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <winrt/Windows.Data.Xml.Dom.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Foundation.Metadata.h>
@@ -35,6 +37,7 @@
#include <winrt/Windows.System.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Input.h>
#include <winrt/Windows.UI.Notifications.h>
#include <winrt/Windows.UI.Text.h>
#include <winrt/Windows.UI.ViewManagement.h>
#include <winrt/Windows.UI.Xaml.Automation.Peers.h>

View File

@@ -126,6 +126,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); };
_terminal->CompletionsChangedCallback(pfnCompletionsChanged);
auto pfnSendNotification = std::bind(&ControlCore::_terminalSendNotification, this, std::placeholders::_1, std::placeholders::_2);
_terminal->SetSendNotificationCallback(pfnSendNotification);
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
@@ -1585,6 +1588,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_midiAudio.PlayNote(reinterpret_cast<HWND>(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
}
void ControlCore::_terminalSendNotification(const std::wstring_view title,
const std::wstring_view body)
{
const auto e = winrt::make_self<implementation::SendNotificationArgs>(title, body);
_SendNotificationHandlers(*this, *e);
}
bool ControlCore::HasSelection() const
{
const auto lock = _terminal->LockForReading();

View File

@@ -280,6 +280,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable);
TYPED_EVENT(Attached, IInspectable, IInspectable);
TYPED_EVENT(SendNotification, IInspectable, Control::SendNotificationArgs);
// clang-format on
private:
@@ -372,6 +374,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);
void _terminalSendNotification(const std::wstring_view title,
const std::wstring_view body);
#pragma endregion
MidiAudio _midiAudio;

View File

@@ -190,5 +190,7 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, CompletionsChangedEventArgs> CompletionsChanged;
event Windows.Foundation.TypedEventHandler<Object, SendNotificationArgs> SendNotification;
};
}

View File

@@ -20,3 +20,4 @@
#include "KeySentEventArgs.g.cpp"
#include "CharSentEventArgs.g.cpp"
#include "StringSentEventArgs.g.cpp"
#include "SendNotificationArgs.g.cpp"

View File

@@ -20,6 +20,7 @@
#include "KeySentEventArgs.g.h"
#include "CharSentEventArgs.g.h"
#include "StringSentEventArgs.g.h"
#include "SendNotificationArgs.g.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
@@ -251,6 +252,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(winrt::hstring, Text);
};
struct SendNotificationArgs : public SendNotificationArgsT<SendNotificationArgs>
{
public:
SendNotificationArgs(const std::wstring_view title,
const std::wstring_view body) :
_Title(title),
_Body(body)
{
}
WINRT_PROPERTY(winrt::hstring, Title);
WINRT_PROPERTY(winrt::hstring, Body);
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation

View File

@@ -121,4 +121,10 @@ namespace Microsoft.Terminal.Control
{
String Text { get; };
}
runtimeclass SendNotificationArgs
{
String Title { get; };
String Body { get; };
}
}

View File

@@ -104,6 +104,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });
// Re-raise the event with us as the sender.
_core.SendNotification([weakThis = get_weak()](auto s, auto e) {
if (auto self{ weakThis.get() })
{
self->_SendNotificationHandlers(*self, e);
}
});
// Initialize the terminal only once the swapchainpanel is loaded - that
// way, we'll be able to query the real pixel size it got on layout
_layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {

View File

@@ -194,6 +194,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(KeySent, IInspectable, Control::KeySentEventArgs);
TYPED_EVENT(CharSent, IInspectable, Control::CharSentEventArgs);
TYPED_EVENT(StringSent, IInspectable, Control::StringSentEventArgs);
TYPED_EVENT(SendNotification, IInspectable, Control::SendNotificationArgs);
// clang-format on
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);

View File

@@ -58,6 +58,7 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
event Windows.Foundation.TypedEventHandler<Object, SendNotificationArgs> SendNotification;
event Windows.Foundation.TypedEventHandler<Object, CompletionsChangedEventArgs> CompletionsChanged;

View File

@@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Core
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> StartingTabColor;
Boolean AutoMarkPrompts;
Boolean AllowNotifications;
};

View File

@@ -95,6 +95,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
_startingTitle = settings.StartingTitle();
_trimBlockSelection = settings.TrimBlockSelection();
_autoMarkPrompts = settings.AutoMarkPrompts();
_allowNotifications = settings.AllowNotifications();
_getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput());
@@ -1160,6 +1161,11 @@ void Terminal::SetPlayMidiNoteCallback(std::function<void(const int, const int,
_pfnPlayMidiNote.swap(pfn);
}
void Terminal::SetSendNotificationCallback(std::function<void(std::wstring_view, std::wstring_view)> pfn) noexcept
{
_pfnSendNotification.swap(pfn);
}
void Terminal::BlinkCursor() noexcept
{
if (_selectionMode != SelectionInteractionMode::Mark)

View File

@@ -163,6 +163,7 @@ public:
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SendNotification(const std::wstring_view title, const std::wstring_view body) override;
#pragma endregion
void ClearMark();
@@ -237,6 +238,7 @@ public:
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
void CompletionsChangedCallback(std::function<void(std::wstring_view, unsigned int)> pfn) noexcept;
void SetSendNotificationCallback(std::function<void(std::wstring_view, std::wstring_view)> pfn) noexcept;
void BlinkCursor() noexcept;
void SetCursorOn(const bool isOn) noexcept;
@@ -345,6 +347,7 @@ private:
std::function<void(bool)> _pfnShowWindowChanged;
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
std::function<void(std::wstring_view, unsigned int)> _pfnCompletionsChanged;
std::function<void(std::wstring_view, std::wstring_view)> _pfnSendNotification;
RenderSettings _renderSettings;
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
@@ -363,6 +366,7 @@ private:
bool _suppressApplicationTitle = false;
bool _trimBlockSelection = false;
bool _autoMarkPrompts = false;
bool _allowNotifications = true;
size_t _taskbarState = 0;
size_t _taskbarProgress = 0;

View File

@@ -511,3 +511,13 @@ void Terminal::NotifyBufferRotation(const int delta)
_NotifyScrollEvent();
}
}
void Terminal::SendNotification(const std::wstring_view title,
const std::wstring_view body)
{
// Only send notifications if enabled in the settings
if (_pfnSendNotification && _allowNotifications)
{
_pfnSendNotification(title, body);
}
}

View File

@@ -98,7 +98,8 @@ Author(s):
X(bool, AutoMarkPrompts, "experimental.autoMarkPrompts", false) \
X(bool, ShowMarks, "experimental.showMarksOnScrollbar", false) \
X(bool, RepositionCursorWithMouse, "experimental.repositionCursorWithMouse", false) \
X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true)
X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true) \
X(bool, AllowNotifications, "allowNotifications", true)
// Intentionally omitted Profile settings:
// * Name

View File

@@ -94,6 +94,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(Boolean, RightClickContextMenu);
INHERITABLE_PROFILE_SETTING(Boolean, RepositionCursorWithMouse);
INHERITABLE_PROFILE_SETTING(Boolean, AllowNotifications);
INHERITABLE_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables);

View File

@@ -339,6 +339,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_RightClickContextMenu = profile.RightClickContextMenu();
_RepositionCursorWithMouse = profile.RepositionCursorWithMouse();
_AllowNotifications = profile.AllowNotifications();
_ReloadEnvironmentVariables = profile.ReloadEnvironmentVariables();
}

View File

@@ -167,6 +167,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, bool, ShowMarks, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, RightClickContextMenu, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, RepositionCursorWithMouse, false);
INHERITABLE_SETTING(Model::TerminalSettings, bool, AllowNotifications, true);
INHERITABLE_SETTING(Model::TerminalSettings, bool, ReloadEnvironmentVariables, true);

View File

@@ -23,6 +23,8 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace ::Microsoft::Console;
using namespace ::Microsoft::Console::Types;
using namespace std::chrono_literals;
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::UI::Notifications;
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
// "If the high-order bit is 1, the key is down; otherwise, it is up."

View File

@@ -71,6 +71,8 @@ private:
void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
void _HandleSessionRestore(const bool startedForContent);
// bool _HandleLaunchArgs();
winrt::Microsoft::Terminal::Settings::Model::LaunchPosition _GetWindowLaunchPosition();
void _HandleCreateWindow(const HWND hwnd, const til::rect& proposedRect);

View File

@@ -27,6 +27,18 @@ using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers;
#define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS"
#define IDM_SYSTEM_MENU_BEGIN 0x1000
#ifndef NDEBUG
void _trace(const wchar_t* const pwsz)
{
TraceLoggingWrite(g_hWindowsTerminalProvider,
"TraceMessage",
TraceLoggingDescription("debug print messages"),
TraceLoggingWideString(pwsz, "message", "the message"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
#endif
IslandWindow::IslandWindow() noexcept :
_interopWindowHandle{ nullptr },
_rootGrid{ nullptr },
@@ -1327,8 +1339,9 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled)
winrt::fire_and_forget IslandWindow::SummonWindow(Remoting::SummonWindowBehavior args)
{
// On the foreground thread:
co_await wil::resume_foreground(_rootGrid.Dispatcher());
// co_await wil::resume_foreground(_rootGrid.Dispatcher());
_summonWindowRoutineBody(args);
co_return;
}
// Method Description:
@@ -1337,6 +1350,8 @@ winrt::fire_and_forget IslandWindow::SummonWindow(Remoting::SummonWindowBehavior
// when this was part of the coroutine body.
void IslandWindow::_summonWindowRoutineBody(Remoting::SummonWindowBehavior args)
{
_trace(L"_summonWindowRoutineBody");
auto actualDropdownDuration = args.DropdownDuration();
// If the user requested an animation, let's check if animations are enabled in the OS.
if (actualDropdownDuration > 0)
@@ -1507,12 +1522,15 @@ void IslandWindow::_slideUpWindow(const uint32_t dropdownDuration)
void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration,
const Remoting::MonitorBehavior toMonitor)
{
_trace(L"_globalActivateWindow");
// First, get the window that's currently in the foreground. We'll need
// _this_ window to be able to appear on top of. If we just use
// GetForegroundWindow after the SetWindowPlacement/ShowWindow call, _we_
// will be the foreground window.
const auto oldForegroundWindow = GetForegroundWindow();
_trace(fmt::format(L"oldForegroundWindow: {}", (uint64_t)oldForegroundWindow).c_str());
// From: https://stackoverflow.com/a/59659421
// > The trick is to make windows think that our process and the target
// > window (hwnd) are related by attaching the threads (using
@@ -1522,6 +1540,8 @@ void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration,
// restore-down the window.
if (IsIconic(_window.get()))
{
_trace(L"window was iconic");
if (dropdownDuration > 0)
{
_dropdownWindow(dropdownDuration, toMonitor);
@@ -1538,6 +1558,36 @@ void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration,
}
ShowWindow(_window.get(), SW_RESTORE);
if (!SetForegroundWindow(_window.get()))
{
const auto gle = GetLastError();
_trace(fmt::format(L"SetForegroundWindow failed: {}", gle).c_str());
const auto fg = GetForegroundWindow();
std::wstring title(GetWindowTextLength(fg) + 1, L'\0');
GetWindowTextW(fg, &title[0], (DWORD)title.size());
DWORD fgPid = 0;
GetWindowThreadProcessId(fg, &fgPid);
_trace(fmt::format(L"Foreground Window is: [{}]={}", (uint64_t)fgPid, title).c_str());
SwitchToThisWindow(_window.get(), false);
}
else
{
_trace(L"got fg?");
}
// LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get()));
if (!BringWindowToTop(_window.get()))
{
const auto gle = GetLastError();
_trace(fmt::format(L"BringWindowToTop failed: {}", gle).c_str());
}
else
{
_trace(L"brought to top?");
}
// Once we've been restored, throw us on the active monitor.
_moveToMonitor(oldForegroundWindow, toMonitor);
}

View File

@@ -14,6 +14,8 @@ using namespace winrt;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::UI::Notifications;
using namespace ::Microsoft::Console;
using namespace std::chrono_literals;
using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers;
@@ -58,8 +60,92 @@ void _buildArgsFromCommandline(std::vector<winrt::hstring>& args)
}
}
// Method Description:
// - Attempt to handle activated event args, which are a kind of "modern"
// activation, which we use for supporting toast notifications.
// - If we do find we were activated from a toast notification, we'll unpack the
// arguments from the toast. Then, we'll try to open up a connection to the
// monarch and ask the monarch to activate the right window.
// Arguments:
// - <none>
// Return Value:
// - <none>
bool WindowEmperor::_handleLaunchArgs()
{
return false;
}
// try
// {
// // AppInstance::GetActivatedEventArgs will throw when unpackaged.
// if (!IsPackaged())
// {
// return false;
// }
// // If someone clicks on a notification, then a fresh instance of
// // windowsterminal.exe will spawn. We certainly don't want to create a new
// // window for that - we only want to activate the window that created the
// // actual notification. In the toast arg's payload will be the window id
// // that sent the notification. We'll ask the window manager to try and
// // activate that window ID, without even bothering to register as the
// // monarch ourselves (if we can't find a monarch, then there are no windows
// // running, so whoever sent it must have died.)
// const auto activatedArgs = AppInstance::GetActivatedEventArgs();
// if (activatedArgs != nullptr &&
// activatedArgs.Kind() == Activation::ActivationKind::ToastNotification)
// {
// if (const auto& toastArgs{ activatedArgs.try_as<Activation::ToastNotificationActivatedEventArgs>() })
// {
// // Obtain the arguments from the notification
// const auto args = toastArgs.Argument();
// // Args is gonna look like
// //
// // "window=id&foo=bar&..."
// //
// // We need to first split on &, then split those pairs on =
// // tabIndex code here is left as reference for parsing multiple
// // arguments, despite it not being used currently.
// uint32_t window;
// // uint32_t tabIndex = 0;
// const std::wstring_view argsView{ args };
// const auto pairs = Utils::SplitString(argsView, L'&');
// for (const auto& pair : pairs)
// {
// const auto pairParts = Utils::SplitString(pair, L'=');
// if (pairParts.size() == 2)
// {
// if (til::at(pairParts, 0) == L"window")
// {
// window = std::wcstoul(pairParts[1].data(), nullptr, 10);
// }
// // else if (pairParts[0] == L"tabIndex")
// // {
// // // convert a wide string to a uint
// // tabIndex = std::wcstoul(pairParts[1].data(), nullptr, 10);
// // }
// }
// }
// return winrt::Microsoft::Terminal::Remoting::WindowManager::SummonForNotification(window);
// }
// }
// return false;
// }
// CATCH_LOG_RETURN_HR(false)
void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
{
// Before handling any commandline arguments, check if this was a toast
// invocation. If it was, we can go ahead and totally ignore everything
// else.
if (_handleLaunchArgs())
{
TerminateProcess(GetCurrentProcess(), 0u);
}
std::vector<winrt::hstring> args;
_buildArgsFromCommandline(args);
const auto cwd{ wil::GetCurrentDirectoryW<std::wstring>() };

View File

@@ -29,6 +29,7 @@ public:
void HandleCommandlineArgs(int nCmdShow);
private:
bool _handleLaunchArgs();
void _createNewWindowThread(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
[[nodiscard]] static LRESULT __stdcall _wndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;

View File

@@ -59,7 +59,14 @@ Abstract:
// * Media for ScaleTransform
// * ApplicationModel for finding the path to wt.exe
// * Primitives for Popup (used by GetOpenPopupsForXamlRoot)
// * XML, Notifications, Activation: for notification activations
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <winrt/Windows.Data.Xml.Dom.h>
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Core.h>
#include <winrt/Windows.UI.Notifications.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Data.h>

View File

@@ -49,7 +49,8 @@
X(bool, DetectURLs, true) \
X(bool, VtPassthrough, false) \
X(bool, AutoMarkPrompts) \
X(bool, RepositionCursorWithMouse, false)
X(bool, RepositionCursorWithMouse, false) \
X(bool, AllowNotifications, true)
// --------------------------- Control Settings ---------------------------
// All of these settings are defined in IControlSettings.

View File

@@ -187,4 +187,15 @@
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_Notifications</name>
<description>Enables OSC777 notifications</description>
<id>16654</id>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Canary</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
</featureStaging>

View File

@@ -76,6 +76,8 @@ public:
void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override;
void SendNotification(const std::wstring_view /*title*/, const std::wstring_view /*body*/) noexcept override{};
private:
Microsoft::Console::IIoProvider& _io;
};

View File

@@ -137,6 +137,7 @@ public:
virtual bool DoITerm2Action(const std::wstring_view string) = 0;
virtual bool DoFinalTermAction(const std::wstring_view string) = 0;
virtual bool DoUrxvtAction(const std::wstring_view string) = 0;
virtual bool DoVsCodeAction(const std::wstring_view string) = 0;

View File

@@ -87,5 +87,7 @@ namespace Microsoft::Console::VirtualTerminal
virtual void MarkCommandFinish(std::optional<unsigned int> error) = 0;
virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0;
virtual void SendNotification(const std::wstring_view title, const std::wstring_view body) = 0;
};
}

View File

@@ -3863,6 +3863,41 @@ bool AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
return false;
}
bool AdaptDispatch::DoUrxvtAction(const std::wstring_view string)
{
// This is not implemented in conhost.
if (_api.IsConsolePty())
{
// Flush the frame manually, to make sure marks end up on the right line, like the alt buffer sequence.
_renderer.TriggerFlush(false);
return false;
}
if constexpr (!Feature_Notifications::IsEnabled())
{
return false;
}
const auto parts = Utils::SplitString(string, L';');
if (parts.size() < 1)
{
return false;
}
const auto action = til::at(parts, 0);
if (action == L"notify")
{
const std::wstring_view title = parts.size() > 1 ? til::at(parts, 1) : L"";
const std::wstring_view body = parts.size() > 2 ? til::at(parts, 2) : L"";
_api.SendNotification(title, body);
return true;
}
return false;
}
// Method Description:
// - DECDLD - Downloads one or more characters of a dynamically redefinable
// character set (DRCS) with a specified pixel pattern. The pixel array is

View File

@@ -139,6 +139,7 @@ namespace Microsoft::Console::VirtualTerminal
bool DoITerm2Action(const std::wstring_view string) override;
bool DoFinalTermAction(const std::wstring_view string) override;
bool DoUrxvtAction(const std::wstring_view string) override;
bool DoVsCodeAction(const std::wstring_view string) override;

View File

@@ -133,6 +133,8 @@ public:
bool DoVsCodeAction(const std::wstring_view /*string*/) override { return false; }
bool DoUrxvtAction(const std::wstring_view /*string*/) override { return false; }
StringHandler DownloadDRCS(const VTInt /*fontNumber*/,
const VTParameter /*startChar*/,
const DispatchTypes::DrcsEraseControl /*eraseControl*/,

View File

@@ -238,6 +238,11 @@ public:
VERIFY_ARE_EQUAL(_expectedReplaceLength, replaceLength);
}
void SendNotification(const std::wstring_view /*title*/, const std::wstring_view /*body*/) override
{
Log::Comment(L"SendNotification MOCK called...");
}
void PrepData()
{
PrepData(CursorDirection::UP); // if called like this, the cursor direction doesn't matter.

View File

@@ -883,6 +883,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
success = _dispatch->DoVsCodeAction(string);
break;
}
case OscActionCodes::UrxvtAction:
{
success = _dispatch->DoUrxvtAction(string);
break;
}
default:
// If no functions to call, overall dispatch was a failure.
success = false;

View File

@@ -218,6 +218,7 @@ namespace Microsoft::Console::VirtualTerminal
ResetCursorColor = 112,
FinalTermAction = 133,
VsCodeAction = 633,
UrxvtAction = 777,
ITerm2Action = 1337,
};