diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index f61168603bf..72e6f4d6ad5 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -32,6 +32,8 @@ IInheritable IMap IObject IStorage +ITaskbar +llabs LCID llabs lround @@ -41,6 +43,7 @@ NCHITTEST NCLBUTTONDBLCLK NCRBUTTONDBLCLK NOAGGREGATION +NOPROGRESS NOREDIRECTIONBITMAP oaidl ocidl @@ -62,6 +65,7 @@ sregex STDCPP strchr syscall +TBPF THEMECHANGED tmp tx diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 7418d80ff95..a6f15bea939 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1051,6 +1051,32 @@ namespace winrt::TerminalApp::implementation } } + // Method Description: + // - Gets the taskbar state value from the last active control + // Return Value: + // - The taskbar state of the last active control + size_t AppLogic::GetLastActiveControlTaskbarState() + { + if (_root) + { + return _root->GetLastActiveControlTaskbarState(); + } + return {}; + } + + // Method Description: + // - Gets the taskbar progress value from the last active control + // Return Value: + // - The taskbar progress of the last active control + size_t AppLogic::GetLastActiveControlTaskbarProgress() + { + if (_root) + { + return _root->GetLastActiveControlTaskbarProgress(); + } + return {}; + } + // Method Description: // - Sets the initial commandline to process on startup, and attempts to // parse it. Commands will be parsed into a list of ShortcutActions that diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index ebd901b44af..67512709ec7 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -50,6 +50,9 @@ namespace winrt::TerminalApp::implementation void WindowCloseButtonClicked(); + size_t GetLastActiveControlTaskbarState(); + size_t GetLastActiveControlTaskbarProgress(); + winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); // -------------------------------- WinRT Events --------------------------------- @@ -110,6 +113,7 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(FocusModeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FocusModeChanged); FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged); FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged); + FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress); }; } diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 7056f71b540..20b2d835c94 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -52,6 +52,9 @@ namespace TerminalApp void TitlebarClicked(); void WindowCloseButtonClicked(); + UInt64 GetLastActiveControlTaskbarState(); + UInt64 GetLastActiveControlTaskbarProgress(); + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); @@ -63,5 +66,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler FocusModeChanged; event Windows.Foundation.TypedEventHandler FullscreenChanged; event Windows.Foundation.TypedEventHandler AlwaysOnTopChanged; + event Windows.Foundation.TypedEventHandler SetTaskbarProgress; } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d9f33e167da..deb5bc53aa5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1132,6 +1132,9 @@ namespace winrt::TerminalApp::implementation term.OpenHyperlink({ this, &TerminalPage::_OpenHyperlinkHandler }); + // Add an event handler for when the terminal wants to set a progress indicator on the taskbar + term.SetTaskbarProgress({ this, &TerminalPage::_SetTaskbarProgressHandler }); + // Bind Tab events to the TermControl and the Tab's Pane hostingTab.Initialize(term); @@ -1984,6 +1987,16 @@ namespace winrt::TerminalApp::implementation return control.CopySelectionToClipboard(singleLine, formats); } + // Method Description: + // - Send an event (which will be caught by AppHost) to set the progress indicator on the taskbar + // Arguments: + // - sender (not used) + // - eventArgs: the arguments specifying how to set the progress indicator + void TerminalPage::_SetTaskbarProgressHandler(const IInspectable /*sender*/, const IInspectable /*eventArgs*/) + { + _setTaskbarProgressHandlers(*this, nullptr); + } + // Method Description: // - Paste text from the Windows Clipboard to the focused terminal void TerminalPage::_PasteText() @@ -2365,6 +2378,32 @@ namespace winrt::TerminalApp::implementation _dialogPresenter = dialogPresenter; } + // Method Description: + // - Gets the taskbar state value from the last active control + // Return Value: + // - The taskbar state of the last active control + size_t TerminalPage::GetLastActiveControlTaskbarState() + { + if (auto control{ _GetActiveControl() }) + { + return gsl::narrow_cast(control.TaskbarState()); + } + return {}; + } + + // Method Description: + // - Gets the taskbar progress value from the last active control + // Return Value: + // - The taskbar progress of the last active control + size_t TerminalPage::GetLastActiveControlTaskbarProgress() + { + if (auto control{ _GetActiveControl() }) + { + return gsl::narrow_cast(control.TaskbarProgress()); + } + return {}; + } + // Method Description: // - This is the method that App will call when the titlebar // has been clicked. It dismisses any open flyouts. @@ -2890,4 +2929,5 @@ namespace winrt::TerminalApp::implementation DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, FocusModeChanged, _focusModeChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, FullscreenChanged, _fullscreenChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, AlwaysOnTopChanged, _alwaysOnTopChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, SetTaskbarProgress, _setTaskbarProgressHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 5275227f613..e3a24cec3f2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -71,6 +71,9 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::IDialogPresenter DialogPresenter() const; void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter); + size_t GetLastActiveControlTaskbarState(); + size_t GetLastActiveControlTaskbarProgress(); + void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); @@ -81,6 +84,7 @@ namespace winrt::TerminalApp::implementation DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(FocusModeChanged, _focusModeChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(FullscreenChanged, _fullscreenChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(AlwaysOnTopChanged, _alwaysOnTopChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTaskbarProgress, _setTaskbarProgressHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(Initialized, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs); private: @@ -195,6 +199,8 @@ namespace winrt::TerminalApp::implementation void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri); bool _CopyText(const bool singleLine, const Windows::Foundation::IReference& formats); + void _SetTaskbarProgressHandler(const IInspectable sender, const IInspectable eventArgs); + void _PasteText(); void _ControlNoticeRaisedHandler(const IInspectable sender, const Microsoft::Terminal::TerminalControl::NoticeEventArgs eventArgs); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index affaa1a5d69..b2580d158c4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -29,6 +29,9 @@ namespace TerminalApp void ShowKeyboardServiceWarning(); String KeyboardServiceDisabledText { get; }; + UInt64 GetLastActiveControlTaskbarState(); + UInt64 GetLastActiveControlTaskbarProgress(); + event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler LastTabClosed; event Windows.Foundation.TypedEventHandler SetTitleBarContent; @@ -36,5 +39,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler FullscreenChanged; event Windows.Foundation.TypedEventHandler AlwaysOnTopChanged; event Windows.Foundation.TypedEventHandler Initialized; + event Windows.Foundation.TypedEventHandler SetTaskbarProgress; } } diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 567d48a7ba1..dcddb42b843 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -105,6 +105,7 @@ namespace winrt::TerminalApp::implementation if (lastFocusedControl) { lastFocusedControl.Focus(_focusState); + lastFocusedControl.TaskbarProgressChanged(); } } } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 1711b2fef0e..01eee9c2698 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -107,6 +107,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation auto pfnCopyToClipboard = std::bind(&TermControl::_CopyToClipboard, this, std::placeholders::_1); _terminal->SetCopyToClipboardCallback(pfnCopyToClipboard); + _terminal->TaskbarProgressChangedCallback([&]() { TermControl::TaskbarProgressChanged(); }); + // This event is explicitly revoked in the destructor: does not need weak_ref auto onReceiveOutputFn = [this](const hstring str) { _terminal->Write(str); @@ -3091,6 +3093,33 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return coreColor.has_value() ? Windows::Foundation::IReference(coreColor.value()) : nullptr; } + // Method Description: + // - Sends an event (which will be caught by TerminalPage and forwarded to AppHost after) + // to set the progress indicator on the taskbar + winrt::fire_and_forget TermControl::TaskbarProgressChanged() + { + co_await resume_foreground(Dispatcher(), CoreDispatcherPriority::High); + _setTaskbarProgressHandlers(*this, nullptr); + } + + // Method Description: + // - Gets the internal taskbar state value + // Return Value: + // - The taskbar state of this control + const size_t TermControl::TaskbarState() const noexcept + { + return _terminal->GetTaskbarState(); + } + + // Method Description: + // - Gets the internal taskbar progress value + // Return Value: + // - The taskbar progress of this control + const size_t TermControl::TaskbarProgress() const noexcept + { + return _terminal->GetTaskbarProgress(); + } + // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. @@ -3101,6 +3130,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs); DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs); DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, SetTaskbarProgress, _setTaskbarProgressHandlers, TerminalControl::TermControl, IInspectable); DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, RaiseNotice, _raiseNoticeHandlers, TerminalControl::TermControl, TerminalControl::NoticeEventArgs); // clang-format on } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index c5dcd819f6c..4e2ebba54ce 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -158,6 +158,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation Windows::Foundation::IReference TabColor() noexcept; + winrt::fire_and_forget TaskbarProgressChanged(); + const size_t TaskbarState() const noexcept; + const size_t TaskbarProgress() const noexcept; + // clang-format off // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT(TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs); @@ -167,6 +171,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTaskbarProgress, _setTaskbarProgressHandlers, TerminalControl::TermControl, IInspectable); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RaiseNotice, _raiseNoticeHandlers, TerminalControl::TermControl, TerminalControl::NoticeEventArgs); TYPED_EVENT(WarningBell, IInspectable, IInspectable); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index f1cfcef3bf1..22a3345d311 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -74,6 +74,7 @@ namespace Microsoft.Terminal.TerminalControl event Windows.Foundation.TypedEventHandler CopyToClipboard; event Windows.Foundation.TypedEventHandler PasteFromClipboard; event Windows.Foundation.TypedEventHandler OpenHyperlink; + event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler RaiseNotice; event Windows.Foundation.TypedEventHandler WarningBell; @@ -105,6 +106,10 @@ namespace Microsoft.Terminal.TerminalControl void SendInput(String input); void ToggleRetroEffect(); + void TaskbarProgressChanged(); + UInt64 TaskbarState { get; }; + UInt64 TaskbarProgress { get; }; + Windows.Foundation.IReference TabColor { get; }; event Windows.Foundation.TypedEventHandler TabColorChanged; } diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index fda9cef77cc..f9f0f06868d 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -63,6 +63,8 @@ namespace Microsoft::Terminal::Core virtual bool AddHyperlink(std::wstring_view uri, std::wstring_view params) noexcept = 0; virtual bool EndHyperlink() noexcept = 0; + virtual bool SetTaskbarProgress(const size_t state, const size_t progress) noexcept = 0; + protected: ITerminalApi() = default; }; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 10dc06c19f0..05e4da5fdd6 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -50,7 +50,9 @@ Terminal::Terminal() : _snapOnInput{ true }, _altGrAliasing{ true }, _blockSelection{ false }, - _selection{ std::nullopt } + _selection{ std::nullopt }, + _taskbarState{ 0 }, + _taskbarProgress{ 0 } { auto dispatch = std::make_unique(*this); auto engine = std::make_unique(std::move(dispatch)); @@ -1102,6 +1104,16 @@ void Terminal::SetBackgroundCallback(std::function pfn) no _pfnBackgroundColorChanged.swap(pfn); } +// Method Description: +// - Allows settings a callback for settings the taskbar progress indicator +// Arguments: +// - pfn: a function callback that takes 2 size_t parameters, one indicating the progress state +// and the other indicating the progress value +void Microsoft::Terminal::Core::Terminal::TaskbarProgressChangedCallback(std::function pfn) noexcept +{ + _pfnTaskbarProgressChanged.swap(pfn); +} + void Terminal::_InitializeColorTable() try { @@ -1168,3 +1180,21 @@ BlinkingState& Terminal::GetBlinkingState() const noexcept { return _blinkingState; } + +// Method Description: +// - Gets the internal taskbar state value +// Return Value: +// - The taskbar state +const size_t Microsoft::Terminal::Core::Terminal::GetTaskbarState() const noexcept +{ + return _taskbarState; +} + +// Method Description: +// - Gets the internal taskbar progress value +// Return Value: +// - The taskbar progress +const size_t Microsoft::Terminal::Core::Terminal::GetTaskbarProgress() const noexcept +{ + return _taskbarProgress; +} diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index c9b30378dfb..c691fc99f06 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -115,6 +115,8 @@ class Microsoft::Terminal::Core::Terminal final : bool AddHyperlink(std::wstring_view uri, std::wstring_view params) noexcept override; bool EndHyperlink() noexcept override; + + bool SetTaskbarProgress(const size_t state, const size_t progress) noexcept override; #pragma endregion #pragma region ITerminalInput @@ -185,6 +187,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetScrollPositionChangedCallback(std::function pfn) noexcept; void SetCursorPositionChangedCallback(std::function pfn) noexcept; void SetBackgroundCallback(std::function pfn) noexcept; + void TaskbarProgressChangedCallback(std::function pfn) noexcept; void SetCursorOn(const bool isOn); bool IsCursorBlinkingAllowed() const noexcept; @@ -196,6 +199,9 @@ class Microsoft::Terminal::Core::Terminal final : Microsoft::Console::Render::BlinkingState& GetBlinkingState() const noexcept; + const size_t GetTaskbarState() const noexcept; + const size_t GetTaskbarProgress() const noexcept; + #pragma region TextSelection // These methods are defined in TerminalSelection.cpp enum class SelectionExpansionMode @@ -221,6 +227,7 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnBackgroundColorChanged; std::function _pfnCursorPositionChanged; std::function)> _pfnTabColorChanged; + std::function _pfnTaskbarProgressChanged; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; std::unique_ptr<::Microsoft::Console::VirtualTerminal::TerminalInput> _terminalInput; @@ -240,6 +247,9 @@ class Microsoft::Terminal::Core::Terminal final : bool _altGrAliasing; bool _suppressApplicationTitle; + size_t _taskbarState; + size_t _taskbarProgress; + size_t _hyperlinkPatternId; #pragma region Text Selection diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 8ed4094184f..cccd9856a8c 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -600,3 +600,21 @@ bool Terminal::EndHyperlink() noexcept _buffer->SetCurrentAttributes(attr); return true; } + +// Method Description: +// - Updates the taskbar progress indicator +// Arguments: +// - state: indicates the progress state +// - progress: indicates the progress value +// Return Value: +// - true +bool Terminal::SetTaskbarProgress(const size_t state, const size_t progress) noexcept +{ + _taskbarState = state; + _taskbarProgress = progress; + if (_pfnTaskbarProgressChanged) + { + _pfnTaskbarProgressChanged(); + } + return true; +} diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index a035febf152..8d873f1102f 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -3,6 +3,9 @@ #include "pch.h" #include "TerminalDispatch.hpp" +#include "../../types/inc/utils.hpp" + +using namespace Microsoft::Console; using namespace ::Microsoft::Terminal::Core; using namespace ::Microsoft::Console::VirtualTerminal; @@ -399,6 +402,60 @@ bool TerminalDispatch::EndHyperlink() noexcept return _terminalApi.EndHyperlink(); } +// Method Description: +// - Performs a ConEmu action +// - Currently, the only action we support is setting the taskbar state/progress +// Arguments: +// - string: contains the parameters that define which action we do +// Return Value: +// - true +bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) noexcept +{ + unsigned int state = 0; + unsigned int progress = 0; + + const auto parts = Utils::SplitString(string, L';'); + unsigned int subParam = 0; + + // For now, the only ConEmu action we support is setting the taskbar state/progress, + // which has a sub param value of 4 + if (parts.size() < 1 || !Utils::StringToUint(til::at(parts, 0), subParam) || subParam != 4) + { + return false; + } + + if (parts.size() >= 2) + { + // A state parameter is defined, parse it out + const auto stateSuccess = Utils::StringToUint(til::at(parts, 1), state); + if (!stateSuccess) + { + return false; + } + if (parts.size() >= 3) + { + // A progress parameter is also defined, parse it out + const auto progressSuccess = Utils::StringToUint(til::at(parts, 2), progress); + if (!progressSuccess) + { + return false; + } + } + } + + if (state > TaskbarMaxState) + { + // state is out of bounds, return false + return false; + } + if (progress > TaskbarMaxProgress) + { + // progress is greater than the maximum allowed value, clamp it to the max + progress = TaskbarMaxProgress; + } + return _terminalApi.SetTaskbarProgress(state, progress); +} + // Routine Description: // - Support routine for routing private mode parameters to be set/reset as flags // Arguments: diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index 5e409161d26..5b0bce31f9c 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -4,6 +4,9 @@ #include "../../terminal/adapter/termDispatch.hpp" #include "ITerminalApi.hpp" +static constexpr size_t TaskbarMaxState{ 4 }; +static constexpr size_t TaskbarMaxProgress{ 100 }; + class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatch { public: @@ -67,6 +70,8 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) noexcept override; bool EndHyperlink() noexcept override; + bool DoConEmuAction(const std::wstring_view string) noexcept override; + private: ::Microsoft::Terminal::Core::ITerminalApi& _terminalApi; diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp index 08a58ef60d3..2060e444c2e 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp @@ -38,6 +38,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(AddHyperlink); TEST_METHOD(AddHyperlinkCustomId); TEST_METHOD(AddHyperlinkCustomIdDifferentUri); + + TEST_METHOD(SetTaskbarProgress); }; }; @@ -339,3 +341,50 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlinkCustomIdDifferentUri() VERIFY_ARE_EQUAL(tbi.GetHyperlinkUriFromId(oldAttributes.GetHyperlinkId()), L"test.url"); VERIFY_ARE_NOT_EQUAL(oldAttributes.GetHyperlinkId(), tbi.GetCurrentAttributes().GetHyperlinkId()); } + +void TerminalCoreUnitTests::TerminalApiTest::SetTaskbarProgress() +{ + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + auto& stateMachine = *(term._stateMachine); + + // Initial values for taskbar state and progress should be 0 + VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow(0)); + VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow(0)); + + // Set some values for taskbar state and progress through state machine + stateMachine.ProcessString(L"\x1b]9;4;1;50\x9c"); + VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow(1)); + VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow(50)); + + // Reset to 0 + stateMachine.ProcessString(L"\x1b]9;4;0;0\x9c"); + VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow(0)); + VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow(0)); + + // Set an out of bounds value for state + stateMachine.ProcessString(L"\x1b]9;4;5;50\x9c"); + // Nothing should have changed (dispatch should have returned false) + VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow(0)); + VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow(0)); + + // Set an out of bounds value for progress + stateMachine.ProcessString(L"\x1b]9;4;1;999\x9c"); + // Progress should have been clamped to 100 + VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow(1)); + VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow(100)); + + // Don't specify any params + stateMachine.ProcessString(L"\x1b]9;4\x9c"); + // State and progress should both be reset to 0 + VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow(0)); + VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow(0)); + + // Specify additional params + stateMachine.ProcessString(L"\x1b]9;4;1;80;123\x9c"); + // Additional params should be ignored, state and progress still set normally + VERIFY_ARE_EQUAL(term.GetTaskbarState(), gsl::narrow(1)); + VERIFY_ARE_EQUAL(term.GetTaskbarProgress(), gsl::narrow(80)); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 5c258b6c59b..2e5339fa220 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -79,6 +79,23 @@ bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, cons return false; } +// Method Description: +// - Event handler to update the taskbar progress indicator +// - Upon receiving the event, we ask the underlying logic for the taskbar state/progress values +// of the last active control +// Arguments: +// - sender: not used +// - args: not used +void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) +{ + if (_logic) + { + const auto state = gsl::narrow_cast(_logic.GetLastActiveControlTaskbarState()); + const auto progress = gsl::narrow_cast(_logic.GetLastActiveControlTaskbarProgress()); + _window->SetTaskbarProgress(state, progress); + } +} + // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to // the app logic for processing. @@ -171,6 +188,7 @@ void AppHost::Initialize() _logic.TitleChanged({ this, &AppHost::AppTitleChanged }); _logic.LastTabClosed({ this, &AppHost::LastTabClosed }); + _logic.SetTaskbarProgress({ this, &AppHost::SetTaskbarProgress }); _window->UpdateTitle(_logic.Title()); diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 4bdc45c5d29..23e7c534d94 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -18,6 +18,7 @@ class AppHost void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args); void Initialize(); bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); + void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); private: bool _useNonClientArea; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 0e8cc1a82c3..9f4d9c92c16 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -261,6 +261,15 @@ void IslandWindow::Initialize() _rootGrid = winrt::Windows::UI::Xaml::Controls::Grid(); _source.Content(_rootGrid); + + // initialize the taskbar object + if (auto taskbar = wil::CoCreateInstanceNoThrow(CLSID_TaskbarList)) + { + if (SUCCEEDED(taskbar->HrInit())) + { + _taskbar = std::move(taskbar); + } + } } void IslandWindow::OnSize(const UINT width, const UINT height) @@ -576,6 +585,48 @@ void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop) } } +// Method Description: +// - Sets the taskbar progress indicator +// - We follow the ConEmu style for the state and progress values, +// more details at https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC +// Arguments: +// - state: indicates the progress state +// - progress: indicates the progress value +void IslandWindow::SetTaskbarProgress(const size_t state, const size_t progress) +{ + if (_taskbar) + { + switch (state) + { + case 0: + // removes the taskbar progress indicator + _taskbar->SetProgressState(_window.get(), TBPF_NOPROGRESS); + break; + case 1: + // 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: + // sets the progress indicator to an error state + _taskbar->SetProgressState(_window.get(), TBPF_ERROR); + _taskbar->SetProgressValue(_window.get(), progress, 100); + break; + case 3: + // sets the progress indicator to an indeterminate state + _taskbar->SetProgressState(_window.get(), TBPF_INDETERMINATE); + break; + case 4: + // sets the progress indicator to a pause state + _taskbar->SetProgressState(_window.get(), TBPF_PAUSED); + _taskbar->SetProgressValue(_window.get(), progress, 100); + break; + default: + break; + } + } +} + // From GdiEngine::s_SetWindowLongWHelper void _SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept { diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index f2ccd4a93e5..882e1525891 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -35,6 +35,8 @@ class IslandWindow : void FullscreenChanged(const bool fullscreen); void SetAlwaysOnTop(const bool alwaysOnTop); + void SetTaskbarProgress(const size_t state, const size_t progress); + #pragma endregion DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); @@ -74,6 +76,8 @@ class IslandWindow : LONG _getDesiredWindowStyle() const; + wil::com_ptr _taskbar; + void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam); long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize); diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index e899da5e1c0..bdf98d0d7d9 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -29,6 +29,7 @@ Module Name: #include #include #include +#include // Manually include til after we include Windows.Foundation to give it winrt superpowers #define BLOCK_TIL diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 9be6e3146b7..d7c2991ec1a 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -122,6 +122,8 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) = 0; virtual bool EndHyperlink() = 0; + + virtual bool DoConEmuAction(const std::wstring_view string) = 0; }; inline Microsoft::Console::VirtualTerminal::ITermDispatch::~ITermDispatch() {} #pragma warning(pop) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 25e896f601f..177648520de 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -2383,6 +2383,16 @@ bool AdaptDispatch::EndHyperlink() return _pConApi->PrivateEndHyperlink(); } +// Method Description: +// - Ascribes to the ITermDispatch interface +// - Not actually used in conhost +// Return Value: +// - false (so that the command gets flushed to terminal) +bool AdaptDispatch::DoConEmuAction(const std::wstring_view /*string*/) noexcept +{ + return false; +} + // Routine Description: // - Determines whether we should pass any sequence that manipulates // TerminalInput's input generator through the PTY. It encapsulates diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 6644c3e0228..aafb267d266 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -123,6 +123,8 @@ namespace Microsoft::Console::VirtualTerminal bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) override; bool EndHyperlink() override; + bool DoConEmuAction(const std::wstring_view string) noexcept override; + private: enum class ScrollDirection { diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index cb400814f4c..9083d2475b8 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -116,4 +116,6 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool AddHyperlink(const std::wstring_view /*uri*/, const std::wstring_view /*params*/) noexcept override { return false; } bool EndHyperlink() noexcept override { return false; } + + bool DoConEmuAction(const std::wstring_view /*string*/) noexcept override { return false; } }; diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 0ee8e109476..13e47578c8a 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -733,6 +733,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, } break; } + case OscActionCodes::ConEmuAction: + { + success = _dispatch->DoConEmuAction(string); + break; + } default: // If no functions to call, overall dispatch was a failure. success = false; diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index 2d64f22e063..b1ebd19b87f 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -160,6 +160,7 @@ namespace Microsoft::Console::VirtualTerminal SetWindowProperty = 3, // Not implemented SetColor = 4, Hyperlink = 8, + ConEmuAction = 9, SetForegroundColor = 10, SetBackgroundColor = 11, SetCursorColor = 12, diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp index 63e39a52d77..6ce4de358e7 100644 --- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp @@ -1456,6 +1456,11 @@ class StatefulDispatch final : public TermDispatch return true; } + bool DoConEmuAction(const std::wstring_view /*string*/) noexcept override + { + return true; + } + size_t _cursorDistance; size_t _line; size_t _column;