From d2862a83862536e7de2cef96ea43a26bcd7343e3 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 12 Apr 2024 00:57:32 +0200 Subject: [PATCH] Backport session persistence improvements --- .github/actions/spelling/expect/expect.txt | 20 +- src/cascadia/Remoting/GetWindowLayoutArgs.cpp | 5 - src/cascadia/Remoting/GetWindowLayoutArgs.h | 31 --- .../Microsoft.Terminal.RemotingLib.vcxproj | 12 - src/cascadia/Remoting/Monarch.cpp | 41 +--- src/cascadia/Remoting/Monarch.h | 7 +- src/cascadia/Remoting/Monarch.idl | 7 - src/cascadia/Remoting/Peasant.cpp | 21 -- src/cascadia/Remoting/Peasant.h | 2 - src/cascadia/Remoting/Peasant.idl | 8 - .../Remoting/QuitAllRequestedArgs.cpp | 5 - src/cascadia/Remoting/QuitAllRequestedArgs.h | 29 --- src/cascadia/Remoting/WindowManager.cpp | 29 --- src/cascadia/Remoting/WindowManager.h | 5 - src/cascadia/Remoting/WindowManager.idl | 4 - src/cascadia/TerminalApp/AppLogic.cpp | 23 +- src/cascadia/TerminalApp/AppLogic.h | 3 - src/cascadia/TerminalApp/AppLogic.idl | 1 - src/cascadia/TerminalApp/Pane.cpp | 34 +-- src/cascadia/TerminalApp/Pane.h | 5 +- src/cascadia/TerminalApp/SettingsTab.cpp | 2 +- src/cascadia/TerminalApp/SettingsTab.h | 2 +- src/cascadia/TerminalApp/TabBase.h | 10 +- src/cascadia/TerminalApp/TabManagement.cpp | 16 +- src/cascadia/TerminalApp/TerminalPage.cpp | 48 ++-- src/cascadia/TerminalApp/TerminalPage.h | 18 +- src/cascadia/TerminalApp/TerminalPage.idl | 7 +- src/cascadia/TerminalApp/TerminalTab.cpp | 6 +- src/cascadia/TerminalApp/TerminalTab.h | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 40 +--- src/cascadia/TerminalApp/TerminalWindow.h | 9 +- src/cascadia/TerminalApp/TerminalWindow.idl | 7 +- .../TerminalSettingsEditor/ProfileViewModel.h | 2 +- .../ApplicationState.cpp | 49 ++-- .../TerminalSettingsModel/ApplicationState.h | 5 +- .../ApplicationState.idl | 4 +- .../CascadiaSettingsSerialization.cpp | 2 +- .../TerminalSettingsModel/GlobalAppSettings.h | 2 +- .../UnitTests_Remoting/RemotingTests.cpp | 4 - src/cascadia/WindowsTerminal/AppHost.cpp | 211 +++++++----------- src/cascadia/WindowsTerminal/AppHost.h | 15 +- .../WindowsTerminal/WindowEmperor.cpp | 110 +-------- src/cascadia/WindowsTerminal/WindowEmperor.h | 9 +- 43 files changed, 207 insertions(+), 665 deletions(-) delete mode 100644 src/cascadia/Remoting/GetWindowLayoutArgs.cpp delete mode 100644 src/cascadia/Remoting/GetWindowLayoutArgs.h delete mode 100644 src/cascadia/Remoting/QuitAllRequestedArgs.cpp delete mode 100644 src/cascadia/Remoting/QuitAllRequestedArgs.h diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 3c140f07a11..e19b672af7d 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -19,6 +19,7 @@ AFew AFill AFX AHelper +ahicon ahz AImpl AInplace @@ -227,6 +228,7 @@ commandline commctrl commdlg COMMITID +commoncontrols componentization conapi conareainfo @@ -283,6 +285,7 @@ convarea conwinuserrefs coordnew COPYCOLOR +COPYFROMRESOURCE CORESYSTEM cotaskmem countof @@ -578,6 +581,7 @@ EVENTID eventing evflags evt +EXACTSIZEONLY execd executionengine exemain @@ -603,6 +607,7 @@ FEEF fesb FFAF FFDE +FFFDb FFrom fgbg FGCOLOR @@ -772,6 +777,7 @@ hhx HIBYTE hicon HIDEWINDOW +HIGHQUALITYSCALE hinst HISTORYBUFS HISTORYNODUP @@ -821,6 +827,7 @@ hwnd HWNDPARENT iccex ICONERROR +ICONINFO ICONINFORMATION IConsole ICONSTOP @@ -842,7 +849,11 @@ IGNOREEND IGNORELANGUAGE IHosted iid +IImage IIo +ILC +ILCo +ILD ime IMPEXP inbox @@ -1116,8 +1127,8 @@ msix msrc MSVCRTD MTSM -munges Munged +munges murmurhash muxes myapplet @@ -1340,11 +1351,14 @@ pgomgr PGONu pguid phhook +phico +phicon phwnd pidl PIDLIST pids pii +piml pinvoke pipename pipestr @@ -1676,6 +1690,8 @@ slpit SManifest SMARTQUOTE SMTO +snapcx +snapcy SOLIDBOX Solutiondir somefile @@ -1876,8 +1892,8 @@ UINTs ul ulcch uld -uldb uldash +uldb ulwave Unadvise unattend diff --git a/src/cascadia/Remoting/GetWindowLayoutArgs.cpp b/src/cascadia/Remoting/GetWindowLayoutArgs.cpp deleted file mode 100644 index f2cc01df40c..00000000000 --- a/src/cascadia/Remoting/GetWindowLayoutArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#include "pch.h" -#include "GetWindowLayoutArgs.h" -#include "GetWindowLayoutArgs.g.cpp" diff --git a/src/cascadia/Remoting/GetWindowLayoutArgs.h b/src/cascadia/Remoting/GetWindowLayoutArgs.h deleted file mode 100644 index 097e375bc70..00000000000 --- a/src/cascadia/Remoting/GetWindowLayoutArgs.h +++ /dev/null @@ -1,31 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- GetWindowLayoutArgs.h - -Abstract: -- This is a helper class for getting the window layout from a peasant. - Depending on if we are running on the monarch or on a peasant we might need - to switch what thread we are executing on. This gives us the option of - either returning the json result synchronously, or as a promise. ---*/ - -#pragma once - -#include "GetWindowLayoutArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct GetWindowLayoutArgs : public GetWindowLayoutArgsT - { - WINRT_PROPERTY(winrt::hstring, WindowLayoutJson, L""); - WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncOperation, WindowLayoutJsonAsync, nullptr) - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(GetWindowLayoutArgs); -} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index ce78fc9c4b2..b5c7ada4790 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -37,12 +37,6 @@ Peasant.idl - - Peasant.idl - - - Monarch.idl - @@ -78,12 +72,6 @@ Peasant.idl - - Peasant.idl - - - Monarch.idl - Create diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cf2edd98fa3..a21433e87a9 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -6,7 +6,6 @@ #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" -#include "QuitAllRequestedArgs.h" #include "ProposeCommandlineResult.h" #include "Monarch.g.cpp" @@ -135,21 +134,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - used // Return Value: // - - winrt::fire_and_forget Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) + void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - // Let the process hosting the monarch run any needed logic before - // closing all windows. - auto args = winrt::make_self(); - _QuitAllRequestedHandlers(*this, *args); - - if (const auto action = args->BeforeQuitAllAction()) + if (_quitting.exchange(true, std::memory_order_relaxed)) { - co_await action; + return; } - _quitting.store(true); - // Tell all peasants to exit. const auto callback = [&](const auto& id, const auto& p) { // We want to tell our peasant to quit last, so that we don't try // to perform a bunch of elections on quit. @@ -197,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // If we are quitting we don't care about maintaining our list of // peasants anymore, and don't need to notify the host that something // changed. - if (_quitting.load(std::memory_order_acquire)) + if (_quitting.load(std::memory_order_relaxed)) { return; } @@ -1036,30 +1027,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _forEachPeasant(func, onError); } - // Method Description: - // - Ask all peasants to return their window layout as json - // Arguments: - // - - // Return Value: - // - The collection of window layouts from each peasant. - Windows::Foundation::Collections::IVector Monarch::GetAllWindowLayouts() - { - std::vector vec; - auto callback = [&](const auto& /*id*/, const auto& p) { - vec.emplace_back(p.GetWindowLayout()); - }; - auto onError = [](auto&& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_GetAllWindowLayouts_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not get a window layout from"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - _forEachPeasant(callback, onError); - - return winrt::single_threaded_vector(std::move(vec)); - } - void Monarch::RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 203119a70ce..d21b8b7ad7f 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -96,7 +96,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void SummonAllWindows(); bool DoesQuakeWindowExist(); Windows::Foundation::Collections::IVectorView GetPeasantInfos(); - Windows::Foundation::Collections::IVector GetAllWindowLayouts(); void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, const Windows::Foundation::IReference& windowBounds); void RequestSendContent(const Remoting::RequestReceiveContentArgs& args); @@ -106,8 +105,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs); - TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs); private: @@ -146,8 +143,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); - winrt::fire_and_forget _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); // Method Description: // - Helper for doing something on each and every peasant. diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index f57bd6b59b2..c81837e96b5 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -46,11 +46,6 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.IReference WindowID; } - [default_interface] runtimeclass QuitAllRequestedArgs { - QuitAllRequestedArgs(); - Windows.Foundation.IAsyncAction BeforeQuitAllAction; - } - struct PeasantInfo { UInt64 Id; @@ -72,7 +67,6 @@ namespace Microsoft.Terminal.Remoting void SummonAllWindows(); Boolean DoesQuakeWindowExist(); Windows.Foundation.Collections.IVectorView GetPeasantInfos { get; }; - Windows.Foundation.Collections.IVector GetAllWindowLayouts(); void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference bounds); void RequestSendContent(RequestReceiveContentArgs args); @@ -82,7 +76,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; event Windows.Foundation.TypedEventHandler WindowCreated; event Windows.Foundation.TypedEventHandler WindowClosed; - event Windows.Foundation.TypedEventHandler QuitAllRequested; event Windows.Foundation.TypedEventHandler RequestNewWindow; }; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 94f8406c00c..639ffb86125 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -5,7 +5,6 @@ #include "Peasant.h" #include "CommandlineArgs.h" #include "SummonWindowBehavior.h" -#include "GetWindowLayoutArgs.h" #include "Peasant.g.cpp" #include "../../types/inc/utils.hpp" #include "AttachRequest.g.cpp" @@ -309,26 +308,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } - // Method Description: - // - Request and return the window layout from the current TerminalPage - // Arguments: - // - - // Return Value: - // - the window layout as a json string - hstring Peasant::GetWindowLayout() - { - auto args = winrt::make_self(); - _GetWindowLayoutRequestedHandlers(nullptr, *args); - if (const auto op = args->WindowLayoutJsonAsync()) - { - // This will fail if called on the UI thread, so the monarch should - // never set WindowLayoutJsonAsync. - auto str = op.get(); - return str; - } - return args->WindowLayoutJson(); - } - void Peasant::SendContent(const Remoting::RequestReceiveContentArgs& args) { _SendContentRequestedHandlers(*this, args); diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 26a82a17fef..e497300d13d 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -65,7 +65,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); - winrt::hstring GetWindowLayout(); void SendContent(const winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs& args); WINRT_PROPERTY(winrt::hstring, WindowName); @@ -82,7 +81,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs); TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index f02d22da9c4..3463d561f07 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -32,12 +32,6 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.DateTime ActivatedTime { get; }; }; - [default_interface] runtimeclass GetWindowLayoutArgs { - GetWindowLayoutArgs(); - String WindowLayoutJson; - Windows.Foundation.IAsyncOperation WindowLayoutJsonAsync; - } - enum MonitorBehavior { InPlace, @@ -88,7 +82,6 @@ namespace Microsoft.Terminal.Remoting void RequestHideNotificationIcon(); void RequestQuitAll(); void Quit(); - String GetWindowLayout(); void AttachContentToWindow(AttachRequest request); void SendContent(RequestReceiveContentArgs args); @@ -102,7 +95,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; - event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; event Windows.Foundation.TypedEventHandler QuitAllRequested; event Windows.Foundation.TypedEventHandler QuitRequested; diff --git a/src/cascadia/Remoting/QuitAllRequestedArgs.cpp b/src/cascadia/Remoting/QuitAllRequestedArgs.cpp deleted file mode 100644 index ed5c39dcf4f..00000000000 --- a/src/cascadia/Remoting/QuitAllRequestedArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#include "pch.h" -#include "QuitAllRequestedArgs.h" -#include "QuitAllRequestedArgs.g.cpp" diff --git a/src/cascadia/Remoting/QuitAllRequestedArgs.h b/src/cascadia/Remoting/QuitAllRequestedArgs.h deleted file mode 100644 index 4fba59cd141..00000000000 --- a/src/cascadia/Remoting/QuitAllRequestedArgs.h +++ /dev/null @@ -1,29 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- QuitAllRequestedArgs.h - -Abstract: -- This is a helper class for allowing the monarch to run code before telling all - peasants to quit. This way the monarch can raise an event and get back a future - to wait for before continuing. ---*/ - -#pragma once - -#include "QuitAllRequestedArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct QuitAllRequestedArgs : public QuitAllRequestedArgsT - { - WINRT_PROPERTY(winrt::Windows::Foundation::IAsyncAction, BeforeQuitAllAction, nullptr) - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(QuitAllRequestedArgs); -} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index aa9f67b0b3f..34e9583051c 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -92,8 +92,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers }); _monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers }); _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); - _monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers }); - _monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow }); } @@ -356,8 +354,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.AddPeasant(*p); - p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers }); - TraceLoggingWrite(g_hRemotingProvider, "WindowManager_CreateOurPeasant", TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"), @@ -412,18 +408,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return 0; } - // Method Description: - // - Ask the monarch to quit all windows. - // Arguments: - // - - // Return Value: - // - - winrt::fire_and_forget WindowManager::RequestQuitAll(Remoting::Peasant peasant) - { - co_await winrt::resume_background(); - peasant.RequestQuitAll(); - } - bool WindowManager::DoesQuakeWindowExist() { return _monarch.DoesQuakeWindowExist(); @@ -434,19 +418,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::get_self(peasant)->ActiveTabTitle(title); } - Windows::Foundation::Collections::IVector WindowManager::GetAllWindowLayouts() - { - if (_monarch) - { - try - { - return _monarch.GetAllWindowLayouts(); - } - CATCH_LOG() - } - return nullptr; - } - winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 015ab9f3472..509a3053bdf 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -38,10 +38,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t GetNumberOfPeasants(); - static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant); - Windows::Foundation::Collections::IVector GetAllWindowLayouts(); bool DoesQuakeWindowExist(); winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference windowBounds); @@ -51,9 +49,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs); - TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs); - TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs); private: diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 5dd72b7f19a..deaf2b7489e 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -14,12 +14,10 @@ namespace Microsoft.Terminal.Remoting void SignalClose(Peasant p); void UpdateActiveTabTitle(String title, Peasant p); - static void RequestQuitAll(Peasant p); void SummonWindow(SummonWindowSelectionArgs args); void SummonAllWindows(); - Windows.Foundation.Collections.IVector GetAllWindowLayouts(); Windows.Foundation.Collections.IVectorView GetPeasantInfos(); UInt64 GetNumberOfPeasants(); @@ -33,8 +31,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler WindowCreated; event Windows.Foundation.TypedEventHandler WindowClosed; - event Windows.Foundation.TypedEventHandler QuitAllRequested; - event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; event Windows.Foundation.TypedEventHandler RequestNewWindow; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index fb39ebf72fd..ee533a261bd 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -124,8 +124,7 @@ namespace winrt::TerminalApp::implementation return appLogic->GetSettings(); } - AppLogic::AppLogic() : - _reloadState{ std::chrono::milliseconds(100), []() { ApplicationState::SharedInstance().Reload(); } } + AppLogic::AppLogic() { // For your own sanity, it's better to do setup outside the ctor. // If you do any setup in the ctor that ends up throwing an exception, @@ -327,10 +326,6 @@ namespace winrt::TerminalApp::implementation { _reloadSettings->Run(); } - else if (ApplicationState::SharedInstance().IsStatePath(modifiedBasename)) - { - _reloadState(); - } }); } @@ -702,22 +697,6 @@ namespace winrt::TerminalApp::implementation return _settings.GlobalSettings().ShouldUsePersistedLayout(); } - void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) - { - std::vector converted; - converted.reserve(layouts.Size()); - - for (const auto& json : layouts) - { - if (json != L"") - { - converted.emplace_back(WindowLayout::FromJson(json)); - } - } - - ApplicationState::SharedInstance().PersistedWindowLayouts(winrt::single_threaded_vector(std::move(converted))); - } - TerminalApp::ParseCommandlineResult AppLogic::GetParseCommandlineMessage(array_view args) { ::TerminalApp::AppCommandlineArgs _appArgs; diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 695632da75a..0e54a8d5154 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -53,9 +53,7 @@ namespace winrt::TerminalApp::implementation void NotifyRootInitialized(); bool HasSettingsStartupActions() const noexcept; - bool ShouldUsePersistedLayout() const; - void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; @@ -91,7 +89,6 @@ namespace winrt::TerminalApp::implementation ::TerminalApp::AppCommandlineArgs _settingsAppArgs; std::shared_ptr> _reloadSettings; - til::throttled_func_trailing<> _reloadState; std::vector _warnings{}; diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 1021e61b99a..362c7405644 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -37,7 +37,6 @@ namespace TerminalApp Boolean HasSettingsStartupActions(); Boolean ShouldUsePersistedLayout(); - void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); void ReloadSettings(); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index fccd8b8c5d1..4ee42d8330e 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -19,7 +19,7 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Microsoft::Terminal::TerminalConnection; using namespace winrt::TerminalApp; -using namespace TerminalApp; +using namespace winrt::TerminalApp::implementation; static const int PaneBorderSize = 2; static const int CombinedPaneBorderSize = 2 * PaneBorderSize; @@ -124,7 +124,7 @@ void Pane::_removeControlEvents() // terminal args. // Return Value: // - Arguments appropriate for a SplitPane or NewTab action -NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const +NewTerminalArgs Pane::GetTerminalArgsForPane(BuildStartupKind kind) const { // Leaves are the only things that have controls assert(_IsLeaf()); @@ -169,12 +169,17 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // object. That would work for schemes set by the Terminal, but not ones set // by VT, but that seems good enough. - // Only fill in the ContentId if absolutely needed. If you fill in a number - // here (even 0), we'll serialize that number, AND treat that action as an - // "attach existing" rather than a "create" - if (asContent) + switch (kind) { + case BuildStartupKind::Content: + case BuildStartupKind::MovePane: + // Only fill in the ContentId if absolutely needed. If you fill in a number + // here (even 0), we'll serialize that number, AND treat that action as an + // "attach existing" rather than a "create" args.ContentId(_control.ContentId()); + break; + default: + break; } return args; @@ -200,16 +205,13 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // - The state from building the startup actions, includes a vector of commands, // the original root pane, the id of the focused pane, and the number of panes // created. -Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, - uint32_t nextId, - const bool asContent, - const bool asMovePane) +Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, BuildStartupKind kind) { // Normally, if we're a leaf, return an empt set of actions, because the // parent pane will build the SplitPane action for us. If we're building // actions for a movePane action though, we'll still need to include // ourselves. - if (!asMovePane && _IsLeaf()) + if (kind != BuildStartupKind::MovePane && _IsLeaf()) { if (_lastActive) { @@ -223,18 +225,18 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, auto buildSplitPane = [&](auto newPane) { ActionAndArgs actionAndArgs; actionAndArgs.Action(ShortcutAction::SplitPane); - const auto terminalArgs{ newPane->GetTerminalArgsForPane(asContent) }; + const auto terminalArgs{ newPane->GetTerminalArgsForPane(kind) }; // When creating a pane the split size is the size of the new pane // and not position. const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right; - const auto splitSize = (asContent && _IsLeaf() ? .5 : 1. - _desiredSplitPosition); + const auto splitSize = (kind != BuildStartupKind::None && _IsLeaf() ? .5 : 1. - _desiredSplitPosition); SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs }; actionAndArgs.Args(args); return actionAndArgs; }; - if (asContent && _IsLeaf()) + if (kind != BuildStartupKind::None && _IsLeaf()) { return { .args = { buildSplitPane(shared_from_this()) }, @@ -281,10 +283,10 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, // We now need to execute the commands for each side of the tree // We've done one split, so the first-most child will have currentId, and the // one after it will be incremented. - auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1); + auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1, kind); // the next id for the second branch depends on how many splits were in the // first child. - auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1); + auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1, kind); std::vector actions{}; actions.reserve(firstState.args.size() + secondState.args.size() + 3); diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 7c04bcabc09..ebe7cf528df 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -31,6 +31,7 @@ namespace TerminalAppLocalTests namespace winrt::TerminalApp::implementation { struct TerminalTab; + enum class BuildStartupKind; } enum class Borders : int @@ -99,8 +100,8 @@ class Pane : public std::enable_shared_from_this std::optional focusedPaneId; uint32_t panesCreated; }; - BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false, const bool asMovePane = false); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const; + BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::implementation::BuildStartupKind kind); + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::implementation::BuildStartupKind kind) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp index 1c9c4049f66..99a70e0dc31 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -57,7 +57,7 @@ namespace winrt::TerminalApp::implementation // re-evaluate including that arg in this action then. // Return Value: // - The list of actions. - std::vector SettingsTab::BuildStartupActions(const bool /*asContent*/) const + std::vector SettingsTab::BuildStartupActions(BuildStartupKind) const { ASSERT_UI_THREAD(); diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h index 657c8387661..e5d7df92faf 100644 --- a/src/cascadia/TerminalApp/SettingsTab.h +++ b/src/cascadia/TerminalApp/SettingsTab.h @@ -30,7 +30,7 @@ namespace winrt::TerminalApp::implementation void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings); void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override; - std::vector BuildStartupActions(const bool asContent = false) const override; + std::vector BuildStartupActions(BuildStartupKind kind) const override; private: winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h index 108b30a502c..2f66ad6d097 100644 --- a/src/cascadia/TerminalApp/TabBase.h +++ b/src/cascadia/TerminalApp/TabBase.h @@ -12,6 +12,14 @@ namespace TerminalAppLocalTests namespace winrt::TerminalApp::implementation { + enum class BuildStartupKind + { + None, + Content, + MovePane, + Persist, + }; + struct TabBase : TabBaseT { public: @@ -22,7 +30,7 @@ namespace winrt::TerminalApp::implementation void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs); void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap); - virtual std::vector BuildStartupActions(const bool asContent = false) const = 0; + virtual std::vector BuildStartupActions(BuildStartupKind kind) const = 0; virtual std::optional GetTabColor(); void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused, diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 1717c05b8b7..9330596db5b 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -428,7 +428,7 @@ namespace winrt::TerminalApp::implementation } auto t = winrt::get_self(tab); - auto actions = t->BuildStartupActions(); + auto actions = t->BuildStartupActions(BuildStartupKind::None); _AddPreviouslyClosedPaneOrTab(std::move(actions)); _RemoveTab(tab); @@ -485,7 +485,7 @@ namespace winrt::TerminalApp::implementation // if the user manually closed all tabs. // Do this only if we are the last window; the monarch will notice // we are missing and remove us that way otherwise. - _LastTabClosedHandlers(*this, winrt::make(!_maintainStateOnTabClose)); + _CloseWindowRequestedHandlers(*this, nullptr); } else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast(tabIndex)) { @@ -766,11 +766,11 @@ namespace winrt::TerminalApp::implementation // This doesn't handle refocusing anything in particular, the // result will be that the last pane created is focused. In the // case of a single pane that is the desired behavior anyways. - auto state = pane->BuildStartupActions(0, 1); + auto state = pane->BuildStartupActions(0, 1, BuildStartupKind::None); { ActionAndArgs splitPaneAction{}; splitPaneAction.Action(ShortcutAction::SplitPane); - SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane() }; + SplitPaneArgs splitPaneArgs{ SplitDirection::Automatic, state.firstPane->GetTerminalArgsForPane(BuildStartupKind::None) }; splitPaneAction.Args(splitPaneArgs); state.args.emplace(state.args.begin(), std::move(splitPaneAction)); @@ -1138,12 +1138,4 @@ namespace winrt::TerminalApp::implementation { return _tabs.Size() > 1; } - - void TerminalPage::_RemoveAllTabs() - { - // Since _RemoveTabs is asynchronous, create a snapshot of the tabs we want to remove - std::vector tabsToRemove; - std::copy(begin(_tabs), end(_tabs), std::back_inserter(tabsToRemove)); - _RemoveTabs(tabsToRemove); - } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index efc802cc20d..bcb2d7e82d2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -5,7 +5,6 @@ #include "pch.h" #include "TerminalPage.h" #include "TerminalPage.g.cpp" -#include "LastTabClosedEventArgs.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" #include "RequestMoveContentArgs.g.cpp" #include "RequestReceiveContentArgs.g.cpp" @@ -654,9 +653,9 @@ namespace winrt::TerminalApp::implementation // GH#12267: Make sure that we don't instantly close ourselves when // we're readying to accept a defterm connection. In that case, we don't // have a tab yet, but will once we're initialized. - if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener)) + if (_tabs.Size() == 0 && !_shouldStartInboundListener && !_isEmbeddingInboundListener) { - _LastTabClosedHandlers(*this, winrt::make(false)); + _CloseWindowRequestedHandlers(*this, nullptr); co_return; } else @@ -1900,19 +1899,11 @@ namespace winrt::TerminalApp::implementation } } - // Method Description: - // - Saves the window position and tab layout to the application state - // - This does not create the InitialPosition field, that needs to be - // added externally. - // Arguments: - // - - // Return Value: - // - the window layout - WindowLayout TerminalPage::GetWindowLayout() + void TerminalPage::PersistState() { if (_startupState != StartupState::Initialized) { - return nullptr; + return; } std::vector actions; @@ -1920,7 +1911,7 @@ namespace winrt::TerminalApp::implementation for (auto tab : _tabs) { auto t = winrt::get_self(tab); - auto tabActions = t->BuildStartupActions(); + auto tabActions = t->BuildStartupActions(BuildStartupKind::Persist); actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end())); } @@ -1947,7 +1938,7 @@ namespace winrt::TerminalApp::implementation actions.emplace_back(std::move(action)); } - WindowLayout layout{}; + WindowLayout layout; layout.TabLayout(winrt::single_threaded_vector(std::move(actions))); auto mode = LaunchMode::DefaultMode; @@ -1964,20 +1955,15 @@ namespace winrt::TerminalApp::implementation layout.InitialSize(windowSize); - return layout; + ApplicationState::SharedInstance().AppendPersistedWindowLayout(layout); } // Method Description: // - Close the terminal app. If there is more // than one tab opened, show a warning dialog. - // Arguments: - // - bypassDialog: if true a dialog won't be shown even if the user would - // normally get confirmation. This is used in the case where the user - // has already been prompted by the Quit action. - fire_and_forget TerminalPage::CloseWindow(bool bypassDialog) + fire_and_forget TerminalPage::CloseWindow() { - if (!bypassDialog && - _HasMultipleTabs() && + if (_HasMultipleTabs() && _settings.GlobalSettings().ConfirmCloseAllTabs() && !_displayingCloseDialog) { @@ -1996,15 +1982,7 @@ namespace winrt::TerminalApp::implementation } } - if (_settings.GlobalSettings().ShouldUsePersistedLayout()) - { - // Don't delete the ApplicationState when all of the tabs are removed. - // If there is still a monarch living they will get the event that - // a window closed and trigger a new save without this window. - _maintainStateOnTabClose = true; - } - - _RemoveAllTabs(); + _CloseWindowRequestedHandlers(*this, nullptr); } // Method Description: @@ -2066,7 +2044,7 @@ namespace winrt::TerminalApp::implementation { if (const auto pane{ terminalTab->GetActivePane() }) { - auto startupActions = pane->BuildStartupActions(0, 1, true, true); + auto startupActions = pane->BuildStartupActions(0, 1, BuildStartupKind::MovePane); _DetachPaneFromWindow(pane); _MoveContent(std::move(startupActions.args), windowId, tabIdx); focusedTab->DetachPane(); @@ -2208,7 +2186,7 @@ namespace winrt::TerminalApp::implementation if (tab) { - auto startupActions = tab->BuildStartupActions(true); + auto startupActions = tab->BuildStartupActions(BuildStartupKind::Content); _DetachTabFromWindow(tab); _MoveContent(std::move(startupActions), windowId, 0); _RemoveTab(*tab); @@ -5135,7 +5113,7 @@ namespace winrt::TerminalApp::implementation const uint32_t tabIndex, std::optional dragPoint) { - auto startupActions = _stashed.draggedTab->BuildStartupActions(true); + auto startupActions = _stashed.draggedTab->BuildStartupActions(BuildStartupKind::Content); _DetachTabFromWindow(_stashed.draggedTab); _MoveContent(std::move(startupActions), windowId, tabIndex, dragPoint); diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 54bd0990d1d..58624ca2d1e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -7,7 +7,6 @@ #include "TerminalTab.h" #include "AppKeyBindings.h" #include "AppCommandlineArgs.h" -#include "LastTabClosedEventArgs.g.h" #include "RenameWindowRequestedArgs.g.h" #include "RequestMoveContentArgs.g.h" #include "RequestReceiveContentArgs.g.h" @@ -44,15 +43,6 @@ namespace winrt::TerminalApp::implementation ScrollDown = 1 }; - struct LastTabClosedEventArgs : LastTabClosedEventArgsT - { - WINRT_PROPERTY(bool, ClearPersistedState); - - public: - LastTabClosedEventArgs(const bool& shouldClear) : - _ClearPersistedState{ shouldClear } {}; - }; - struct RenameWindowRequestedArgs : RenameWindowRequestedArgsT { WINRT_PROPERTY(winrt::hstring, ProposedName); @@ -105,7 +95,6 @@ namespace winrt::TerminalApp::implementation bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout(); hstring Title(); @@ -121,7 +110,8 @@ namespace winrt::TerminalApp::implementation SuggestionsControl LoadSuggestionsUI(); winrt::fire_and_forget RequestQuit(); - winrt::fire_and_forget CloseWindow(bool bypassDialog); + winrt::fire_and_forget CloseWindow(); + void PersistState(); void ToggleFocusMode(); void ToggleFullscreen(); @@ -175,7 +165,7 @@ namespace winrt::TerminalApp::implementation // -------------------------------- WinRT Events --------------------------------- TYPED_EVENT(TitleChanged, IInspectable, winrt::hstring); - TYPED_EVENT(LastTabClosed, IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); + TYPED_EVENT(CloseWindowRequested, IInspectable, IInspectable); TYPED_EVENT(SetTitleBarContent, IInspectable, winrt::Windows::UI::Xaml::UIElement); TYPED_EVENT(FocusModeChanged, IInspectable, IInspectable); TYPED_EVENT(FullscreenChanged, IInspectable, IInspectable); @@ -232,7 +222,6 @@ namespace winrt::TerminalApp::implementation std::optional _loadFromPersistedLayoutIdx{}; - bool _maintainStateOnTabClose{ false }; bool _rearranging{ false }; std::optional _rearrangeFrom{}; std::optional _rearrangeTo{}; @@ -349,7 +338,6 @@ namespace winrt::TerminalApp::implementation void _DismissTabContextMenus(); void _FocusCurrentTab(const bool focusAlways); bool _HasMultipleTabs() const; - void _RemoveAllTabs(); void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference& customTabSwitcherMode); bool _SelectTab(uint32_t tabIndex); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 76ee847fb80..f1397b48c49 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -15,11 +15,6 @@ namespace TerminalApp void Detach(Microsoft.Terminal.Control.TermControl control); } - [default_interface] runtimeclass LastTabClosedEventArgs - { - Boolean ClearPersistedState { get; }; - }; - [default_interface] runtimeclass RenameWindowRequestedArgs { String ProposedName { get; }; @@ -87,7 +82,7 @@ namespace TerminalApp void SendContentToOther(RequestReceiveContentArgs args); event Windows.Foundation.TypedEventHandler TitleChanged; - event Windows.Foundation.TypedEventHandler LastTabClosed; + event Windows.Foundation.TypedEventHandler CloseWindowRequested; event Windows.Foundation.TypedEventHandler SetTitleBarContent; event Windows.Foundation.TypedEventHandler FocusModeChanged; event Windows.Foundation.TypedEventHandler FullscreenChanged; diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 08c51cb730c..c8fb74a369c 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -427,18 +427,18 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - A vector of commands - std::vector TerminalTab::BuildStartupActions(const bool asContent) const + std::vector TerminalTab::BuildStartupActions(BuildStartupKind kind) const { ASSERT_UI_THREAD(); // Give initial ids (0 for the child created with this tab, // 1 for the child after the first split. - auto state = _rootPane->BuildStartupActions(0, 1, asContent); + auto state = _rootPane->BuildStartupActions(0, 1, kind); { ActionAndArgs newTabAction{}; newTabAction.Action(ShortcutAction::NewTab); - NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) }; + NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(kind) }; newTabAction.Args(newTabArgs); state.args.emplace(state.args.begin(), std::move(newTabAction)); diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index d3b03f426fd..d0b71ce57fb 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -78,7 +78,7 @@ namespace winrt::TerminalApp::implementation void EnterZoom(); void ExitZoom(); - std::vector BuildStartupActions(const bool asContent = false) const override; + std::vector BuildStartupActions(BuildStartupKind kind) const override; int GetLeafPaneCount() const noexcept; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 66db1eb70b3..13cf3b540e6 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -267,7 +267,7 @@ namespace winrt::TerminalApp::implementation { if (_root) { - _root->CloseWindow(true); + _root->PersistState(); } } @@ -916,32 +916,11 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void TerminalWindow::CloseWindow(LaunchPosition pos, const bool isLastWindow) + void TerminalWindow::CloseWindow() { if (_root) { - // If persisted layout is enabled and we are the last window closing - // we should save our state. - if (_settings.GlobalSettings().ShouldUsePersistedLayout() && isLastWindow) - { - if (const auto layout = _root->GetWindowLayout()) - { - layout.InitialPosition(pos); - const auto state = ApplicationState::SharedInstance(); - state.PersistedWindowLayouts(winrt::single_threaded_vector({ layout })); - } - } - - _root->CloseWindow(false); - } - } - - void TerminalWindow::ClearPersistedWindowState() - { - if (_settings.GlobalSettings().ShouldUsePersistedLayout()) - { - auto state = ApplicationState::SharedInstance(); - state.PersistedWindowLayouts(nullptr); + _root->CloseWindow(); } } @@ -1134,19 +1113,6 @@ namespace winrt::TerminalApp::implementation return winrt::to_hstring(_appArgs.GetExitMessage()); } - hstring TerminalWindow::GetWindowLayoutJson(LaunchPosition position) - { - if (_root != nullptr) - { - if (const auto layout = _root->GetWindowLayout()) - { - layout.InitialPosition(position); - return WindowLayout::ToJson(layout); - } - } - return L""; - } - void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) { _loadFromPersistedLayoutIdx = idx; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 8eff22ef455..72dabd1edb9 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -95,8 +95,6 @@ namespace winrt::TerminalApp::implementation bool AlwaysOnTop() const; bool AutoHideWindow(); - hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); - void IdentifyWindow(); void RenameFailed(); @@ -104,9 +102,6 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(); void SetPersistedLayoutIdx(const uint32_t idx); - void SetNumberOfOpenWindows(const uint64_t num); - bool ShouldUsePersistedLayout() const; - void ClearPersistedWindowState(); void RequestExitFullscreen(); @@ -125,7 +120,7 @@ namespace winrt::TerminalApp::implementation void TitlebarClicked(); bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); - void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position, const bool isLastWindow); + void CloseWindow(); void WindowVisibilityChanged(const bool showOrHide); winrt::TerminalApp::TaskbarState TaskbarState(); @@ -215,7 +210,7 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(SetTitleBarContent, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement, _root, SetTitleBarContent); FORWARDED_TYPED_EVENT(TitleChanged, winrt::Windows::Foundation::IInspectable, winrt::hstring, _root, TitleChanged); - FORWARDED_TYPED_EVENT(LastTabClosed, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs, _root, LastTabClosed); + FORWARDED_TYPED_EVENT(CloseWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, CloseWindowRequested); 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(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index a41cfb96c8e..9072732fc95 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -75,7 +75,6 @@ namespace TerminalApp void IdentifyWindow(); void SetPersistedLayoutIdx(UInt32 idx); - void ClearPersistedWindowState(); void RenameFailed(); void RequestExitFullscreen(); @@ -90,7 +89,7 @@ namespace TerminalApp Boolean GetInitialAlwaysOnTop(); Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension); void TitlebarClicked(); - void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position, Boolean isLastWindow); + void CloseWindow(); void WindowVisibilityChanged(Boolean showOrHide); TaskbarState TaskbarState{ get; }; @@ -98,8 +97,6 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush FrameBrush { get; }; void WindowActivated(Boolean activated); - String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); - Boolean GetMinimizeToNotificationArea(); Boolean GetAlwaysShowNotificationIcon(); Boolean GetShowTitleInTitlebar(); @@ -119,7 +116,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SetTitleBarContent; event Windows.Foundation.TypedEventHandler TitleChanged; - event Windows.Foundation.TypedEventHandler LastTabClosed; + event Windows.Foundation.TypedEventHandler CloseWindowRequested; event Windows.Foundation.TypedEventHandler RequestedThemeChanged; event Windows.Foundation.TypedEventHandler FocusModeChanged; event Windows.Foundation.TypedEventHandler FullscreenChanged; diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index 751645b79ba..7a4bd26c13f 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -117,7 +117,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation private: Model::Profile _profile; - winrt::guid _originalProfileGuid; + winrt::guid _originalProfileGuid{}; winrt::hstring _lastBgImagePath; winrt::hstring _lastStartingDirectoryPath; Editor::AppearanceViewModel _defaultAppearanceViewModel; diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp index 6f684f91d66..30b44a55b21 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.cpp +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.cpp @@ -102,34 +102,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // The destructor ensures that the last write is flushed to disk before returning. ApplicationState::~ApplicationState() { - TraceLoggingWrite(g_hSettingsModelProvider, - "ApplicationState_Dtor_Start", - TraceLoggingDescription("Event at the start of the ApplicationState destructor"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + Flush(); + } + void ApplicationState::Flush() + { // This will ensure that we not just cancel the last outstanding timer, // but instead force it to run as soon as possible and wait for it to complete. _throttler.flush(); - - TraceLoggingWrite(g_hSettingsModelProvider, - "ApplicationState_Dtor_End", - TraceLoggingDescription("Event at the end of the ApplicationState destructor"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - // Re-read the state.json from disk. - void ApplicationState::Reload() const noexcept - { - _read(); - } - - bool ApplicationState::IsStatePath(const winrt::hstring& filename) - { - static const auto sharedPath{ _sharedPath.filename() }; - static const auto elevatedPath{ _elevatedPath.filename() }; - return filename == sharedPath || filename == elevatedPath; } // Method Description: @@ -260,8 +240,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Returns the application-global ApplicationState object. Microsoft::Terminal::Settings::Model::ApplicationState ApplicationState::SharedInstance() { - auto root{ GetBaseSettingsPath() }; - static auto state = winrt::make_self(root); + static auto state = winrt::make_self(GetBaseSettingsPath()); return *state; } @@ -299,7 +278,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Json::Value ApplicationState::_toJsonWithBlob(Json::Value& root, FileSource parseSource) const noexcept { { - auto state = _state.lock_shared(); + const auto state = _state.lock_shared(); // GH#11222: We only write properties that are of the same type (Local // or Shared) which we requested. If we didn't want to serialize this @@ -314,6 +293,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return root; } + void ApplicationState::AppendPersistedWindowLayout(Model::WindowLayout layout) + { + { + const auto state = _state.lock(); + if (!state->PersistedWindowLayouts || !*state->PersistedWindowLayouts) + { + state->PersistedWindowLayouts = winrt::single_threaded_vector(); + } + state->PersistedWindowLayouts->Append(std::move(layout)); + } + + _throttler(); + } + // Generate all getter/setters #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \ type ApplicationState::name() const noexcept \ @@ -326,7 +319,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void ApplicationState::name(const type& value) noexcept \ { \ { \ - auto state = _state.lock(); \ + const auto state = _state.lock(); \ state->name.emplace(value); \ } \ \ diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.h b/src/cascadia/TerminalSettingsModel/ApplicationState.h index 91183bad076..df7fd803e70 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.h +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.h @@ -63,14 +63,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ~ApplicationState(); // Methods - void Reload() const noexcept; + void Flush(); void Reset() noexcept; void FromJson(const Json::Value& root, FileSource parseSource) const noexcept; Json::Value ToJson(FileSource parseSource) const noexcept; - // General getters/setters - bool IsStatePath(const winrt::hstring& filename); + void AppendPersistedWindowLayout(Model::WindowLayout layout); // State getters/setters #define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \ diff --git a/src/cascadia/TerminalSettingsModel/ApplicationState.idl b/src/cascadia/TerminalSettingsModel/ApplicationState.idl index f49b706330d..e35447773a6 100644 --- a/src/cascadia/TerminalSettingsModel/ApplicationState.idl +++ b/src/cascadia/TerminalSettingsModel/ApplicationState.idl @@ -29,10 +29,10 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass ApplicationState { static ApplicationState SharedInstance(); - void Reload(); + void Flush(); void Reset(); - Boolean IsStatePath(String filename); + void AppendPersistedWindowLayout(WindowLayout layout); String SettingsHash; Windows.Foundation.Collections.IVector PersistedWindowLayouts; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 15e62efd4fa..7510bb0ebf7 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -410,7 +410,7 @@ bool SettingsLoader::FixupUserSettings() { struct CommandlinePatch { - winrt::guid guid; + winrt::guid guid{}; std::wstring_view before; std::wstring_view after; }; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 3ccc0972bea..9c14376d053 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -83,7 +83,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static constexpr bool debugFeaturesDefault{ true }; #endif - winrt::guid _defaultProfile; + winrt::guid _defaultProfile{}; bool _legacyReloadEnvironmentVariables{ true }; winrt::com_ptr _actionMap{ winrt::make_self() }; diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 7dec3c052e6..e22d56c823c 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -82,7 +82,6 @@ namespace RemotingUnitTests void Summon(const Remoting::SummonWindowBehavior& /*args*/) DIE; void RequestShowNotificationIcon() DIE; void RequestHideNotificationIcon() DIE; - winrt::hstring GetWindowLayout() DIE; void RequestQuitAll() DIE; void Quit() DIE; void AttachContentToWindow(Remoting::AttachRequest) DIE; @@ -97,7 +96,6 @@ namespace RemotingUnitTests TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, Remoting::GetWindowLayoutArgs); TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs); }; @@ -117,7 +115,6 @@ namespace RemotingUnitTests void SummonAllWindows() DIE; bool DoesQuakeWindowExist() DIE; winrt::Windows::Foundation::Collections::IVectorView GetPeasantInfos() DIE; - winrt::Windows::Foundation::Collections::IVector GetAllWindowLayouts() DIE; void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t, winrt::Windows::Foundation::IReference) DIE; void RequestSendContent(Remoting::RequestReceiveContentArgs) DIE; @@ -126,7 +123,6 @@ namespace RemotingUnitTests TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, Remoting::QuitAllRequestedArgs); TYPED_EVENT(RequestNewWindow, winrt::Windows::Foundation::IInspectable, Remoting::WindowRequestedArgs); }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6a0e0ed698d..c653d56e21a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -215,49 +215,51 @@ void AppHost::_HandleSessionRestore(const bool startedForContent) // we'll leave it here. const auto numPeasants = _windowManager.GetNumberOfPeasants(); // Don't attempt to session restore if we're just making a window for tear-out - if (!startedForContent && numPeasants == 1) + if (startedForContent || numPeasants != 1 || !_appLogic.ShouldUsePersistedLayout()) { - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (_appLogic.ShouldUsePersistedLayout() && - layouts && - layouts.Size() > 0) + return; + } + + const auto state = ApplicationState::SharedInstance(); + const auto layouts = state.PersistedWindowLayouts(); + + if (layouts && layouts.Size() > 0) + { + uint32_t startIdx = 0; + // We want to create a window for every saved layout. + // If we are the only window, and no commandline arguments were provided + // then we should just use the current window to load the first layout. + // Otherwise create this window normally with its commandline, and create + // a new window using the first saved layout information. + // The 2nd+ layout will always get a new window. + if (!_windowLogic.HasCommandlineArguments() && + !_appLogic.HasSettingsStartupActions()) { - uint32_t startIdx = 0; - // We want to create a window for every saved layout. - // If we are the only window, and no commandline arguments were provided - // then we should just use the current window to load the first layout. - // Otherwise create this window normally with its commandline, and create - // a new window using the first saved layout information. - // The 2nd+ layout will always get a new window. - if (!_windowLogic.HasCommandlineArguments() && - !_appLogic.HasSettingsStartupActions()) - { - _windowLogic.SetPersistedLayoutIdx(startIdx); - startIdx += 1; - } + _windowLogic.SetPersistedLayoutIdx(startIdx); + startIdx += 1; + } - // Create new windows for each of the other saved layouts. - for (const auto size = layouts.Size(); startIdx < size; startIdx += 1) - { - auto newWindowArgs = fmt::format(L"{0} -w new -s {1}", args.Commandline()[0], startIdx); - - STARTUPINFO si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - wil::unique_process_information pi; - - LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr, - newWindowArgs.data(), - nullptr, // lpProcessAttributes - nullptr, // lpThreadAttributes - false, // bInheritHandles - DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, // doCreationFlags - nullptr, // lpEnvironment - nullptr, // lpStartingDirectory - &si, // lpStartupInfo - &pi // lpProcessInformation - )); - } + // Create new windows for each of the other saved layouts. + for (const auto size = layouts.Size(); startIdx < size; startIdx += 1) + { + auto newWindowArgs = fmt::format(L"{0} -w new -s {1}", args.Commandline()[0], startIdx); + + STARTUPINFO si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + wil::unique_process_information pi; + + LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr, + newWindowArgs.data(), + nullptr, // lpProcessAttributes + nullptr, // lpThreadAttributes + false, // bInheritHandles + DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, // doCreationFlags + nullptr, // lpEnvironment + nullptr, // lpStartingDirectory + &si, // lpStartupInfo + &pi // lpProcessInformation + )); } } } @@ -317,7 +319,7 @@ void AppHost::Initialize() // Register the 'X' button of the window for a warning experience of multiple // tabs opened, this is consistent with Alt+F4 closing _windowCallbacks.WindowCloseButtonClicked = _window->WindowCloseButtonClicked([this]() { - _CloseRequested(nullptr, nullptr); + _windowLogic.CloseWindow(); }); // If the user requests a close in another way handle the same as if the 'X' // was clicked. @@ -350,7 +352,7 @@ void AppHost::Initialize() _windowCallbacks.AutomaticShutdownRequested = _window->AutomaticShutdownRequested([this]() { // Raised when the OS is beginning an update of the app. We will quit, // to save our state, before the OS manually kills us. - Remoting::WindowManager::RequestQuitAll(_peasant); + _quit(); }); // Load bearing: make sure the PropertyChanged handler is added before we @@ -362,7 +364,7 @@ void AppHost::Initialize() _windowLogic.Create(); _revokers.TitleChanged = _windowLogic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged }); - _revokers.LastTabClosed = _windowLogic.LastTabClosed(winrt::auto_revoke, { this, &AppHost::LastTabClosed }); + _revokers.CloseWindowRequested = _windowLogic.CloseWindowRequested(winrt::auto_revoke, { this, &AppHost::_CloseRequested }); _revokers.SetTaskbarProgress = _windowLogic.SetTaskbarProgress(winrt::auto_revoke, { this, &AppHost::SetTaskbarProgress }); _revokers.IdentifyWindowsRequested = _windowLogic.IdentifyWindowsRequested(winrt::auto_revoke, { this, &AppHost::_IdentifyWindowsRequested }); _revokers.RenameWindowRequested = _windowLogic.RenameWindowRequested(winrt::auto_revoke, { this, &AppHost::_RenameWindowRequested }); @@ -382,22 +384,6 @@ void AppHost::Initialize() _revokers.RequestReceiveContent = _windowLogic.RequestReceiveContent(winrt::auto_revoke, { this, &AppHost::_handleReceiveContent }); _revokers.SendContentRequested = _peasant.SendContentRequested(winrt::auto_revoke, { this, &AppHost::_handleSendContent }); - // Add our GetWindowLayoutRequested handler AFTER the xaml island is - // started. Our _GetWindowLayoutAsync handler requires us to be able to work - // on our UI thread, which requires that we have a Dispatcher ready for us - // to move to. If we set up this callback in the ctor, then it is possible - // for there to be a time slice where - // * the monarch creates the peasant for us, - // * we get constructed (registering the callback) - // * then the monarch attempts to query all _peasants_ for their layout, - // coming back to ask us even before XAML has been created. - _GetWindowLayoutRequestedToken = _peasant.GetWindowLayoutRequested([this](auto&&, - const Remoting::GetWindowLayoutArgs& args) { - // The peasants are running on separate threads, so they'll need to - // swap what context they are in to the ui thread to get the actual layout. - args.WindowLayoutJsonAsync(_GetWindowLayoutAsync()); - }); - // BODGY // On certain builds of Windows, when Terminal is set as the default // it will accumulate an unbounded amount of queued animations while @@ -452,6 +438,16 @@ void AppHost::Close() } } +winrt::fire_and_forget AppHost::_quit() +{ + const auto peasant = _peasant; + + co_await winrt::resume_background(); + + ApplicationState::SharedInstance().PersistedWindowLayouts(nullptr); + peasant.RequestQuitAll(); +} + void AppHost::_revokeWindowCallbacks() { // You'll recall, IslandWindow isn't a WinRT type so it can't have auto-revokers. @@ -514,42 +510,6 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* _windowManager.UpdateActiveTabTitle(newTitle, _peasant); } -// Method Description: -// - Called when no tab is remaining to close the window. -// Arguments: -// - sender: unused -// - LastTabClosedEventArgs: unused -// Return Value: -// - -void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& args) -{ - // We don't want to try to save layouts if we are about to close. - _peasant.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); - - // If the user closes the last tab, in the last window, _by closing the tab_ - // (not by closing the whole window), we need to manually persist an empty - // window state here. That will cause the terminal to re-open with the usual - // settings (not the persisted state) - if (args.ClearPersistedState() && - _windowManager.GetNumberOfPeasants() == 1) - { - _windowLogic.ClearPersistedWindowState(); - } - // Remove ourself from the list of peasants so that we aren't included in - // any future requests. This will also mean we block until any existing - // event handler finishes. - _windowManager.SignalClose(_peasant); - - if (Utils::IsWindows11()) - { - PostQuitMessage(0); - } - else - { - PostMessageW(_window->GetInteropHandle(), WM_REFRIGERATE, 0, 0); - } -} - LaunchPosition AppHost::_GetWindowLaunchPosition() { LaunchPosition pos{}; @@ -952,42 +912,6 @@ winrt::fire_and_forget AppHost::_peasantNotifyActivateWindow() }); } -// Method Description: -// - Asynchronously get the window layout from the current page. This is -// done async because we need to switch between the ui thread and the calling -// thread. -// - NB: The peasant calling this must not be running on the UI thread, otherwise -// they will crash since they just call .get on the async operation. -// Arguments: -// - -// Return Value: -// - The window layout as a json string. -winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowLayoutAsync() -{ - winrt::hstring layoutJson = L""; - - auto weakThis{ weak_from_this() }; - - // Use the main thread since we are accessing controls. - co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); - - const auto strongThis = weakThis.lock(); - // GH #16235: If we don't have a window logic, we're already refrigerating, and won't have our _window either. - if (!strongThis || _windowLogic == nullptr) - { - co_return layoutJson; - } - - try - { - const auto pos = _GetWindowLaunchPosition(); - layoutJson = _windowLogic.GetWindowLayoutJson(pos); - } - CATCH_LOG() - - co_return layoutJson; -} - void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::SummonWindowBehavior& args) { @@ -1264,13 +1188,14 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation: } _windowLogic.Quit(); + PostQuitMessage(0); } // Raised from TerminalWindow. We handle by bubbling the request to the window manager. void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - Remoting::WindowManager::RequestQuitAll(_peasant); + _quit(); } void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&, @@ -1370,9 +1295,25 @@ void AppHost::_WindowMoved() void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - const auto pos = _GetWindowLaunchPosition(); - const bool isLastWindow = _windowManager.GetNumberOfPeasants() == 1; - _windowLogic.CloseWindow(pos, isLastWindow); + if (_windowManager.GetNumberOfPeasants() <= 1) + { + _quit(); + return; + } + + // Remove ourself from the list of peasants so that we aren't included in + // any future requests. This will also mean we block until any existing + // event handler finishes. + _windowManager.SignalClose(_peasant); + + if (Utils::IsWindows11()) + { + PostQuitMessage(0); + } + else + { + PostMessageW(_window->GetInteropHandle(), WM_REFRIGERATE, 0, 0); + } } void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 60b96103124..7a46186cbd5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -20,7 +20,6 @@ class AppHost : public std::enable_shared_from_this std::unique_ptr window = nullptr) noexcept; void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); - void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args); void Initialize(); void Close(); @@ -64,8 +63,7 @@ class AppHost : public std::enable_shared_from_this uint32_t _launchShowWindowCommand{ SW_NORMAL }; - void _preInit(); - + winrt::fire_and_forget _quit(); void _revokeWindowCallbacks(); void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); @@ -100,8 +98,6 @@ class AppHost : public std::enable_shared_from_this void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); - winrt::Windows::Foundation::IAsyncOperation _GetWindowLayoutAsync(); - void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args); @@ -112,10 +108,6 @@ class AppHost : public std::enable_shared_from_this winrt::fire_and_forget _RenameWindowRequested(const winrt::Windows::Foundation::IInspectable sender, const winrt::TerminalApp::RenameWindowRequestedArgs args); - GUID _CurrentDesktopGuid(); - - bool _LazyLoadDesktopManager(); - void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& args); @@ -168,8 +160,6 @@ class AppHost : public std::enable_shared_from_this void _stopFrameTimer(); void _updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); - winrt::event_token _GetWindowLayoutRequestedToken; - // Helper struct. By putting these all into one struct, we can revoke them // all at once, by assigning _revokers to a fresh Revokers instance. That'll // cause us to dtor the old one, which will immediately call revoke on all @@ -194,7 +184,7 @@ class AppHost : public std::enable_shared_from_this winrt::TerminalApp::TerminalWindow::SystemMenuChangeRequested_revoker SystemMenuChangeRequested; winrt::TerminalApp::TerminalWindow::ChangeMaximizeRequested_revoker ChangeMaximizeRequested; winrt::TerminalApp::TerminalWindow::TitleChanged_revoker TitleChanged; - winrt::TerminalApp::TerminalWindow::LastTabClosed_revoker LastTabClosed; + winrt::TerminalApp::TerminalWindow::CloseWindowRequested_revoker CloseWindowRequested; winrt::TerminalApp::TerminalWindow::SetTaskbarProgress_revoker SetTaskbarProgress; winrt::TerminalApp::TerminalWindow::IdentifyWindowsRequested_revoker IdentifyWindowsRequested; winrt::TerminalApp::TerminalWindow::RenameWindowRequested_revoker RenameWindowRequested; @@ -208,7 +198,6 @@ class AppHost : public std::enable_shared_from_this winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged; winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged; - winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; winrt::Microsoft::Terminal::Remoting::Peasant::SendContentRequested_revoker SendContentRequested; } _revokers{}; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 3ff3f06f546..51aadcfda58 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -127,6 +127,9 @@ void WindowEmperor::WaitForWindows() TranslateMessage(&message); DispatchMessage(&message); } + + _finalizeSessionPersistence(); + TerminateProcess(GetCurrentProcess(), 0); } void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& args) @@ -345,116 +348,17 @@ void WindowEmperor::_becomeMonarch() _revokers.WindowCreated = _manager.WindowCreated(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged }); _revokers.WindowClosed = _manager.WindowClosed(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged }); - - // If the monarch receives a QuitAll event it will signal this event to be - // ran before each peasant is closed. - _revokers.QuitAllRequested = _manager.QuitAllRequested(winrt::auto_revoke, { this, &WindowEmperor::_quitAllRequested }); - - // The monarch should be monitoring if it should save the window layout. - // We want at least some delay to prevent the first save from overwriting - _getWindowLayoutThrottler.emplace(std::move(std::chrono::seconds(10)), std::move([this]() { _saveWindowLayoutsRepeat(); })); - _getWindowLayoutThrottler.value()(); } // sender and args are always nullptr void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - if (_getWindowLayoutThrottler) - { - _getWindowLayoutThrottler.value()(); - } - // If we closed out the quake window, and don't otherwise need the tray // icon, let's get rid of it. _checkWindowsForNotificationIcon(); } -// Raised from our windowManager (on behalf of the monarch). We respond by -// giving the monarch an async function that the manager should wait on before -// completing the quit. -void WindowEmperor::_quitAllRequested(const winrt::Windows::Foundation::IInspectable&, - const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& args) -{ - _quitting = true; - - // Make sure that the current timer is destroyed so that it doesn't attempt - // to run while we are in the middle of quitting. - if (_getWindowLayoutThrottler.has_value()) - { - _getWindowLayoutThrottler.reset(); - } - - // Tell the monarch to wait for the window layouts to save before - // everyone quits. - args.BeforeQuitAllAction(_saveWindowLayouts()); -} - -#pragma region LayoutPersistence - -winrt::Windows::Foundation::IAsyncAction WindowEmperor::_saveWindowLayouts() -{ - // Make sure we run on a background thread to not block anything. - co_await winrt::resume_background(); - - if (_app.Logic().ShouldUsePersistedLayout()) - { - try - { - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_SaveWindowLayouts_Collect", - TraceLoggingDescription("Logged when collecting window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - const auto layoutJsons = _manager.GetAllWindowLayouts(); - - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_SaveWindowLayouts_Save", - TraceLoggingDescription("Logged when writing window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - _app.Logic().SaveWindowLayoutJsons(layoutJsons); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_SaveWindowLayouts_Failed", - TraceLoggingDescription("An error occurred when collecting or writing window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - - co_return; -} - -winrt::fire_and_forget WindowEmperor::_saveWindowLayoutsRepeat() -{ - // Make sure we run on a background thread to not block anything. - co_await winrt::resume_background(); - - co_await _saveWindowLayouts(); - - // Don't need to save too frequently. - co_await winrt::resume_after(30s); - - // As long as we are supposed to keep saving, request another save. - // This will be delayed by the throttler so that at most one save happens - // per 10 seconds, if a save is requested by another source simultaneously. - if (_getWindowLayoutThrottler.has_value()) - { - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_requestGetLayout", - TraceLoggingDescription("Logged when triggering a throttled write of the window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - _getWindowLayoutThrottler.value()(); - } -} #pragma endregion #pragma region WindowProc @@ -575,6 +479,14 @@ winrt::fire_and_forget WindowEmperor::_close() PostQuitMessage(0); } +void WindowEmperor::_finalizeSessionPersistence() const +{ + const auto state = ApplicationState::SharedInstance(); + + // Ensure to write the state.json before we TerminateProcess() + state.Flush(); +} + #pragma endregion #pragma region GlobalHotkeys diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 524a8a3d534..a35032e6d5e 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -44,8 +44,6 @@ class WindowEmperor : public std::enable_shared_from_this til::shared_mutex>> _oldThreads; - std::optional> _getWindowLayoutThrottler; - winrt::event_token _WindowCreatedToken; winrt::event_token _WindowClosedToken; @@ -61,15 +59,10 @@ class WindowEmperor : public std::enable_shared_from_this void _becomeMonarch(); void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); - void _quitAllRequested(const winrt::Windows::Foundation::IInspectable&, - const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs&); winrt::fire_and_forget _windowIsQuakeWindowChanged(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable args); winrt::fire_and_forget _windowRequestUpdateSettings(); - winrt::Windows::Foundation::IAsyncAction _saveWindowLayouts(); - winrt::fire_and_forget _saveWindowLayoutsRepeat(); - void _createMessageWindow(); void _hotkeyPressed(const long hotkeyIndex); @@ -78,6 +71,7 @@ class WindowEmperor : public std::enable_shared_from_this winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _close(); + void _finalizeSessionPersistence() const; void _createNotificationIcon(); void _destroyNotificationIcon(); @@ -89,6 +83,5 @@ class WindowEmperor : public std::enable_shared_from_this { winrt::Microsoft::Terminal::Remoting::WindowManager::WindowCreated_revoker WindowCreated; winrt::Microsoft::Terminal::Remoting::WindowManager::WindowClosed_revoker WindowClosed; - winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; } _revokers{}; };