Compare commits

...

30 Commits

Author SHA1 Message Date
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
40 changed files with 391 additions and 2 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

@@ -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,110 @@ 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>\
<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"//text[1]").InnerText(title);
doc.SelectSingleNode(L"//text[2]").InnerText(body);
// Construct the notification
ToastNotification notification{ doc };
// lazy-init
if (!_toastNotifier)
{
_toastNotifier = ToastNotificationManager::CreateToastNotifier();
}
// And show it!
_toastNotifier.Show(notification);
}
// Method Description:
// - Paste text from the Windows Clipboard to the focused terminal
void TerminalPage::_PasteText()

View File

@@ -287,6 +287,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 +546,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

@@ -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,89 @@ 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()
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,
};