From 581acd40d9a5f7bcefdbaf29516bf418207be727 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 30 Jan 2023 12:37:12 -0600 Subject: [PATCH 001/122] I definitely want all of this. But I started down a path for refactoring AppLogic that I hate so I'm gonna start over --- .../#5000 - Process Model 3.0.md | 27 +++++++ src/cascadia/WindowsTerminal/AppHost.h | 4 +- .../WindowsTerminal/WindowEmperor.cpp | 26 ++++++ src/cascadia/WindowsTerminal/WindowEmperor.h | 15 ++++ src/cascadia/WindowsTerminal/WindowThread.cpp | 80 +++++++++++++++++++ src/cascadia/WindowsTerminal/WindowThread.h | 15 ++++ .../WindowsTerminal/WindowsTerminal.vcxproj | 4 + src/cascadia/WindowsTerminal/main.cpp | 79 +----------------- 8 files changed, 174 insertions(+), 76 deletions(-) create mode 100644 doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md create mode 100644 src/cascadia/WindowsTerminal/WindowEmperor.cpp create mode 100644 src/cascadia/WindowsTerminal/WindowEmperor.h create mode 100644 src/cascadia/WindowsTerminal/WindowThread.cpp create mode 100644 src/cascadia/WindowsTerminal/WindowThread.h diff --git a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md new file mode 100644 index 00000000000..ca3914617bf --- /dev/null +++ b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md @@ -0,0 +1,27 @@ +--- +author: Mike Griese @zadjii-msft +created on: 2023-01-27 +last updated: 2023-01-27 +issue id: #5000 +--- + +# Windows Terminal Process Model 3.0 + +Everything is one process. + +WIP branch: https://github.com/microsoft/terminal/compare/main...dev/migrie/f/process-model-v3-test-0 + +* [ ] One `App` per process. +* [ ] One `AppHost` per thread. + * Which means one `AppLogic` per thread/window. +* [ ] `ContentManager` for storing GUID -> `ControlInteractivity`'s + * Idea: Let's make it `map`, and then QI to get the `FrameworkElement`? + * Remoting could host it then +* [ ] `WindowManager` tries to get the monarch. If there isn't one, then Terminal isn't running. + * [ ] `Don't register as a `Monarch` host if we found one. + * [ ] Wait to register as the monarch till we determine that we want to actually make a window. + * [ ] `WindowManager::ProposeCommandline`: we only ever need to create a new window if we are the `Monarch`. +* [ ] +* [ ] +* [ ] +* [ ] diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 2de84742189..91c5c480021 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -22,7 +22,7 @@ class AppHost private: std::unique_ptr _window; - winrt::TerminalApp::App _app; + // winrt::TerminalApp::App _app; winrt::TerminalApp::AppLogic _logic; winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; @@ -37,6 +37,8 @@ class AppHost winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); winrt::fire_and_forget _SaveWindowLayoutsRepeat(); + void _preInit(); + void _HandleCommandlineArgs(); winrt::Microsoft::Terminal::Settings::Model::LaunchPosition _GetWindowLaunchPosition(); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp new file mode 100644 index 00000000000..56781e6c074 --- /dev/null +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "WindowEmperor.h" +#include "WindowThread.h" + +WindowEmperor::WindowEmperor() noexcept : + _app{} +{ +} + +bool WindowEmperor::ShouldExit() +{ + // TODO! + return false; +} + +void WindowEmperor::WaitForWindows() +{ + std::thread one{ []() { + WindowThread foo{}; + return foo.WindowProc(); + } }; + one.join(); +} diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h new file mode 100644 index 00000000000..e2c80a2a8f0 --- /dev/null +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +class WindowEmperor +{ +public: + WindowEmperor() noexcept; + bool ShouldExit(); + void WaitForWindows(); + +private: + winrt::TerminalApp::App _app; +}; diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp new file mode 100644 index 00000000000..e26123bdd8e --- /dev/null +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "WindowThread.h" + +WindowThread::WindowThread() : + _host{} +{ +} + +static bool _messageIsF7Keypress(const MSG& message) +{ + return (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN) && message.wParam == VK_F7; +} +static bool _messageIsAltKeyup(const MSG& message) +{ + return (message.message == WM_KEYUP || message.message == WM_SYSKEYUP) && message.wParam == VK_MENU; +} +static bool _messageIsAltSpaceKeypress(const MSG& message) +{ + return message.message == WM_SYSKEYDOWN && message.wParam == VK_SPACE; +} + +int WindowThread::WindowProc() +{ + winrt::init_apartment(winrt::apartment_type::single_threaded); + + // Initialize the xaml content. This must be called AFTER the + // WindowsXamlManager is initialized. + _host.Initialize(); + + MSG message; + + while (GetMessage(&message, nullptr, 0, 0)) + { + // GH#638 (Pressing F7 brings up both the history AND a caret browsing message) + // The Xaml input stack doesn't allow an application to suppress the "caret browsing" + // dialog experience triggered when you press F7. Official recommendation from the Xaml + // team is to catch F7 before we hand it off. + // AppLogic contains an ad-hoc implementation of event bubbling for a runtime classes + // implementing a custom IF7Listener interface. + // If the recipient of IF7Listener::OnF7Pressed suggests that the F7 press has, in fact, + // been handled we can discard the message before we even translate it. + if (_messageIsF7Keypress(message)) + { + if (_host.OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true)) + { + // The application consumed the F7. Don't let Xaml get it. + continue; + } + } + + // GH#6421 - System XAML will never send an Alt KeyUp event. So, similar + // to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp + // here, and plumb it through. + if (_messageIsAltKeyup(message)) + { + // Let's pass to the application + if (_host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false)) + { + // The application consumed the Alt. Don't let Xaml get it. + continue; + } + } + + // GH#7125 = System XAML will show a system dialog on Alt Space. We want to + // explicitly prevent that because we handle that ourselves. So similar to + // above, we steal the event and hand it off to the host. + if (_messageIsAltSpaceKeypress(message)) + { + _host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true); + continue; + } + + TranslateMessage(&message); + DispatchMessage(&message); + } + return 0; +} diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h new file mode 100644 index 00000000000..979716174a0 --- /dev/null +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "AppHost.h" + +class WindowThread +{ +public: + WindowThread(); + int WindowProc(); + +private: + ::AppHost _host; +}; diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index cd555c70499..85359d58274 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -56,6 +56,8 @@ + + @@ -67,6 +69,8 @@ + + diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index a54d37faddc..a90b2d5e69f 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT license. #include "pch.h" -#include "AppHost.h" +#include "WindowEmperor.h" #include "resource.h" #include "../types/inc/User32Utils.hpp" #include @@ -83,19 +83,6 @@ static void EnsureNativeArchitecture() } } -static bool _messageIsF7Keypress(const MSG& message) -{ - return (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN) && message.wParam == VK_F7; -} -static bool _messageIsAltKeyup(const MSG& message) -{ - return (message.message == WM_KEYUP || message.message == WM_SYSKEYUP) && message.wParam == VK_MENU; -} -static bool _messageIsAltSpaceKeypress(const MSG& message) -{ - return message.message == WM_SYSKEYDOWN && message.wParam == VK_SPACE; -} - int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { TraceLoggingRegister(g_hWindowsTerminalProvider); @@ -127,68 +114,10 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) // doing that, we can safely init as STA before any WinRT dispatches. winrt::init_apartment(winrt::apartment_type::single_threaded); - // Create the AppHost object, which will create both the window and the - // Terminal App. This MUST BE constructed before the Xaml manager as TermApp - // provides an implementation of Windows.UI.Xaml.Application. - AppHost host; - if (!host.HasWindow()) + ::WindowEmperor emperor{}; + if (emperor.ShouldExit()) { - // If we were told to not have a window, exit early. Make sure to use - // ExitProcess to die here. If you try just `return 0`, then - // the XAML app host will crash during teardown. ExitProcess avoids - // that. ExitProcess(0); } - - // Initialize the xaml content. This must be called AFTER the - // WindowsXamlManager is initialized. - host.Initialize(); - - MSG message; - - while (GetMessage(&message, nullptr, 0, 0)) - { - // GH#638 (Pressing F7 brings up both the history AND a caret browsing message) - // The Xaml input stack doesn't allow an application to suppress the "caret browsing" - // dialog experience triggered when you press F7. Official recommendation from the Xaml - // team is to catch F7 before we hand it off. - // AppLogic contains an ad-hoc implementation of event bubbling for a runtime classes - // implementing a custom IF7Listener interface. - // If the recipient of IF7Listener::OnF7Pressed suggests that the F7 press has, in fact, - // been handled we can discard the message before we even translate it. - if (_messageIsF7Keypress(message)) - { - if (host.OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true)) - { - // The application consumed the F7. Don't let Xaml get it. - continue; - } - } - - // GH#6421 - System XAML will never send an Alt KeyUp event. So, similar - // to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp - // here, and plumb it through. - if (_messageIsAltKeyup(message)) - { - // Let's pass to the application - if (host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false)) - { - // The application consumed the Alt. Don't let Xaml get it. - continue; - } - } - - // GH#7125 = System XAML will show a system dialog on Alt Space. We want to - // explicitly prevent that because we handle that ourselves. So similar to - // above, we steal the event and hand it off to the host. - if (_messageIsAltSpaceKeypress(message)) - { - host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true); - continue; - } - - TranslateMessage(&message); - DispatchMessage(&message); - } - return 0; + emperor.WaitForWindows(); } From a5255ba8ed4cce537d3ac32b875a5a1516d4f4cb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 30 Jan 2023 12:37:26 -0600 Subject: [PATCH 002/122] I don't think I want any of these --- src/cascadia/TerminalApp/AppLogic.cpp | 181 ++++++++++++++------- src/cascadia/TerminalApp/AppLogic.h | 6 +- src/cascadia/TerminalApp/TabManagement.cpp | 21 ++- src/cascadia/TerminalApp/TerminalPage.cpp | 116 ++++++------- src/cascadia/TerminalApp/TerminalPage.h | 10 +- src/cascadia/WindowsTerminal/AppHost.cpp | 34 +++- 6 files changed, 231 insertions(+), 137 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 4381e24e0b1..0e2e066afa1 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -187,7 +187,6 @@ namespace winrt::TerminalApp::implementation // make sure that there's a terminal page for callers of // SetTitleBarContent _isElevated = ::Microsoft::Console::Utils::IsElevated(); - _root = winrt::make_self(); _reloadSettings = std::make_shared>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() { if (auto self{ weakSelf.get() }) @@ -205,6 +204,26 @@ namespace winrt::TerminalApp::implementation // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT AppLogic::Initialize(HWND hwnd) { + _root = winrt::make_self(); + _dialog = winrt::Windows::UI::Xaml::Controls::ContentDialog{}; + + /*if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + { + _root->SetPersistedLayoutIdx(idx.value()); + }*/ + _root->SetStartupActions(_appArgs.GetStartupActions()); + + // Check if we were started as a COM server for inbound connections of console sessions + // coming out of the operating system default application feature. If so, + // tell TerminalPage to start the listener as we have to make sure it has the chance + // to register a handler to hear about the requests first and is all ready to receive + // them before the COM server registers itself. Otherwise, the request might come + // in and be routed to an event with no handlers or a non-ready Page. + if (_appArgs.IsHandoffListener()) + { + _root->SetInboundListener(true); + } + return _root->Initialize(hwnd); } @@ -622,7 +641,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Size proposedSize{}; const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = _LoadPersistedLayout(_settings)) { if (layout.InitialSize()) { @@ -647,42 +666,70 @@ namespace winrt::TerminalApp::implementation commandlineSize.height); } - // GH#2061 - If the global setting "Always show tab bar" is - // set or if "Show tabs in title bar" is set, then we'll need to add - // the height of the tab bar here. - if (_settings.GlobalSettings().ShowTabsInTitlebar()) - { - // If we're showing the tabs in the titlebar, we need to use a - // TitlebarControl here to calculate how much space to reserve. - // - // We'll create a fake TitlebarControl, and we'll propose an - // available size to it with Measure(). After Measure() is called, - // the TitlebarControl's DesiredSize will contain the _unscaled_ - // size that the titlebar would like to use. We'll use that as part - // of the height calculation here. - auto titlebar = TitlebarControl{ static_cast(0) }; - titlebar.Measure({ SHRT_MAX, SHRT_MAX }); - proposedSize.Height += (titlebar.DesiredSize().Height) * scale; - } - else if (_settings.GlobalSettings().AlwaysShowTabs()) - { - // Otherwise, let's use a TabRowControl to calculate how much extra - // space we'll need. - // - // Similarly to above, we'll measure it with an arbitrarily large - // available space, to make sure we get all the space it wants. - auto tabControl = TabRowControl(); - tabControl.Measure({ SHRT_MAX, SHRT_MAX }); - - // For whatever reason, there's about 10px of unaccounted-for space - // in the application. I couldn't tell you where these 10px are - // coming from, but they need to be included in this math. - proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; - } + // TODO! + + // // GH#2061 - If the global setting "Always show tab bar" is + // // set or if "Show tabs in title bar" is set, then we'll need to add + // // the height of the tab bar here. + // if (_settings.GlobalSettings().ShowTabsInTitlebar()) + // { + // // If we're showing the tabs in the titlebar, we need to use a + // // TitlebarControl here to calculate how much space to reserve. + // // + // // We'll create a fake TitlebarControl, and we'll propose an + // // available size to it with Measure(). After Measure() is called, + // // the TitlebarControl's DesiredSize will contain the _unscaled_ + // // size that the titlebar would like to use. We'll use that as part + // // of the height calculation here. + // auto titlebar = TitlebarControl{ static_cast(0) }; + // titlebar.Measure({ SHRT_MAX, SHRT_MAX }); + // proposedSize.Height += (titlebar.DesiredSize().Height) * scale; + // } + // else if (_settings.GlobalSettings().AlwaysShowTabs()) + // { + // // Otherwise, let's use a TabRowControl to calculate how much extra + // // space we'll need. + // // + // // Similarly to above, we'll measure it with an arbitrarily large + // // available space, to make sure we get all the space it wants. + // auto tabControl = TabRowControl(); + // tabControl.Measure({ SHRT_MAX, SHRT_MAX }); + + // // For whatever reason, there's about 10px of unaccounted-for space + // // in the application. I couldn't tell you where these 10px are + // // coming from, but they need to be included in this math. + // proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; + // } return proposedSize; } + // Method Description; + // - Checks if the current window is configured to load a particular layout + // Arguments: + // - settings: The settings to use as this may be called before the page is + // fully initialized. + // Return Value: + // - non-null if there is a particular saved layout to use + std::optional AppLogic::_LoadPersistedLayoutIdx(CascadiaSettings& settings) const + { + return _ShouldUsePersistedLayout(settings) ? _appArgs.GetPersistedLayoutIdx() : std::nullopt; + } + + WindowLayout AppLogic::_LoadPersistedLayout(CascadiaSettings& settings) const + { + if (const auto idx = _LoadPersistedLayoutIdx(settings)) + { + const auto i = idx.value(); + const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + if (layouts && layouts.Size() > i) + { + return layouts.GetAt(i); + } + } + return nullptr; + } + // Method Description: // - Get the launch mode in json settings file. Now there // two launch mode: default, maximized. Default means the window @@ -704,7 +751,7 @@ namespace winrt::TerminalApp::implementation // commandline, then use that to override the value from the settings. const auto valueFromSettings = _settings.GlobalSettings().LaunchMode(); const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode(); - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = this->_LoadPersistedLayout(_settings)) { if (layout.LaunchMode()) { @@ -736,7 +783,7 @@ namespace winrt::TerminalApp::implementation auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = _LoadPersistedLayout(_settings)) { if (layout.InitialPosition()) { @@ -798,7 +845,11 @@ namespace winrt::TerminalApp::implementation // - See Pane::CalcSnappedDimension float AppLogic::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const { - return _root->CalcSnappedDimension(widthOrHeight, dimension); + if (_root) + { + return _root->CalcSnappedDimension(widthOrHeight, dimension); + } + return dimension; } // Method Description: @@ -1067,6 +1118,9 @@ namespace winrt::TerminalApp::implementation return; } + if (!_root) + return; + if (_settingsLoadedResult == S_FALSE) { _ShowLoadWarningsDialog(); @@ -1209,7 +1263,7 @@ namespace winrt::TerminalApp::implementation { // If persisted layout is enabled and we are the last window closing // we should save our state. - if (_root->ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + if (_ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) { if (const auto layout = _root->GetWindowLayout()) { @@ -1242,7 +1296,7 @@ namespace winrt::TerminalApp::implementation } void AppLogic::WindowActivated(const bool activated) { - _root->WindowActivated(activated); + if (_root) _root->WindowActivated(activated); } bool AppLogic::HasCommandlineArguments() const noexcept @@ -1278,22 +1332,22 @@ namespace winrt::TerminalApp::implementation // then it contains only the executable name and no other arguments. _hasCommandLineArguments = args.size() > 1; _appArgs.ValidateStartupCommands(); - if (const auto idx = _appArgs.GetPersistedLayoutIdx()) - { - _root->SetPersistedLayoutIdx(idx.value()); - } - _root->SetStartupActions(_appArgs.GetStartupActions()); - - // Check if we were started as a COM server for inbound connections of console sessions - // coming out of the operating system default application feature. If so, - // tell TerminalPage to start the listener as we have to make sure it has the chance - // to register a handler to hear about the requests first and is all ready to receive - // them before the COM server registers itself. Otherwise, the request might come - // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs.IsHandoffListener()) - { - _root->SetInboundListener(true); - } + //if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + //{ + // _root->SetPersistedLayoutIdx(idx.value()); + //} + //_root->SetStartupActions(_appArgs.GetStartupActions()); + + //// Check if we were started as a COM server for inbound connections of console sessions + //// coming out of the operating system default application feature. If so, + //// tell TerminalPage to start the listener as we have to make sure it has the chance + //// to register a handler to hear about the requests first and is all ready to receive + //// them before the COM server registers itself. Otherwise, the request might come + //// in and be routed to an event with no handlers or a non-ready Page. + //if (_appArgs.IsHandoffListener()) + //{ + // _root->SetInboundListener(true); + //} } return result; @@ -1540,9 +1594,20 @@ namespace winrt::TerminalApp::implementation bool AppLogic::ShouldUsePersistedLayout() { - return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; + return _ShouldUsePersistedLayout(_settings); } + // Method Description; + // - Checks if the current terminal window should load or save its layout information. + // Arguments: + // - settings: The settings to use as this may be called before the page is + // fully initialized. + // Return Value: + // - true if the ApplicationState should be used. + bool AppLogic::_ShouldUsePersistedLayout(CascadiaSettings& settings) const + { + return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + } bool AppLogic::ShouldImmediatelyHandoffToElevated() { if (!_loadedInitialSettings) @@ -1621,11 +1686,11 @@ namespace winrt::TerminalApp::implementation } } - void AppLogic::SetPersistedLayoutIdx(const uint32_t idx) + void AppLogic::SetPersistedLayoutIdx(const uint32_t) { if (_root) { - _root->SetPersistedLayoutIdx(idx); + // Todo! _root->SetPersistedLayoutIdx(idx); } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 5534979b973..f1f06b18415 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -166,7 +166,7 @@ namespace winrt::TerminalApp::implementation uint64_t _numOpenWindows{ 0 }; std::shared_mutex _dialogLock; - winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog; + winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog{ nullptr }; ::TerminalApp::AppCommandlineArgs _appArgs; ::TerminalApp::AppCommandlineArgs _settingsAppArgs; @@ -198,6 +198,10 @@ namespace winrt::TerminalApp::implementation fire_and_forget _DispatchReloadSettings(); void _OpenSettingsUI(); + std::optional _LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + Microsoft::Terminal::Settings::Model::WindowLayout _LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + bool _ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + bool _hasCommandLineArguments{ false }; bool _hasSettingsStartupActions{ false }; std::vector _warnings; diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 7acd7d3f349..06ece696bd1 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -519,15 +519,18 @@ namespace winrt::TerminalApp::implementation // To close the window here, we need to close the hosting window. if (_tabs.Size() == 0) { - // If we are supposed to save state, make sure we clear it out - // 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. - if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) - { - auto state = ApplicationState::SharedInstance(); - state.PersistedWindowLayouts(nullptr); - } + + // TODO! + // + //// If we are supposed to save state, make sure we clear it out + //// 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. + //if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + //{ + // auto state = ApplicationState::SharedInstance(); + // state.PersistedWindowLayouts(nullptr); + //} _LastTabClosedHandlers(*this, nullptr); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index cf4f0140a27..ca48924ac6d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -321,17 +321,17 @@ namespace winrt::TerminalApp::implementation ShowSetAsDefaultInfoBar(); } - // Method Description; - // - Checks if the current terminal window should load or save its layout information. - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - true if the ApplicationState should be used. - bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const - { - return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; - } + // // Method Description; + // // - Checks if the current terminal window should load or save its layout information. + // // Arguments: + // // - settings: The settings to use as this may be called before the page is + // // fully initialized. + // // Return Value: + // // - true if the ApplicationState should be used. + // bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const + // { + // return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + // } // Method Description: // - This is a bit of trickiness: If we're running unelevated, and the user @@ -444,31 +444,31 @@ namespace winrt::TerminalApp::implementation } } - // Method Description; - // - Checks if the current window is configured to load a particular layout - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - non-null if there is a particular saved layout to use - std::optional TerminalPage::LoadPersistedLayoutIdx(CascadiaSettings& settings) const - { - return ShouldUsePersistedLayout(settings) ? _loadFromPersistedLayoutIdx : std::nullopt; - } - - WindowLayout TerminalPage::LoadPersistedLayout(CascadiaSettings& settings) const - { - if (const auto idx = LoadPersistedLayoutIdx(settings)) - { - const auto i = idx.value(); - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (layouts && layouts.Size() > i) - { - return layouts.GetAt(i); - } - } - return nullptr; - } + // // Method Description; + // // - Checks if the current window is configured to load a particular layout + // // Arguments: + // // - settings: The settings to use as this may be called before the page is + // // fully initialized. + // // Return Value: + // // - non-null if there is a particular saved layout to use + // std::optional TerminalPage::LoadPersistedLayoutIdx(CascadiaSettings& settings) const + // { + // return ShouldUsePersistedLayout(settings) ? _loadFromPersistedLayoutIdx : std::nullopt; + // } + + // WindowLayout TerminalPage::LoadPersistedLayout(CascadiaSettings& settings) const + // { + // if (const auto idx = LoadPersistedLayoutIdx(settings)) + // { + // const auto i = idx.value(); + // const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + // if (layouts && layouts.Size() > i) + // { + // return layouts.GetAt(i); + // } + // } + // return nullptr; + // } winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e) { @@ -553,15 +553,18 @@ namespace winrt::TerminalApp::implementation { _startupState = StartupState::InStartup; - // If we are provided with an index, the cases where we have - // commandline args and startup actions are already handled. - if (const auto layout = LoadPersistedLayout(_settings)) - { - if (layout.TabLayout().Size() > 0) - { - _startupActions = layout.TabLayout(); - } - } + + // TODO! + // + //// If we are provided with an index, the cases where we have + //// commandline args and startup actions are already handled. + //if (const auto layout = LoadPersistedLayout(_settings)) + //{ + // if (layout.TabLayout().Size() > 0) + // { + // _startupActions = layout.TabLayout(); + // } + //} ProcessStartupActions(_startupActions, true); @@ -1894,13 +1897,14 @@ namespace winrt::TerminalApp::implementation } } - if (ShouldUsePersistedLayout(_settings)) - { - // 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; - } + // TODO! + //if (ShouldUsePersistedLayout(_settings)) + //{ + // // 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(); } @@ -3912,10 +3916,10 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx) - { - _loadFromPersistedLayoutIdx = idx; - } + // void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx) + // { + // _loadFromPersistedLayoutIdx = idx; + // } void TerminalPage::SetNumberOfOpenWindows(const uint64_t num) { diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 9c1705706a0..0d5fb18fef8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -63,11 +63,11 @@ namespace winrt::TerminalApp::implementation void Create(); - bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + // bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - std::optional LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; - winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + // std::optional LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + // winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout(); winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e); @@ -126,7 +126,7 @@ namespace winrt::TerminalApp::implementation void WindowId(const uint64_t& value); void SetNumberOfOpenWindows(const uint64_t value); - void SetPersistedLayoutIdx(const uint32_t value); + // void SetPersistedLayoutIdx(const uint32_t value); winrt::hstring WindowIdForDisplay() const noexcept; winrt::hstring WindowNameForDisplay() const noexcept; @@ -194,7 +194,7 @@ namespace winrt::TerminalApp::implementation bool _isAlwaysOnTop{ false }; winrt::hstring _WindowName{}; uint64_t _WindowId{ 0 }; - std::optional _loadFromPersistedLayoutIdx{}; + // std::optional _loadFromPersistedLayoutIdx{}; uint64_t _numOpenWindows{ 0 }; bool _maintainStateOnTabClose{ false }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 9b20e3c7aa2..b060f89adb5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -29,13 +29,19 @@ using namespace std::chrono_literals; static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; AppHost::AppHost() noexcept : - _app{}, + // _app{}, _windowManager{}, _logic{ nullptr }, // don't make one, we're going to take a ref on app's _window{ nullptr }, _getWindowLayoutThrottler{} // this will get set if we become the monarch { - _logic = _app.Logic(); // get a ref to app's logic + _preInit(); +} + +void AppHost::_preInit() +{ + // _logic = _app.Logic(); // get a ref to app's logic + _logic = winrt::TerminalApp::AppLogic(); // Make a new logic // Inform the WindowManager that it can use us to find the target window for // a set of commandline args. This needs to be done before @@ -65,7 +71,7 @@ AppHost::AppHost() noexcept : } // Update our own internal state tracking if we're in quake mode or not. - _IsQuakeWindowChanged(nullptr, nullptr); + // _IsQuakeWindowChanged(nullptr, nullptr); _window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea()); @@ -146,8 +152,9 @@ AppHost::~AppHost() _showHideWindowThrottler.reset(); _window = nullptr; - _app.Close(); - _app = nullptr; + // TODO! + // _app.Close(); + // _app = nullptr; } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) @@ -350,11 +357,19 @@ void AppHost::Initialize() { _window->Initialize(); + // _preInit(); + if (auto withWindow{ _logic.try_as() }) { withWindow->Initialize(_window->GetHandle()); } + _IsQuakeWindowChanged(nullptr, nullptr); + if (_windowManager.IsMonarch()) + { + _setupGlobalHotkeys(); + } + if (_useNonClientArea) { // Register our callback for when the app's non-client content changes. @@ -463,8 +478,9 @@ void AppHost::Initialize() // time when this object would actually need to get cleaned up is _during // exit_. So we can safely leak this Application object, and have it just // get cleaned up normally when our process exits. - auto a{ _app }; - ::winrt::detach_abi(a); + // auto a{ _app }; + // ::winrt::detach_abi(a); + // TODO! Do we still need that? ^^ } // Method Description: @@ -649,7 +665,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, til::rect proposedRect, Launc til::point origin{ (proposedRect.left + nonClientFrame.left), (proposedRect.top) }; - if (_logic.IsQuakeWindow()) + if (false /*TODO! _logic.IsQuakeWindow() */) { // If we just use rcWork by itself, we'll fail to account for the invisible // space reserved for the resize handles. So retrieve that size here. @@ -1063,6 +1079,8 @@ void AppHost::_listenForInboundConnections() winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { + if (!_logic.GetRoot()) + co_return; // The hotkey MUST be registered on the main thread. It will fail otherwise! co_await wil::resume_foreground(_logic.GetRoot().Dispatcher()); From e6220b7fe7fbe293a2a1f99357d0973f2dde4f95 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 30 Jan 2023 12:37:35 -0600 Subject: [PATCH 003/122] Revert "I don't think I want any of these" This reverts commit a5255ba8ed4cce537d3ac32b875a5a1516d4f4cb. --- src/cascadia/TerminalApp/AppLogic.cpp | 181 +++++++-------------- src/cascadia/TerminalApp/AppLogic.h | 6 +- src/cascadia/TerminalApp/TabManagement.cpp | 21 +-- src/cascadia/TerminalApp/TerminalPage.cpp | 116 +++++++------ src/cascadia/TerminalApp/TerminalPage.h | 10 +- src/cascadia/WindowsTerminal/AppHost.cpp | 34 +--- 6 files changed, 137 insertions(+), 231 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 0e2e066afa1..4381e24e0b1 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -187,6 +187,7 @@ namespace winrt::TerminalApp::implementation // make sure that there's a terminal page for callers of // SetTitleBarContent _isElevated = ::Microsoft::Console::Utils::IsElevated(); + _root = winrt::make_self(); _reloadSettings = std::make_shared>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() { if (auto self{ weakSelf.get() }) @@ -204,26 +205,6 @@ namespace winrt::TerminalApp::implementation // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT AppLogic::Initialize(HWND hwnd) { - _root = winrt::make_self(); - _dialog = winrt::Windows::UI::Xaml::Controls::ContentDialog{}; - - /*if (const auto idx = _appArgs.GetPersistedLayoutIdx()) - { - _root->SetPersistedLayoutIdx(idx.value()); - }*/ - _root->SetStartupActions(_appArgs.GetStartupActions()); - - // Check if we were started as a COM server for inbound connections of console sessions - // coming out of the operating system default application feature. If so, - // tell TerminalPage to start the listener as we have to make sure it has the chance - // to register a handler to hear about the requests first and is all ready to receive - // them before the COM server registers itself. Otherwise, the request might come - // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs.IsHandoffListener()) - { - _root->SetInboundListener(true); - } - return _root->Initialize(hwnd); } @@ -641,7 +622,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Size proposedSize{}; const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); - if (const auto layout = _LoadPersistedLayout(_settings)) + if (const auto layout = _root->LoadPersistedLayout(_settings)) { if (layout.InitialSize()) { @@ -666,68 +647,40 @@ namespace winrt::TerminalApp::implementation commandlineSize.height); } - // TODO! - - // // GH#2061 - If the global setting "Always show tab bar" is - // // set or if "Show tabs in title bar" is set, then we'll need to add - // // the height of the tab bar here. - // if (_settings.GlobalSettings().ShowTabsInTitlebar()) - // { - // // If we're showing the tabs in the titlebar, we need to use a - // // TitlebarControl here to calculate how much space to reserve. - // // - // // We'll create a fake TitlebarControl, and we'll propose an - // // available size to it with Measure(). After Measure() is called, - // // the TitlebarControl's DesiredSize will contain the _unscaled_ - // // size that the titlebar would like to use. We'll use that as part - // // of the height calculation here. - // auto titlebar = TitlebarControl{ static_cast(0) }; - // titlebar.Measure({ SHRT_MAX, SHRT_MAX }); - // proposedSize.Height += (titlebar.DesiredSize().Height) * scale; - // } - // else if (_settings.GlobalSettings().AlwaysShowTabs()) - // { - // // Otherwise, let's use a TabRowControl to calculate how much extra - // // space we'll need. - // // - // // Similarly to above, we'll measure it with an arbitrarily large - // // available space, to make sure we get all the space it wants. - // auto tabControl = TabRowControl(); - // tabControl.Measure({ SHRT_MAX, SHRT_MAX }); - - // // For whatever reason, there's about 10px of unaccounted-for space - // // in the application. I couldn't tell you where these 10px are - // // coming from, but they need to be included in this math. - // proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; - // } - - return proposedSize; - } - - // Method Description; - // - Checks if the current window is configured to load a particular layout - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - non-null if there is a particular saved layout to use - std::optional AppLogic::_LoadPersistedLayoutIdx(CascadiaSettings& settings) const - { - return _ShouldUsePersistedLayout(settings) ? _appArgs.GetPersistedLayoutIdx() : std::nullopt; - } - - WindowLayout AppLogic::_LoadPersistedLayout(CascadiaSettings& settings) const - { - if (const auto idx = _LoadPersistedLayoutIdx(settings)) + // GH#2061 - If the global setting "Always show tab bar" is + // set or if "Show tabs in title bar" is set, then we'll need to add + // the height of the tab bar here. + if (_settings.GlobalSettings().ShowTabsInTitlebar()) { - const auto i = idx.value(); - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (layouts && layouts.Size() > i) - { - return layouts.GetAt(i); - } + // If we're showing the tabs in the titlebar, we need to use a + // TitlebarControl here to calculate how much space to reserve. + // + // We'll create a fake TitlebarControl, and we'll propose an + // available size to it with Measure(). After Measure() is called, + // the TitlebarControl's DesiredSize will contain the _unscaled_ + // size that the titlebar would like to use. We'll use that as part + // of the height calculation here. + auto titlebar = TitlebarControl{ static_cast(0) }; + titlebar.Measure({ SHRT_MAX, SHRT_MAX }); + proposedSize.Height += (titlebar.DesiredSize().Height) * scale; } - return nullptr; + else if (_settings.GlobalSettings().AlwaysShowTabs()) + { + // Otherwise, let's use a TabRowControl to calculate how much extra + // space we'll need. + // + // Similarly to above, we'll measure it with an arbitrarily large + // available space, to make sure we get all the space it wants. + auto tabControl = TabRowControl(); + tabControl.Measure({ SHRT_MAX, SHRT_MAX }); + + // For whatever reason, there's about 10px of unaccounted-for space + // in the application. I couldn't tell you where these 10px are + // coming from, but they need to be included in this math. + proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; + } + + return proposedSize; } // Method Description: @@ -751,7 +704,7 @@ namespace winrt::TerminalApp::implementation // commandline, then use that to override the value from the settings. const auto valueFromSettings = _settings.GlobalSettings().LaunchMode(); const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode(); - if (const auto layout = this->_LoadPersistedLayout(_settings)) + if (const auto layout = _root->LoadPersistedLayout(_settings)) { if (layout.LaunchMode()) { @@ -783,7 +736,7 @@ namespace winrt::TerminalApp::implementation auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; - if (const auto layout = _LoadPersistedLayout(_settings)) + if (const auto layout = _root->LoadPersistedLayout(_settings)) { if (layout.InitialPosition()) { @@ -845,11 +798,7 @@ namespace winrt::TerminalApp::implementation // - See Pane::CalcSnappedDimension float AppLogic::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const { - if (_root) - { - return _root->CalcSnappedDimension(widthOrHeight, dimension); - } - return dimension; + return _root->CalcSnappedDimension(widthOrHeight, dimension); } // Method Description: @@ -1118,9 +1067,6 @@ namespace winrt::TerminalApp::implementation return; } - if (!_root) - return; - if (_settingsLoadedResult == S_FALSE) { _ShowLoadWarningsDialog(); @@ -1263,7 +1209,7 @@ namespace winrt::TerminalApp::implementation { // If persisted layout is enabled and we are the last window closing // we should save our state. - if (_ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + if (_root->ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) { if (const auto layout = _root->GetWindowLayout()) { @@ -1296,7 +1242,7 @@ namespace winrt::TerminalApp::implementation } void AppLogic::WindowActivated(const bool activated) { - if (_root) _root->WindowActivated(activated); + _root->WindowActivated(activated); } bool AppLogic::HasCommandlineArguments() const noexcept @@ -1332,22 +1278,22 @@ namespace winrt::TerminalApp::implementation // then it contains only the executable name and no other arguments. _hasCommandLineArguments = args.size() > 1; _appArgs.ValidateStartupCommands(); - //if (const auto idx = _appArgs.GetPersistedLayoutIdx()) - //{ - // _root->SetPersistedLayoutIdx(idx.value()); - //} - //_root->SetStartupActions(_appArgs.GetStartupActions()); - - //// Check if we were started as a COM server for inbound connections of console sessions - //// coming out of the operating system default application feature. If so, - //// tell TerminalPage to start the listener as we have to make sure it has the chance - //// to register a handler to hear about the requests first and is all ready to receive - //// them before the COM server registers itself. Otherwise, the request might come - //// in and be routed to an event with no handlers or a non-ready Page. - //if (_appArgs.IsHandoffListener()) - //{ - // _root->SetInboundListener(true); - //} + if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + { + _root->SetPersistedLayoutIdx(idx.value()); + } + _root->SetStartupActions(_appArgs.GetStartupActions()); + + // Check if we were started as a COM server for inbound connections of console sessions + // coming out of the operating system default application feature. If so, + // tell TerminalPage to start the listener as we have to make sure it has the chance + // to register a handler to hear about the requests first and is all ready to receive + // them before the COM server registers itself. Otherwise, the request might come + // in and be routed to an event with no handlers or a non-ready Page. + if (_appArgs.IsHandoffListener()) + { + _root->SetInboundListener(true); + } } return result; @@ -1594,20 +1540,9 @@ namespace winrt::TerminalApp::implementation bool AppLogic::ShouldUsePersistedLayout() { - return _ShouldUsePersistedLayout(_settings); + return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; } - // Method Description; - // - Checks if the current terminal window should load or save its layout information. - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - true if the ApplicationState should be used. - bool AppLogic::_ShouldUsePersistedLayout(CascadiaSettings& settings) const - { - return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; - } bool AppLogic::ShouldImmediatelyHandoffToElevated() { if (!_loadedInitialSettings) @@ -1686,11 +1621,11 @@ namespace winrt::TerminalApp::implementation } } - void AppLogic::SetPersistedLayoutIdx(const uint32_t) + void AppLogic::SetPersistedLayoutIdx(const uint32_t idx) { if (_root) { - // Todo! _root->SetPersistedLayoutIdx(idx); + _root->SetPersistedLayoutIdx(idx); } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index f1f06b18415..5534979b973 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -166,7 +166,7 @@ namespace winrt::TerminalApp::implementation uint64_t _numOpenWindows{ 0 }; std::shared_mutex _dialogLock; - winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog{ nullptr }; + winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog; ::TerminalApp::AppCommandlineArgs _appArgs; ::TerminalApp::AppCommandlineArgs _settingsAppArgs; @@ -198,10 +198,6 @@ namespace winrt::TerminalApp::implementation fire_and_forget _DispatchReloadSettings(); void _OpenSettingsUI(); - std::optional _LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; - Microsoft::Terminal::Settings::Model::WindowLayout _LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; - bool _ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; - bool _hasCommandLineArguments{ false }; bool _hasSettingsStartupActions{ false }; std::vector _warnings; diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 06ece696bd1..7acd7d3f349 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -519,18 +519,15 @@ namespace winrt::TerminalApp::implementation // To close the window here, we need to close the hosting window. if (_tabs.Size() == 0) { - - // TODO! - // - //// If we are supposed to save state, make sure we clear it out - //// 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. - //if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) - //{ - // auto state = ApplicationState::SharedInstance(); - // state.PersistedWindowLayouts(nullptr); - //} + // If we are supposed to save state, make sure we clear it out + // 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. + if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + { + auto state = ApplicationState::SharedInstance(); + state.PersistedWindowLayouts(nullptr); + } _LastTabClosedHandlers(*this, nullptr); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index ca48924ac6d..cf4f0140a27 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -321,17 +321,17 @@ namespace winrt::TerminalApp::implementation ShowSetAsDefaultInfoBar(); } - // // Method Description; - // // - Checks if the current terminal window should load or save its layout information. - // // Arguments: - // // - settings: The settings to use as this may be called before the page is - // // fully initialized. - // // Return Value: - // // - true if the ApplicationState should be used. - // bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const - // { - // return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; - // } + // Method Description; + // - Checks if the current terminal window should load or save its layout information. + // Arguments: + // - settings: The settings to use as this may be called before the page is + // fully initialized. + // Return Value: + // - true if the ApplicationState should be used. + bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const + { + return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + } // Method Description: // - This is a bit of trickiness: If we're running unelevated, and the user @@ -444,31 +444,31 @@ namespace winrt::TerminalApp::implementation } } - // // Method Description; - // // - Checks if the current window is configured to load a particular layout - // // Arguments: - // // - settings: The settings to use as this may be called before the page is - // // fully initialized. - // // Return Value: - // // - non-null if there is a particular saved layout to use - // std::optional TerminalPage::LoadPersistedLayoutIdx(CascadiaSettings& settings) const - // { - // return ShouldUsePersistedLayout(settings) ? _loadFromPersistedLayoutIdx : std::nullopt; - // } - - // WindowLayout TerminalPage::LoadPersistedLayout(CascadiaSettings& settings) const - // { - // if (const auto idx = LoadPersistedLayoutIdx(settings)) - // { - // const auto i = idx.value(); - // const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - // if (layouts && layouts.Size() > i) - // { - // return layouts.GetAt(i); - // } - // } - // return nullptr; - // } + // Method Description; + // - Checks if the current window is configured to load a particular layout + // Arguments: + // - settings: The settings to use as this may be called before the page is + // fully initialized. + // Return Value: + // - non-null if there is a particular saved layout to use + std::optional TerminalPage::LoadPersistedLayoutIdx(CascadiaSettings& settings) const + { + return ShouldUsePersistedLayout(settings) ? _loadFromPersistedLayoutIdx : std::nullopt; + } + + WindowLayout TerminalPage::LoadPersistedLayout(CascadiaSettings& settings) const + { + if (const auto idx = LoadPersistedLayoutIdx(settings)) + { + const auto i = idx.value(); + const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + if (layouts && layouts.Size() > i) + { + return layouts.GetAt(i); + } + } + return nullptr; + } winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e) { @@ -553,18 +553,15 @@ namespace winrt::TerminalApp::implementation { _startupState = StartupState::InStartup; - - // TODO! - // - //// If we are provided with an index, the cases where we have - //// commandline args and startup actions are already handled. - //if (const auto layout = LoadPersistedLayout(_settings)) - //{ - // if (layout.TabLayout().Size() > 0) - // { - // _startupActions = layout.TabLayout(); - // } - //} + // If we are provided with an index, the cases where we have + // commandline args and startup actions are already handled. + if (const auto layout = LoadPersistedLayout(_settings)) + { + if (layout.TabLayout().Size() > 0) + { + _startupActions = layout.TabLayout(); + } + } ProcessStartupActions(_startupActions, true); @@ -1897,14 +1894,13 @@ namespace winrt::TerminalApp::implementation } } - // TODO! - //if (ShouldUsePersistedLayout(_settings)) - //{ - // // 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; - //} + if (ShouldUsePersistedLayout(_settings)) + { + // 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(); } @@ -3916,10 +3912,10 @@ namespace winrt::TerminalApp::implementation } } - // void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx) - // { - // _loadFromPersistedLayoutIdx = idx; - // } + void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx) + { + _loadFromPersistedLayoutIdx = idx; + } void TerminalPage::SetNumberOfOpenWindows(const uint64_t num) { diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 0d5fb18fef8..9c1705706a0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -63,11 +63,11 @@ namespace winrt::TerminalApp::implementation void Create(); - // bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - // std::optional LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; - // winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + std::optional LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout(); winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e); @@ -126,7 +126,7 @@ namespace winrt::TerminalApp::implementation void WindowId(const uint64_t& value); void SetNumberOfOpenWindows(const uint64_t value); - // void SetPersistedLayoutIdx(const uint32_t value); + void SetPersistedLayoutIdx(const uint32_t value); winrt::hstring WindowIdForDisplay() const noexcept; winrt::hstring WindowNameForDisplay() const noexcept; @@ -194,7 +194,7 @@ namespace winrt::TerminalApp::implementation bool _isAlwaysOnTop{ false }; winrt::hstring _WindowName{}; uint64_t _WindowId{ 0 }; - // std::optional _loadFromPersistedLayoutIdx{}; + std::optional _loadFromPersistedLayoutIdx{}; uint64_t _numOpenWindows{ 0 }; bool _maintainStateOnTabClose{ false }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index b060f89adb5..9b20e3c7aa2 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -29,19 +29,13 @@ using namespace std::chrono_literals; static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; AppHost::AppHost() noexcept : - // _app{}, + _app{}, _windowManager{}, _logic{ nullptr }, // don't make one, we're going to take a ref on app's _window{ nullptr }, _getWindowLayoutThrottler{} // this will get set if we become the monarch { - _preInit(); -} - -void AppHost::_preInit() -{ - // _logic = _app.Logic(); // get a ref to app's logic - _logic = winrt::TerminalApp::AppLogic(); // Make a new logic + _logic = _app.Logic(); // get a ref to app's logic // Inform the WindowManager that it can use us to find the target window for // a set of commandline args. This needs to be done before @@ -71,7 +65,7 @@ void AppHost::_preInit() } // Update our own internal state tracking if we're in quake mode or not. - // _IsQuakeWindowChanged(nullptr, nullptr); + _IsQuakeWindowChanged(nullptr, nullptr); _window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea()); @@ -152,9 +146,8 @@ AppHost::~AppHost() _showHideWindowThrottler.reset(); _window = nullptr; - // TODO! - // _app.Close(); - // _app = nullptr; + _app.Close(); + _app = nullptr; } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) @@ -357,19 +350,11 @@ void AppHost::Initialize() { _window->Initialize(); - // _preInit(); - if (auto withWindow{ _logic.try_as() }) { withWindow->Initialize(_window->GetHandle()); } - _IsQuakeWindowChanged(nullptr, nullptr); - if (_windowManager.IsMonarch()) - { - _setupGlobalHotkeys(); - } - if (_useNonClientArea) { // Register our callback for when the app's non-client content changes. @@ -478,9 +463,8 @@ void AppHost::Initialize() // time when this object would actually need to get cleaned up is _during // exit_. So we can safely leak this Application object, and have it just // get cleaned up normally when our process exits. - // auto a{ _app }; - // ::winrt::detach_abi(a); - // TODO! Do we still need that? ^^ + auto a{ _app }; + ::winrt::detach_abi(a); } // Method Description: @@ -665,7 +649,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, til::rect proposedRect, Launc til::point origin{ (proposedRect.left + nonClientFrame.left), (proposedRect.top) }; - if (false /*TODO! _logic.IsQuakeWindow() */) + if (_logic.IsQuakeWindow()) { // If we just use rcWork by itself, we'll fail to account for the invisible // space reserved for the resize handles. So retrieve that size here. @@ -1079,8 +1063,6 @@ void AppHost::_listenForInboundConnections() winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { - if (!_logic.GetRoot()) - co_return; // The hotkey MUST be registered on the main thread. It will fail otherwise! co_await wil::resume_foreground(_logic.GetRoot().Dispatcher()); From 936c01f948ac58d1a520dc7fbced8bb6d25c36b6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 30 Jan 2023 14:52:19 -0600 Subject: [PATCH 004/122] Start splitting AppLogic into AppLogic and Window logic We'll need this for #5000, for ainulindale. This refactoring will be annoying enough as it is so we may as well do it as a first, separate PR. --- src/cascadia/TerminalApp/App.cpp | 30 +- src/cascadia/TerminalApp/AppLogic.cpp | 1085 +--------------- src/cascadia/TerminalApp/AppLogic.h | 136 +- src/cascadia/TerminalApp/AppLogic.idl | 105 +- .../TerminalApp/TerminalAppLib.vcxproj | 7 + src/cascadia/TerminalApp/TerminalWindow.cpp | 1140 +++++++++++++++++ src/cascadia/TerminalApp/TerminalWindow.h | 181 +++ src/cascadia/TerminalApp/TerminalWindow.idl | 131 ++ 8 files changed, 1516 insertions(+), 1299 deletions(-) create mode 100644 src/cascadia/TerminalApp/TerminalWindow.cpp create mode 100644 src/cascadia/TerminalApp/TerminalWindow.h create mode 100644 src/cascadia/TerminalApp/TerminalWindow.idl diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 138d4a7d249..1a22cfc5b32 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -77,22 +77,24 @@ namespace winrt::TerminalApp::implementation /// Details about the launch request and process. void App::OnLaunched(const LaunchActivatedEventArgs& /*e*/) { + // TODO! UWP mode is straight up not supported anymore, yea? + // // if this is a UWP... it means its our problem to hook up the content to the window here. - if (_isUwp) - { - auto content = Window::Current().Content(); - if (content == nullptr) - { - auto logic = Logic(); - logic.RunAsUwp(); // Must set UWP status first, settings might change based on it. - logic.ReloadSettings(); - logic.Create(); + //if (_isUwp) + //{ + // auto content = Window::Current().Content(); + // if (content == nullptr) + // { + // auto logic = Logic(); + // logic.RunAsUwp(); // Must set UWP status first, settings might change based on it. + // logic.ReloadSettings(); + // logic.Create(); - auto page = logic.GetRoot().as(); + // auto page = logic.GetRoot().as(); - Window::Current().Content(page); - Window::Current().Activate(); - } - } + // Window::Current().Content(page); + // Window::Current().Activate(); + // } + //} } } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 4381e24e0b1..4d5cabbed7c 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -31,49 +31,14 @@ namespace winrt using IInspectable = Windows::Foundation::IInspectable; } -static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask"; +//////////////////////////////////////////////////////////////////////////////// +// TODO! This section probably should be in TerminalWindow with the warnings + // clang-format off -// !!! IMPORTANT !!! -// Make sure that these keys are in the same order as the -// SettingsLoadWarnings/Errors enum is! -static const std::array settingsLoadWarningsLabels { - USES_RESOURCE(L"MissingDefaultProfileText"), - USES_RESOURCE(L"DuplicateProfileText"), - USES_RESOURCE(L"UnknownColorSchemeText"), - USES_RESOURCE(L"InvalidBackgroundImage"), - USES_RESOURCE(L"InvalidIcon"), - USES_RESOURCE(L"AtLeastOneKeybindingWarning"), - USES_RESOURCE(L"TooManyKeysForChord"), - USES_RESOURCE(L"MissingRequiredParameter"), - USES_RESOURCE(L"FailedToParseCommandJson"), - USES_RESOURCE(L"FailedToWriteToSettings"), - USES_RESOURCE(L"InvalidColorSchemeInCmd"), - USES_RESOURCE(L"InvalidSplitSize"), - USES_RESOURCE(L"FailedToParseStartupActions"), - USES_RESOURCE(L"FailedToParseSubCommands"), - USES_RESOURCE(L"UnknownTheme"), - USES_RESOURCE(L"DuplicateRemainingProfilesEntry"), -}; static const std::array settingsLoadErrorsLabels { USES_RESOURCE(L"NoProfilesText"), USES_RESOURCE(L"AllProfilesHiddenText") }; -// clang-format on - -static_assert(settingsLoadWarningsLabels.size() == static_cast(SettingsLoadWarnings::WARNINGS_SIZE)); -static_assert(settingsLoadErrorsLabels.size() == static_cast(SettingsLoadErrors::ERRORS_SIZE)); - -// Function Description: -// - General-purpose helper for looking up a localized string for a -// warning/error. First will look for the given key in the provided map of -// keys->strings, where the values in the map are ResourceKeys. If it finds -// one, it will lookup the localized string from that ResourceKey. -// - If it does not find a key, it'll return an empty string -// Arguments: -// - key: the value to use to look for a resource key in the given map -// - map: A map of keys->Resource keys. -// Return Value: -// - the localized string for the given type, if it exists. template winrt::hstring _GetMessageText(uint32_t index, const T& keys) { @@ -83,20 +48,6 @@ winrt::hstring _GetMessageText(uint32_t index, const T& keys) } return {}; } - -// Function Description: -// - Gets the text from our ResourceDictionary for the given -// SettingsLoadWarning. If there is no such text, we'll return nullptr. -// - The warning should have an entry in settingsLoadWarningsLabels. -// Arguments: -// - warning: the SettingsLoadWarnings value to get the localized text for. -// Return Value: -// - localized text for the given warning -static winrt::hstring _GetWarningText(SettingsLoadWarnings warning) -{ - return _GetMessageText(static_cast(warning), settingsLoadWarningsLabels); -} - // Function Description: // - Gets the text from our ResourceDictionary for the given // SettingsLoadError. If there is no such text, we'll return nullptr. @@ -110,30 +61,11 @@ static winrt::hstring _GetErrorText(SettingsLoadErrors error) return _GetMessageText(static_cast(error), settingsLoadErrorsLabels); } -// Function Description: -// - Creates a Run of text to display an error message. The text is yellow or -// red for dark/light theme, respectively. -// Arguments: -// - text: The text of the error message. -// - resources: The application's resource loader. -// Return Value: -// - The fully styled text run. -static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceDictionary& resources) -{ - Documents::Run textRun; - textRun.Text(text); +// clang-format on - // Color the text red (light theme) or yellow (dark theme) based on the system theme - auto key = winrt::box_value(L"ErrorTextBrush"); - if (resources.HasKey(key)) - { - auto g = resources.Lookup(key); - auto brush = g.try_as(); - textRun.Foreground(brush); - } +//////////////////////////////////////////////////////////////////////////////// - return textRun; -} +static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask"; namespace winrt::TerminalApp::implementation { @@ -187,7 +119,6 @@ namespace winrt::TerminalApp::implementation // make sure that there's a terminal page for callers of // SetTitleBarContent _isElevated = ::Microsoft::Console::Utils::IsElevated(); - _root = winrt::make_self(); _reloadSettings = std::make_shared>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() { if (auto self{ weakSelf.get() }) @@ -201,13 +132,6 @@ namespace winrt::TerminalApp::implementation }); } - // Method Description: - // - Implements the IInitializeWithWindow interface from shobjidl_core. - HRESULT AppLogic::Initialize(HWND hwnd) - { - return _root->Initialize(hwnd); - } - // Method Description: // - Called around the codebase to discover if this is a UWP where we need to turn off specific settings. // Arguments: @@ -257,8 +181,6 @@ namespace winrt::TerminalApp::implementation // this as a MTA, before the app is Create()'d WINRT_ASSERT(_loadedInitialSettings); - _root->DialogPresenter(*this); - // In UWP mode, we cannot handle taking over the title bar for tabs, // so this setting is overridden to false no matter what the preference is. if (_isUwp) @@ -266,39 +188,8 @@ namespace winrt::TerminalApp::implementation _settings.GlobalSettings().ShowTabsInTitlebar(false); } - // Pay attention, that even if some command line arguments were parsed (like launch mode), - // we will not use the startup actions from settings. - // While this simplifies the logic, we might want to reconsider this behavior in the future. - if (!_hasCommandLineArguments && _hasSettingsStartupActions) + // TODO! These used to be in `_root->Initialized`: { - _root->SetStartupActions(_settingsAppArgs.GetStartupActions()); - } - - _root->SetSettings(_settings, false); - _root->Loaded({ this, &AppLogic::_OnLoaded }); - _root->Initialized([this](auto&&, auto&&) { - // GH#288 - When we finish initialization, if the user wanted us - // launched _fullscreen_, toggle fullscreen mode. This will make sure - // that the window size is _first_ set up as something sensible, so - // leaving fullscreen returns to a reasonable size. - const auto launchMode = this->GetLaunchMode(); - if (IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) - { - _root->SetFocusMode(true); - } - - // The IslandWindow handles (creating) the maximized state - // we just want to record it here on the page as well. - if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode)) - { - _root->Maximized(true); - } - - if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow()) - { - _root->SetFullscreen(true); - } - // Both LoadSettings and ReloadSettings are supposed to call this function, // but LoadSettings skips it, so that the UI starts up faster. // Now that the UI is present we can do them with a less significant UX impact. @@ -329,16 +220,11 @@ namespace winrt::TerminalApp::implementation TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } - }); - _root->Create(); + }; _ApplyLanguageSettingChange(); - _RefreshThemeRoutine(); _ApplyStartupTaskStateChange(); - auto args = winrt::make_self(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &AppLogic::_OpenSettingsUI)); - _SystemMenuChangeRequestedHandlers(*this, *args); - TraceLoggingWrite( g_hTerminalAppProvider, "AppCreated", @@ -348,459 +234,6 @@ namespace winrt::TerminalApp::implementation TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } - void AppLogic::Quit() - { - if (_root) - { - _root->CloseWindow(true); - } - } - - // Method Description: - // - Show a ContentDialog with buttons to take further action. Uses the - // FrameworkElements provided as the title and content of this dialog, and - // displays buttons (or a single button). Two buttons (primary and secondary) - // will be displayed if this is an warning dialog for closing the terminal, - // this allows the users to abandon the closing action. Otherwise, a single - // close button will be displayed. - // - Only one dialog can be visible at a time. If another dialog is visible - // when this is called, nothing happens. - // Arguments: - // - dialog: the dialog object that is going to show up - // Return value: - // - an IAsyncOperation with the dialog result - winrt::Windows::Foundation::IAsyncOperation AppLogic::ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog) - { - // DON'T release this lock in a wil::scope_exit. The scope_exit will get - // called when we await, which is not what we want. - std::unique_lock lock{ _dialogLock, std::try_to_lock }; - if (!lock) - { - // Another dialog is visible. - co_return ContentDialogResult::None; - } - - _dialog = dialog; - - // IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs. - // Since we're hosting the dialog in a Xaml island, we need to connect it to the - // xaml tree somehow. - dialog.XamlRoot(_root->XamlRoot()); - - // IMPORTANT: Set the requested theme of the dialog, because the - // PopupRoot isn't directly in the Xaml tree of our root. So the dialog - // won't inherit our RequestedTheme automagically. - // GH#5195, GH#3654 Because we cannot set RequestedTheme at the application level, - // we occasionally run into issues where parts of our UI end up themed incorrectly. - // Dialogs, for example, live under a different Xaml root element than the rest of - // our application. This makes our popup menus and buttons "disappear" when the - // user wants Terminal to be in a different theme than the rest of the system. - // This hack---and it _is_ a hack--walks up a dialog's ancestry and forces the - // theme on each element up to the root. We're relying a bit on Xaml's implementation - // details here, but it does have the desired effect. - // It's not enough to set the theme on the dialog alone. - auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) { - auto theme{ _settings.GlobalSettings().CurrentTheme() }; - auto requestedTheme{ theme.RequestedTheme() }; - auto element{ sender.try_as() }; - while (element) - { - element.RequestedTheme(requestedTheme); - element = element.Parent().try_as(); - } - } }; - - themingLambda(dialog, nullptr); // if it's already in the tree - auto loadedRevoker{ dialog.Loaded(winrt::auto_revoke, themingLambda) }; // if it's not yet in the tree - - // Display the dialog. - co_return co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); - - // After the dialog is dismissed, the dialog lock (held by `lock`) will - // be released so another can be shown - } - - // Method Description: - // - Dismiss the (only) visible ContentDialog - void AppLogic::DismissDialog() - { - if (auto localDialog = std::exchange(_dialog, nullptr)) - { - localDialog.Hide(); - } - } - - // Method Description: - // - Displays a dialog for errors found while loading or validating the - // settings. Uses the resources under the provided title and content keys - // as the title and first content of the dialog, then also displays a - // message for whatever exception was found while validating the settings. - // - Only one dialog can be visible at a time. If another dialog is visible - // when this is called, nothing happens. See ShowDialog for details - // Arguments: - // - titleKey: The key to use to lookup the title text from our resources. - // - contentKey: The key to use to lookup the content text from our resources. - void AppLogic::_ShowLoadErrorsDialog(const winrt::hstring& titleKey, - const winrt::hstring& contentKey, - HRESULT settingsLoadedResult) - { - auto title = GetLibraryResourceString(titleKey); - auto buttonText = RS_(L"Ok"); - - Controls::TextBlock warningsTextBlock; - // Make sure you can copy-paste - warningsTextBlock.IsTextSelectionEnabled(true); - // Make sure the lines of text wrap - warningsTextBlock.TextWrapping(TextWrapping::Wrap); - - winrt::Windows::UI::Xaml::Documents::Run errorRun; - const auto errorLabel = GetLibraryResourceString(contentKey); - errorRun.Text(errorLabel); - warningsTextBlock.Inlines().Append(errorRun); - warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - - if (FAILED(settingsLoadedResult)) - { - if (!_settingsLoadExceptionText.empty()) - { - warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); - warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - } - } - - // Add a note that we're using the default settings in this case. - winrt::Windows::UI::Xaml::Documents::Run usingDefaultsRun; - const auto usingDefaultsText = RS_(L"UsingDefaultSettingsText"); - usingDefaultsRun.Text(usingDefaultsText); - warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - warningsTextBlock.Inlines().Append(usingDefaultsRun); - - Controls::ContentDialog dialog; - dialog.Title(winrt::box_value(title)); - dialog.Content(winrt::box_value(warningsTextBlock)); - dialog.CloseButtonText(buttonText); - dialog.DefaultButton(Controls::ContentDialogButton::Close); - - ShowDialog(dialog); - } - - // Method Description: - // - Displays a dialog for warnings found while loading or validating the - // settings. Displays messages for whatever warnings were found while - // validating the settings. - // - Only one dialog can be visible at a time. If another dialog is visible - // when this is called, nothing happens. See ShowDialog for details - void AppLogic::_ShowLoadWarningsDialog() - { - auto title = RS_(L"SettingsValidateErrorTitle"); - auto buttonText = RS_(L"Ok"); - - Controls::TextBlock warningsTextBlock; - // Make sure you can copy-paste - warningsTextBlock.IsTextSelectionEnabled(true); - // Make sure the lines of text wrap - warningsTextBlock.TextWrapping(TextWrapping::Wrap); - - for (const auto& warning : _warnings) - { - // Try looking up the warning message key for each warning. - const auto warningText = _GetWarningText(warning); - if (!warningText.empty()) - { - warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); - warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - } - } - - Controls::ContentDialog dialog; - dialog.Title(winrt::box_value(title)); - dialog.Content(winrt::box_value(warningsTextBlock)); - dialog.CloseButtonText(buttonText); - dialog.DefaultButton(Controls::ContentDialogButton::Close); - - ShowDialog(dialog); - } - - // Method Description: - // - Triggered when the application is finished loading. If we failed to load - // the settings, then this will display the error dialog. This is done - // here instead of when loading the settings, because we need our UI to be - // visible to display the dialog, and when we're loading the settings, - // the UI might not be visible yet. - // Arguments: - // - - void AppLogic::_OnLoaded(const IInspectable& /*sender*/, - const RoutedEventArgs& /*eventArgs*/) - { - if (_settings.GlobalSettings().InputServiceWarning()) - { - const auto keyboardServiceIsDisabled = !_IsKeyboardServiceEnabled(); - if (keyboardServiceIsDisabled) - { - _root->ShowKeyboardServiceWarning(); - } - } - - if (FAILED(_settingsLoadedResult)) - { - const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle"); - const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); - } - else if (_settingsLoadedResult == S_FALSE) - { - _ShowLoadWarningsDialog(); - } - } - - // Method Description: - // - Helper for determining if the "Touch Keyboard and Handwriting Panel - // Service" is enabled. If it isn't, we want to be able to display a - // warning to the user, because they won't be able to type in the - // Terminal. - // Return Value: - // - true if the service is enabled, or if we fail to query the service. We - // return true in that case, to be less noisy (though, that is unexpected) - bool AppLogic::_IsKeyboardServiceEnabled() - { - if (IsUwp()) - { - return true; - } - - // If at any point we fail to open the service manager, the service, - // etc, then just quick return true to disable the dialog. We'd rather - // not be noisy with this dialog if we failed for some reason. - - // Open the service manager. This will return 0 if it failed. - wil::unique_schandle hManager{ OpenSCManagerW(nullptr, nullptr, 0) }; - - if (LOG_LAST_ERROR_IF(!hManager.is_valid())) - { - return true; - } - - // Get a handle to the keyboard service - wil::unique_schandle hService{ OpenServiceW(hManager.get(), TabletInputServiceKey.data(), SERVICE_QUERY_STATUS) }; - - // Windows 11 doesn't have a TabletInputService. - // (It was renamed to TextInputManagementService, because people kept thinking that a - // service called "tablet-something" is system-irrelevant on PCs and can be disabled.) - if (!hService.is_valid()) - { - return true; - } - - // Get the current state of the service - SERVICE_STATUS status{ 0 }; - if (!LOG_IF_WIN32_BOOL_FALSE(QueryServiceStatus(hService.get(), &status))) - { - return true; - } - - const auto state = status.dwCurrentState; - return (state == SERVICE_RUNNING || state == SERVICE_START_PENDING); - } - - // Method Description: - // - Get the size in pixels of the client area we'll need to launch this - // terminal app. This method will use the default profile's settings to do - // this calculation, as well as the _system_ dpi scaling. See also - // TermControl::GetProposedDimensions. - // Arguments: - // - - // Return Value: - // - a point containing the requested dimensions in pixels. - winrt::Windows::Foundation::Size AppLogic::GetLaunchDimensions(uint32_t dpi) - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - winrt::Windows::Foundation::Size proposedSize{}; - - const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); - if (const auto layout = _root->LoadPersistedLayout(_settings)) - { - if (layout.InitialSize()) - { - proposedSize = layout.InitialSize().Value(); - // The size is saved as a non-scaled real pixel size, - // so we need to scale it appropriately. - proposedSize.Height = proposedSize.Height * scale; - proposedSize.Width = proposedSize.Width * scale; - } - } - - if (_appArgs.GetSize().has_value() || (proposedSize.Width == 0 && proposedSize.Height == 0)) - { - // Use the default profile to determine how big of a window we need. - const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) }; - - const til::size emptySize{}; - const auto commandlineSize = _appArgs.GetSize().value_or(emptySize); - proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), - dpi, - commandlineSize.width, - commandlineSize.height); - } - - // GH#2061 - If the global setting "Always show tab bar" is - // set or if "Show tabs in title bar" is set, then we'll need to add - // the height of the tab bar here. - if (_settings.GlobalSettings().ShowTabsInTitlebar()) - { - // If we're showing the tabs in the titlebar, we need to use a - // TitlebarControl here to calculate how much space to reserve. - // - // We'll create a fake TitlebarControl, and we'll propose an - // available size to it with Measure(). After Measure() is called, - // the TitlebarControl's DesiredSize will contain the _unscaled_ - // size that the titlebar would like to use. We'll use that as part - // of the height calculation here. - auto titlebar = TitlebarControl{ static_cast(0) }; - titlebar.Measure({ SHRT_MAX, SHRT_MAX }); - proposedSize.Height += (titlebar.DesiredSize().Height) * scale; - } - else if (_settings.GlobalSettings().AlwaysShowTabs()) - { - // Otherwise, let's use a TabRowControl to calculate how much extra - // space we'll need. - // - // Similarly to above, we'll measure it with an arbitrarily large - // available space, to make sure we get all the space it wants. - auto tabControl = TabRowControl(); - tabControl.Measure({ SHRT_MAX, SHRT_MAX }); - - // For whatever reason, there's about 10px of unaccounted-for space - // in the application. I couldn't tell you where these 10px are - // coming from, but they need to be included in this math. - proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; - } - - return proposedSize; - } - - // Method Description: - // - Get the launch mode in json settings file. Now there - // two launch mode: default, maximized. Default means the window - // will launch according to the launch dimensions provided. Maximized - // means the window will launch as a maximized window - // Arguments: - // - - // Return Value: - // - LaunchMode enum that indicates the launch mode - LaunchMode AppLogic::GetLaunchMode() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - // GH#4620/#5801 - If the user passed --maximized or --fullscreen on the - // commandline, then use that to override the value from the settings. - const auto valueFromSettings = _settings.GlobalSettings().LaunchMode(); - const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode(); - if (const auto layout = _root->LoadPersistedLayout(_settings)) - { - if (layout.LaunchMode()) - { - return layout.LaunchMode().Value(); - } - } - return valueFromCommandlineArgs.has_value() ? - valueFromCommandlineArgs.value() : - valueFromSettings; - } - - // Method Description: - // - Get the user defined initial position from Json settings file. - // This position represents the top left corner of the Terminal window. - // This setting is optional, if not provided, we will use the system - // default size, which is provided in IslandWindow::MakeWindow. - // Arguments: - // - defaultInitialX: the system default x coordinate value - // - defaultInitialY: the system default y coordinate value - // Return Value: - // - a point containing the requested initial position in pixels. - TerminalApp::InitialPosition AppLogic::GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY) - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; - - if (const auto layout = _root->LoadPersistedLayout(_settings)) - { - if (layout.InitialPosition()) - { - initialPosition = layout.InitialPosition().Value(); - } - } - - // Commandline args trump everything else - if (_appArgs.GetPosition().has_value()) - { - initialPosition = _appArgs.GetPosition().value(); - } - - return { - initialPosition.X ? initialPosition.X.Value() : defaultInitialX, - initialPosition.Y ? initialPosition.Y.Value() : defaultInitialY - }; - } - - bool AppLogic::CenterOnLaunch() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - // If the position has been specified on the commandline, don't center on launch - return _settings.GlobalSettings().CenterOnLaunch() && !_appArgs.GetPosition().has_value(); - } - - winrt::Windows::UI::Xaml::ElementTheme AppLogic::GetRequestedTheme() - { - return Theme().RequestedTheme(); - } - - bool AppLogic::GetShowTabsInTitlebar() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - return _settings.GlobalSettings().ShowTabsInTitlebar(); - } - - bool AppLogic::GetInitialAlwaysOnTop() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - return _settings.GlobalSettings().AlwaysOnTop(); - } - - // Method Description: - // - See Pane::CalcSnappedDimension - float AppLogic::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const - { - return _root->CalcSnappedDimension(widthOrHeight, dimension); - } - // Method Description: // - Attempt to load the settings. If we fail for any reason, returns an error. // Return Value: @@ -830,6 +263,7 @@ namespace winrt::TerminalApp::implementation _warnings.push_back(newSettings.Warnings().GetAt(i)); } + // TODO! These _settingsAppArgs need to get plumbed into TerminalWindow somehow _hasSettingsStartupActions = false; const auto startupActions = newSettings.GlobalSettings().StartupActions(); if (!startupActions.empty()) @@ -957,18 +391,6 @@ namespace winrt::TerminalApp::implementation } CATCH_LOG() - // Method Description: - // - Update the current theme of the application. This will trigger our - // RequestedThemeChanged event, to have our host change the theme of the - // root of the application. - // Arguments: - // - newTheme: The ElementTheme to apply to our elements. - void AppLogic::_RefreshThemeRoutine() - { - // Propagate the event to the host layer, so it can update its own UI - _RequestedThemeChangedHandlers(*this, Theme()); - } - // Function Description: // Returns the current app package or nullptr. // TRANSITIONAL @@ -1053,10 +475,10 @@ namespace winrt::TerminalApp::implementation } else { - const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); - const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); - return; + // const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); + // const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); + // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); + // return; } } @@ -1067,30 +489,21 @@ namespace winrt::TerminalApp::implementation return; } - if (_settingsLoadedResult == S_FALSE) - { - _ShowLoadWarningsDialog(); - } + // if (_settingsLoadedResult == S_FALSE) + // { + // _ShowLoadWarningsDialog(); + // } // Here, we successfully reloaded the settings, and created a new // TerminalSettings object. - // Update the settings in TerminalPage - _root->SetSettings(_settings, true); - _ApplyLanguageSettingChange(); - _RefreshThemeRoutine(); _ApplyStartupTaskStateChange(); _ProcessLazySettingsChanges(); _SettingsChangedHandlers(*this, nullptr); } - void AppLogic::_OpenSettingsUI() - { - _root->OpenSettingsUI(); - } - // Method Description: // - Returns a pointer to the global shared settings. [[nodiscard]] CascadiaSettings AppLogic::GetSettings() const noexcept @@ -1098,207 +511,6 @@ namespace winrt::TerminalApp::implementation return _settings; } - UIElement AppLogic::GetRoot() noexcept - { - return _root.as(); - } - - // Method Description: - // - Gets the title of the currently focused terminal control. If there - // isn't a control selected for any reason, returns "Terminal" - // Arguments: - // - - // Return Value: - // - the title of the focused control if there is one, else "Terminal" - hstring AppLogic::Title() - { - if (_root) - { - return _root->Title(); - } - return { L"Terminal" }; - } - - // Method Description: - // - Used to tell the app that the titlebar has been clicked. The App won't - // actually receive any clicks in the titlebar area, so this is a helper - // to clue the app in that a click has happened. The App will use this as - // a indicator that it needs to dismiss any open flyouts. - // Arguments: - // - - // Return Value: - // - - void AppLogic::TitlebarClicked() - { - if (_root) - { - _root->TitlebarClicked(); - } - } - - // Method Description: - // - Used to tell the PTY connection that the window visibility has changed. - // The underlying PTY might need to expose window visibility status to the - // client application for the `::GetConsoleWindow()` API. - // Arguments: - // - showOrHide - True is show; false is hide. - // Return Value: - // - - void AppLogic::WindowVisibilityChanged(const bool showOrHide) - { - if (_root) - { - _root->WindowVisibilityChanged(showOrHide); - } - } - - // Method Description: - // - Implements the F7 handler (per GH#638) - // - Implements the Alt handler (per GH#6421) - // Return value: - // - whether the key was handled - bool AppLogic::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) - { - if (_root) - { - // Manually bubble the OnDirectKeyEvent event up through the focus tree. - auto xamlRoot{ _root->XamlRoot() }; - auto focusedObject{ Windows::UI::Xaml::Input::FocusManager::GetFocusedElement(xamlRoot) }; - do - { - if (auto keyListener{ focusedObject.try_as() }) - { - if (keyListener.OnDirectKeyEvent(vkey, scanCode, down)) - { - return true; - } - // otherwise, keep walking. bubble the event manually. - } - - if (auto focusedElement{ focusedObject.try_as() }) - { - focusedObject = focusedElement.Parent(); - - // Parent() seems to return null when the focusedElement is created from an ItemTemplate. - // Use the VisualTreeHelper's GetParent as a fallback. - if (!focusedObject) - { - focusedObject = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(focusedElement); - } - } - else - { - break; // we hit a non-FE object, stop bubbling. - } - } while (focusedObject); - } - return false; - } - - // Method Description: - // - Used to tell the app that the 'X' button has been clicked and - // the user wants to close the app. We kick off the close warning - // experience. - // Arguments: - // - - // Return Value: - // - - void AppLogic::CloseWindow(LaunchPosition pos) - { - if (_root) - { - // If persisted layout is enabled and we are the last window closing - // we should save our state. - if (_root->ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) - { - if (const auto layout = _root->GetWindowLayout()) - { - layout.InitialPosition(pos); - const auto state = ApplicationState::SharedInstance(); - state.PersistedWindowLayouts(winrt::single_threaded_vector({ layout })); - } - } - - _root->CloseWindow(false); - } - } - - winrt::TerminalApp::TaskbarState AppLogic::TaskbarState() - { - if (_root) - { - return _root->TaskbarState(); - } - return {}; - } - - winrt::Windows::UI::Xaml::Media::Brush AppLogic::TitlebarBrush() - { - if (_root) - { - return _root->TitlebarBrush(); - } - return { nullptr }; - } - void AppLogic::WindowActivated(const bool activated) - { - _root->WindowActivated(activated); - } - - bool AppLogic::HasCommandlineArguments() const noexcept - { - return _hasCommandLineArguments; - } - - bool AppLogic::HasSettingsStartupActions() const noexcept - { - return _hasSettingsStartupActions; - } - - // 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 - // will be processed on TerminalPage::Create(). - // - This function will have no effective result after Create() is called. - // - This function returns 0, unless a there was a non-zero result from - // trying to parse one of the commands provided. In that case, no commands - // after the failing command will be parsed, and the non-zero code - // returned. - // Arguments: - // - args: an array of strings to process as a commandline. These args can contain spaces - // Return Value: - // - the result of the first command who's parsing returned a non-zero code, - // or 0. (see AppLogic::_ParseArgs) - int32_t AppLogic::SetStartupCommandline(array_view args) - { - const auto result = _appArgs.ParseArgs(args); - if (result == 0) - { - // If the size of the arguments list is 1, - // then it contains only the executable name and no other arguments. - _hasCommandLineArguments = args.size() > 1; - _appArgs.ValidateStartupCommands(); - if (const auto idx = _appArgs.GetPersistedLayoutIdx()) - { - _root->SetPersistedLayoutIdx(idx.value()); - } - _root->SetStartupActions(_appArgs.GetStartupActions()); - - // Check if we were started as a COM server for inbound connections of console sessions - // coming out of the operating system default application feature. If so, - // tell TerminalPage to start the listener as we have to make sure it has the chance - // to register a handler to hear about the requests first and is all ready to receive - // them before the COM server registers itself. Otherwise, the request might come - // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs.IsHandoffListener()) - { - _root->SetInboundListener(true); - } - } - - return result; - } - // Method Description: // - Triggers the setup of the listener for incoming console connections // from the operating system. @@ -1308,43 +520,27 @@ namespace winrt::TerminalApp::implementation // - void AppLogic::SetInboundListener() { - _root->SetInboundListener(false); + // TODO! + // _root->SetInboundListener(false); } - // Method Description: - // - Parse the provided commandline arguments into actions, and try to - // perform them immediately. - // - This function returns 0, unless a there was a non-zero result from - // trying to parse one of the commands provided. In that case, no commands - // after the failing command will be parsed, and the non-zero code - // returned. - // - If a non-empty cwd is provided, the entire terminal exe will switch to - // that CWD while we handle these actions, then return to the original - // CWD. - // Arguments: - // - args: an array of strings to process as a commandline. These args can contain spaces - // - cwd: The directory to use as the CWD while performing these actions. - // Return Value: - // - the result of the first command who's parsing returned a non-zero code, - // or 0. (see AppLogic::_ParseArgs) - int32_t AppLogic::ExecuteCommandline(array_view args, - const winrt::hstring& cwd) + bool AppLogic::ShouldImmediatelyHandoffToElevated() { - ::TerminalApp::AppCommandlineArgs appArgs; - auto result = appArgs.ParseArgs(args); - if (result == 0) - { - auto actions = winrt::single_threaded_vector(std::move(appArgs.GetStartupActions())); + // TODO! Merge that code in here. + // * Probably need to pass in the startupActions that the first window is being started with + // * Or like, a reference to the first TerminalWindow object, or something - _root->ProcessStartupActions(actions, false, cwd); + // return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; + return false; + } + void AppLogic::HandoffToElevated() + { + // TODO! Merge that code in here. - if (appArgs.IsHandoffListener()) - { - _root->SetInboundListener(true); - } - } - // Return the result of parsing with commandline, though it may or may not be used. - return result; + // if (_root) + // { + // _root->HandoffToElevated(_settings); + // } } // Method Description: @@ -1468,228 +664,13 @@ namespace winrt::TerminalApp::implementation return winrt::make(WindowingBehaviorUseNew); } - // Method Description: - // - If there were any errors parsing the commandline that was used to - // initialize the terminal, this will return a string containing that - // message. If there were no errors, this message will be blank. - // - If the user requested help on any command (using --help), this will - // contain the help message. - // - If the user requested the version number (using --version), this will - // contain the version string. - // Arguments: - // - - // Return Value: - // - the help text or error message for the provided commandline, if one - // exists, otherwise the empty string. - winrt::hstring AppLogic::ParseCommandlineMessage() - { - return winrt::to_hstring(_appArgs.GetExitMessage()); - } - - // Method Description: - // - Returns true if we should exit the application before even starting the - // window. We might want to do this if we're displaying an error message or - // the version string, or if we want to open the settings file. - // Arguments: - // - - // Return Value: - // - true iff we should exit the application before even starting the window - bool AppLogic::ShouldExitEarly() - { - return _appArgs.ShouldExitEarly(); - } - - bool AppLogic::FocusMode() const - { - return _root ? _root->FocusMode() : false; - } - - bool AppLogic::Fullscreen() const - { - return _root ? _root->Fullscreen() : false; - } - - void AppLogic::Maximized(bool newMaximized) - { - if (_root) - { - _root->Maximized(newMaximized); - } - } - - bool AppLogic::AlwaysOnTop() const - { - return _root ? _root->AlwaysOnTop() : false; - } - - bool AppLogic::AutoHideWindow() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - return _settings.GlobalSettings().AutoHideWindow(); - } - Windows::Foundation::Collections::IMapView AppLogic::GlobalHotkeys() { return _settings.GlobalSettings().ActionMap().GlobalHotkeys(); } - bool AppLogic::ShouldUsePersistedLayout() - { - return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; - } - - bool AppLogic::ShouldImmediatelyHandoffToElevated() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; - } - void AppLogic::HandoffToElevated() - { - if (_root) - { - _root->HandoffToElevated(_settings); - } - } - - 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))); - } - - hstring AppLogic::GetWindowLayoutJson(LaunchPosition position) - { - if (_root != nullptr) - { - if (const auto layout = _root->GetWindowLayout()) - { - layout.InitialPosition(position); - return WindowLayout::ToJson(layout); - } - } - return L""; - } - - void AppLogic::IdentifyWindow() - { - if (_root) - { - _root->IdentifyWindow(); - } - } - - winrt::hstring AppLogic::WindowName() - { - return _root ? _root->WindowName() : L""; - } - void AppLogic::WindowName(const winrt::hstring& name) - { - if (_root) - { - _root->WindowName(name); - } - } - uint64_t AppLogic::WindowId() - { - return _root ? _root->WindowId() : 0; - } - void AppLogic::WindowId(const uint64_t& id) - { - if (_root) - { - _root->WindowId(id); - } - } - - void AppLogic::SetPersistedLayoutIdx(const uint32_t idx) - { - if (_root) - { - _root->SetPersistedLayoutIdx(idx); - } - } - - void AppLogic::SetNumberOfOpenWindows(const uint64_t num) - { - _numOpenWindows = num; - if (_root) - { - _root->SetNumberOfOpenWindows(num); - } - } - - void AppLogic::RenameFailed() - { - if (_root) - { - _root->RenameFailed(); - } - } - - bool AppLogic::IsQuakeWindow() const noexcept - { - return _root->IsQuakeWindow(); - } - - void AppLogic::RequestExitFullscreen() - { - _root->SetFullscreen(false); - } - - bool AppLogic::GetMinimizeToNotificationArea() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - return _settings.GlobalSettings().MinimizeToNotificationArea(); - } - - bool AppLogic::GetAlwaysShowNotificationIcon() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - return _settings.GlobalSettings().AlwaysShowNotificationIcon(); - } - - bool AppLogic::GetShowTitleInTitlebar() - { - return _settings.GlobalSettings().ShowTitleInTitlebar(); - } - Microsoft::Terminal::Settings::Model::Theme AppLogic::Theme() { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } return _settings.GlobalSettings().CurrentTheme(); } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 5534979b973..b6a3a8b6ba9 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -5,10 +5,10 @@ #include "AppLogic.g.h" #include "FindTargetWindowResult.g.h" -#include "SystemMenuChangeArgs.g.h" + #include "Jumplist.h" #include "LanguageProfileNotifier.h" -#include "TerminalPage.h" +#include "AppCommandlineArgs.h" #include #include @@ -36,18 +36,7 @@ namespace winrt::TerminalApp::implementation FindTargetWindowResult(id, L""){}; }; - struct SystemMenuChangeArgs : SystemMenuChangeArgsT - { - WINRT_PROPERTY(winrt::hstring, Name, L""); - WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add); - WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr); - - public: - SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) : - _Name{ name }, _Action{ action }, _Handler{ handler } {}; - }; - - struct AppLogic : AppLogicT + struct AppLogic : AppLogicT { public: static AppLogic* Current() noexcept; @@ -56,8 +45,6 @@ namespace winrt::TerminalApp::implementation AppLogic(); ~AppLogic() = default; - STDMETHODIMP Initialize(HWND hwnd); - void Create(); bool IsUwp() const noexcept; void RunAsUwp(); @@ -66,114 +53,36 @@ namespace winrt::TerminalApp::implementation [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; - void Quit(); - - bool HasCommandlineArguments() const noexcept; - bool HasSettingsStartupActions() const noexcept; - int32_t SetStartupCommandline(array_view actions); - int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); TerminalApp::FindTargetWindowResult FindTargetWindow(array_view actions); - winrt::hstring ParseCommandlineMessage(); - bool ShouldExitEarly(); - - bool FocusMode() const; - bool Fullscreen() const; - void Maximized(bool newMaximized); - bool AlwaysOnTop() const; - bool AutoHideWindow(); - - bool ShouldUsePersistedLayout(); bool ShouldImmediatelyHandoffToElevated(); void HandoffToElevated(); - hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); - void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); - void IdentifyWindow(); - void RenameFailed(); - winrt::hstring WindowName(); - void WindowName(const winrt::hstring& name); - uint64_t WindowId(); - void WindowId(const uint64_t& id); - void SetPersistedLayoutIdx(const uint32_t idx); - void SetNumberOfOpenWindows(const uint64_t num); - bool IsQuakeWindow() const noexcept; - void RequestExitFullscreen(); - - Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); - bool CenterOnLaunch(); - TerminalApp::InitialPosition GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY); - winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme(); - Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode(); - bool GetShowTabsInTitlebar(); - bool GetInitialAlwaysOnTop(); - float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; - - Windows::UI::Xaml::UIElement GetRoot() noexcept; void SetInboundListener(); - hstring Title(); - void TitlebarClicked(); - bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); - - void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position); - void WindowVisibilityChanged(const bool showOrHide); - - winrt::TerminalApp::TaskbarState TaskbarState(); - winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush(); - void WindowActivated(const bool activated); - - bool GetMinimizeToNotificationArea(); - bool GetAlwaysShowNotificationIcon(); - bool GetShowTitleInTitlebar(); - - winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); - void DismissDialog(); - Windows::Foundation::Collections::IMapView GlobalHotkeys(); Microsoft::Terminal::Settings::Model::Theme Theme(); - // -------------------------------- WinRT Events --------------------------------- - // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. - // Usually we'd just do - // WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - // - // But what we're doing here is exposing the Page's PropertyChanged _as - // our own event_. It's a FORWARDED_CALLBACK, essentially. - winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); } - void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); } - - TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme); TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); private: bool _isUwp{ false }; bool _isElevated{ false }; - // If you add controls here, but forget to null them either here or in - // the ctor, you're going to have a bad time. It'll mysteriously fail to - // activate the AppLogic. - // ALSO: If you add any UIElements as roots here, make sure they're - // updated in _ApplyTheme. The root currently is _root. - winrt::com_ptr _root{ nullptr }; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; winrt::hstring _settingsLoadExceptionText; HRESULT _settingsLoadedResult = S_OK; bool _loadedInitialSettings = false; - uint64_t _numOpenWindows{ 0 }; - - std::shared_mutex _dialogLock; - winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog; - - ::TerminalApp::AppCommandlineArgs _appArgs; + bool _hasSettingsStartupActions{ false }; ::TerminalApp::AppCommandlineArgs _settingsAppArgs; std::shared_ptr> _reloadSettings; til::throttled_func_trailing<> _reloadState; + std::vector _warnings; + // These fields invoke _reloadSettings and must be destroyed before _reloadSettings. // (C++ destroys members in reverse-declaration-order.) winrt::com_ptr _languageProfileNotifier; @@ -182,46 +91,13 @@ namespace winrt::TerminalApp::implementation static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view args, const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior); - void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); - void _ShowLoadWarningsDialog(); - bool _IsKeyboardServiceEnabled(); - void _ApplyLanguageSettingChange() noexcept; - void _RefreshThemeRoutine(); fire_and_forget _ApplyStartupTaskStateChange(); - void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); - [[nodiscard]] HRESULT _TryLoadSettings() noexcept; void _ProcessLazySettingsChanges(); void _RegisterSettingsChange(); fire_and_forget _DispatchReloadSettings(); - void _OpenSettingsUI(); - - bool _hasCommandLineArguments{ false }; - bool _hasSettingsStartupActions{ false }; - std::vector _warnings; - - // These are events that are handled by the TerminalPage, but are - // exposed through the AppLogic. This macro is used to forward the event - // directly to them. - 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(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); - FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged); - FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell); - FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress); - FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested); - FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested); - FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged); - FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested); - FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested); - FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu); - FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested); - FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged); #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index b7a92ea1103..bc48cf5a965 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -1,41 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import "TerminalPage.idl"; -import "ShortcutActionDispatch.idl"; -import "IDirectKeyListener.idl"; - namespace TerminalApp { - struct InitialPosition - { - Int64 X; - Int64 Y; - }; - [default_interface] runtimeclass FindTargetWindowResult { Int32 WindowId { get; }; String WindowName { get; }; }; - delegate void SystemMenuItemHandler(); - - enum SystemMenuChangeAction - { - Add = 0, - Remove = 1 - }; - - [default_interface] runtimeclass SystemMenuChangeArgs { - String Name { get; }; - SystemMenuChangeAction Action { get; }; - SystemMenuItemHandler Handler { get; }; - }; - // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. - [default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged + [default_interface] runtimeclass AppLogic { AppLogic(); @@ -50,94 +26,17 @@ namespace TerminalApp void RunAsUwp(); Boolean IsElevated(); - Boolean HasCommandlineArguments(); - Boolean HasSettingsStartupActions(); - Int32 SetStartupCommandline(String[] commands); - Int32 ExecuteCommandline(String[] commands, String cwd); - String ParseCommandlineMessage { get; }; - Boolean ShouldExitEarly { get; }; - - void Quit(); - void ReloadSettings(); - Windows.UI.Xaml.UIElement GetRoot(); void SetInboundListener(); - String Title { get; }; - - Boolean FocusMode { get; }; - Boolean Fullscreen { get; }; - void Maximized(Boolean newMaximized); - Boolean AlwaysOnTop { get; }; - Boolean AutoHideWindow { get; }; - - void IdentifyWindow(); - String WindowName; - UInt64 WindowId; - void SetPersistedLayoutIdx(UInt32 idx); - void SetNumberOfOpenWindows(UInt64 num); - void RenameFailed(); - void RequestExitFullscreen(); - Boolean IsQuakeWindow(); - - Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); - Boolean CenterOnLaunch { get; }; - - InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY); - Windows.UI.Xaml.ElementTheme GetRequestedTheme(); - Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode(); - Boolean GetShowTabsInTitlebar(); - Boolean GetInitialAlwaysOnTop(); - Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension); - void TitlebarClicked(); - void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position); - void WindowVisibilityChanged(Boolean showOrHide); - - TaskbarState TaskbarState{ get; }; - Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; - void WindowActivated(Boolean activated); - - Boolean ShouldUsePersistedLayout(); - Boolean ShouldImmediatelyHandoffToElevated(); - void HandoffToElevated(); - String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); - void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); - - Boolean GetMinimizeToNotificationArea(); - Boolean GetAlwaysShowNotificationIcon(); - Boolean GetShowTitleInTitlebar(); - Microsoft.Terminal.Settings.Model.Theme Theme { get; }; FindTargetWindowResult FindTargetWindow(String[] args); Windows.Foundation.Collections.IMapView GlobalHotkeys(); - // See IDialogPresenter and TerminalPage's DialogPresenter for more - // information. - Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); - void DismissDialog(); - - event Windows.Foundation.TypedEventHandler SetTitleBarContent; - event Windows.Foundation.TypedEventHandler TitleChanged; - event Windows.Foundation.TypedEventHandler LastTabClosed; - event Windows.Foundation.TypedEventHandler RequestedThemeChanged; - event Windows.Foundation.TypedEventHandler FocusModeChanged; - event Windows.Foundation.TypedEventHandler FullscreenChanged; - event Windows.Foundation.TypedEventHandler ChangeMaximizeRequested; - event Windows.Foundation.TypedEventHandler AlwaysOnTopChanged; - event Windows.Foundation.TypedEventHandler RaiseVisualBell; - event Windows.Foundation.TypedEventHandler SetTaskbarProgress; - event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; - event Windows.Foundation.TypedEventHandler RenameWindowRequested; event Windows.Foundation.TypedEventHandler SettingsChanged; - event Windows.Foundation.TypedEventHandler IsQuakeWindowChanged; - event Windows.Foundation.TypedEventHandler SummonWindowRequested; - event Windows.Foundation.TypedEventHandler CloseRequested; - event Windows.Foundation.TypedEventHandler OpenSystemMenu; - event Windows.Foundation.TypedEventHandler QuitRequested; - event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; - event Windows.Foundation.TypedEventHandler ShowWindowChanged; + } } diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 6123f0de1c3..7fb88842a1c 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -138,6 +138,9 @@ AppLogic.idl + + TerminalWindow.idl + @@ -231,6 +234,9 @@ AppLogic.idl + + TerminalWindow.idl + @@ -252,6 +258,7 @@ + MinMaxCloseControl.xaml Code diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp new file mode 100644 index 00000000000..0b1c9e27632 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -0,0 +1,1140 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TerminalWindow.h" +#include "../inc/WindowingBehavior.h" +#include "TerminalWindow.g.cpp" + +#include +#include +#include + +#include "../../types/inc/utils.hpp" + +using namespace winrt::Windows::ApplicationModel; +using namespace winrt::Windows::ApplicationModel::DataTransfer; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::System; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Microsoft::Terminal::Control; +using namespace winrt::Microsoft::Terminal::Settings::Model; +using namespace ::TerminalApp; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} + +// clang-format off +// !!! IMPORTANT !!! +// Make sure that these keys are in the same order as the +// SettingsLoadWarnings/Errors enum is! +static const std::array settingsLoadWarningsLabels { + USES_RESOURCE(L"MissingDefaultProfileText"), + USES_RESOURCE(L"DuplicateProfileText"), + USES_RESOURCE(L"UnknownColorSchemeText"), + USES_RESOURCE(L"InvalidBackgroundImage"), + USES_RESOURCE(L"InvalidIcon"), + USES_RESOURCE(L"AtLeastOneKeybindingWarning"), + USES_RESOURCE(L"TooManyKeysForChord"), + USES_RESOURCE(L"MissingRequiredParameter"), + USES_RESOURCE(L"FailedToParseCommandJson"), + USES_RESOURCE(L"FailedToWriteToSettings"), + USES_RESOURCE(L"InvalidColorSchemeInCmd"), + USES_RESOURCE(L"InvalidSplitSize"), + USES_RESOURCE(L"FailedToParseStartupActions"), + USES_RESOURCE(L"FailedToParseSubCommands"), + USES_RESOURCE(L"UnknownTheme"), + USES_RESOURCE(L"DuplicateRemainingProfilesEntry"), +}; +static const std::array settingsLoadErrorsLabels { + USES_RESOURCE(L"NoProfilesText"), + USES_RESOURCE(L"AllProfilesHiddenText") +}; +// clang-format on + +static_assert(settingsLoadWarningsLabels.size() == static_cast(SettingsLoadWarnings::WARNINGS_SIZE)); +static_assert(settingsLoadErrorsLabels.size() == static_cast(SettingsLoadErrors::ERRORS_SIZE)); + +// Function Description: +// - General-purpose helper for looking up a localized string for a +// warning/error. First will look for the given key in the provided map of +// keys->strings, where the values in the map are ResourceKeys. If it finds +// one, it will lookup the localized string from that ResourceKey. +// - If it does not find a key, it'll return an empty string +// Arguments: +// - key: the value to use to look for a resource key in the given map +// - map: A map of keys->Resource keys. +// Return Value: +// - the localized string for the given type, if it exists. +template +winrt::hstring _GetMessageText(uint32_t index, const T& keys) +{ + if (index < keys.size()) + { + return GetLibraryResourceString(til::at(keys, index)); + } + return {}; +} + +// Function Description: +// - Gets the text from our ResourceDictionary for the given +// SettingsLoadWarning. If there is no such text, we'll return nullptr. +// - The warning should have an entry in settingsLoadWarningsLabels. +// Arguments: +// - warning: the SettingsLoadWarnings value to get the localized text for. +// Return Value: +// - localized text for the given warning +static winrt::hstring _GetWarningText(SettingsLoadWarnings warning) +{ + return _GetMessageText(static_cast(warning), settingsLoadWarningsLabels); +} + +// // Function Description: +// // - Gets the text from our ResourceDictionary for the given +// // SettingsLoadError. If there is no such text, we'll return nullptr. +// // - The warning should have an entry in settingsLoadErrorsLabels. +// // Arguments: +// // - error: the SettingsLoadErrors value to get the localized text for. +// // Return Value: +// // - localized text for the given error +// static winrt::hstring _GetErrorText(SettingsLoadErrors error) +// { +// return _GetMessageText(static_cast(error), settingsLoadErrorsLabels); +// } + +// Function Description: +// - Creates a Run of text to display an error message. The text is yellow or +// red for dark/light theme, respectively. +// Arguments: +// - text: The text of the error message. +// - resources: The application's resource loader. +// Return Value: +// - The fully styled text run. +static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceDictionary& resources) +{ + Documents::Run textRun; + textRun.Text(text); + + // Color the text red (light theme) or yellow (dark theme) based on the system theme + auto key = winrt::box_value(L"ErrorTextBrush"); + if (resources.HasKey(key)) + { + auto g = resources.Lookup(key); + auto brush = g.try_as(); + textRun.Foreground(brush); + } + + return textRun; +} + +namespace winrt::TerminalApp::implementation +{ + TerminalWindow::TerminalWindow(const CascadiaSettings& settings) : + _settings{ settings } + { + // 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, + // then it might look like App just failed to activate, which will + // cause you to chase down the rabbit hole of "why is App not + // registered?" when it definitely is. + + // The TerminalPage has to be constructed during our construction, to + // make sure that there's a terminal page for callers of + // SetTitleBarContent + _isElevated = ::Microsoft::Console::Utils::IsElevated(); + _root = winrt::make_self(); + } + + // Method Description: + // - Implements the IInitializeWithWindow interface from shobjidl_core. + HRESULT TerminalWindow::Initialize(HWND hwnd) + { + _root = winrt::make_self(); + _dialog = ContentDialog{}; + return _root->Initialize(hwnd); + } + // Method Description: + // - Called around the codebase to discover if this is a UWP where we need to turn off specific settings. + // Arguments: + // - - reports internal state + // Return Value: + // - True if UWP, false otherwise. + bool TerminalWindow::IsUwp() const noexcept + { + return _isUwp; + } + + // Method Description: + // - Called around the codebase to discover if Terminal is running elevated + // Arguments: + // - - reports internal state + // Return Value: + // - True if elevated, false otherwise. + bool TerminalWindow::IsElevated() const noexcept + { + return _isElevated; + } + + // Method Description: + // - Called by UWP context invoker to let us know that we may have to change some of our behaviors + // for being a UWP + // Arguments: + // - (sets to UWP = true, one way change) + // Return Value: + // - + void TerminalWindow::RunAsUwp() + { + _isUwp = true; + } + + // Method Description: + // - Build the UI for the terminal app. Before this method is called, it + // should not be assumed that the TerminalApp is usable. The Settings + // should be loaded before this is called, either with LoadSettings or + // GetLaunchDimensions (which will call LoadSettings) + // Arguments: + // - + // Return Value: + // - + void TerminalWindow::Create() + { + _root->DialogPresenter(*this); + + // In UWP mode, we cannot handle taking over the title bar for tabs, + // so this setting is overridden to false no matter what the preference is. + if (_isUwp) + { + _settings.GlobalSettings().ShowTabsInTitlebar(false); + } + + // TODO! handle startupActions + // + //// Pay attention, that even if some command line arguments were parsed (like launch mode), + //// we will not use the startup actions from settings. + //// While this simplifies the logic, we might want to reconsider this behavior in the future. + //if (!_hasCommandLineArguments && _hasSettingsStartupActions) + //{ + // _root->SetStartupActions(_settingsAppArgs.GetStartupActions()); + //} + + _root->SetSettings(_settings, false); + _root->Loaded({ this, &TerminalWindow::_OnLoaded }); + _root->Initialized([this](auto&&, auto&&) { + // GH#288 - When we finish initialization, if the user wanted us + // launched _fullscreen_, toggle fullscreen mode. This will make sure + // that the window size is _first_ set up as something sensible, so + // leaving fullscreen returns to a reasonable size. + const auto launchMode = this->GetLaunchMode(); + if (IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) + { + _root->SetFocusMode(true); + } + + // The IslandWindow handles (creating) the maximized state + // we just want to record it here on the page as well. + if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode)) + { + _root->Maximized(true); + } + + if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow()) + { + _root->SetFullscreen(true); + } + }); + _root->Create(); + + _RefreshThemeRoutine(); + + auto args = winrt::make_self(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &TerminalWindow::_OpenSettingsUI)); + _SystemMenuChangeRequestedHandlers(*this, *args); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "WindowCreated", + TraceLoggingDescription("Event emitted when the window is started"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + } + void TerminalWindow::Quit() + { + if (_root) + { + _root->CloseWindow(true); + } + } + + winrt::Windows::UI::Xaml::ElementTheme TerminalWindow::GetRequestedTheme() + { + return Theme().RequestedTheme(); + } + + bool TerminalWindow::GetShowTabsInTitlebar() + { + return _settings.GlobalSettings().ShowTabsInTitlebar(); + } + + bool TerminalWindow::GetInitialAlwaysOnTop() + { + return _settings.GlobalSettings().AlwaysOnTop(); + } + + bool TerminalWindow::GetMinimizeToNotificationArea() + { + return _settings.GlobalSettings().MinimizeToNotificationArea(); + } + + bool TerminalWindow::GetAlwaysShowNotificationIcon() + { + return _settings.GlobalSettings().AlwaysShowNotificationIcon(); + } + + bool TerminalWindow::GetShowTitleInTitlebar() + { + return _settings.GlobalSettings().ShowTitleInTitlebar(); + } + + Microsoft::Terminal::Settings::Model::Theme TerminalWindow::Theme() + { + return _settings.GlobalSettings().CurrentTheme(); + } + // Method Description: + // - Show a ContentDialog with buttons to take further action. Uses the + // FrameworkElements provided as the title and content of this dialog, and + // displays buttons (or a single button). Two buttons (primary and secondary) + // will be displayed if this is an warning dialog for closing the terminal, + // this allows the users to abandon the closing action. Otherwise, a single + // close button will be displayed. + // - Only one dialog can be visible at a time. If another dialog is visible + // when this is called, nothing happens. + // Arguments: + // - dialog: the dialog object that is going to show up + // Return value: + // - an IAsyncOperation with the dialog result + winrt::Windows::Foundation::IAsyncOperation TerminalWindow::ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog) + { + // DON'T release this lock in a wil::scope_exit. The scope_exit will get + // called when we await, which is not what we want. + std::unique_lock lock{ _dialogLock, std::try_to_lock }; + if (!lock) + { + // Another dialog is visible. + co_return ContentDialogResult::None; + } + + _dialog = dialog; + + // IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs. + // Since we're hosting the dialog in a Xaml island, we need to connect it to the + // xaml tree somehow. + dialog.XamlRoot(_root->XamlRoot()); + + // IMPORTANT: Set the requested theme of the dialog, because the + // PopupRoot isn't directly in the Xaml tree of our root. So the dialog + // won't inherit our RequestedTheme automagically. + // GH#5195, GH#3654 Because we cannot set RequestedTheme at the application level, + // we occasionally run into issues where parts of our UI end up themed incorrectly. + // Dialogs, for example, live under a different Xaml root element than the rest of + // our application. This makes our popup menus and buttons "disappear" when the + // user wants Terminal to be in a different theme than the rest of the system. + // This hack---and it _is_ a hack--walks up a dialog's ancestry and forces the + // theme on each element up to the root. We're relying a bit on Xaml's implementation + // details here, but it does have the desired effect. + // It's not enough to set the theme on the dialog alone. + auto themingLambda{ [this](const Windows::Foundation::IInspectable& sender, const RoutedEventArgs&) { + auto theme{ _settings.GlobalSettings().CurrentTheme() }; + auto requestedTheme{ theme.RequestedTheme() }; + auto element{ sender.try_as() }; + while (element) + { + element.RequestedTheme(requestedTheme); + element = element.Parent().try_as(); + } + } }; + + themingLambda(dialog, nullptr); // if it's already in the tree + auto loadedRevoker{ dialog.Loaded(winrt::auto_revoke, themingLambda) }; // if it's not yet in the tree + + // Display the dialog. + co_return co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); + + // After the dialog is dismissed, the dialog lock (held by `lock`) will + // be released so another can be shown + } + + // Method Description: + // - Dismiss the (only) visible ContentDialog + void TerminalWindow::DismissDialog() + { + if (auto localDialog = std::exchange(_dialog, nullptr)) + { + localDialog.Hide(); + } + } + + // Method Description: + // - Displays a dialog for errors found while loading or validating the + // settings. Uses the resources under the provided title and content keys + // as the title and first content of the dialog, then also displays a + // message for whatever exception was found while validating the settings. + // - Only one dialog can be visible at a time. If another dialog is visible + // when this is called, nothing happens. See ShowDialog for details + // Arguments: + // - titleKey: The key to use to lookup the title text from our resources. + // - contentKey: The key to use to lookup the content text from our resources. + void TerminalWindow::_ShowLoadErrorsDialog(const winrt::hstring& titleKey, + const winrt::hstring& contentKey, + HRESULT settingsLoadedResult) + { + auto title = GetLibraryResourceString(titleKey); + auto buttonText = RS_(L"Ok"); + + Controls::TextBlock warningsTextBlock; + // Make sure you can copy-paste + warningsTextBlock.IsTextSelectionEnabled(true); + // Make sure the lines of text wrap + warningsTextBlock.TextWrapping(TextWrapping::Wrap); + + winrt::Windows::UI::Xaml::Documents::Run errorRun; + const auto errorLabel = GetLibraryResourceString(contentKey); + errorRun.Text(errorLabel); + warningsTextBlock.Inlines().Append(errorRun); + warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + + if (FAILED(settingsLoadedResult)) + { + // TODO! _settingsLoadExceptionText needs to get into the TerminalWindow somehow + + // if (!_settingsLoadExceptionText.empty()) + // { + // warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); + // warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + // } + } + + // Add a note that we're using the default settings in this case. + winrt::Windows::UI::Xaml::Documents::Run usingDefaultsRun; + const auto usingDefaultsText = RS_(L"UsingDefaultSettingsText"); + usingDefaultsRun.Text(usingDefaultsText); + warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + warningsTextBlock.Inlines().Append(usingDefaultsRun); + + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(title)); + dialog.Content(winrt::box_value(warningsTextBlock)); + dialog.CloseButtonText(buttonText); + dialog.DefaultButton(Controls::ContentDialogButton::Close); + + ShowDialog(dialog); + } + + // Method Description: + // - Displays a dialog for warnings found while loading or validating the + // settings. Displays messages for whatever warnings were found while + // validating the settings. + // - Only one dialog can be visible at a time. If another dialog is visible + // when this is called, nothing happens. See ShowDialog for details + void TerminalWindow::_ShowLoadWarningsDialog() + { + auto title = RS_(L"SettingsValidateErrorTitle"); + auto buttonText = RS_(L"Ok"); + + Controls::TextBlock warningsTextBlock; + // Make sure you can copy-paste + warningsTextBlock.IsTextSelectionEnabled(true); + // Make sure the lines of text wrap + warningsTextBlock.TextWrapping(TextWrapping::Wrap); + + // TODO! warnings need to get into here somehow + // + // for (const auto& warning : _warnings) + // { + // // Try looking up the warning message key for each warning. + // const auto warningText = _GetWarningText(warning); + // if (!warningText.empty()) + // { + // warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); + // warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + // } + // } + + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(title)); + dialog.Content(winrt::box_value(warningsTextBlock)); + dialog.CloseButtonText(buttonText); + dialog.DefaultButton(Controls::ContentDialogButton::Close); + + ShowDialog(dialog); + } + + // Method Description: + // - Triggered when the application is finished loading. If we failed to load + // the settings, then this will display the error dialog. This is done + // here instead of when loading the settings, because we need our UI to be + // visible to display the dialog, and when we're loading the settings, + // the UI might not be visible yet. + // Arguments: + // - + void TerminalWindow::_OnLoaded(const IInspectable& /*sender*/, + const RoutedEventArgs& /*eventArgs*/) + { + if (_settings.GlobalSettings().InputServiceWarning()) + { + const auto keyboardServiceIsDisabled = !_IsKeyboardServiceEnabled(); + if (keyboardServiceIsDisabled) + { + _root->ShowKeyboardServiceWarning(); + } + } + + // TODO! Yea, more of "how do we get _settingsLoadedResult / warnings / error" into here? + // + // if (FAILED(_settingsLoadedResult)) + // { + // const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle"); + // const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText"); + // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); + // } + // else if (_settingsLoadedResult == S_FALSE) + // { + // _ShowLoadWarningsDialog(); + // } + } + + // Method Description: + // - Helper for determining if the "Touch Keyboard and Handwriting Panel + // Service" is enabled. If it isn't, we want to be able to display a + // warning to the user, because they won't be able to type in the + // Terminal. + // Return Value: + // - true if the service is enabled, or if we fail to query the service. We + // return true in that case, to be less noisy (though, that is unexpected) + bool TerminalWindow::_IsKeyboardServiceEnabled() + { + if (IsUwp()) + { + return true; + } + + // If at any point we fail to open the service manager, the service, + // etc, then just quick return true to disable the dialog. We'd rather + // not be noisy with this dialog if we failed for some reason. + + // Open the service manager. This will return 0 if it failed. + wil::unique_schandle hManager{ OpenSCManagerW(nullptr, nullptr, 0) }; + + if (LOG_LAST_ERROR_IF(!hManager.is_valid())) + { + return true; + } + + // Get a handle to the keyboard service + wil::unique_schandle hService{ OpenServiceW(hManager.get(), TabletInputServiceKey.data(), SERVICE_QUERY_STATUS) }; + + // Windows 11 doesn't have a TabletInputService. + // (It was renamed to TextInputManagementService, because people kept thinking that a + // service called "tablet-something" is system-irrelevant on PCs and can be disabled.) + if (!hService.is_valid()) + { + return true; + } + + // Get the current state of the service + SERVICE_STATUS status{ 0 }; + if (!LOG_IF_WIN32_BOOL_FALSE(QueryServiceStatus(hService.get(), &status))) + { + return true; + } + + const auto state = status.dwCurrentState; + return (state == SERVICE_RUNNING || state == SERVICE_START_PENDING); + } + + // Method Description: + // - Get the size in pixels of the client area we'll need to launch this + // terminal app. This method will use the default profile's settings to do + // this calculation, as well as the _system_ dpi scaling. See also + // TermControl::GetProposedDimensions. + // Arguments: + // - + // Return Value: + // - a point containing the requested dimensions in pixels. + winrt::Windows::Foundation::Size TerminalWindow::GetLaunchDimensions(uint32_t dpi) + { + winrt::Windows::Foundation::Size proposedSize{}; + + const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); + if (const auto layout = _root->LoadPersistedLayout(_settings)) + { + if (layout.InitialSize()) + { + proposedSize = layout.InitialSize().Value(); + // The size is saved as a non-scaled real pixel size, + // so we need to scale it appropriately. + proposedSize.Height = proposedSize.Height * scale; + proposedSize.Width = proposedSize.Width * scale; + } + } + + if (_appArgs.GetSize().has_value() || (proposedSize.Width == 0 && proposedSize.Height == 0)) + { + // Use the default profile to determine how big of a window we need. + const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) }; + + const til::size emptySize{}; + const auto commandlineSize = _appArgs.GetSize().value_or(emptySize); + proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), + dpi, + commandlineSize.width, + commandlineSize.height); + } + + // GH#2061 - If the global setting "Always show tab bar" is + // set or if "Show tabs in title bar" is set, then we'll need to add + // the height of the tab bar here. + if (_settings.GlobalSettings().ShowTabsInTitlebar()) + { + // If we're showing the tabs in the titlebar, we need to use a + // TitlebarControl here to calculate how much space to reserve. + // + // We'll create a fake TitlebarControl, and we'll propose an + // available size to it with Measure(). After Measure() is called, + // the TitlebarControl's DesiredSize will contain the _unscaled_ + // size that the titlebar would like to use. We'll use that as part + // of the height calculation here. + auto titlebar = TitlebarControl{ static_cast(0) }; + titlebar.Measure({ SHRT_MAX, SHRT_MAX }); + proposedSize.Height += (titlebar.DesiredSize().Height) * scale; + } + else if (_settings.GlobalSettings().AlwaysShowTabs()) + { + // Otherwise, let's use a TabRowControl to calculate how much extra + // space we'll need. + // + // Similarly to above, we'll measure it with an arbitrarily large + // available space, to make sure we get all the space it wants. + auto tabControl = TabRowControl(); + tabControl.Measure({ SHRT_MAX, SHRT_MAX }); + + // For whatever reason, there's about 10px of unaccounted-for space + // in the application. I couldn't tell you where these 10px are + // coming from, but they need to be included in this math. + proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; + } + + return proposedSize; + } + + // Method Description: + // - Get the launch mode in json settings file. Now there + // two launch mode: default, maximized. Default means the window + // will launch according to the launch dimensions provided. Maximized + // means the window will launch as a maximized window + // Arguments: + // - + // Return Value: + // - LaunchMode enum that indicates the launch mode + LaunchMode TerminalWindow::GetLaunchMode() + { + // GH#4620/#5801 - If the user passed --maximized or --fullscreen on the + // commandline, then use that to override the value from the settings. + const auto valueFromSettings = _settings.GlobalSettings().LaunchMode(); + const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode(); + if (const auto layout = _root->LoadPersistedLayout(_settings)) + { + if (layout.LaunchMode()) + { + return layout.LaunchMode().Value(); + } + } + return valueFromCommandlineArgs.has_value() ? + valueFromCommandlineArgs.value() : + valueFromSettings; + } + + // Method Description: + // - Get the user defined initial position from Json settings file. + // This position represents the top left corner of the Terminal window. + // This setting is optional, if not provided, we will use the system + // default size, which is provided in IslandWindow::MakeWindow. + // Arguments: + // - defaultInitialX: the system default x coordinate value + // - defaultInitialY: the system default y coordinate value + // Return Value: + // - a point containing the requested initial position in pixels. + TerminalApp::InitialPosition TerminalWindow::GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY) + { + auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; + + if (const auto layout = _root->LoadPersistedLayout(_settings)) + { + if (layout.InitialPosition()) + { + initialPosition = layout.InitialPosition().Value(); + } + } + + // Commandline args trump everything else + if (_appArgs.GetPosition().has_value()) + { + initialPosition = _appArgs.GetPosition().value(); + } + + return { + initialPosition.X ? initialPosition.X.Value() : defaultInitialX, + initialPosition.Y ? initialPosition.Y.Value() : defaultInitialY + }; + } + + bool TerminalWindow::CenterOnLaunch() + { + // If the position has been specified on the commandline, don't center on launch + return _settings.GlobalSettings().CenterOnLaunch() && !_appArgs.GetPosition().has_value(); + } + + // Method Description: + // - See Pane::CalcSnappedDimension + float TerminalWindow::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const + { + return _root->CalcSnappedDimension(widthOrHeight, dimension); + } + + // Method Description: + // - Update the current theme of the application. This will trigger our + // RequestedThemeChanged event, to have our host change the theme of the + // root of the application. + // Arguments: + // - newTheme: The ElementTheme to apply to our elements. + void TerminalWindow::_RefreshThemeRoutine() + { + // Propagate the event to the host layer, so it can update its own UI + _RequestedThemeChangedHandlers(*this, Theme()); + } + + void TerminalWindow::UpdateSettings(const HRESULT settingsLoadedResult, const CascadiaSettings& settings) + { + if (FAILED(settingsLoadedResult)) + { + const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); + _ShowLoadErrorsDialog(titleKey, textKey, settingsLoadedResult); + return; + } + else if (settingsLoadedResult == S_FALSE) + { + _ShowLoadWarningsDialog(); + } + _settings = settings; + // Update the settings in TerminalPage + _root->SetSettings(_settings, true); + _RefreshThemeRoutine(); + } + void TerminalWindow::_OpenSettingsUI() + { + _root->OpenSettingsUI(); + } + UIElement TerminalWindow::GetRoot() noexcept + { + return _root.as(); + } + + // Method Description: + // - Gets the title of the currently focused terminal control. If there + // isn't a control selected for any reason, returns "Terminal" + // Arguments: + // - + // Return Value: + // - the title of the focused control if there is one, else "Terminal" + hstring TerminalWindow::Title() + { + if (_root) + { + return _root->Title(); + } + return { L"Terminal" }; + } + + // Method Description: + // - Used to tell the app that the titlebar has been clicked. The App won't + // actually receive any clicks in the titlebar area, so this is a helper + // to clue the app in that a click has happened. The App will use this as + // a indicator that it needs to dismiss any open flyouts. + // Arguments: + // - + // Return Value: + // - + void TerminalWindow::TitlebarClicked() + { + if (_root) + { + _root->TitlebarClicked(); + } + } + + // Method Description: + // - Used to tell the PTY connection that the window visibility has changed. + // The underlying PTY might need to expose window visibility status to the + // client application for the `::GetConsoleWindow()` API. + // Arguments: + // - showOrHide - True is show; false is hide. + // Return Value: + // - + void TerminalWindow::WindowVisibilityChanged(const bool showOrHide) + { + if (_root) + { + _root->WindowVisibilityChanged(showOrHide); + } + } + + // Method Description: + // - Implements the F7 handler (per GH#638) + // - Implements the Alt handler (per GH#6421) + // Return value: + // - whether the key was handled + bool TerminalWindow::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) + { + if (_root) + { + // Manually bubble the OnDirectKeyEvent event up through the focus tree. + auto xamlRoot{ _root->XamlRoot() }; + auto focusedObject{ Windows::UI::Xaml::Input::FocusManager::GetFocusedElement(xamlRoot) }; + do + { + if (auto keyListener{ focusedObject.try_as() }) + { + if (keyListener.OnDirectKeyEvent(vkey, scanCode, down)) + { + return true; + } + // otherwise, keep walking. bubble the event manually. + } + + if (auto focusedElement{ focusedObject.try_as() }) + { + focusedObject = focusedElement.Parent(); + + // Parent() seems to return null when the focusedElement is created from an ItemTemplate. + // Use the VisualTreeHelper's GetParent as a fallback. + if (!focusedObject) + { + focusedObject = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(focusedElement); + } + } + else + { + break; // we hit a non-FE object, stop bubbling. + } + } while (focusedObject); + } + return false; + } + + // Method Description: + // - Used to tell the app that the 'X' button has been clicked and + // the user wants to close the app. We kick off the close warning + // experience. + // Arguments: + // - + // Return Value: + // - + void TerminalWindow::CloseWindow(LaunchPosition pos) + { + if (_root) + { + // If persisted layout is enabled and we are the last window closing + // we should save our state. + if (_root->ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + { + if (const auto layout = _root->GetWindowLayout()) + { + layout.InitialPosition(pos); + const auto state = ApplicationState::SharedInstance(); + state.PersistedWindowLayouts(winrt::single_threaded_vector({ layout })); + } + } + + _root->CloseWindow(false); + } + } + + winrt::TerminalApp::TaskbarState TerminalWindow::TaskbarState() + { + if (_root) + { + return _root->TaskbarState(); + } + return {}; + } + + winrt::Windows::UI::Xaml::Media::Brush TerminalWindow::TitlebarBrush() + { + if (_root) + { + return _root->TitlebarBrush(); + } + return { nullptr }; + } + void TerminalWindow::WindowActivated(const bool activated) + { + _root->WindowActivated(activated); + } + + // Method Description: + // - Returns true if we should exit the application before even starting the + // window. We might want to do this if we're displaying an error message or + // the version string, or if we want to open the settings file. + // Arguments: + // - + // Return Value: + // - true iff we should exit the application before even starting the window + bool TerminalWindow::ShouldExitEarly() + { + return _appArgs.ShouldExitEarly(); + } + + bool TerminalWindow::FocusMode() const + { + return _root ? _root->FocusMode() : false; + } + + bool TerminalWindow::Fullscreen() const + { + return _root ? _root->Fullscreen() : false; + } + + void TerminalWindow::Maximized(bool newMaximized) + { + if (_root) + { + _root->Maximized(newMaximized); + } + } + + bool TerminalWindow::AlwaysOnTop() const + { + return _root ? _root->AlwaysOnTop() : false; + } + + //////////////////////////////////////////////////////////////////////////// + + // bool TerminalWindow::HasSettingsStartupActions() const noexcept + // { + // return _hasSettingsStartupActions; + // } + + bool TerminalWindow::HasCommandlineArguments() const noexcept + { + return _hasCommandLineArguments; + } + + // 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 + // will be processed on TerminalPage::Create(). + // - This function will have no effective result after Create() is called. + // - This function returns 0, unless a there was a non-zero result from + // trying to parse one of the commands provided. In that case, no commands + // after the failing command will be parsed, and the non-zero code + // returned. + // Arguments: + // - args: an array of strings to process as a commandline. These args can contain spaces + // Return Value: + // - the result of the first command who's parsing returned a non-zero code, + // or 0. (see TerminalWindow::_ParseArgs) + int32_t TerminalWindow::SetStartupCommandline(array_view args) + { + const auto result = _appArgs.ParseArgs(args); + if (result == 0) + { + // If the size of the arguments list is 1, + // then it contains only the executable name and no other arguments. + _hasCommandLineArguments = args.size() > 1; + _appArgs.ValidateStartupCommands(); + if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + { + _root->SetPersistedLayoutIdx(idx.value()); + } + _root->SetStartupActions(_appArgs.GetStartupActions()); + + // Check if we were started as a COM server for inbound connections of console sessions + // coming out of the operating system default application feature. If so, + // tell TerminalPage to start the listener as we have to make sure it has the chance + // to register a handler to hear about the requests first and is all ready to receive + // them before the COM server registers itself. Otherwise, the request might come + // in and be routed to an event with no handlers or a non-ready Page. + if (_appArgs.IsHandoffListener()) + { + _root->SetInboundListener(true); + } + } + + return result; + } + + // Method Description: + // - Parse the provided commandline arguments into actions, and try to + // perform them immediately. + // - This function returns 0, unless a there was a non-zero result from + // trying to parse one of the commands provided. In that case, no commands + // after the failing command will be parsed, and the non-zero code + // returned. + // - If a non-empty cwd is provided, the entire terminal exe will switch to + // that CWD while we handle these actions, then return to the original + // CWD. + // Arguments: + // - args: an array of strings to process as a commandline. These args can contain spaces + // - cwd: The directory to use as the CWD while performing these actions. + // Return Value: + // - the result of the first command who's parsing returned a non-zero code, + // or 0. (see TerminalWindow::_ParseArgs) + int32_t TerminalWindow::ExecuteCommandline(array_view args, + const winrt::hstring& cwd) + { + ::TerminalApp::AppCommandlineArgs appArgs; + auto result = appArgs.ParseArgs(args); + if (result == 0) + { + auto actions = winrt::single_threaded_vector(std::move(appArgs.GetStartupActions())); + + _root->ProcessStartupActions(actions, false, cwd); + + if (appArgs.IsHandoffListener()) + { + _root->SetInboundListener(true); + } + } + // Return the result of parsing with commandline, though it may or may not be used. + return result; + } + + // Method Description: + // - If there were any errors parsing the commandline that was used to + // initialize the terminal, this will return a string containing that + // message. If there were no errors, this message will be blank. + // - If the user requested help on any command (using --help), this will + // contain the help message. + // - If the user requested the version number (using --version), this will + // contain the version string. + // Arguments: + // - + // Return Value: + // - the help text or error message for the provided commandline, if one + // exists, otherwise the empty string. + winrt::hstring TerminalWindow::ParseCommandlineMessage() + { + return winrt::to_hstring(_appArgs.GetExitMessage()); + } + + //////////////////////////////////////////////////////////////////////////// + + bool TerminalWindow::ShouldUsePersistedLayout() + { + return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; + } + + void TerminalWindow::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))); + } + + 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::IdentifyWindow() + { + if (_root) + { + _root->IdentifyWindow(); + } + } + + winrt::hstring TerminalWindow::WindowName() + { + return _root ? _root->WindowName() : L""; + } + void TerminalWindow::WindowName(const winrt::hstring& name) + { + if (_root) + { + _root->WindowName(name); + } + } + uint64_t TerminalWindow::WindowId() + { + return _root ? _root->WindowId() : 0; + } + void TerminalWindow::WindowId(const uint64_t& id) + { + if (_root) + { + _root->WindowId(id); + } + } + + void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) + { + if (_root) + { + _root->SetPersistedLayoutIdx(idx); + } + } + + void TerminalWindow::SetNumberOfOpenWindows(const uint64_t num) + { + _numOpenWindows = num; + if (_root) + { + _root->SetNumberOfOpenWindows(num); + } + } + + void TerminalWindow::RenameFailed() + { + if (_root) + { + _root->RenameFailed(); + } + } + + bool TerminalWindow::IsQuakeWindow() const noexcept + { + return _root->IsQuakeWindow(); + } + + void TerminalWindow::RequestExitFullscreen() + { + _root->SetFullscreen(false); + } + + bool TerminalWindow::AutoHideWindow() + { + return _settings.GlobalSettings().AutoHideWindow(); + } +}; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h new file mode 100644 index 00000000000..aee3173bcb8 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "TerminalWindow.g.h" +#include "SystemMenuChangeArgs.g.h" + +#include "TerminalPage.h" + +#include +#include + +#ifdef UNIT_TESTING +// fwdecl unittest classes +namespace TerminalAppLocalTests +{ + class CommandlineTest; +}; +#endif + +namespace winrt::TerminalApp::implementation +{ + struct SystemMenuChangeArgs : SystemMenuChangeArgsT + { + WINRT_PROPERTY(winrt::hstring, Name, L""); + WINRT_PROPERTY(SystemMenuChangeAction, Action, SystemMenuChangeAction::Add); + WINRT_PROPERTY(SystemMenuItemHandler, Handler, nullptr); + + public: + SystemMenuChangeArgs(const winrt::hstring& name, SystemMenuChangeAction action, SystemMenuItemHandler handler = nullptr) : + _Name{ name }, _Action{ action }, _Handler{ handler } {}; + }; + + struct TerminalWindow : TerminalWindowT + { + public: + TerminalWindow(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + ~TerminalWindow() = default; + + STDMETHODIMP Initialize(HWND hwnd); + + void Create(); + bool IsUwp() const noexcept; + void RunAsUwp(); + bool IsElevated() const noexcept; + + void Quit(); + + void UpdateSettings(const HRESULT settingsLoadedResult, const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + + bool HasCommandlineArguments() const noexcept; + // bool HasSettingsStartupActions() const noexcept; + int32_t SetStartupCommandline(array_view actions); + int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); + winrt::hstring ParseCommandlineMessage(); + bool ShouldExitEarly(); + + bool FocusMode() const; + bool Fullscreen() const; + void Maximized(bool newMaximized); + bool AlwaysOnTop() const; + bool AutoHideWindow(); + + bool ShouldUsePersistedLayout(); + + hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); + void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); + void IdentifyWindow(); + void RenameFailed(); + winrt::hstring WindowName(); + void WindowName(const winrt::hstring& name); + uint64_t WindowId(); + void WindowId(const uint64_t& id); + void SetPersistedLayoutIdx(const uint32_t idx); + void SetNumberOfOpenWindows(const uint64_t num); + bool IsQuakeWindow() const noexcept; + void RequestExitFullscreen(); + + Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); + bool CenterOnLaunch(); + TerminalApp::InitialPosition GetInitialPosition(int64_t defaultInitialX, int64_t defaultInitialY); + winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme(); + Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode(); + bool GetShowTabsInTitlebar(); + bool GetInitialAlwaysOnTop(); + float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; + + Windows::UI::Xaml::UIElement GetRoot() noexcept; + + hstring Title(); + void TitlebarClicked(); + bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); + + void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position); + void WindowVisibilityChanged(const bool showOrHide); + + winrt::TerminalApp::TaskbarState TaskbarState(); + winrt::Windows::UI::Xaml::Media::Brush TitlebarBrush(); + void WindowActivated(const bool activated); + + bool GetMinimizeToNotificationArea(); + bool GetAlwaysShowNotificationIcon(); + bool GetShowTitleInTitlebar(); + + winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); + void DismissDialog(); + + Microsoft::Terminal::Settings::Model::Theme Theme(); + + // -------------------------------- WinRT Events --------------------------------- + // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. + // Usually we'd just do + // WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + // + // But what we're doing here is exposing the Page's PropertyChanged _as + // our own event_. It's a FORWARDED_CALLBACK, essentially. + winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); } + void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); } + + TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme); + + private: + bool _isUwp{ false }; + bool _isElevated{ false }; + // If you add controls here, but forget to null them either here or in + // the ctor, you're going to have a bad time. It'll mysteriously fail to + // activate the AppLogic. + // ALSO: If you add any UIElements as roots here, make sure they're + // updated in _ApplyTheme. The root currently is _root. + winrt::com_ptr _root{ nullptr }; + winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog{ nullptr }; + std::shared_mutex _dialogLock; + + bool _hasCommandLineArguments{ false }; + ::TerminalApp::AppCommandlineArgs _appArgs; + + uint64_t _numOpenWindows{ 0 }; + + Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; + + void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); + void _ShowLoadWarningsDialog(); + bool _IsKeyboardServiceEnabled(); + + void _RefreshThemeRoutine(); + void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + void _OpenSettingsUI(); + // These are events that are handled by the TerminalPage, but are + // exposed through the AppLogic. This macro is used to forward the event + // directly to them. + 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(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); + FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged); + FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell); + FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress); + FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested); + FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested); + FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged); + FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested); + FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested); + FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu); + FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested); + FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged); + + TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); + +#ifdef UNIT_TESTING + friend class TerminalAppLocalTests::CommandlineTest; +#endif + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(TerminalWindow); +} diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl new file mode 100644 index 00000000000..fb5bfeeeaa8 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "TerminalPage.idl"; +import "ShortcutActionDispatch.idl"; +import "IDirectKeyListener.idl"; + +namespace TerminalApp +{ + struct InitialPosition + { + Int64 X; + Int64 Y; + }; + + delegate void SystemMenuItemHandler(); + + enum SystemMenuChangeAction + { + Add = 0, + Remove = 1 + }; + + [default_interface] runtimeclass SystemMenuChangeArgs { + String Name { get; }; + SystemMenuChangeAction Action { get; }; + SystemMenuItemHandler Handler { get; }; + }; + + // See IDialogPresenter and TerminalPage's DialogPresenter for more + // information. + [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + TerminalWindow(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); + + + // 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, + // then it might look like TermApp just failed to activate, which will + // cause you to chase down the rabbit hole of "why is TermApp not + // registered?" when it definitely is. + void Create(); + + Boolean IsUwp(); + void RunAsUwp(); + Boolean IsElevated(); + + Boolean HasCommandlineArguments(); + // Boolean HasSettingsStartupActions(); + + Int32 SetStartupCommandline(String[] commands); + + Int32 ExecuteCommandline(String[] commands, String cwd); + String ParseCommandlineMessage { get; }; + Boolean ShouldExitEarly { get; }; + + void Quit(); + + + Windows.UI.Xaml.UIElement GetRoot(); + + String Title { get; }; + Boolean FocusMode { get; }; + Boolean Fullscreen { get; }; + void Maximized(Boolean newMaximized); + Boolean AlwaysOnTop { get; }; + Boolean AutoHideWindow { get; }; + + void IdentifyWindow(); + String WindowName; + UInt64 WindowId; + void SetPersistedLayoutIdx(UInt32 idx); + void SetNumberOfOpenWindows(UInt64 num); + void RenameFailed(); + void RequestExitFullscreen(); + Boolean IsQuakeWindow(); + + Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); + Boolean CenterOnLaunch { get; }; + + InitialPosition GetInitialPosition(Int64 defaultInitialX, Int64 defaultInitialY); + Windows.UI.Xaml.ElementTheme GetRequestedTheme(); + Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode(); + Boolean GetShowTabsInTitlebar(); + Boolean GetInitialAlwaysOnTop(); + Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension); + void TitlebarClicked(); + void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position); + void WindowVisibilityChanged(Boolean showOrHide); + + TaskbarState TaskbarState{ get; }; + Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; + void WindowActivated(Boolean activated); + + Boolean ShouldUsePersistedLayout(); + // Boolean ShouldImmediatelyHandoffToElevated(); + // void HandoffToElevated(); + String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); + void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); + + Boolean GetMinimizeToNotificationArea(); + Boolean GetAlwaysShowNotificationIcon(); + Boolean GetShowTitleInTitlebar(); + + // See IDialogPresenter and TerminalPage's DialogPresenter for more + // information. + Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); + void DismissDialog(); + + event Windows.Foundation.TypedEventHandler SetTitleBarContent; + event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler LastTabClosed; + event Windows.Foundation.TypedEventHandler RequestedThemeChanged; + event Windows.Foundation.TypedEventHandler FocusModeChanged; + event Windows.Foundation.TypedEventHandler FullscreenChanged; + event Windows.Foundation.TypedEventHandler ChangeMaximizeRequested; + event Windows.Foundation.TypedEventHandler AlwaysOnTopChanged; + event Windows.Foundation.TypedEventHandler RaiseVisualBell; + event Windows.Foundation.TypedEventHandler SetTaskbarProgress; + event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; + event Windows.Foundation.TypedEventHandler RenameWindowRequested; + event Windows.Foundation.TypedEventHandler IsQuakeWindowChanged; + event Windows.Foundation.TypedEventHandler SummonWindowRequested; + event Windows.Foundation.TypedEventHandler CloseRequested; + event Windows.Foundation.TypedEventHandler OpenSystemMenu; + event Windows.Foundation.TypedEventHandler QuitRequested; + event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; + event Windows.Foundation.TypedEventHandler ShowWindowChanged; + + } +} From 439b21f879be488b0ebc8e3901da6b3dd568bb99 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 30 Jan 2023 17:06:31 -0600 Subject: [PATCH 005/122] this is dangerously close to compiling --- src/cascadia/TerminalApp/AppLogic.cpp | 5 +- src/cascadia/TerminalApp/AppLogic.h | 3 + src/cascadia/TerminalApp/AppLogic.idl | 3 + src/cascadia/TerminalApp/TerminalWindow.idl | 1 - src/cascadia/WindowsTerminal/AppHost.cpp | 216 ++++++++++---------- src/cascadia/WindowsTerminal/AppHost.h | 41 ++-- 6 files changed, 142 insertions(+), 127 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 4d5cabbed7c..40d2637aa3b 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -6,7 +6,6 @@ #include "../inc/WindowingBehavior.h" #include "AppLogic.g.cpp" #include "FindTargetWindowResult.g.cpp" -#include #include #include @@ -674,4 +673,8 @@ namespace winrt::TerminalApp::implementation return _settings.GlobalSettings().CurrentTheme(); } + TerminalApp::TerminalWindow AppLogic::CreateNewWindow() + { + return *winrt::make_self(_settings); + } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index b6a3a8b6ba9..eeee6673366 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -9,6 +9,7 @@ #include "Jumplist.h" #include "LanguageProfileNotifier.h" #include "AppCommandlineArgs.h" +#include "TerminalWindow.h" #include #include @@ -63,6 +64,8 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::Theme Theme(); + TerminalApp::TerminalWindow CreateNewWindow(); + TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); private: diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index bc48cf5a965..2a3488e6f95 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "TerminalWindow.idl"; namespace TerminalApp { @@ -34,6 +35,8 @@ namespace TerminalApp FindTargetWindowResult FindTargetWindow(String[] args); + TerminalWindow CreateNewWindow(); + Windows.Foundation.Collections.IMapView GlobalHotkeys(); event Windows.Foundation.TypedEventHandler SettingsChanged; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index fb5bfeeeaa8..fcd8cd9346f 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -56,7 +56,6 @@ namespace TerminalApp void Quit(); - Windows.UI.Xaml.UIElement GetRoot(); String Title { get; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 9b20e3c7aa2..48bc98e683e 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -31,11 +31,12 @@ static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; AppHost::AppHost() noexcept : _app{}, _windowManager{}, - _logic{ nullptr }, // don't make one, we're going to take a ref on app's + _appLogic{ nullptr }, // don't make one, we're going to take a ref on app's + _windowLogic{ nullptr }, _window{ nullptr }, _getWindowLayoutThrottler{} // this will get set if we become the monarch { - _logic = _app.Logic(); // get a ref to app's logic + _appLogic = _app.Logic(); // get a ref to app's logic // Inform the WindowManager that it can use us to find the target window for // a set of commandline args. This needs to be done before @@ -54,10 +55,11 @@ AppHost::AppHost() noexcept : return; } - _useNonClientArea = _logic.GetShowTabsInTitlebar(); + // _HandleCommandlineArgs will create a _windowLogic + _useNonClientArea = _windowLogic.GetShowTabsInTitlebar(); if (_useNonClientArea) { - _window = std::make_unique(_logic.GetRequestedTheme()); + _window = std::make_unique(_windowLogic.GetRequestedTheme()); } else { @@ -67,7 +69,7 @@ AppHost::AppHost() noexcept : // Update our own internal state tracking if we're in quake mode or not. _IsQuakeWindowChanged(nullptr, nullptr); - _window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea()); + _window->SetMinimizeToNotificationAreaBehavior(_windowLogic.GetMinimizeToNotificationArea()); // Tell the window to callback to us when it's about to handle a WM_CREATE auto pfn = std::bind(&AppHost::_HandleCreateWindow, @@ -77,8 +79,8 @@ AppHost::AppHost() noexcept : std::placeholders::_3); _window->SetCreateCallback(pfn); - _window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::AppLogic::CalcSnappedDimension, - _logic, + _window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::TerminalWindow::CalcSnappedDimension, + _windowLogic, std::placeholders::_1, std::placeholders::_2)); @@ -100,10 +102,10 @@ AppHost::AppHost() noexcept : _window->WindowActivated({ this, &AppHost::_WindowActivated }); _window->WindowMoved({ this, &AppHost::_WindowMoved }); _window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed }); - _window->ShouldExitFullscreen({ &_logic, &winrt::TerminalApp::AppLogic::RequestExitFullscreen }); + _window->ShouldExitFullscreen({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen }); - _window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); - _window->SetAutoHideWindow(_logic.AutoHideWindow()); + _window->SetAlwaysOnTop(_windowLogic.GetInitialAlwaysOnTop()); + _window->SetAutoHideWindow(_windowLogic.AutoHideWindow()); _window->MakeWindow(); @@ -152,9 +154,9 @@ AppHost::~AppHost() bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) { - if (_logic) + if (_windowLogic) { - return _logic.OnDirectKeyEvent(vkey, scanCode, down); + return _windowLogic.OnDirectKeyEvent(vkey, scanCode, down); } return false; } @@ -169,9 +171,9 @@ bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, cons void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - if (_logic) + if (_windowLogic) { - const auto state = _logic.TaskbarState(); + const auto state = _windowLogic.TaskbarState(); _window->SetTaskbarProgress(gsl::narrow_cast(state.State()), gsl::narrow_cast(state.Progress())); } @@ -231,12 +233,16 @@ void AppHost::_HandleCommandlineArgs() return; } + // We did want to make a window, so let's instantiate it here. + // We don't have XAML yet, but we do have other stuff. + _windowLogic = _appLogic.CreateNewWindow(); + if (auto peasant{ _windowManager.CurrentWindow() }) { if (auto args{ peasant.InitialArgs() }) { - const auto result = _logic.SetStartupCommandline(args.Commandline()); - const auto message = _logic.ParseCommandlineMessage(); + const auto result = _windowLogic.SetStartupCommandline(args.Commandline()); + const auto message = _windowLogic.ParseCommandlineMessage(); if (!message.empty()) { const auto displayHelp = result == 0; @@ -249,7 +255,7 @@ void AppHost::_HandleCommandlineArgs() GetStringResource(messageTitle).data(), MB_OK | messageIcon); - if (_logic.ShouldExitEarly()) + if (_windowLogic.ShouldExitEarly()) { ExitProcess(result); } @@ -263,10 +269,10 @@ void AppHost::_HandleCommandlineArgs() // the window at all here. In that case, we're going through this // special escape hatch to dispatch all the calls to elevate-shim, and // then we're going to exit immediately. - if (_logic.ShouldImmediatelyHandoffToElevated()) + if (_windowLogic.ShouldImmediatelyHandoffToElevated()) { _shouldCreateWindow = false; - _logic.HandoffToElevated(); + _windowLogic.HandoffToElevated(); return; } @@ -288,7 +294,7 @@ void AppHost::_HandleCommandlineArgs() { const auto numPeasants = _windowManager.GetNumberOfPeasants(); const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (_logic.ShouldUsePersistedLayout() && + if (_windowLogic.ShouldUsePersistedLayout() && layouts && layouts.Size() > 0) { @@ -299,9 +305,9 @@ void AppHost::_HandleCommandlineArgs() // 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 (numPeasants == 1 && !_logic.HasCommandlineArguments() && !_logic.HasSettingsStartupActions()) + if (numPeasants == 1 && !_windowLogic.HasCommandlineArguments() && !_appLogic.HasSettingsStartupActions()) { - _logic.SetPersistedLayoutIdx(startIdx); + _windowLogic.SetPersistedLayoutIdx(startIdx); startIdx += 1; } @@ -328,10 +334,10 @@ void AppHost::_HandleCommandlineArgs() )); } } - _logic.SetNumberOfOpenWindows(numPeasants); + _windowLogic.SetNumberOfOpenWindows(numPeasants); } - _logic.WindowName(peasant.WindowName()); - _logic.WindowId(peasant.GetID()); + _windowLogic.WindowName(peasant.WindowName()); + _windowLogic.WindowId(peasant.GetID()); } } @@ -350,7 +356,7 @@ void AppHost::Initialize() { _window->Initialize(); - if (auto withWindow{ _logic.try_as() }) + if (auto withWindow{ _windowLogic.try_as() }) { withWindow->Initialize(_window->GetHandle()); } @@ -360,7 +366,7 @@ void AppHost::Initialize() // Register our callback for when the app's non-client content changes. // This has to be done _before_ App::Create, as the app might set the // content in Create. - _logic.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent }); + _windowLogic.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent }); } // MORE EVENT HANDLERS HERE! @@ -383,27 +389,27 @@ void AppHost::Initialize() }); // If the user requests a close in another way handle the same as if the 'X' // was clicked. - _revokers.CloseRequested = _logic.CloseRequested(winrt::auto_revoke, { this, &AppHost::_CloseRequested }); + _revokers.CloseRequested = _windowLogic.CloseRequested(winrt::auto_revoke, { this, &AppHost::_CloseRequested }); // Add an event handler to plumb clicks in the titlebar area down to the // application layer. - _window->DragRegionClicked([this]() { _logic.TitlebarClicked(); }); + _window->DragRegionClicked([this]() { _windowLogic.TitlebarClicked(); }); - _window->WindowVisibilityChanged([this](bool showOrHide) { _logic.WindowVisibilityChanged(showOrHide); }); - _window->UpdateSettingsRequested([this]() { _logic.ReloadSettings(); }); + _window->WindowVisibilityChanged([this](bool showOrHide) { _windowLogic.WindowVisibilityChanged(showOrHide); }); + _window->UpdateSettingsRequested([this]() { _appLogic.ReloadSettings(); }); - _revokers.RequestedThemeChanged = _logic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme }); - _revokers.FullscreenChanged = _logic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged }); - _revokers.FocusModeChanged = _logic.FocusModeChanged(winrt::auto_revoke, { this, &AppHost::_FocusModeChanged }); - _revokers.AlwaysOnTopChanged = _logic.AlwaysOnTopChanged(winrt::auto_revoke, { this, &AppHost::_AlwaysOnTopChanged }); - _revokers.RaiseVisualBell = _logic.RaiseVisualBell(winrt::auto_revoke, { this, &AppHost::_RaiseVisualBell }); - _revokers.SystemMenuChangeRequested = _logic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested }); - _revokers.ChangeMaximizeRequested = _logic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested }); + _revokers.RequestedThemeChanged = _windowLogic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme }); + _revokers.FullscreenChanged = _windowLogic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged }); + _revokers.FocusModeChanged = _windowLogic.FocusModeChanged(winrt::auto_revoke, { this, &AppHost::_FocusModeChanged }); + _revokers.AlwaysOnTopChanged = _windowLogic.AlwaysOnTopChanged(winrt::auto_revoke, { this, &AppHost::_AlwaysOnTopChanged }); + _revokers.RaiseVisualBell = _windowLogic.RaiseVisualBell(winrt::auto_revoke, { this, &AppHost::_RaiseVisualBell }); + _revokers.SystemMenuChangeRequested = _windowLogic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested }); + _revokers.ChangeMaximizeRequested = _windowLogic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested }); _window->MaximizeChanged([this](bool newMaximize) { - if (_logic) + if (_appLogic) { - _logic.Maximized(newMaximize); + _windowLogic.Maximized(newMaximize); } }); @@ -416,21 +422,21 @@ void AppHost::Initialize() // Load bearing: make sure the PropertyChanged handler is added before we // call Create, so that when the app sets up the titlebar brush, we're // already prepared to listen for the change notification - _revokers.PropertyChanged = _logic.PropertyChanged(winrt::auto_revoke, { this, &AppHost::_PropertyChangedHandler }); - - _logic.Create(); - - _revokers.TitleChanged = _logic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged }); - _revokers.LastTabClosed = _logic.LastTabClosed(winrt::auto_revoke, { this, &AppHost::LastTabClosed }); - _revokers.SetTaskbarProgress = _logic.SetTaskbarProgress(winrt::auto_revoke, { this, &AppHost::SetTaskbarProgress }); - _revokers.IdentifyWindowsRequested = _logic.IdentifyWindowsRequested(winrt::auto_revoke, { this, &AppHost::_IdentifyWindowsRequested }); - _revokers.RenameWindowRequested = _logic.RenameWindowRequested(winrt::auto_revoke, { this, &AppHost::_RenameWindowRequested }); - _revokers.SettingsChanged = _logic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); - _revokers.IsQuakeWindowChanged = _logic.IsQuakeWindowChanged(winrt::auto_revoke, { this, &AppHost::_IsQuakeWindowChanged }); - _revokers.SummonWindowRequested = _logic.SummonWindowRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowRequested }); - _revokers.OpenSystemMenu = _logic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu }); - _revokers.QuitRequested = _logic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll }); - _revokers.ShowWindowChanged = _logic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged }); + _revokers.PropertyChanged = _windowLogic.PropertyChanged(winrt::auto_revoke, { this, &AppHost::_PropertyChangedHandler }); + + _appLogic.Create(); + + _revokers.TitleChanged = _windowLogic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged }); + _revokers.LastTabClosed = _windowLogic.LastTabClosed(winrt::auto_revoke, { this, &AppHost::LastTabClosed }); + _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 }); + _revokers.SettingsChanged = _appLogic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); + _revokers.IsQuakeWindowChanged = _windowLogic.IsQuakeWindowChanged(winrt::auto_revoke, { this, &AppHost::_IsQuakeWindowChanged }); + _revokers.SummonWindowRequested = _windowLogic.SummonWindowRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowRequested }); + _revokers.OpenSystemMenu = _windowLogic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu }); + _revokers.QuitRequested = _windowLogic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll }); + _revokers.ShowWindowChanged = _windowLogic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged }); // BODGY // On certain builds of Windows, when Terminal is set as the default @@ -439,13 +445,13 @@ void AppHost::Initialize() // applications. This call into TerminalThemeHelpers will tell our // compositor to automatically complete animations that are scheduled // while the screen is off. - TerminalTrySetAutoCompleteAnimationsWhenOccluded(static_cast<::IUnknown*>(winrt::get_abi(_logic.GetRoot())), true); + TerminalTrySetAutoCompleteAnimationsWhenOccluded(static_cast<::IUnknown*>(winrt::get_abi(_windowLogic.GetRoot())), true); - _window->UpdateTitle(_logic.Title()); + _window->UpdateTitle(_windowLogic.Title()); // Set up the content of the application. If the app has a custom titlebar, // set that content as well. - _window->SetContent(_logic.GetRoot()); + _window->SetContent(_windowLogic.GetRoot()); _window->OnAppInitialized(); // BODGY @@ -478,7 +484,7 @@ void AppHost::Initialize() // - void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle) { - if (_logic.GetShowTitleInTitlebar()) + if (_windowLogic.GetShowTitleInTitlebar()) { _window->UpdateTitle(newTitle); } @@ -564,11 +570,11 @@ LaunchPosition AppHost::_GetWindowLaunchPosition() // - None void AppHost::_HandleCreateWindow(const HWND hwnd, til::rect proposedRect, LaunchMode& launchMode) { - launchMode = _logic.GetLaunchMode(); + launchMode = _windowLogic.GetLaunchMode(); // Acquire the actual initial position - auto initialPos = _logic.GetInitialPosition(proposedRect.left, proposedRect.top); - const auto centerOnLaunch = _logic.CenterOnLaunch(); + auto initialPos = _windowLogic.GetInitialPosition(proposedRect.left, proposedRect.top); + const auto centerOnLaunch = _windowLogic.CenterOnLaunch(); proposedRect.left = gsl::narrow(initialPos.X); proposedRect.top = gsl::narrow(initialPos.Y); @@ -615,7 +621,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, til::rect proposedRect, Launc proposedRect.top = monitorInfo.rcWork.top; } - auto initialSize = _logic.GetLaunchDimensions(dpix); + auto initialSize = _windowLogic.GetLaunchDimensions(dpix); const auto islandWidth = Utils::ClampToShortMax( static_cast(ceil(initialSize.Width)), 1); @@ -649,7 +655,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, til::rect proposedRect, Launc til::point origin{ (proposedRect.left + nonClientFrame.left), (proposedRect.top) }; - if (_logic.IsQuakeWindow()) + if (_windowLogic.IsQuakeWindow()) { // If we just use rcWork by itself, we'll fail to account for the invisible // space reserved for the resize handles. So retrieve that size here. @@ -706,7 +712,7 @@ void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspecta { auto nonClientWindow{ static_cast(_window.get()) }; nonClientWindow->SetTitlebarContent(arg); - nonClientWindow->SetTitlebarBackground(_logic.TitlebarBrush()); + nonClientWindow->SetTitlebarBackground(_windowLogic.TitlebarBrush()); } _updateTheme(); @@ -729,13 +735,13 @@ void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, void AppHost::_FocusModeChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - _window->FocusModeChanged(_logic.FocusMode()); + _window->FocusModeChanged(_windowLogic.FocusMode()); } void AppHost::_FullscreenChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - _window->FullscreenChanged(_logic.Fullscreen()); + _window->FullscreenChanged(_windowLogic.Fullscreen()); } void AppHost::_ChangeMaximizeRequested(const winrt::Windows::Foundation::IInspectable&, @@ -773,7 +779,7 @@ void AppHost::_AlwaysOnTopChanged(const winrt::Windows::Foundation::IInspectable return; } - _window->SetAlwaysOnTop(_logic.AlwaysOnTop()); + _window->SetAlwaysOnTop(_windowLogic.AlwaysOnTop()); } // Method Description @@ -801,10 +807,10 @@ void AppHost::_RaiseVisualBell(const winrt::Windows::Foundation::IInspectable&, // - void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta) { - if (_logic) + if (_appLogic) { // Find all the elements that are underneath the mouse - auto elems = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord.to_winrt_point(), _logic.GetRoot()); + auto elems = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord.to_winrt_point(), _windowLogic.GetRoot()); for (const auto& e : elems) { // If that element has implemented IMouseWheelListener, call OnMouseWheel on that element. @@ -862,7 +868,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable send // Summon the window whenever we dispatch a commandline to it. This will // make it obvious when a new tab/pane is created in a window. _HandleSummon(sender, summonArgs); - _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); + _windowLogic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } // Method Description: @@ -881,11 +887,11 @@ winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowL winrt::hstring layoutJson = L""; // Use the main thread since we are accessing controls. - co_await wil::resume_foreground(_logic.GetRoot().Dispatcher()); + co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); try { const auto pos = _GetWindowLaunchPosition(); - layoutJson = _logic.GetWindowLayoutJson(pos); + layoutJson = _windowLogic.GetWindowLayoutJson(pos); } CATCH_LOG() @@ -908,14 +914,14 @@ winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowL void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::FindTargetWindowArgs& args) { - const auto targetWindow = _logic.FindTargetWindow(args.Args().Commandline()); + const auto targetWindow = _appLogic.FindTargetWindow(args.Args().Commandline()); args.ResultTargetWindow(targetWindow.WindowId()); args.ResultTargetWindowName(targetWindow.WindowName()); } winrt::fire_and_forget AppHost::_WindowActivated(bool activated) { - _logic.WindowActivated(activated); + _windowLogic.WindowActivated(activated); if (!activated) { @@ -955,27 +961,27 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s if (_windowManager.DoesQuakeWindowExist() || _window->IsQuakeWindow() || - (_logic.GetAlwaysShowNotificationIcon() || _logic.GetMinimizeToNotificationArea())) + (_windowLogic.GetAlwaysShowNotificationIcon() || _windowLogic.GetMinimizeToNotificationArea())) { _CreateNotificationIcon(); } // Set the number of open windows (so we know if we are the last window) // and subscribe for updates if there are any changes to that number. - _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); + _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); _WindowCreatedToken = _windowManager.WindowCreated([this](auto&&, auto&&) { if (_getWindowLayoutThrottler) { _getWindowLayoutThrottler.value()(); } - _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); + _appLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); _WindowClosedToken = _windowManager.WindowClosed([this](auto&&, auto&&) { if (_getWindowLayoutThrottler) { _getWindowLayoutThrottler.value()(); } - _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); + _appLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); // These events are coming from peasants that become or un-become quake windows. @@ -1000,7 +1006,7 @@ winrt::Windows::Foundation::IAsyncAction AppHost::_SaveWindowLayouts() // Make sure we run on a background thread to not block anything. co_await winrt::resume_background(); - if (_logic.ShouldUsePersistedLayout()) + if (_windowLogic.ShouldUsePersistedLayout()) { try { @@ -1015,7 +1021,7 @@ winrt::Windows::Foundation::IAsyncAction AppHost::_SaveWindowLayouts() TraceLoggingDescription("Logged when writing window state"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _logic.SaveWindowLayoutJsons(layoutJsons); + _windowLogic.SaveWindowLayoutJsons(layoutJsons); } catch (...) { @@ -1058,13 +1064,13 @@ winrt::fire_and_forget AppHost::_SaveWindowLayoutsRepeat() void AppHost::_listenForInboundConnections() { - _logic.SetInboundListener(); + _appLogic.SetInboundListener(); } winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { // The hotkey MUST be registered on the main thread. It will fail otherwise! - co_await wil::resume_foreground(_logic.GetRoot().Dispatcher()); + co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); if (!_window) { @@ -1089,7 +1095,7 @@ winrt::fire_and_forget AppHost::_setupGlobalHotkeys() _hotkeys.clear(); // Re-register all current hotkeys. - for (const auto& [keyChord, cmd] : _logic.GlobalHotkeys()) + for (const auto& [keyChord, cmd] : _appLogic.GlobalHotkeys()) { if (auto summonArgs = cmd.ActionAndArgs().Args().try_as()) { @@ -1311,7 +1317,7 @@ winrt::fire_and_forget AppHost::_IdentifyWindowsRequested(const winrt::Windows:: void AppHost::_DisplayWindowId(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - _logic.IdentifyWindow(); + _windowLogic.IdentifyWindow(); } winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Foundation::IInspectable /*sender*/, @@ -1335,11 +1341,11 @@ winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Fou if (requestArgs.Succeeded()) { - _logic.WindowName(args.ProposedName()); + _windowLogic.WindowName(args.ProposedName()); } else { - _logic.RenameFailed(); + _windowLogic.RenameFailed(); } } } @@ -1373,11 +1379,11 @@ static bool _isActuallyDarkTheme(const auto requestedTheme) void AppHost::_updateTheme() { - auto theme = _logic.Theme(); + auto theme = _appLogic.Theme(); _window->OnApplicationThemeChanged(theme.RequestedTheme()); - const auto b = _logic.TitlebarBrush(); + const auto b = _windowLogic.TitlebarBrush(); const auto color = ThemeColor::ColorFromBrush(b); const auto colorOpacity = b ? color.A / 255.0 : 0.0; const auto brushOpacity = _opacityFromBrush(b); @@ -1409,11 +1415,11 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta { if (!_windowManager.DoesQuakeWindowExist()) { - if (!_notificationIcon && (_logic.GetMinimizeToNotificationArea() || _logic.GetAlwaysShowNotificationIcon())) + if (!_notificationIcon && (_windowLogic.GetMinimizeToNotificationArea() || _windowLogic.GetAlwaysShowNotificationIcon())) { _CreateNotificationIcon(); } - else if (_notificationIcon && !_logic.GetMinimizeToNotificationArea() && !_logic.GetAlwaysShowNotificationIcon()) + else if (_notificationIcon && !_windowLogic.GetMinimizeToNotificationArea() && !_windowLogic.GetAlwaysShowNotificationIcon()) { _windowManager.SummonAllWindows(); _DestroyNotificationIcon(); @@ -1421,8 +1427,8 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta } } - _window->SetMinimizeToNotificationAreaBehavior(_logic.GetMinimizeToNotificationArea()); - _window->SetAutoHideWindow(_logic.AutoHideWindow()); + _window->SetMinimizeToNotificationAreaBehavior(_windowLogic.GetMinimizeToNotificationArea()); + _window->SetAutoHideWindow(_windowLogic.AutoHideWindow()); _updateTheme(); } @@ -1434,25 +1440,25 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab // to show regardless of the notification icon settings. // This also means we'll need to destroy the notification icon if it was created // specifically for the quake window. If not, it should not be destroyed. - if (!_window->IsQuakeWindow() && _logic.IsQuakeWindow()) + if (!_window->IsQuakeWindow() && _windowLogic.IsQuakeWindow()) { _ShowNotificationIconRequested(nullptr, nullptr); } - else if (_window->IsQuakeWindow() && !_logic.IsQuakeWindow()) + else if (_window->IsQuakeWindow() && !_windowLogic.IsQuakeWindow()) { _HideNotificationIconRequested(nullptr, nullptr); } - _window->IsQuakeWindow(_logic.IsQuakeWindow()); + _window->IsQuakeWindow(_windowLogic.IsQuakeWindow()); } winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { // Need to be on the main thread to close out all of the tabs. - co_await wil::resume_foreground(_logic.GetRoot().Dispatcher()); + co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); - _logic.Quit(); + _windowLogic.Quit(); } void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, @@ -1585,8 +1591,8 @@ void AppHost::_HideNotificationIconRequested(const winrt::Windows::Foundation::I { // Destroy it only if our settings allow it if (_notificationIcon && - !_logic.GetAlwaysShowNotificationIcon() && - !_logic.GetMinimizeToNotificationArea()) + !_windowLogic.GetAlwaysShowNotificationIcon() && + !_windowLogic.GetMinimizeToNotificationArea()) { _DestroyNotificationIcon(); } @@ -1607,14 +1613,14 @@ void AppHost::_HideNotificationIconRequested(const winrt::Windows::Foundation::I // - void AppHost::_WindowMoved() { - if (_logic) + if (_windowLogic) { // Ensure any open ContentDialog is dismissed. // Closing the popup in the UI tree as done below is not sufficient because // it does not terminate the dialog's async operation. - _logic.DismissDialog(); + _windowLogic.DismissDialog(); - const auto root{ _logic.GetRoot() }; + const auto root{ _windowLogic.GetRoot() }; if (root && root.XamlRoot()) { try @@ -1643,7 +1649,7 @@ void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /* const winrt::Windows::Foundation::IInspectable& /*args*/) { const auto pos = _GetWindowLaunchPosition(); - _logic.CloseWindow(pos); + _windowLogic.CloseWindow(pos); } void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, @@ -1654,7 +1660,7 @@ void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspect if (_useNonClientArea) { auto nonClientWindow{ static_cast(_window.get()) }; - nonClientWindow->SetTitlebarBackground(_logic.TitlebarBrush()); + nonClientWindow->SetTitlebarBackground(_windowLogic.TitlebarBrush()); _updateTheme(); } } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 2de84742189..5f9b48dc33e 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -23,7 +23,8 @@ class AppHost private: std::unique_ptr _window; winrt::TerminalApp::App _app; - winrt::TerminalApp::AppLogic _logic; + winrt::TerminalApp::AppLogic _appLogic; + winrt::TerminalApp::TerminalWindow _windowLogic; winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; std::vector _hotkeys; @@ -151,28 +152,28 @@ class AppHost winrt::Microsoft::Terminal::Remoting::Peasant::SummonRequested_revoker peasantSummonRequested; winrt::Microsoft::Terminal::Remoting::Peasant::DisplayWindowIdRequested_revoker peasantDisplayWindowIdRequested; winrt::Microsoft::Terminal::Remoting::Peasant::QuitRequested_revoker peasantQuitRequested; - winrt::TerminalApp::AppLogic::CloseRequested_revoker CloseRequested; - winrt::TerminalApp::AppLogic::RequestedThemeChanged_revoker RequestedThemeChanged; - winrt::TerminalApp::AppLogic::FullscreenChanged_revoker FullscreenChanged; - winrt::TerminalApp::AppLogic::FocusModeChanged_revoker FocusModeChanged; - winrt::TerminalApp::AppLogic::AlwaysOnTopChanged_revoker AlwaysOnTopChanged; - winrt::TerminalApp::AppLogic::RaiseVisualBell_revoker RaiseVisualBell; - winrt::TerminalApp::AppLogic::SystemMenuChangeRequested_revoker SystemMenuChangeRequested; - winrt::TerminalApp::AppLogic::ChangeMaximizeRequested_revoker ChangeMaximizeRequested; - winrt::TerminalApp::AppLogic::TitleChanged_revoker TitleChanged; - winrt::TerminalApp::AppLogic::LastTabClosed_revoker LastTabClosed; - winrt::TerminalApp::AppLogic::SetTaskbarProgress_revoker SetTaskbarProgress; - winrt::TerminalApp::AppLogic::IdentifyWindowsRequested_revoker IdentifyWindowsRequested; - winrt::TerminalApp::AppLogic::RenameWindowRequested_revoker RenameWindowRequested; + winrt::TerminalApp::TerminalWindow::CloseRequested_revoker CloseRequested; + winrt::TerminalApp::TerminalWindow::RequestedThemeChanged_revoker RequestedThemeChanged; + winrt::TerminalApp::TerminalWindow::FullscreenChanged_revoker FullscreenChanged; + winrt::TerminalApp::TerminalWindow::FocusModeChanged_revoker FocusModeChanged; + winrt::TerminalApp::TerminalWindow::AlwaysOnTopChanged_revoker AlwaysOnTopChanged; + winrt::TerminalApp::TerminalWindow::RaiseVisualBell_revoker RaiseVisualBell; + 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::SetTaskbarProgress_revoker SetTaskbarProgress; + winrt::TerminalApp::TerminalWindow::IdentifyWindowsRequested_revoker IdentifyWindowsRequested; + winrt::TerminalApp::TerminalWindow::RenameWindowRequested_revoker RenameWindowRequested; + winrt::TerminalApp::TerminalWindow::IsQuakeWindowChanged_revoker IsQuakeWindowChanged; + winrt::TerminalApp::TerminalWindow::SummonWindowRequested_revoker SummonWindowRequested; + winrt::TerminalApp::TerminalWindow::OpenSystemMenu_revoker OpenSystemMenu; + winrt::TerminalApp::TerminalWindow::QuitRequested_revoker QuitRequested; + winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged; + winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged; winrt::TerminalApp::AppLogic::SettingsChanged_revoker SettingsChanged; - winrt::TerminalApp::AppLogic::IsQuakeWindowChanged_revoker IsQuakeWindowChanged; - winrt::TerminalApp::AppLogic::SummonWindowRequested_revoker SummonWindowRequested; - winrt::TerminalApp::AppLogic::OpenSystemMenu_revoker OpenSystemMenu; - winrt::TerminalApp::AppLogic::QuitRequested_revoker QuitRequested; - winrt::TerminalApp::AppLogic::ShowWindowChanged_revoker ShowWindowChanged; winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; - winrt::TerminalApp::AppLogic::PropertyChanged_revoker PropertyChanged; } _revokers{}; }; From 99bc2802076ec7fa833bb77ef4a2c3997d93e52b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 31 Jan 2023 09:34:02 -0600 Subject: [PATCH 006/122] It doesn't crash on launch. That's something. There's no startupActions though, so it immediately exits --- src/cascadia/TerminalApp/AppLogic.cpp | 19 +++++++++++++++++-- src/cascadia/TerminalApp/AppLogic.h | 2 ++ src/cascadia/TerminalApp/AppLogic.idl | 5 +++++ src/cascadia/TerminalApp/TerminalWindow.cpp | 10 ++++++++++ src/cascadia/TerminalApp/TerminalWindow.h | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 13 +++++++------ 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 40d2637aa3b..1ab897ca2ea 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -301,6 +301,11 @@ namespace winrt::TerminalApp::implementation return hr; } + bool AppLogic::HasSettingsStartupActions() const noexcept + { + return _hasSettingsStartupActions; + } + // Call this function after loading your _settings. // It handles any CPU intensive settings updates (like updating the Jumplist) // which should thus only occur if the settings file actually changed. @@ -474,6 +479,10 @@ namespace winrt::TerminalApp::implementation } else { + // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} + // + // _SettingsChangedHandlers(*this, make_self( _settingsLoadedResult, warnings, _settingsLoadExceptionText, settings})) + // const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); // const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); @@ -500,7 +509,7 @@ namespace winrt::TerminalApp::implementation _ApplyStartupTaskStateChange(); _ProcessLazySettingsChanges(); - _SettingsChangedHandlers(*this, nullptr); + _SettingsChangedHandlers(*this, _settings); } // Method Description: @@ -675,6 +684,12 @@ namespace winrt::TerminalApp::implementation TerminalApp::TerminalWindow AppLogic::CreateNewWindow() { - return *winrt::make_self(_settings); + if (_settings == nullptr) + { + ReloadSettings(); + } + auto window = winrt::make_self(_settings); + this->SettingsChanged({ window->get_weak(), &implementation::TerminalWindow::UpdateSettingsHandler }); + return *window; } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index eeee6673366..ffe744b8978 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -52,6 +52,8 @@ namespace winrt::TerminalApp::implementation bool IsElevated() const noexcept; void ReloadSettings(); + bool HasSettingsStartupActions() const noexcept; + [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; TerminalApp::FindTargetWindowResult FindTargetWindow(array_view actions); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 2a3488e6f95..7946403aeed 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -27,6 +27,8 @@ namespace TerminalApp void RunAsUwp(); Boolean IsElevated(); + Boolean HasSettingsStartupActions(); + void ReloadSettings(); void SetInboundListener(); @@ -37,6 +39,9 @@ namespace TerminalApp TerminalWindow CreateNewWindow(); + Boolean ShouldImmediatelyHandoffToElevated(); + void HandoffToElevated(); + Windows.Foundation.Collections.IMapView GlobalHotkeys(); event Windows.Foundation.TypedEventHandler SettingsChanged; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 0b1c9e27632..89ce8856c17 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1137,4 +1137,14 @@ namespace winrt::TerminalApp::implementation { return _settings.GlobalSettings().AutoHideWindow(); } + // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} + void TerminalWindow::UpdateSettingsHandler(const winrt::IInspectable& /*sender*/, + const winrt::IInspectable& arg) + { + if (const auto& settings{ arg.try_as() }) + { + this->UpdateSettings(S_OK, settings); + _root->SetSettings(_settings, true); + } + } }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index aee3173bcb8..b2cef567f93 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -107,6 +107,7 @@ namespace winrt::TerminalApp::implementation void DismissDialog(); Microsoft::Terminal::Settings::Model::Theme Theme(); + void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 48bc98e683e..60ded0d3fe8 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -32,7 +32,7 @@ AppHost::AppHost() noexcept : _app{}, _windowManager{}, _appLogic{ nullptr }, // don't make one, we're going to take a ref on app's - _windowLogic{ nullptr }, + _windowLogic{ nullptr }, _window{ nullptr }, _getWindowLayoutThrottler{} // this will get set if we become the monarch { @@ -234,7 +234,7 @@ void AppHost::_HandleCommandlineArgs() } // We did want to make a window, so let's instantiate it here. - // We don't have XAML yet, but we do have other stuff. + // We don't have XAML yet, but we do have other stuff. _windowLogic = _appLogic.CreateNewWindow(); if (auto peasant{ _windowManager.CurrentWindow() }) @@ -269,10 +269,10 @@ void AppHost::_HandleCommandlineArgs() // the window at all here. In that case, we're going through this // special escape hatch to dispatch all the calls to elevate-shim, and // then we're going to exit immediately. - if (_windowLogic.ShouldImmediatelyHandoffToElevated()) + if (_appLogic.ShouldImmediatelyHandoffToElevated()) { _shouldCreateWindow = false; - _windowLogic.HandoffToElevated(); + _appLogic.HandoffToElevated(); return; } @@ -425,6 +425,7 @@ void AppHost::Initialize() _revokers.PropertyChanged = _windowLogic.PropertyChanged(winrt::auto_revoke, { this, &AppHost::_PropertyChangedHandler }); _appLogic.Create(); + _windowLogic.Create(); _revokers.TitleChanged = _windowLogic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged }); _revokers.LastTabClosed = _windowLogic.LastTabClosed(winrt::auto_revoke, { this, &AppHost::LastTabClosed }); @@ -974,14 +975,14 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s if (_getWindowLayoutThrottler) { _getWindowLayoutThrottler.value()(); } - _appLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); + _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); _WindowClosedToken = _windowManager.WindowClosed([this](auto&&, auto&&) { if (_getWindowLayoutThrottler) { _getWindowLayoutThrottler.value()(); } - _appLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); + _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); // These events are coming from peasants that become or un-become quake windows. From 219551593724eb1d822bc957c8497295d1b4fe5d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 31 Jan 2023 10:44:38 -0600 Subject: [PATCH 007/122] it launches --- src/cascadia/TerminalApp/AppLogic.cpp | 4 ++++ src/cascadia/TerminalApp/TerminalPage.cpp | 11 ++++++++++ src/cascadia/TerminalApp/TerminalWindow.cpp | 23 ++++++++++++++------- src/cascadia/TerminalApp/TerminalWindow.h | 3 +++ 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 1ab897ca2ea..5a2ec597963 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -690,6 +690,10 @@ namespace winrt::TerminalApp::implementation } auto window = winrt::make_self(_settings); this->SettingsChanged({ window->get_weak(), &implementation::TerminalWindow::UpdateSettingsHandler }); + if (_hasSettingsStartupActions) + { + window->SetSettingsStartupArgs(_settingsAppArgs.GetStartupActions()); + } return *window; } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index cf4f0140a27..e382d1e7743 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -621,6 +621,8 @@ namespace winrt::TerminalApp::implementation const bool initial, const winrt::hstring cwd) { + const auto paramFistSize = actions.Size(); + auto weakThis{ get_weak() }; // Handle it on a subsequent pass of the UI thread. @@ -651,6 +653,11 @@ namespace winrt::TerminalApp::implementation if (auto page{ weakThis.get() }) { + const auto memberSize = page->_startupActions.Size(); + const auto paramSecondSize = actions.Size(); + memberSize; + paramFistSize; + paramSecondSize; for (const auto& action : actions) { if (auto page{ weakThis.get() }) @@ -3045,11 +3052,15 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::SetStartupActions(std::vector& actions) { + const auto initSize = actions.size(); + initSize; // The fastest way to copy all the actions out of the std::vector and // put them into a winrt::IVector is by making a copy, then moving the // copy into the winrt vector ctor. auto listCopy = actions; _startupActions = winrt::single_threaded_vector(std::move(listCopy)); + const auto afterSize = _startupActions.Size(); + assert(initSize == afterSize);// you donkey } // Routine Description: diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 89ce8856c17..a46ba9390ad 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -154,7 +154,6 @@ namespace winrt::TerminalApp::implementation // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT TerminalWindow::Initialize(HWND hwnd) { - _root = winrt::make_self(); _dialog = ContentDialog{}; return _root->Initialize(hwnd); } @@ -214,13 +213,13 @@ namespace winrt::TerminalApp::implementation // TODO! handle startupActions // - //// Pay attention, that even if some command line arguments were parsed (like launch mode), - //// we will not use the startup actions from settings. - //// While this simplifies the logic, we might want to reconsider this behavior in the future. - //if (!_hasCommandLineArguments && _hasSettingsStartupActions) - //{ - // _root->SetStartupActions(_settingsAppArgs.GetStartupActions()); - //} + // Pay attention, that even if some command line arguments were parsed (like launch mode), + // we will not use the startup actions from settings. + // While this simplifies the logic, we might want to reconsider this behavior in the future. + if (!_hasCommandLineArguments && _gotSettingsStartupActions) + { + _root->SetStartupActions(_settingsStartupArgs); + } _root->SetSettings(_settings, false); _root->Loaded({ this, &TerminalWindow::_OnLoaded }); @@ -927,6 +926,14 @@ namespace winrt::TerminalApp::implementation // { // return _hasSettingsStartupActions; // } + void TerminalWindow::SetSettingsStartupArgs(const std::vector& actions) + { + for (const auto& action : actions) + { + _settingsStartupArgs.push_back(action); + } + _gotSettingsStartupActions = true; + } bool TerminalWindow::HasCommandlineArguments() const noexcept { diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index b2cef567f93..9d5605d00ca 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -53,6 +53,7 @@ namespace winrt::TerminalApp::implementation // bool HasSettingsStartupActions() const noexcept; int32_t SetStartupCommandline(array_view actions); int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); + void SetSettingsStartupArgs(const std::vector& actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); @@ -135,6 +136,8 @@ namespace winrt::TerminalApp::implementation bool _hasCommandLineArguments{ false }; ::TerminalApp::AppCommandlineArgs _appArgs; + bool _gotSettingsStartupActions{ false }; + std::vector _settingsStartupArgs{}; uint64_t _numOpenWindows{ 0 }; From 5116ca1e77ed7ed75ab51ab58e803aa211ef850f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 31 Jan 2023 10:47:46 -0600 Subject: [PATCH 008/122] I think the todo's that are left, we can move on without them for now. --- src/cascadia/TerminalApp/AppLogic.cpp | 3 +-- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 5a2ec597963..ad77ccdc3cf 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -187,7 +187,7 @@ namespace winrt::TerminalApp::implementation _settings.GlobalSettings().ShowTabsInTitlebar(false); } - // TODO! These used to be in `_root->Initialized`: + // TODO! These used to be in `_root->Initialized`. Where do they belong now? { // Both LoadSettings and ReloadSettings are supposed to call this function, // but LoadSettings skips it, so that the UI starts up faster. @@ -262,7 +262,6 @@ namespace winrt::TerminalApp::implementation _warnings.push_back(newSettings.Warnings().GetAt(i)); } - // TODO! These _settingsAppArgs need to get plumbed into TerminalWindow somehow _hasSettingsStartupActions = false; const auto startupActions = newSettings.GlobalSettings().StartupActions(); if (!startupActions.empty()) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index a46ba9390ad..44ddc48c6f3 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -211,8 +211,6 @@ namespace winrt::TerminalApp::implementation _settings.GlobalSettings().ShowTabsInTitlebar(false); } - // TODO! handle startupActions - // // Pay attention, that even if some command line arguments were parsed (like launch mode), // we will not use the startup actions from settings. // While this simplifies the logic, we might want to reconsider this behavior in the future. From af14c2b75100ade347eda75582499d9caa460c2c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 31 Jan 2023 12:58:39 -0600 Subject: [PATCH 009/122] [TO PARENT] Move the page ctor call, so that it can happen after the XAML island is started. I think this can work in the parent at least --- src/cascadia/TerminalApp/TerminalWindow.cpp | 42 ++++++++++++--------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 44ddc48c6f3..1a3654250f6 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -147,14 +147,35 @@ namespace winrt::TerminalApp::implementation // make sure that there's a terminal page for callers of // SetTitleBarContent _isElevated = ::Microsoft::Console::Utils::IsElevated(); - _root = winrt::make_self(); } // Method Description: // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT TerminalWindow::Initialize(HWND hwnd) { + _root = winrt::make_self(); _dialog = ContentDialog{}; + + // Pass commandline args into the TerminalPage. + // * we first got these in SetStartupCommandline, but at that time, we + // don't have a page yet. + if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + { + _root->SetPersistedLayoutIdx(idx.value()); + } + _root->SetStartupActions(_appArgs.GetStartupActions()); + + // Check if we were started as a COM server for inbound connections of console sessions + // coming out of the operating system default application feature. If so, + // tell TerminalPage to start the listener as we have to make sure it has the chance + // to register a handler to hear about the requests first and is all ready to receive + // them before the COM server registers itself. Otherwise, the request might come + // in and be routed to an event with no handlers or a non-ready Page. + if (_appArgs.IsHandoffListener()) + { + _root->SetInboundListener(true); + } + return _root->Initialize(hwnd); } // Method Description: @@ -961,22 +982,9 @@ namespace winrt::TerminalApp::implementation // then it contains only the executable name and no other arguments. _hasCommandLineArguments = args.size() > 1; _appArgs.ValidateStartupCommands(); - if (const auto idx = _appArgs.GetPersistedLayoutIdx()) - { - _root->SetPersistedLayoutIdx(idx.value()); - } - _root->SetStartupActions(_appArgs.GetStartupActions()); - - // Check if we were started as a COM server for inbound connections of console sessions - // coming out of the operating system default application feature. If so, - // tell TerminalPage to start the listener as we have to make sure it has the chance - // to register a handler to hear about the requests first and is all ready to receive - // them before the COM server registers itself. Otherwise, the request might come - // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs.IsHandoffListener()) - { - _root->SetInboundListener(true); - } + + // DON'T pass the args into the page yet. It doesn't exist yet. + // Instead, we'll handle that in Initialize, when we first instantiate the page. } return result; From d456210f375d866bf951f6f403b83e9f6f6aa2bd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 31 Jan 2023 12:59:30 -0600 Subject: [PATCH 010/122] Use the single AppLogic for all windows. Remove the _app references. It launches and crashes immediately. We'll keep shuffling code. --- src/cascadia/WindowsTerminal/AppHost.cpp | 50 +++++++++++-------- src/cascadia/WindowsTerminal/AppHost.h | 2 +- .../WindowsTerminal/WindowEmperor.cpp | 4 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 4 +- src/cascadia/WindowsTerminal/WindowThread.h | 2 +- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 60ded0d3fe8..ba4e0717c99 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -28,15 +28,15 @@ using namespace std::chrono_literals; // "If the high-order bit is 1, the key is down; otherwise, it is up." static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; -AppHost::AppHost() noexcept : - _app{}, +AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept : + // _app{}, _windowManager{}, - _appLogic{ nullptr }, // don't make one, we're going to take a ref on app's + _appLogic{ logic }, // don't make one, we're going to take a ref on app's _windowLogic{ nullptr }, _window{ nullptr }, _getWindowLayoutThrottler{} // this will get set if we become the monarch { - _appLogic = _app.Logic(); // get a ref to app's logic + // _appLogic = _app.Logic(); // get a ref to app's logic // Inform the WindowManager that it can use us to find the target window for // a set of commandline args. This needs to be done before @@ -148,8 +148,10 @@ AppHost::~AppHost() _showHideWindowThrottler.reset(); _window = nullptr; - _app.Close(); - _app = nullptr; + /// TODO! + // + // _app.Close(); + // _app = nullptr; } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) @@ -354,10 +356,12 @@ void AppHost::_HandleCommandlineArgs() // - void AppHost::Initialize() { + // You aren't allowed to do ANY XAML before this line!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! _window->Initialize(); if (auto withWindow{ _windowLogic.try_as() }) { + // You aren't allowed to do anything with the TerminalPage before this line!!!!!!! withWindow->Initialize(_window->GetHandle()); } @@ -455,23 +459,25 @@ void AppHost::Initialize() _window->SetContent(_windowLogic.GetRoot()); _window->OnAppInitialized(); - // BODGY - // - // We've got a weird crash that happens terribly inconsistently, but pretty - // readily on migrie's laptop, only in Debug mode. Apparently, there's some - // weird ref-counting magic that goes on during teardown, and our - // Application doesn't get closed quite right, which can cause us to crash - // into the debugger. This of course, only happens on exit, and happens - // somewhere in the XamlHost.dll code. + // TODO! is this still an issue? // - // Crazily, if we _manually leak the Application_ here, then the crash - // doesn't happen. This doesn't matter, because we really want the - // Application to live for _the entire lifetime of the process_, so the only - // time when this object would actually need to get cleaned up is _during - // exit_. So we can safely leak this Application object, and have it just - // get cleaned up normally when our process exits. - auto a{ _app }; - ::winrt::detach_abi(a); + // // BODGY + // // + // // We've got a weird crash that happens terribly inconsistently, but pretty + // // readily on migrie's laptop, only in Debug mode. Apparently, there's some + // // weird ref-counting magic that goes on during teardown, and our + // // Application doesn't get closed quite right, which can cause us to crash + // // into the debugger. This of course, only happens on exit, and happens + // // somewhere in the XamlHost.dll code. + // // + // // Crazily, if we _manually leak the Application_ here, then the crash + // // doesn't happen. This doesn't matter, because we really want the + // // Application to live for _the entire lifetime of the process_, so the only + // // time when this object would actually need to get cleaned up is _during + // // exit_. So we can safely leak this Application object, and have it just + // // get cleaned up normally when our process exits. + // auto a{ _app }; + // ::winrt::detach_abi(a); } // Method Description: diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 193b75953f7..a48bb247498 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -9,7 +9,7 @@ class AppHost { public: - AppHost() noexcept; + AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept; virtual ~AppHost(); void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 56781e6c074..63e9a968e1a 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -18,8 +18,8 @@ bool WindowEmperor::ShouldExit() void WindowEmperor::WaitForWindows() { - std::thread one{ []() { - WindowThread foo{}; + std::thread one{ [this]() { + WindowThread foo{ _app.Logic() }; return foo.WindowProc(); } }; one.join(); diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index e26123bdd8e..be218fca10b 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -4,8 +4,8 @@ #include "pch.h" #include "WindowThread.h" -WindowThread::WindowThread() : - _host{} +WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic) : + _host{ logic } { } diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 979716174a0..5a67e407b47 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -7,7 +7,7 @@ class WindowThread { public: - WindowThread(); + WindowThread(const winrt::TerminalApp::AppLogic& logic); int WindowProc(); private: From f5b030c28ca95e60782c82a3a090d990e18e9dc7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 31 Jan 2023 15:21:29 -0600 Subject: [PATCH 011/122] it runs! --- src/cascadia/TerminalApp/TerminalPage.cpp | 160 ++++++-------- src/cascadia/TerminalApp/TerminalPage.h | 42 +++- src/cascadia/TerminalApp/TerminalPage.idl | 15 +- src/cascadia/TerminalApp/TerminalPage.xaml | 8 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 197 ++++++++++++------ src/cascadia/TerminalApp/TerminalWindow.h | 25 ++- src/cascadia/TerminalApp/TerminalWindow.idl | 8 +- src/cascadia/WindowsTerminal/AppHost.cpp | 29 +-- src/cascadia/WindowsTerminal/IslandWindow.cpp | 7 +- 9 files changed, 293 insertions(+), 198 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e382d1e7743..f272c743d55 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1847,11 +1847,11 @@ namespace winrt::TerminalApp::implementation } // If the user set a custom name, save it - if (_WindowName != L"") + if (_WindowProperties.WindowName() != L"") { ActionAndArgs action; action.Action(ShortcutAction::RenameWindow); - RenameWindowArgs args{ _WindowName }; + RenameWindowArgs args{ _WindowProperties.WindowName() }; action.Args(args); actions.emplace_back(std::move(action)); @@ -3060,7 +3060,7 @@ namespace winrt::TerminalApp::implementation auto listCopy = actions; _startupActions = winrt::single_threaded_vector(std::move(listCopy)); const auto afterSize = _startupActions.Size(); - assert(initSize == afterSize);// you donkey + assert(initSize == afterSize); // you donkey } // Routine Description: @@ -3860,69 +3860,6 @@ namespace winrt::TerminalApp::implementation } } - // WindowName is a otherwise generic WINRT_OBSERVABLE_PROPERTY, but it needs - // to raise a PropertyChanged for WindowNameForDisplay, instead of - // WindowName. - winrt::hstring TerminalPage::WindowName() const noexcept - { - return _WindowName; - } - - winrt::fire_and_forget TerminalPage::WindowName(const winrt::hstring& value) - { - const auto oldIsQuakeMode = IsQuakeWindow(); - const auto changed = _WindowName != value; - if (changed) - { - _WindowName = value; - } - auto weakThis{ get_weak() }; - // On the foreground thread, raise property changed notifications, and - // display the success toast. - co_await wil::resume_foreground(Dispatcher()); - if (auto page{ weakThis.get() }) - { - if (changed) - { - page->_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowName" }); - page->_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); - - // DON'T display the confirmation if this is the name we were - // given on startup! - if (page->_startupState == StartupState::Initialized) - { - page->IdentifyWindow(); - - // If we're entering quake mode, or leaving it - if (IsQuakeWindow() != oldIsQuakeMode) - { - // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode - // If we're entering Quake Mode from Focus Mode, then this will do nothing - // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing - SetFocusMode(true); - _IsQuakeWindowChangedHandlers(*this, nullptr); - } - } - } - } - } - - // WindowId is a otherwise generic WINRT_OBSERVABLE_PROPERTY, but it needs - // to raise a PropertyChanged for WindowIdForDisplay, instead of - // WindowId. - uint64_t TerminalPage::WindowId() const noexcept - { - return _WindowId; - } - void TerminalPage::WindowId(const uint64_t& value) - { - if (_WindowId != value) - { - _WindowId = value; - _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); - } - } - void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx) { _loadFromPersistedLayoutIdx = idx; @@ -3933,32 +3870,6 @@ namespace winrt::TerminalApp::implementation _numOpenWindows = num; } - // Method Description: - // - Returns a label like "Window: 1234" for the ID of this window - // Arguments: - // - - // Return Value: - // - a string for displaying the name of the window. - winrt::hstring TerminalPage::WindowIdForDisplay() const noexcept - { - return winrt::hstring{ fmt::format(L"{}: {}", - std::wstring_view(RS_(L"WindowIdLabel")), - _WindowId) }; - } - - // Method Description: - // - Returns a label like "" when the window has no name, or the name of the window. - // Arguments: - // - - // Return Value: - // - a string for displaying the name of the window. - winrt::hstring TerminalPage::WindowNameForDisplay() const noexcept - { - return _WindowName.empty() ? - winrt::hstring{ fmt::format(L"<{}>", RS_(L"UnnamedWindowName")) } : - _WindowName; - } - // Method Description: // - Called when an attempt to rename the window has failed. This will open // the toast displaying a message to the user that the attempt to rename @@ -4070,17 +3981,12 @@ namespace winrt::TerminalApp::implementation else if (key == Windows::System::VirtualKey::Escape) { // User wants to discard the changes they made - WindowRenamerTextBox().Text(WindowName()); + WindowRenamerTextBox().Text(WindowProperties().WindowName()); WindowRenamer().IsOpen(false); _renamerPressedEnter = false; } } - bool TerminalPage::IsQuakeWindow() const noexcept - { - return WindowName() == QuakeWindowName; - } - // Method Description: // - This function stops people from duplicating the base profile, because // it gets ~ ~ weird ~ ~ when they do. Remove when TODO GH#5047 is done. @@ -4456,4 +4362,62 @@ namespace winrt::TerminalApp::implementation _activated = activated; _updateThemeColors(); } + + TerminalApp::IWindowProperties TerminalPage::WindowProperties() + { + return _WindowProperties; + } + void TerminalPage::WindowProperties(const TerminalApp::IWindowProperties& props) + { + _WindowProperties = props; + // if (const auto& observable{ props.try_as() }) + // { + // observable.PropertyChanged([weakThis = get_weak()](auto& /*sender*/, auto& e) { + // if (auto page{ weakThis.get() }) + // { + // if (e.PropertyName() == L"WindowName") + // { + // page->_windowNameChanged(); + // } + // } + // }); + // } + } + + winrt::fire_and_forget TerminalPage::WindowNameChanged() + { + auto weakThis{ get_weak() }; + // On the foreground thread, raise property changed notifications, and + // display the success toast. + co_await wil::resume_foreground(Dispatcher()); + if (auto page{ weakThis.get() }) + { + _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowName" }); + _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); + _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); + + // if (changed) + // { + // DON'T display the confirmation if this is the name we were + // given on startup! + if (page->_startupState == StartupState::Initialized) + { + page->IdentifyWindow(); + + // TODO! This is wacky. Reconcile with oldIsQuakeMode in TerminalWindow::WindowName + + // // If we're entering quake mode, or leaving it + // if (IsQuakeWindow() != oldIsQuakeMode) + // { + // // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode + // // If we're entering Quake Mode from Focus Mode, then this will do nothing + // // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing + // SetFocusMode(true); + // _IsQuakeWindowChangedHandlers(*this, nullptr); + // } + } + // } + } + } + } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 9c1705706a0..99415eb333c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -50,6 +50,22 @@ namespace winrt::TerminalApp::implementation _ProposedName{ name } {}; }; + // struct WindowProperties : WindowPropertiesT + // { + // // Normally, WindowName and WindowId would be + // // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise + // // WindowNameForDisplay and WindowIdForDisplay instead + // winrt::hstring WindowName() const noexcept; + // winrt::fire_and_forget WindowName(const winrt::hstring& value); + // uint64_t WindowId() const noexcept; + // void WindowId(const uint64_t& value); + // winrt::hstring WindowIdForDisplay() const noexcept; + // winrt::hstring WindowNameForDisplay() const noexcept; + // bool IsQuakeWindow() const noexcept; + + // public: + // }; + struct TerminalPage : TerminalPageT { public: @@ -120,17 +136,17 @@ namespace winrt::TerminalApp::implementation // Normally, WindowName and WindowId would be // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise // WindowNameForDisplay and WindowIdForDisplay instead - winrt::hstring WindowName() const noexcept; - winrt::fire_and_forget WindowName(const winrt::hstring& value); - uint64_t WindowId() const noexcept; - void WindowId(const uint64_t& value); + // winrt::hstring WindowName() const noexcept; + // winrt::fire_and_forget WindowName(const winrt::hstring& value); + // uint64_t WindowId() const noexcept; + // void WindowId(const uint64_t& value); void SetNumberOfOpenWindows(const uint64_t value); void SetPersistedLayoutIdx(const uint32_t value); - winrt::hstring WindowIdForDisplay() const noexcept; - winrt::hstring WindowNameForDisplay() const noexcept; - bool IsQuakeWindow() const noexcept; + // winrt::hstring WindowIdForDisplay() const noexcept; + // winrt::hstring WindowNameForDisplay() const noexcept; + // bool IsQuakeWindow() const noexcept; bool IsElevated() const noexcept; void OpenSettingsUI(); @@ -138,6 +154,12 @@ namespace winrt::TerminalApp::implementation bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); + TerminalApp::IWindowProperties WindowProperties(); + void WindowProperties(const TerminalApp::IWindowProperties& props); + winrt::fire_and_forget WindowNameChanged(); + + // WINRT_PROPERTY(TerminalApp::IWindowProperties, WindowProperties, nullptr); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -192,8 +214,8 @@ namespace winrt::TerminalApp::implementation bool _isFullscreen{ false }; bool _isMaximized{ false }; bool _isAlwaysOnTop{ false }; - winrt::hstring _WindowName{}; - uint64_t _WindowId{ 0 }; + // winrt::hstring _WindowName{}; + // uint64_t _WindowId{ 0 }; std::optional _loadFromPersistedLayoutIdx{}; uint64_t _numOpenWindows{ 0 }; @@ -230,6 +252,8 @@ namespace winrt::TerminalApp::implementation int _renamerLayoutCount{ 0 }; bool _renamerPressedEnter{ false }; + TerminalApp::IWindowProperties _WindowProperties{ nullptr }; + winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); void _ShowAboutDialog(); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 5535c527a21..82abf118666 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -17,6 +17,15 @@ namespace TerminalApp Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); }; + interface IWindowProperties + { + String WindowName; + UInt64 WindowId; + String WindowNameForDisplay { get; }; + String WindowIdForDisplay { get; }; + Boolean IsQuakeWindow(); + }; + [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener { TerminalPage(); @@ -30,12 +39,8 @@ namespace TerminalApp Boolean AlwaysOnTop { get; }; void IdentifyWindow(); - String WindowName; - UInt64 WindowId; - String WindowNameForDisplay { get; }; - String WindowIdForDisplay { get; }; void RenameFailed(); - Boolean IsQuakeWindow(); + IWindowProperties WindowProperties { get; }; // We cannot use the default XAML APIs because we want to make sure // that there's only one application-global dialog visible at a time, diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 3434cd868ee..38d7c0f69f7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -201,10 +201,10 @@ tracked by MUX#4382 --> + Subtitle="{x:Bind WindowProperties.WindowNameForDisplay, Mode=OneWay}" /> + Text="{x:Bind WindowProperties.WindowName, Mode=OneWay}" /> diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 1a3654250f6..75e9c5fc0da 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -26,6 +26,7 @@ using namespace ::TerminalApp; namespace winrt { namespace MUX = Microsoft::UI::Xaml; + namespace WUX = Windows::UI::Xaml; using IInspectable = Windows::Foundation::IInspectable; } @@ -154,6 +155,7 @@ namespace winrt::TerminalApp::implementation HRESULT TerminalWindow::Initialize(HWND hwnd) { _root = winrt::make_self(); + _root->WindowProperties(*this); _dialog = ContentDialog{}; // Pass commandline args into the TerminalPage. @@ -612,38 +614,40 @@ namespace winrt::TerminalApp::implementation commandlineSize.height); } - // GH#2061 - If the global setting "Always show tab bar" is - // set or if "Show tabs in title bar" is set, then we'll need to add - // the height of the tab bar here. - if (_settings.GlobalSettings().ShowTabsInTitlebar()) - { - // If we're showing the tabs in the titlebar, we need to use a - // TitlebarControl here to calculate how much space to reserve. - // - // We'll create a fake TitlebarControl, and we'll propose an - // available size to it with Measure(). After Measure() is called, - // the TitlebarControl's DesiredSize will contain the _unscaled_ - // size that the titlebar would like to use. We'll use that as part - // of the height calculation here. - auto titlebar = TitlebarControl{ static_cast(0) }; - titlebar.Measure({ SHRT_MAX, SHRT_MAX }); - proposedSize.Height += (titlebar.DesiredSize().Height) * scale; - } - else if (_settings.GlobalSettings().AlwaysShowTabs()) - { - // Otherwise, let's use a TabRowControl to calculate how much extra - // space we'll need. - // - // Similarly to above, we'll measure it with an arbitrarily large - // available space, to make sure we get all the space it wants. - auto tabControl = TabRowControl(); - tabControl.Measure({ SHRT_MAX, SHRT_MAX }); - - // For whatever reason, there's about 10px of unaccounted-for space - // in the application. I couldn't tell you where these 10px are - // coming from, but they need to be included in this math. - proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; - } + // TODO! + // + // // GH#2061 - If the global setting "Always show tab bar" is + // // set or if "Show tabs in title bar" is set, then we'll need to add + // // the height of the tab bar here. + // if (_settings.GlobalSettings().ShowTabsInTitlebar()) + // { + // // If we're showing the tabs in the titlebar, we need to use a + // // TitlebarControl here to calculate how much space to reserve. + // // + // // We'll create a fake TitlebarControl, and we'll propose an + // // available size to it with Measure(). After Measure() is called, + // // the TitlebarControl's DesiredSize will contain the _unscaled_ + // // size that the titlebar would like to use. We'll use that as part + // // of the height calculation here. + // auto titlebar = TitlebarControl{ static_cast(0) }; + // titlebar.Measure({ SHRT_MAX, SHRT_MAX }); + // proposedSize.Height += (titlebar.DesiredSize().Height) * scale; + // } + // else if (_settings.GlobalSettings().AlwaysShowTabs()) + // { + // // Otherwise, let's use a TabRowControl to calculate how much extra + // // space we'll need. + // // + // // Similarly to above, we'll measure it with an arbitrarily large + // // available space, to make sure we get all the space it wants. + // auto tabControl = TabRowControl(); + // tabControl.Measure({ SHRT_MAX, SHRT_MAX }); + + // // For whatever reason, there's about 10px of unaccounted-for space + // // in the application. I couldn't tell you where these 10px are + // // coming from, but they need to be included in this math. + // proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; + // } return proposedSize; } @@ -900,7 +904,10 @@ namespace winrt::TerminalApp::implementation } void TerminalWindow::WindowActivated(const bool activated) { - _root->WindowActivated(activated); + if (_root) + { + _root->WindowActivated(activated); + } } // Method Description: @@ -1088,28 +1095,28 @@ namespace winrt::TerminalApp::implementation } } - winrt::hstring TerminalWindow::WindowName() - { - return _root ? _root->WindowName() : L""; - } - void TerminalWindow::WindowName(const winrt::hstring& name) - { - if (_root) - { - _root->WindowName(name); - } - } - uint64_t TerminalWindow::WindowId() - { - return _root ? _root->WindowId() : 0; - } - void TerminalWindow::WindowId(const uint64_t& id) - { - if (_root) - { - _root->WindowId(id); - } - } + // winrt::hstring TerminalWindow::WindowName() + // { + // return _root ? _root->WindowName() : L""; + // } + // void TerminalWindow::WindowName(const winrt::hstring& name) + // { + // if (_root) + // { + // _root->WindowName(name); + // } + // } + // uint64_t TerminalWindow::WindowId() + // { + // return _root ? _root->WindowId() : 0; + // } + // void TerminalWindow::WindowId(const uint64_t& id) + // { + // if (_root) + // { + // _root->WindowId(id); + // } + // } void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) { @@ -1136,10 +1143,10 @@ namespace winrt::TerminalApp::implementation } } - bool TerminalWindow::IsQuakeWindow() const noexcept - { - return _root->IsQuakeWindow(); - } + // bool TerminalWindow::IsQuakeWindow() const noexcept + // { + // return _root->IsQuakeWindow(); + // } void TerminalWindow::RequestExitFullscreen() { @@ -1160,4 +1167,76 @@ namespace winrt::TerminalApp::implementation _root->SetSettings(_settings, true); } } + + //////////////////////////////////////////////////////////////////////////// + + // WindowName is a otherwise generic WINRT_OBSERVABLE_PROPERTY, but it needs + // to raise a PropertyChanged for WindowNameForDisplay, instead of + // WindowName. + winrt::hstring TerminalWindow::WindowName() const noexcept + { + return _WindowName; + } + + void TerminalWindow::WindowName(const winrt::hstring& value) + { + // const auto oldIsQuakeMode = IsQuakeWindow(); + const auto changed = _WindowName != value; + if (changed) + { + _WindowName = value; + if (_root) + _root->WindowNameChanged(); // TODO! is bodge + } + } + + // WindowId is a otherwise generic WINRT_OBSERVABLE_PROPERTY, but it needs + // to raise a PropertyChanged for WindowIdForDisplay, instead of + // WindowId. + uint64_t TerminalWindow::WindowId() const noexcept + { + return _WindowId; + } + void TerminalWindow::WindowId(const uint64_t& value) + { + if (_WindowId != value) + { + _WindowId = value; + if (_root) + _root->WindowNameChanged(); // TODO! is bodge + // _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); + } + } + + // Method Description: + // - Returns a label like "Window: 1234" for the ID of this window + // Arguments: + // - + // Return Value: + // - a string for displaying the name of the window. + winrt::hstring TerminalWindow::WindowIdForDisplay() const noexcept + { + return winrt::hstring{ fmt::format(L"{}: {}", + std::wstring_view(RS_(L"WindowIdLabel")), + _WindowId) }; + } + + // Method Description: + // - Returns a label like "" when the window has no name, or the name of the window. + // Arguments: + // - + // Return Value: + // - a string for displaying the name of the window. + winrt::hstring TerminalWindow::WindowNameForDisplay() const noexcept + { + return _WindowName.empty() ? + winrt::hstring{ fmt::format(L"<{}>", RS_(L"UnnamedWindowName")) } : + _WindowName; + } + + bool TerminalWindow::IsQuakeWindow() const noexcept + { + return WindowName() == QuakeWindowName; + } + //////////////////////////////////////////////////////////////////////////// }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 9d5605d00ca..bde6e1bfac6 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -69,13 +69,13 @@ namespace winrt::TerminalApp::implementation void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); void IdentifyWindow(); void RenameFailed(); - winrt::hstring WindowName(); - void WindowName(const winrt::hstring& name); - uint64_t WindowId(); - void WindowId(const uint64_t& id); + // winrt::hstring WindowName(); + // void WindowName(const winrt::hstring& name); + // uint64_t WindowId(); + // void WindowId(const uint64_t& id); void SetPersistedLayoutIdx(const uint32_t idx); void SetNumberOfOpenWindows(const uint64_t num); - bool IsQuakeWindow() const noexcept; + // bool IsQuakeWindow() const noexcept; void RequestExitFullscreen(); Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); @@ -110,6 +110,16 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::Theme Theme(); void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); + // Normally, WindowName and WindowId would be + // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise + // WindowNameForDisplay and WindowIdForDisplay instead + winrt::hstring WindowName() const noexcept; + void WindowName(const winrt::hstring& value); + uint64_t WindowId() const noexcept; + void WindowId(const uint64_t& value); + winrt::hstring WindowIdForDisplay() const noexcept; + winrt::hstring WindowNameForDisplay() const noexcept; + bool IsQuakeWindow() const noexcept; // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. // Usually we'd just do @@ -120,6 +130,8 @@ namespace winrt::TerminalApp::implementation winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); } void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); } + // WINRT_PROPERTY(TerminalApp::WindowProperties, WindowProperties, nullptr); + TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme); private: @@ -139,6 +151,9 @@ namespace winrt::TerminalApp::implementation bool _gotSettingsStartupActions{ false }; std::vector _settingsStartupArgs{}; + winrt::hstring _WindowName{}; + uint64_t _WindowId{ 0 }; + uint64_t _numOpenWindows{ 0 }; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index fcd8cd9346f..e827dd7423d 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -29,7 +29,7 @@ namespace TerminalApp // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. - [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged + [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, IWindowProperties, Windows.UI.Xaml.Data.INotifyPropertyChanged { TerminalWindow(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); @@ -66,13 +66,13 @@ namespace TerminalApp Boolean AutoHideWindow { get; }; void IdentifyWindow(); - String WindowName; - UInt64 WindowId; + // String WindowName; + // UInt64 WindowId; void SetPersistedLayoutIdx(UInt32 idx); void SetNumberOfOpenWindows(UInt64 num); void RenameFailed(); void RequestExitFullscreen(); - Boolean IsQuakeWindow(); + // Boolean IsQuakeWindow(); Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); Boolean CenterOnLaunch { get; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index ba4e0717c99..dbf9a7ea1d4 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -121,18 +121,19 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept : _BecomeMonarch(nullptr, nullptr); } - // Create a throttled function for updating the window state, to match the - // one requested by the pty. A 200ms delay was chosen because it's the - // typical animation timeout in Windows. This does result in a delay between - // the PTY requesting a change to the window state and the Terminal - // realizing it, but should mitigate issues where the Terminal and PTY get - // de-sync'd. - _showHideWindowThrottler = std::make_shared>( - winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), - std::chrono::milliseconds(200), - [this](const bool show) { - _window->ShowWindowChanged(show); - }); + // TODO! + // // Create a throttled function for updating the window state, to match the + // // one requested by the pty. A 200ms delay was chosen because it's the + // // typical animation timeout in Windows. This does result in a delay between + // // the PTY requesting a change to the window state and the Terminal + // // realizing it, but should mitigate issues where the Terminal and PTY get + // // de-sync'd. + // _showHideWindowThrottler = std::make_shared>( + // winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), + // std::chrono::milliseconds(200), + // [this](const bool show) { + // _window->ShowWindowChanged(show); + // }); } AppHost::~AppHost() @@ -1076,6 +1077,10 @@ void AppHost::_listenForInboundConnections() winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { + if (_windowLogic.GetRoot() == nullptr) + { + co_return; + } // The hotkey MUST be registered on the main thread. It will fail otherwise! co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index a06dd7818e2..1b0defb64ee 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -403,9 +403,12 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam // - nonClientSizeScaled: the exclusive non-client size (already scaled) // Return Value: // - The total dimension -long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize) +long IslandWindow::_calculateTotalSize(const bool /*isWidth*/, const long clientSize, const long nonClientSize) { - return gsl::narrow_cast(_pfnSnapDimensionCallback(isWidth, gsl::narrow_cast(clientSize)) + nonClientSize); + return clientSize + nonClientSize; + // TODO! + // + // return gsl::narrow_cast(_pfnSnapDimensionCallback(isWidth, gsl::narrow_cast(clientSize)) + nonClientSize); } [[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept From 2c4613a4949bec608ca6eeca94635b69ababbf2c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 1 Feb 2023 08:07:29 -0600 Subject: [PATCH 012/122] wow it just... worked --- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 63e9a968e1a..c2a97a0076c 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -22,5 +22,14 @@ void WindowEmperor::WaitForWindows() WindowThread foo{ _app.Logic() }; return foo.WindowProc(); } }; + + Sleep(2000); + + std::thread two{ [this]() { + WindowThread foo{ _app.Logic() }; + return foo.WindowProc(); + } }; + one.join(); + two.join(); } From 7a3e2e098df6c7c4c245794eef8e1e36cce47d15 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 1 Feb 2023 11:48:43 -0600 Subject: [PATCH 013/122] Start rewriting WindowManager to facilitate the new process model At this point, I determined that I would need to make some big changes to AppHost and decided that it was time to commit before moving on. --- .../Microsoft.Terminal.RemotingLib.vcxproj | 7 + src/cascadia/Remoting/Monarch.cpp | 1 + src/cascadia/Remoting/Monarch.h | 22 ++ src/cascadia/Remoting/Monarch.idl | 10 + src/cascadia/Remoting/WindowManager2.cpp | 295 ++++++++++++++++++ src/cascadia/Remoting/WindowManager2.h | 33 ++ src/cascadia/Remoting/WindowManager2.idl | 17 + src/cascadia/WindowsTerminal/AppHost.cpp | 44 +-- src/cascadia/WindowsTerminal/IslandWindow.h | 1 - .../WindowsTerminal/WindowEmperor.cpp | 96 +++++- src/cascadia/WindowsTerminal/WindowEmperor.h | 23 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 3 +- src/cascadia/WindowsTerminal/WindowThread.h | 5 +- src/cascadia/inc/WindowingBehavior.h | 1 + 14 files changed, 518 insertions(+), 40 deletions(-) create mode 100644 src/cascadia/Remoting/WindowManager2.cpp create mode 100644 src/cascadia/Remoting/WindowManager2.h create mode 100644 src/cascadia/Remoting/WindowManager2.idl diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index ce78fc9c4b2..ed9d78dd450 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -51,6 +51,9 @@ WindowManager.idl + + WindowManager2.idl + Peasant.idl @@ -93,6 +96,9 @@ WindowManager.idl + + WindowManager2.idl + Peasant.idl @@ -104,6 +110,7 @@ + diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 8d69e2768e9..778ff2f6160 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -10,6 +10,7 @@ #include "ProposeCommandlineResult.h" #include "Monarch.g.cpp" +#include "WindowRequestedArgs.g.cpp" #include "../../types/inc/utils.hpp" using namespace winrt; diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index a9eac206daf..bb8fffbe900 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -6,6 +6,7 @@ #include "Monarch.g.h" #include "Peasant.h" #include "WindowActivatedArgs.h" +#include "WindowRequestedArgs.g.h" #include // We sure different GUIDs here depending on whether we're running a Release, @@ -38,6 +39,26 @@ namespace RemotingUnitTests namespace winrt::Microsoft::Terminal::Remoting::implementation { + struct WindowRequestedArgs : public WindowRequestedArgsT + { + public: + WindowRequestedArgs(const Remoting::ProposeCommandlineResult& result, const Remoting::CommandlineArgs& command) : + _Id{ result.Id() }, + _WindowName{ result.WindowName() }, + _args{ command.Commandline() }, + _CurrentDirectory{ command.CurrentDirectory() } {}; + + void Commandline(const winrt::array_view& value) { _args = { value.begin(), value.end() }; }; + winrt::com_array Commandline() { return winrt::com_array{ _args.begin(), _args.end() }; } + + WINRT_PROPERTY(Windows::Foundation::IReference, Id); + WINRT_PROPERTY(winrt::hstring, WindowName); + WINRT_PROPERTY(winrt::hstring, CurrentDirectory); + + private: + winrt::com_array _args; + }; + struct Monarch : public MonarchT { Monarch(); @@ -191,4 +212,5 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation namespace winrt::Microsoft::Terminal::Remoting::factory_implementation { BASIC_FACTORY(Monarch); + BASIC_FACTORY(WindowRequestedArgs); } diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 2ef09807080..adc29036a9a 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -18,6 +18,16 @@ namespace Microsoft.Terminal.Remoting Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode } + [default_interface] runtimeclass WindowRequestedArgs { + WindowRequestedArgs(ProposeCommandlineResult result, CommandlineArgs command); + + Windows.Foundation.IReference Id { get; }; + String WindowName { get; }; + + String[] Commandline { get; }; + String CurrentDirectory { get; }; + } + [default_interface] runtimeclass SummonWindowSelectionArgs { SummonWindowSelectionArgs(); SummonWindowSelectionArgs(String windowName); diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp new file mode 100644 index 00000000000..0d279eb33c1 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "WindowManager2.h" + +#include "../inc/WindowingBehavior.h" +#include "MonarchFactory.h" + +#include "CommandlineArgs.h" +#include "FindTargetWindowArgs.h" +#include "ProposeCommandlineResult.h" + +#include "WindowManager2.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + WindowManager2::WindowManager2() + { + } + + void WindowManager2::_createMonarch() + { + // Heads up! This only works because we're using + // "metadata-based-marshalling" for our WinRT types. That means the OS is + // using the .winmd file we generate to figure out the proxy/stub + // definitions for our types automatically. This only works in the following + // cases: + // + // * If we're running unpackaged: the .winmd must be a sibling of the .exe + // * If we're running packaged: the .winmd must be in the package root + _monarch = create_instance(Monarch_clsid, + CLSCTX_LOCAL_SERVER); + } + + void WindowManager2::_registerAsMonarch() + { + winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, + winrt::make<::MonarchFactory>().get(), + CLSCTX_LOCAL_SERVER, + REGCLS_MULTIPLEUSE, + &_registrationHostClass)); + } + + Remoting::ProposeCommandlineResult WindowManager2::ProposeCommandline2(const Remoting::CommandlineArgs& args) + { + bool shouldCreateWindow = false; + + _createMonarch(); + if (_monarch == nullptr) + { + // No pre-existing instance. + + // Raise an event, to ask how to handle this commandline. We can't ask + // the app ourselves - we exist isolated from that knowledge (and + // dependency hell). The WindowManager will raise this up to the app + // host, which will then ask the AppLogic, who will then parse the + // commandline and determine the provided ID of the window. + auto findWindowArgs{ winrt::make_self(args) }; + + // This is handled by some handler in-proc + _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); + // { + // const auto targetWindow = _appLogic.FindTargetWindow(findWindowArgs->Args().Commandline()); + // findWindowArgs->ResultTargetWindow(targetWindow.WindowId()); + // findWindowArgs->ResultTargetWindowName(targetWindow.WindowName()); + // } + + // After the event was handled, ResultTargetWindow() will be filled with + // the parsed result. + const auto targetWindow = findWindowArgs->ResultTargetWindow(); + const auto targetWindowName = findWindowArgs->ResultTargetWindowName(); + + if (targetWindow == WindowingBehaviorUseNone) + { + // This commandline doesn't deserve a window. Don't make a monarch + // either. + shouldCreateWindow = false; + + return *winrt::make_self(shouldCreateWindow); + // return; + } + else + { + // This commandline _does_ want a window, which means we do want + // to create a window, and a monarch. + // + // Congrats! This is now THE PROCESS. It's the only one that's + // getting any windows. + _registerAsMonarch(); + _createMonarch(); + if (!_monarch) + { + // TODO! something catastrophically bad happened here. + } + + // I don't _really_ think we need to propose this again. WE + // basically did that work above. We've already gotten the + // result - + // * we don't care about the name or ID, we want a new window. + + //Remoting::ProposeCommandlineResult result = _monarch.ProposeCommandline(args); + + // TODO! So, we wanted a new peasant. Cool! + // + // We need to fill in args.ResultTargetWindow, + // args.ResultTargetWindowName so that we can create the new window + // with those values. + shouldCreateWindow = true; + auto result = winrt::make_self(shouldCreateWindow); + result->Id(); + result->WindowName(); + return *result; + } + } + else + { + // We connectecd to a monarch instance, not us though. + + shouldCreateWindow = false; + std::optional givenID; + winrt::hstring givenName{}; + + // Send the commandline over to the monarch process + _proposeToMonarch(args, givenID, givenName); + + // Our job is done. Either the monarch is going to run the + // commandline in an existing window, or a new one, but either way, + // this process doesn't need to make a new window. + + return *winrt::make_self(shouldCreateWindow); + } + } + + void WindowManager2::_proposeToMonarch(const Remoting::CommandlineArgs& args, + std::optional& /*givenID*/, + winrt::hstring& /*givenName*/) + { + // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below. + static constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); + static constexpr auto RPC_CALL_FAILED_HR = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED); + + // The monarch may respond back "you should be a new + // window, with ID,name of (id, name)". Really the responses are: + // * You should not create a new window + // * Create a new window (but without a given ID or name). The + // Monarch will assign your ID/name later + // * Create a new window, and you'll have this ID or name + // - This is the case where the user provides `wt -w 1`, and + // there's no existing window 1 + + // You can emulate the monarch dying by: starting a terminal, sticking a + // breakpoint in + // TerminalApp!winrt::TerminalApp::implementation::AppLogic::_doFindTargetWindow, + // starting a defterm, and when that BP gets hit, kill the original + // monarch, and see what happens here. + + auto proposedCommandline = false; + Remoting::ProposeCommandlineResult result{ nullptr }; + auto attempts = 0; + while (!proposedCommandline) + { + try + { + // MSFT:38542548 _We believe_ that this is the source of the + // crash here. After we get the result, stash its values into a + // local copy, so that we can check them later. If the Monarch + // dies between now and the inspection of + // `result.ShouldCreateWindow` below, we don't want to explode + // (since _proposeToMonarch is not try/caught). + auto outOfProcResult = _monarch.ProposeCommandline(args); + result = winrt::make(outOfProcResult); + + proposedCommandline = true; + } + catch (...) + { + // We did not successfully ask the king what to do. This could + // be for many reasons. Most commonly, the monarch died as we + // were talking to it. That could be a RPC_SERVER_UNAVAILABLE_HR + // or RPC_CALL_FAILED_HR (GH#12666). We also saw a + // RPC_S_CALL_FAILED_DNE in GH#11790. Ultimately, if this is + // gonna fail, we want to just try again, regardless of the + // cause. That's why we're no longer checking what the exception + // was, we're just always gonna try again regardless. + // + // They hopefully just died here. That's okay, let's just go + // ask the next in the line of succession. At the very worst, + // we'll find _us_, (likely last in the line). + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_proposeToMonarch_unexpectedExceptionFromKing", + TraceLoggingInt32(attempts, "attempts", "How many times we've tried"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + LOG_CAUGHT_EXCEPTION(); + attempts++; + + if (attempts >= 10) + { + // TODO! + break; + } + // if (attempts >= 10) + // { + // // We've tried 10 times to find the monarch, failing each + // // time. Since we have no idea why, we're guessing that in + // // this case, there's just a Monarch registered that's + // // misbehaving. In this case, just fall back to + // // "IsolatedMonarchMode" - we can't trust the currently + // // registered one. + // TraceLoggingWrite(g_hRemotingProvider, + // "WindowManager_TooManyAttempts_NullMonarchIsolateMode", + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // _monarch = winrt::make(); + // _createCallbacks(); + // } + // else + // { + // // We failed to ask the monarch. It must have died. Try and + // // find the real monarch. Don't perform an election, that + // // assumes we have a peasant, which we don't yet. + // _createMonarchAndCallbacks(); + // // _createMonarchAndCallbacks will initialize _isKing + // } + // if (_isKing) + // { + // // We became the king. We don't need to ProposeCommandline to ourself, we're just + // // going to do it. + // // + // // Return early, because there's nothing else for us to do here. + // TraceLoggingWrite(g_hRemotingProvider, + // "WindowManager_proposeToMonarch_becameKing", + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // // In WindowManager::ProposeCommandline, had we been the + // // king originally, we would have started by setting + // // this to true. We became the monarch here, so set it + // // here as well. + // _shouldCreateWindow = true; + // return; + // } + + // // Here, we created the new monarch, it wasn't us, so we're + // // gonna go through the while loop again and ask the new + // // king. + // TraceLoggingWrite(g_hRemotingProvider, + // "WindowManager_proposeToMonarch_tryAgain", + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } + } + + // // Here, the monarch (not us) has replied to the message. Get the + // // valuables out of the response: + // _shouldCreateWindow = result.ShouldCreateWindow(); + // if (result.Id()) + // { + // givenID = result.Id().Value(); + // } + // givenName = result.WindowName(); + + // // TraceLogging doesn't have a good solution for logging an + // // optional. So we have to repeat the calls here: + // if (givenID) + // { + // TraceLoggingWrite(g_hRemotingProvider, + // "WindowManager_ProposeCommandline", + // TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + // TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), + // TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + // } + // else + // { + // TraceLoggingWrite(g_hRemotingProvider, + // "WindowManager_ProposeCommandline", + // TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + // TraceLoggingPointer(nullptr, "Id", "No ID provided"), + // TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + // } + } +} diff --git a/src/cascadia/Remoting/WindowManager2.h b/src/cascadia/Remoting/WindowManager2.h new file mode 100644 index 00000000000..f78d7104a5b --- /dev/null +++ b/src/cascadia/Remoting/WindowManager2.h @@ -0,0 +1,33 @@ + +#pragma once + +#include "WindowManager2.g.h" +#include "Peasant.h" +#include "Monarch.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct WindowManager2 : public WindowManager2T + { + public: + WindowManager2(); + winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline2(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + + private: + DWORD _registrationHostClass{ 0 }; + winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr }; + + void _createMonarch(); + void _registerAsMonarch(); + + void _proposeToMonarch(const Remoting::CommandlineArgs& args, + std::optional& givenID, + winrt::hstring& givenName); + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(WindowManager2); +} diff --git a/src/cascadia/Remoting/WindowManager2.idl b/src/cascadia/Remoting/WindowManager2.idl new file mode 100644 index 00000000000..06ec15fd517 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager2.idl @@ -0,0 +1,17 @@ +import "Peasant.idl"; +import "Monarch.idl"; + + +namespace Microsoft.Terminal.Remoting +{ + [default_interface] runtimeclass WindowManager2 + { + WindowManager2(); + + ProposeCommandlineResult ProposeCommandline2(CommandlineArgs args); + + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; + + + }; +} diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index dbf9a7ea1d4..23fcb6fc54b 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -182,27 +182,27 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& } } -void _buildArgsFromCommandline(std::vector& args) -{ - if (auto commandline{ GetCommandLineW() }) - { - auto argc = 0; - - // Get the argv, and turn them into a hstring array to pass to the app. - wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - if (argv) - { - for (auto& elem : wil::make_range(argv.get(), argc)) - { - args.emplace_back(elem); - } - } - } - if (args.empty()) - { - args.emplace_back(L"wt.exe"); - } -} +// void _buildArgsFromCommandline(std::vector& args) +// { +// if (auto commandline{ GetCommandLineW() }) +// { +// auto argc = 0; + +// // Get the argv, and turn them into a hstring array to pass to the app. +// wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; +// if (argv) +// { +// for (auto& elem : wil::make_range(argv.get(), argc)) +// { +// args.emplace_back(elem); +// } +// } +// } +// if (args.empty()) +// { +// args.emplace_back(L"wt.exe"); +// } +// } // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to @@ -224,7 +224,7 @@ void _buildArgsFromCommandline(std::vector& args) void AppHost::_HandleCommandlineArgs() { std::vector args; - _buildArgsFromCommandline(args); + // _buildArgsFromCommandline(args); auto cwd{ wil::GetCurrentDirectoryW() }; Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 12100cc78ba..1fe0c4e60cb 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -3,7 +3,6 @@ #include "pch.h" #include "BaseWindow.h" -#include void SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index c2a97a0076c..09c8ca2100f 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -3,11 +3,73 @@ #include "pch.h" #include "WindowEmperor.h" -#include "WindowThread.h" + +// #include "MonarchFactory.h" +// #include "CommandlineArgs.h" +#include "../inc/WindowingBehavior.h" +// #include "FindTargetWindowArgs.h" +// #include "ProposeCommandlineResult.h" + +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; WindowEmperor::WindowEmperor() noexcept : _app{} { + _manager.FindTargetWindowRequested([this](const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& findWindowArgs) { + { + const auto targetWindow = _app.Logic().FindTargetWindow(findWindowArgs.Args().Commandline()); + findWindowArgs.ResultTargetWindow(targetWindow.WindowId()); + findWindowArgs.ResultTargetWindowName(targetWindow.WindowName()); + } + }); +} + +void _buildArgsFromCommandline(std::vector& args) +{ + if (auto commandline{ GetCommandLineW() }) + { + auto argc = 0; + + // Get the argv, and turn them into a hstring array to pass to the app. + wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + if (argv) + { + for (auto& elem : wil::make_range(argv.get(), argc)) + { + args.emplace_back(elem); + } + } + } + if (args.empty()) + { + args.emplace_back(L"wt.exe"); + } +} + +bool WindowEmperor::HandleCommandlineArgs() +{ + std::vector args; + _buildArgsFromCommandline(args); + auto cwd{ wil::GetCurrentDirectoryW() }; + + Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; + // _windowManager.ProposeCommandline(eventArgs); + const auto result = _manager.ProposeCommandline2(eventArgs); + + // TODO! createWindow is false in cases like wt --help. Figure that out. + + if (result.ShouldCreateWindow()) + { + CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }); + } + + return result.ShouldCreateWindow(); } bool WindowEmperor::ShouldExit() @@ -18,18 +80,28 @@ bool WindowEmperor::ShouldExit() void WindowEmperor::WaitForWindows() { - std::thread one{ [this]() { - WindowThread foo{ _app.Logic() }; - return foo.WindowProc(); - } }; + // std::thread one{ [this]() { + // WindowThread foo{ _app.Logic() }; + // return foo.WindowProc(); + // } }; - Sleep(2000); + // Sleep(2000); - std::thread two{ [this]() { - WindowThread foo{ _app.Logic() }; - return foo.WindowProc(); - } }; + // std::thread two{ [this]() { + // WindowThread foo{ _app.Logic() }; + // return foo.WindowProc(); + // } }; - one.join(); - two.join(); + // one.join(); + // two.join(); + + Sleep(10000); +} + +void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) +{ + _threads.emplace_back( [this, args]() { + WindowThread foo{ _app.Logic(), args }; + return foo.WindowProc(); + } ); } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index e2c80a2a8f0..16c762a6d0a 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -1,8 +1,20 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +Class Name: +- WindowEmperor.h + +Abstract: +- TODO! + +--*/ + +#pragma once #include "pch.h" +#include "WindowThread.h" + class WindowEmperor { public: @@ -10,6 +22,13 @@ class WindowEmperor bool ShouldExit(); void WaitForWindows(); + bool HandleCommandlineArgs(); + void CreateNewWindowThread(winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args); + private: winrt::TerminalApp::App _app; + winrt::Microsoft::Terminal::Remoting::WindowManager2 _manager; + + // std::vector> _threads; + std::vector _threads; }; diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index be218fca10b..adb7f1c4a8f 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -4,7 +4,8 @@ #include "pch.h" #include "WindowThread.h" -WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic) : +WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic, + winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs /*args*/) : _host{ logic } { } diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 5a67e407b47..e6c9d27b3a8 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. - +#pragma once #include "pch.h" #include "AppHost.h" class WindowThread { public: - WindowThread(const winrt::TerminalApp::AppLogic& logic); + WindowThread(const winrt::TerminalApp::AppLogic& logic, + winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args); int WindowProc(); private: diff --git a/src/cascadia/inc/WindowingBehavior.h b/src/cascadia/inc/WindowingBehavior.h index 04260893191..43515e7b468 100644 --- a/src/cascadia/inc/WindowingBehavior.h +++ b/src/cascadia/inc/WindowingBehavior.h @@ -9,5 +9,6 @@ constexpr int32_t WindowingBehaviorUseNew{ -1 }; constexpr int32_t WindowingBehaviorUseExisting{ -2 }; constexpr int32_t WindowingBehaviorUseAnyExisting{ -3 }; constexpr int32_t WindowingBehaviorUseName{ -4 }; +constexpr int32_t WindowingBehaviorUseNone{ -5 }; static constexpr std::wstring_view QuakeWindowName{ L"_quake" }; From f655296c1e61d668a17cfcaebaae3b9692349472 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 1 Feb 2023 13:15:44 -0600 Subject: [PATCH 014/122] This does successfully get a window on the screen, which is pretty impressive It exits itself after 30s, but hey it worked --- src/cascadia/Remoting/Peasant.idl | 1 - src/cascadia/Remoting/WindowManager2.cpp | 152 +++- src/cascadia/Remoting/WindowManager2.h | 22 + src/cascadia/Remoting/WindowManager2.idl | 25 + src/cascadia/WindowsTerminal/AppHost.cpp | 709 +++++++++--------- src/cascadia/WindowsTerminal/AppHost.h | 23 +- .../WindowsTerminal/WindowEmperor.cpp | 10 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 7 +- src/cascadia/WindowsTerminal/WindowThread.h | 5 +- src/cascadia/WindowsTerminal/main.cpp | 5 +- 10 files changed, 597 insertions(+), 362 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index ec87c85188d..4a94f1e2255 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -43,7 +43,6 @@ namespace Microsoft.Terminal.Remoting ToMouse, }; - [default_interface] runtimeclass SummonWindowBehavior { SummonWindowBehavior(); Boolean MoveToCurrentDesktop; diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index 0d279eb33c1..bd410014a97 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -25,6 +25,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager2::WindowManager2() { } + WindowManager2::~WindowManager2() + { + // IMPORTANT! Tear down the registration as soon as we exit. If we're not a + // real peasant window (the monarch passed our commandline to someone else), + // then the monarch dies, we don't want our registration becoming the active + // monarch! + CoRevokeClassObject(_registrationHostClass); + _registrationHostClass = 0; + } void WindowManager2::_createMonarch() { @@ -36,7 +45,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // * If we're running unpackaged: the .winmd must be a sibling of the .exe // * If we're running packaged: the .winmd must be in the package root - _monarch = create_instance(Monarch_clsid, + _monarch = try_create_instance(Monarch_clsid, CLSCTX_LOCAL_SERVER); } @@ -292,4 +301,145 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); // } } + + Remoting::Peasant WindowManager2::CreateAPeasant(Remoting::WindowRequestedArgs args) + { + auto p = winrt::make_self(); + if (args.Id()) + { + p->AssignID(args.Id().Value()); + } + + // If the name wasn't specified, this will be an empty string. + p->WindowName(args.WindowName()); + + p->ExecuteCommandline(*winrt::make_self(args.Commandline(), args.CurrentDirectory())); + + _monarch.AddPeasant(*p); + + // TODO! + // _peasant.GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers }); + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_CreateOurPeasant", + TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // If the peasant asks us to quit we should not try to act in future elections. + p->QuitRequested([weakThis{ get_weak() }](auto&&, auto&&) { + // if (auto wm = weakThis.get()) + // { + // wm->_monarchWaitInterrupt.SetEvent(); + // } + }); + + return *p; + } + + void WindowManager2::SignalClose(Remoting::Peasant peasant) + { + if (_monarch) + { + try + { + _monarch.SignalClose(peasant.GetID()); + } + CATCH_LOG() + } + } + + void WindowManager2::SummonWindow(const Remoting::SummonWindowSelectionArgs& args) + { + // We should only ever get called when we are the monarch, because only + // the monarch ever registers for the global hotkey. So the monarch is + // the only window that will be calling this. + _monarch.SummonWindow(args); + } + + void WindowManager2::SummonAllWindows() + { + _monarch.SummonAllWindows(); + } + + Windows::Foundation::Collections::IVectorView WindowManager2::GetPeasantInfos() + { + // We should only get called when we're the monarch since the monarch + // is the only one that knows about all peasants. + return _monarch.GetPeasantInfos(); + } + + uint64_t WindowManager2::GetNumberOfPeasants() + { + if (_monarch) + { + try + { + return _monarch.GetNumberOfPeasants(); + } + CATCH_LOG() + } + return 0; + } + + // Method Description: + // - Ask the monarch to show a notification icon. + // Arguments: + // - + // Return Value: + // - + winrt::fire_and_forget WindowManager2::RequestShowNotificationIcon(Remoting::Peasant peasant) + { + co_await winrt::resume_background(); + peasant.RequestShowNotificationIcon(); + } + + // Method Description: + // - Ask the monarch to hide its notification icon. + // Arguments: + // - + // Return Value: + // - + winrt::fire_and_forget WindowManager2::RequestHideNotificationIcon(Remoting::Peasant peasant) + { + auto strongThis{ get_strong() }; + co_await winrt::resume_background(); + peasant.RequestHideNotificationIcon(); + } + + // Method Description: + // - Ask the monarch to quit all windows. + // Arguments: + // - + // Return Value: + // - + winrt::fire_and_forget WindowManager2::RequestQuitAll(Remoting::Peasant peasant) + { + auto strongThis{ get_strong() }; + co_await winrt::resume_background(); + peasant.RequestQuitAll(); + } + + bool WindowManager2::DoesQuakeWindowExist() + { + return _monarch.DoesQuakeWindowExist(); + } + + void WindowManager2::UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant) + { + winrt::get_self(peasant)->ActiveTabTitle(title); + } + + Windows::Foundation::Collections::IVector WindowManager2::GetAllWindowLayouts() + { + if (_monarch) + { + try + { + return _monarch.GetAllWindowLayouts(); + } + CATCH_LOG() + } + return nullptr; + } } diff --git a/src/cascadia/Remoting/WindowManager2.h b/src/cascadia/Remoting/WindowManager2.h index f78d7104a5b..34a9f9b84ac 100644 --- a/src/cascadia/Remoting/WindowManager2.h +++ b/src/cascadia/Remoting/WindowManager2.h @@ -11,9 +11,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { public: WindowManager2(); + ~WindowManager2(); winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline2(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + Remoting::Peasant CreateAPeasant(Remoting::WindowRequestedArgs args); + + void SignalClose(Remoting::Peasant peasant); + void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); + void SummonAllWindows(); + Windows::Foundation::Collections::IVectorView GetPeasantInfos(); + uint64_t GetNumberOfPeasants(); + winrt::fire_and_forget RequestShowNotificationIcon(Remoting::Peasant peasant); + winrt::fire_and_forget RequestHideNotificationIcon(Remoting::Peasant peasant); + winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); + void UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant); + Windows::Foundation::Collections::IVector GetAllWindowLayouts(); + bool DoesQuakeWindowExist(); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(HideNotificationIconRequested, 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); + private: DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr }; diff --git a/src/cascadia/Remoting/WindowManager2.idl b/src/cascadia/Remoting/WindowManager2.idl index 06ec15fd517..c037d8c1b2d 100644 --- a/src/cascadia/Remoting/WindowManager2.idl +++ b/src/cascadia/Remoting/WindowManager2.idl @@ -9,9 +9,34 @@ namespace Microsoft.Terminal.Remoting WindowManager2(); ProposeCommandlineResult ProposeCommandline2(CommandlineArgs args); + Peasant CreateAPeasant(WindowRequestedArgs args); + + void SignalClose(Peasant p); + void RequestShowNotificationIcon(Peasant p); + void RequestHideNotificationIcon(Peasant p); + void UpdateActiveTabTitle(String title, Peasant p); + void RequestQuitAll(Peasant p); + + void SummonWindow(SummonWindowSelectionArgs args); + void SummonAllWindows(); + + Windows.Foundation.Collections.IVector GetAllWindowLayouts(); + Windows.Foundation.Collections.IVectorView GetPeasantInfos(); + + UInt64 GetNumberOfPeasants(); + + + Boolean DoesQuakeWindowExist(); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; + event Windows.Foundation.TypedEventHandler WindowCreated; + event Windows.Foundation.TypedEventHandler WindowClosed; + event Windows.Foundation.TypedEventHandler QuitAllRequested; + event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; + event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; + event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; + }; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 23fcb6fc54b..0979c27c2a5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -28,32 +28,38 @@ using namespace std::chrono_literals; // "If the high-order bit is 1, the key is down; otherwise, it is up." static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; -AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept : +AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, + winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, + const Remoting::WindowManager2& manager, + const Remoting::Peasant& peasant) noexcept : // _app{}, - _windowManager{}, + _windowManager2{ manager }, + _peasant{ peasant }, _appLogic{ logic }, // don't make one, we're going to take a ref on app's _windowLogic{ nullptr }, - _window{ nullptr }, - _getWindowLayoutThrottler{} // this will get set if we become the monarch + _window{ nullptr } +// _getWindowLayoutThrottler{} // this will get set if we become the monarch { // _appLogic = _app.Logic(); // get a ref to app's logic - // Inform the WindowManager that it can use us to find the target window for - // a set of commandline args. This needs to be done before - // _HandleCommandlineArgs, because WE might end up being the monarch. That - // would mean we'd need to be responsible for looking that up. - _windowManager.FindTargetWindowRequested({ this, &AppHost::_FindTargetWindow }); + // // Inform the WindowManager that it can use us to find the target window for + // // a set of commandline args. This needs to be done before + // // _HandleCommandlineArgs, because WE might end up being the monarch. That + // // would mean we'd need to be responsible for looking that up. + // _windowManager2.FindTargetWindowRequested({ this, &AppHost::_FindTargetWindow }); + + // // If there were commandline args to our process, try and process them here. + // // Do this before AppLogic::Create, otherwise this will have no effect. + // // + // // This will send our commandline to the Monarch, to ask if we should make a + // // new window or not. If not, exit immediately. + // _HandleCommandlineArgs(); + // if (!_shouldCreateWindow) + // { + // return; + // } - // If there were commandline args to our process, try and process them here. - // Do this before AppLogic::Create, otherwise this will have no effect. - // - // This will send our commandline to the Monarch, to ask if we should make a - // new window or not. If not, exit immediately. _HandleCommandlineArgs(); - if (!_shouldCreateWindow) - { - return; - } // _HandleCommandlineArgs will create a _windowLogic _useNonClientArea = _windowLogic.GetShowTabsInTitlebar(); @@ -109,17 +115,19 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept : _window->MakeWindow(); - _GetWindowLayoutRequestedToken = _windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::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()); - }); - - _revokers.BecameMonarch = _windowManager.BecameMonarch(winrt::auto_revoke, { this, &AppHost::_BecomeMonarch }); - if (_windowManager.IsMonarch()) - { - _BecomeMonarch(nullptr, nullptr); - } + // TODO! + // _GetWindowLayoutRequestedToken = _windowManager2.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::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()); + // }); + + // TODO! Where are we putting the monarch stuff now? + // _revokers.BecameMonarch = _windowManager2.BecameMonarch(winrt::auto_revoke, { this, &AppHost::_BecomeMonarch }); + // if (_windowManager2.IsMonarch()) + // { + // _BecomeMonarch(nullptr, nullptr); + // } // TODO! // // Create a throttled function for updating the window state, to match the @@ -221,28 +229,28 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& // - // Return Value: // - -void AppHost::_HandleCommandlineArgs() +void AppHost::_HandleCommandlineArgs(/*const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args*/) { - std::vector args; - // _buildArgsFromCommandline(args); - auto cwd{ wil::GetCurrentDirectoryW() }; + // std::vector args; + // // _buildArgsFromCommandline(args); + // auto cwd{ wil::GetCurrentDirectoryW() }; - Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; - _windowManager.ProposeCommandline(eventArgs); + // Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; + // _windowManager2.ProposeCommandline(eventArgs); - _shouldCreateWindow = _windowManager.ShouldCreateWindow(); - if (!_shouldCreateWindow) - { - return; - } + // _shouldCreateWindow = _windowManager2.ShouldCreateWindow(); + // if (!_shouldCreateWindow) + // { + // return; + // } // We did want to make a window, so let's instantiate it here. // We don't have XAML yet, but we do have other stuff. _windowLogic = _appLogic.CreateNewWindow(); - if (auto peasant{ _windowManager.CurrentWindow() }) + if (_peasant) { - if (auto args{ peasant.InitialArgs() }) + if (auto args{ _peasant.InitialArgs() }) { const auto result = _windowLogic.SetStartupCommandline(args.Commandline()); const auto message = _windowLogic.ParseCommandlineMessage(); @@ -265,19 +273,21 @@ void AppHost::_HandleCommandlineArgs() } } - // This is a fix for GH#12190 and hopefully GH#12169. + // TODO! // - // If the commandline we were provided is going to result in us only - // opening elevated terminal instances, then we need to not even create - // the window at all here. In that case, we're going through this - // special escape hatch to dispatch all the calls to elevate-shim, and - // then we're going to exit immediately. - if (_appLogic.ShouldImmediatelyHandoffToElevated()) - { - _shouldCreateWindow = false; - _appLogic.HandoffToElevated(); - return; - } + // // This is a fix for GH#12190 and hopefully GH#12169. + // // + // // If the commandline we were provided is going to result in us only + // // opening elevated terminal instances, then we need to not even create + // // the window at all here. In that case, we're going through this + // // special escape hatch to dispatch all the calls to elevate-shim, and + // // then we're going to exit immediately. + // if (_appLogic.ShouldImmediatelyHandoffToElevated()) + // { + // _shouldCreateWindow = false; + // _appLogic.HandoffToElevated(); + // return; + // } // After handling the initial args, hookup the callback for handling // future commandline invocations. When our peasant is told to execute a @@ -285,62 +295,63 @@ void AppHost::_HandleCommandlineArgs() // use to send the actions to the app. // // MORE EVENT HANDLERS, same rules as the ones above. - _revokers.peasantExecuteCommandlineRequested = peasant.ExecuteCommandlineRequested(winrt::auto_revoke, { this, &AppHost::_DispatchCommandline }); - _revokers.peasantSummonRequested = peasant.SummonRequested(winrt::auto_revoke, { this, &AppHost::_HandleSummon }); - _revokers.peasantDisplayWindowIdRequested = peasant.DisplayWindowIdRequested(winrt::auto_revoke, { this, &AppHost::_DisplayWindowId }); - _revokers.peasantQuitRequested = peasant.QuitRequested(winrt::auto_revoke, { this, &AppHost::_QuitRequested }); - - // We need this property to be set before we get the InitialSize/Position - // and BecameMonarch which normally sets it is only run after the window - // is created. - if (_windowManager.IsMonarch()) - { - const auto numPeasants = _windowManager.GetNumberOfPeasants(); - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (_windowLogic.ShouldUsePersistedLayout() && - 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 (numPeasants == 1 && !_windowLogic.HasCommandlineArguments() && !_appLogic.HasSettingsStartupActions()) - { - _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[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 - )); - } - } - _windowLogic.SetNumberOfOpenWindows(numPeasants); - } - _windowLogic.WindowName(peasant.WindowName()); - _windowLogic.WindowId(peasant.GetID()); + _revokers.peasantExecuteCommandlineRequested = _peasant.ExecuteCommandlineRequested(winrt::auto_revoke, { this, &AppHost::_DispatchCommandline }); + _revokers.peasantSummonRequested = _peasant.SummonRequested(winrt::auto_revoke, { this, &AppHost::_HandleSummon }); + _revokers.peasantDisplayWindowIdRequested = _peasant.DisplayWindowIdRequested(winrt::auto_revoke, { this, &AppHost::_DisplayWindowId }); + _revokers.peasantQuitRequested = _peasant.QuitRequested(winrt::auto_revoke, { this, &AppHost::_QuitRequested }); + + // TODO! + // // We need this property to be set before we get the InitialSize/Position + // // and BecameMonarch which normally sets it is only run after the window + // // is created. + // if (_windowManager2.IsMonarch()) + // { + // const auto numPeasants = _windowManager2.GetNumberOfPeasants(); + // const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + // if (_windowLogic.ShouldUsePersistedLayout() && + // 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 (numPeasants == 1 && !_windowLogic.HasCommandlineArguments() && !_appLogic.HasSettingsStartupActions()) + // { + // _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[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 + // )); + // } + // } + // _windowLogic.SetNumberOfOpenWindows(numPeasants); + // } + _windowLogic.WindowName(_peasant.WindowName()); + _windowLogic.WindowId(_peasant.GetID()); } } @@ -421,7 +432,7 @@ void AppHost::Initialize() _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. - _windowManager.RequestQuitAll(); + _windowManager2.RequestQuitAll(_peasant); }); // Load bearing: make sure the PropertyChanged handler is added before we @@ -496,7 +507,7 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* { _window->UpdateTitle(newTitle); } - _windowManager.UpdateActiveTabTitle(newTitle); + _windowManager2.UpdateActiveTabTitle(newTitle, _peasant); // TODO! this probably needs a peasant } // Method Description: @@ -508,27 +519,29 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* // - void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/) { - if (_windowManager.IsMonarch() && _notificationIcon) - { - _DestroyNotificationIcon(); - } - else if (_window->IsQuakeWindow()) - { - _HideNotificationIconRequested(nullptr, nullptr); - } + // TODO! + // if (_windowManager2.IsMonarch() && _notificationIcon) + // { + // _DestroyNotificationIcon(); + // } + // else if (_window->IsQuakeWindow()) + // { + // _HideNotificationIconRequested(nullptr, nullptr); + // } - // We don't want to try to save layouts if we are about to close. - _getWindowLayoutThrottler.reset(); - _windowManager.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); + // TODO! + // // We don't want to try to save layouts if we are about to close. + // _getWindowLayoutThrottler.reset(); + // _windowManager2.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); // We also don't need to update any of our bookkeeping on how many // windows are open. - _windowManager.WindowCreated(_WindowCreatedToken); - _windowManager.WindowClosed(_WindowClosedToken); + _windowManager2.WindowCreated(_WindowCreatedToken); + _windowManager2.WindowClosed(_WindowClosedToken); // 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(); + _windowManager2.SignalClose(_peasant); _window->Close(); } @@ -879,53 +892,54 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable send _windowLogic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } -// 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::apartment_context peasant_thread; +// TODO! +// // 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::apartment_context peasant_thread; - winrt::hstring layoutJson = L""; - // Use the main thread since we are accessing controls. - co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); - try - { - const auto pos = _GetWindowLaunchPosition(); - layoutJson = _windowLogic.GetWindowLayoutJson(pos); - } - CATCH_LOG() +// winrt::hstring layoutJson = L""; +// // Use the main thread since we are accessing controls. +// co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); +// try +// { +// const auto pos = _GetWindowLaunchPosition(); +// layoutJson = _windowLogic.GetWindowLayoutJson(pos); +// } +// CATCH_LOG() - // go back to give the result to the peasant. - co_await peasant_thread; +// // go back to give the result to the peasant. +// co_await peasant_thread; - co_return layoutJson; -} +// co_return layoutJson; +// } -// Method Description: -// - Event handler for the WindowManager::FindTargetWindowRequested event. The -// manager will ask us how to figure out what the target window is for a set -// of commandline arguments. We'll take those arguments, and ask AppLogic to -// parse them for us. We'll then set ResultTargetWindow in the given args, so -// the sender can use that result. -// Arguments: -// - args: the bundle of a commandline and working directory to find the correct target window for. -// Return Value: -// - -void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const Remoting::FindTargetWindowArgs& args) -{ - const auto targetWindow = _appLogic.FindTargetWindow(args.Args().Commandline()); - args.ResultTargetWindow(targetWindow.WindowId()); - args.ResultTargetWindowName(targetWindow.WindowName()); -} +// // Method Description: +// // - Event handler for the WindowManager::FindTargetWindowRequested event. The +// // manager will ask us how to figure out what the target window is for a set +// // of commandline arguments. We'll take those arguments, and ask AppLogic to +// // parse them for us. We'll then set ResultTargetWindow in the given args, so +// // the sender can use that result. +// // Arguments: +// // - args: the bundle of a commandline and working directory to find the correct target window for. +// // Return Value: +// // - +// void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, +// const Remoting::FindTargetWindowArgs& args) +// { +// const auto targetWindow = _appLogic.FindTargetWindow(args.Args().Commandline()); +// args.ResultTargetWindow(targetWindow.WindowId()); +// args.ResultTargetWindowName(targetWindow.WindowName()); +// } winrt::fire_and_forget AppHost::_WindowActivated(bool activated) { @@ -938,138 +952,139 @@ winrt::fire_and_forget AppHost::_WindowActivated(bool activated) co_await winrt::resume_background(); - if (auto peasant{ _windowManager.CurrentWindow() }) + if (_peasant) { const auto currentDesktopGuid{ _CurrentDesktopGuid() }; // TODO: projects/5 - in the future, we'll want to actually get the // desktop GUID in IslandWindow, and bubble that up here, then down to // the Peasant. For now, we're just leaving space for it. - Remoting::WindowActivatedArgs args{ peasant.GetID(), + Remoting::WindowActivatedArgs args{ _peasant.GetID(), (uint64_t)_window->GetHandle(), currentDesktopGuid, winrt::clock().now() }; - peasant.ActivateWindow(args); + _peasant.ActivateWindow(args); } } -void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) -{ - // MSFT:35726322 - // - // Although we're manually revoking the event handler now in the dtor before - // we null out the window, let's be extra careful and check JUST IN CASE. - if (_window == nullptr) - { - return; - } - - _setupGlobalHotkeys(); - - if (_windowManager.DoesQuakeWindowExist() || - _window->IsQuakeWindow() || - (_windowLogic.GetAlwaysShowNotificationIcon() || _windowLogic.GetMinimizeToNotificationArea())) - { - _CreateNotificationIcon(); - } +// void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, +// const winrt::Windows::Foundation::IInspectable& /*args*/) +// { +// // MSFT:35726322 +// // +// // Although we're manually revoking the event handler now in the dtor before +// // we null out the window, let's be extra careful and check JUST IN CASE. +// if (_window == nullptr) +// { +// return; +// } - // Set the number of open windows (so we know if we are the last window) - // and subscribe for updates if there are any changes to that number. - _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); +// _setupGlobalHotkeys(); - _WindowCreatedToken = _windowManager.WindowCreated([this](auto&&, auto&&) { - if (_getWindowLayoutThrottler) { - _getWindowLayoutThrottler.value()(); - } - _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); +// if (_windowManager2.DoesQuakeWindowExist() || +// _window->IsQuakeWindow() || +// (_windowLogic.GetAlwaysShowNotificationIcon() || _windowLogic.GetMinimizeToNotificationArea())) +// { +// _CreateNotificationIcon(); +// } - _WindowClosedToken = _windowManager.WindowClosed([this](auto&&, auto&&) { - if (_getWindowLayoutThrottler) - { - _getWindowLayoutThrottler.value()(); - } - _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); - }); +// // Set the number of open windows (so we know if we are the last window) +// // and subscribe for updates if there are any changes to that number. +// _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); - // These events are coming from peasants that become or un-become quake windows. - _revokers.ShowNotificationIconRequested = _windowManager.ShowNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_ShowNotificationIconRequested }); - _revokers.HideNotificationIconRequested = _windowManager.HideNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_HideNotificationIconRequested }); - // If the monarch receives a QuitAll event it will signal this event to be - // ran before each peasant is closed. - _revokers.QuitAllRequested = _windowManager.QuitAllRequested(winrt::auto_revoke, { this, &AppHost::_QuitAllRequested }); +// _WindowCreatedToken = _windowManager2.WindowCreated([this](auto&&, auto&&) { +// if (_getWindowLayoutThrottler) { +// _getWindowLayoutThrottler.value()(); +// } +// _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); }); - // The monarch should be monitoring if it should save the window layout. - if (!_getWindowLayoutThrottler.has_value()) - { - // We want at least some delay to prevent the first save from overwriting - // the data as we try load windows initially. - _getWindowLayoutThrottler.emplace(std::move(std::chrono::seconds(10)), std::move([this]() { _SaveWindowLayoutsRepeat(); })); - _getWindowLayoutThrottler.value()(); - } -} +// _WindowClosedToken = _windowManager2.WindowClosed([this](auto&&, auto&&) { +// if (_getWindowLayoutThrottler) +// { +// _getWindowLayoutThrottler.value()(); +// } +// _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); +// }); + +// // These events are coming from peasants that become or un-become quake windows. +// _revokers.ShowNotificationIconRequested = _windowManager2.ShowNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_ShowNotificationIconRequested }); +// _revokers.HideNotificationIconRequested = _windowManager2.HideNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_HideNotificationIconRequested }); +// // If the monarch receives a QuitAll event it will signal this event to be +// // ran before each peasant is closed. +// _revokers.QuitAllRequested = _windowManager2.QuitAllRequested(winrt::auto_revoke, { this, &AppHost::_QuitAllRequested }); + +// // The monarch should be monitoring if it should save the window layout. +// if (!_getWindowLayoutThrottler.has_value()) +// { +// // We want at least some delay to prevent the first save from overwriting +// // the data as we try load windows initially. +// _getWindowLayoutThrottler.emplace(std::move(std::chrono::seconds(10)), std::move([this]() { _SaveWindowLayoutsRepeat(); })); +// _getWindowLayoutThrottler.value()(); +// } +// } -winrt::Windows::Foundation::IAsyncAction AppHost::_SaveWindowLayouts() -{ - // Make sure we run on a background thread to not block anything. - co_await winrt::resume_background(); +// winrt::Windows::Foundation::IAsyncAction AppHost::_SaveWindowLayouts() +// { +// // Make sure we run on a background thread to not block anything. +// co_await winrt::resume_background(); - if (_windowLogic.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 = _windowManager.GetAllWindowLayouts(); - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_SaveWindowLayouts_Save", - TraceLoggingDescription("Logged when writing window state"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _windowLogic.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)); - } - } +// if (_windowLogic.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 = _windowManager2.GetAllWindowLayouts(); +// TraceLoggingWrite(g_hWindowsTerminalProvider, +// "AppHost_SaveWindowLayouts_Save", +// TraceLoggingDescription("Logged when writing window state"), +// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), +// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); +// _windowLogic.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; -} +// co_return; +// } -winrt::fire_and_forget AppHost::_SaveWindowLayoutsRepeat() -{ - // Make sure we run on a background thread to not block anything. - co_await winrt::resume_background(); +// winrt::fire_and_forget AppHost::_SaveWindowLayoutsRepeat() +// { +// // Make sure we run on a background thread to not block anything. +// co_await winrt::resume_background(); - co_await _SaveWindowLayouts(); +// co_await _SaveWindowLayouts(); - // Don't need to save too frequently. - co_await 30s; +// // Don't need to save too frequently. +// co_await 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)); +// // 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()(); - } -} +// _getWindowLayoutThrottler.value()(); +// } +// } +// TODO! this looks like something that belongs elsewhere void AppHost::_listenForInboundConnections() { _appLogic.SetInboundListener(); @@ -1170,7 +1185,7 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) break; } - _windowManager.SummonWindow(args); + _windowManager2.SummonWindow(args); if (args.FoundMatch()) { // Excellent, the window was found. We have nothing else to do here. @@ -1313,9 +1328,9 @@ winrt::fire_and_forget AppHost::_IdentifyWindowsRequested(const winrt::Windows:: // make sure we're on the background thread, or this will silently fail co_await winrt::resume_background(); - if (auto peasant{ _windowManager.CurrentWindow() }) + if (_peasant) { - peasant.RequestIdentifyWindows(); + _peasant.RequestIdentifyWindows(); } } @@ -1341,11 +1356,11 @@ winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Fou // Switch to the BG thread - anything x-proc must happen on a BG thread co_await winrt::resume_background(); - if (auto peasant{ _windowManager.CurrentWindow() }) + if (_peasant) { Remoting::RenameRequestArgs requestArgs{ args.ProposedName() }; - peasant.RequestRename(requestArgs); + _peasant.RequestRename(requestArgs); // Switch back to the UI thread. Setting the WindowName needs to happen // on the UI thread, because it'll raise a PropertyChanged event @@ -1414,30 +1429,32 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta { _setupGlobalHotkeys(); - // If we're monarch, we need to check some conditions to show the notification icon. - // If there's a Quake window somewhere, we'll want to keep the notification icon. - // There's two settings - MinimizeToNotificationArea and AlwaysShowNotificationIcon. If either - // one of them are true, we want to make sure there's a notification icon. - // If both are false, we want to remove our icon from the notification area. - // When we remove our icon from the notification area, we'll also want to re-summon - // any hidden windows, but right now we're not keeping track of who's hidden, - // so just summon them all. Tracking the work to do a "summon all minimized" in - // GH#10448 - if (_windowManager.IsMonarch()) - { - if (!_windowManager.DoesQuakeWindowExist()) - { - if (!_notificationIcon && (_windowLogic.GetMinimizeToNotificationArea() || _windowLogic.GetAlwaysShowNotificationIcon())) - { - _CreateNotificationIcon(); - } - else if (_notificationIcon && !_windowLogic.GetMinimizeToNotificationArea() && !_windowLogic.GetAlwaysShowNotificationIcon()) - { - _windowManager.SummonAllWindows(); - _DestroyNotificationIcon(); - } - } - } + // TODO! tray icon + // + // // If we're monarch, we need to check some conditions to show the notification icon. + // // If there's a Quake window somewhere, we'll want to keep the notification icon. + // // There's two settings - MinimizeToNotificationArea and AlwaysShowNotificationIcon. If either + // // one of them are true, we want to make sure there's a notification icon. + // // If both are false, we want to remove our icon from the notification area. + // // When we remove our icon from the notification area, we'll also want to re-summon + // // any hidden windows, but right now we're not keeping track of who's hidden, + // // so just summon them all. Tracking the work to do a "summon all minimized" in + // // GH#10448 + // if (_windowManager2.IsMonarch()) + // { + // if (!_windowManager2.DoesQuakeWindowExist()) + // { + // if (!_notificationIcon && (_windowLogic.GetMinimizeToNotificationArea() || _windowLogic.GetAlwaysShowNotificationIcon())) + // { + // _CreateNotificationIcon(); + // } + // else if (_notificationIcon && !_windowLogic.GetMinimizeToNotificationArea() && !_windowLogic.GetAlwaysShowNotificationIcon()) + // { + // _windowManager2.SummonAllWindows(); + // _DestroyNotificationIcon(); + // } + // } + // } _window->SetMinimizeToNotificationAreaBehavior(_windowLogic.GetMinimizeToNotificationArea()); _window->SetAutoHideWindow(_windowLogic.AutoHideWindow()); @@ -1476,22 +1493,24 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation: void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - _windowManager.RequestQuitAll(); + _windowManager2.RequestQuitAll(_peasant); } void AppHost::_QuitAllRequested(const winrt::Windows::Foundation::IInspectable&, - const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& args) + const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs&) { - // 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()); + // TODO! wat do + // + //// 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()); } void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&, @@ -1558,9 +1577,9 @@ void AppHost::_CreateNotificationIcon() // Hookup the handlers, save the tokens for revoking if settings change. _ReAddNotificationIconToken = _window->NotifyReAddNotificationIcon([this]() { _notificationIcon->ReAddNotificationIcon(); }); _NotificationIconPressedToken = _window->NotifyNotificationIconPressed([this]() { _notificationIcon->NotificationIconPressed(); }); - _ShowNotificationIconContextMenuToken = _window->NotifyShowNotificationIconContextMenu([this](til::point coord) { _notificationIcon->ShowContextMenu(coord, _windowManager.GetPeasantInfos()); }); + _ShowNotificationIconContextMenuToken = _window->NotifyShowNotificationIconContextMenu([this](til::point coord) { _notificationIcon->ShowContextMenu(coord, _windowManager2.GetPeasantInfos()); }); _NotificationIconMenuItemSelectedToken = _window->NotifyNotificationIconMenuItemSelected([this](HMENU hm, UINT idx) { _notificationIcon->MenuItemSelected(hm, idx); }); - _notificationIcon->SummonWindowRequested([this](auto& args) { _windowManager.SummonWindow(args); }); + _notificationIcon->SummonWindowRequested([this](auto& args) { _windowManager2.SummonWindow(args); }); } // Method Description: @@ -1583,36 +1602,40 @@ void AppHost::_DestroyNotificationIcon() void AppHost::_ShowNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - if (_windowManager.IsMonarch()) - { - if (!_notificationIcon) - { - _CreateNotificationIcon(); - } - } - else - { - _windowManager.RequestShowNotificationIcon(); - } + // TODO! tray icon + // + // if (_windowManager2.IsMonarch()) + // { + // if (!_notificationIcon) + // { + // _CreateNotificationIcon(); + // } + // } + // else + // { + // _windowManager2.RequestShowNotificationIcon(); + // } } void AppHost::_HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - if (_windowManager.IsMonarch()) - { - // Destroy it only if our settings allow it - if (_notificationIcon && - !_windowLogic.GetAlwaysShowNotificationIcon() && - !_windowLogic.GetMinimizeToNotificationArea()) - { - _DestroyNotificationIcon(); - } - } - else - { - _windowManager.RequestHideNotificationIcon(); - } + // TODO! tray icon + // + // if (_windowManager2.IsMonarch()) + // { + // // Destroy it only if our settings allow it + // if (_notificationIcon && + // !_windowLogic.GetAlwaysShowNotificationIcon() && + // !_windowLogic.GetMinimizeToNotificationArea()) + // { + // _DestroyNotificationIcon(); + // } + // } + // else + // { + // _windowManager2.RequestHideNotificationIcon(); + // } } // Method Description: diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index a48bb247498..cc03a258f64 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -9,7 +9,11 @@ class AppHost { public: - AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept; + // AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept; + AppHost(const winrt::TerminalApp::AppLogic& logic, + winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, + const winrt::Microsoft::Terminal::Remoting::WindowManager2& manager, + const winrt::Microsoft::Terminal::Remoting::Peasant& peasant) noexcept; virtual ~AppHost(); void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); @@ -27,7 +31,8 @@ class AppHost winrt::TerminalApp::AppLogic _appLogic; winrt::TerminalApp::TerminalWindow _windowLogic; - winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; + winrt::Microsoft::Terminal::Remoting::WindowManager2 _windowManager2{ nullptr }; + winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; std::vector _hotkeys; winrt::com_ptr _desktopManager{ nullptr }; @@ -35,10 +40,10 @@ class AppHost bool _shouldCreateWindow{ false }; bool _useNonClientArea{ false }; - std::optional> _getWindowLayoutThrottler; + // std::optional> _getWindowLayoutThrottler; std::shared_ptr> _showHideWindowThrottler; - winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); - winrt::fire_and_forget _SaveWindowLayoutsRepeat(); + // winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); + // winrt::fire_and_forget _SaveWindowLayoutsRepeat(); void _preInit(); @@ -69,11 +74,11 @@ class AppHost winrt::Windows::Foundation::IAsyncOperation _GetWindowLayoutAsync(); - void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + // void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, + // const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); - void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + // void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, + // const winrt::Windows::Foundation::IInspectable& args); void _GlobalHotkeyPressed(const long hotkeyIndex); void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 09c8ca2100f..6d3203a5d14 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -95,13 +95,15 @@ void WindowEmperor::WaitForWindows() // one.join(); // two.join(); - Sleep(10000); + Sleep(30000); //30s } void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) { - _threads.emplace_back( [this, args]() { - WindowThread foo{ _app.Logic(), args }; + Remoting::Peasant peasant{ _manager.CreateAPeasant(args) }; + + _threads.emplace_back([this, args, peasant]() { + WindowThread foo{ _app.Logic(), args, _manager, peasant }; return foo.WindowProc(); - } ); + }); } diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index adb7f1c4a8f..1ef44717fae 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -5,8 +5,11 @@ #include "WindowThread.h" WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic, - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs /*args*/) : - _host{ logic } + winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, + winrt::Microsoft::Terminal::Remoting::WindowManager2 manager, + winrt::Microsoft::Terminal::Remoting::Peasant peasant) : + _peasant{ peasant }, + _host{ logic, args, manager, peasant } { } diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index e6c9d27b3a8..2f5ff8027da 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -8,9 +8,12 @@ class WindowThread { public: WindowThread(const winrt::TerminalApp::AppLogic& logic, - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args); + winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, + winrt::Microsoft::Terminal::Remoting::WindowManager2 manager, + winrt::Microsoft::Terminal::Remoting::Peasant peasant); int WindowProc(); private: ::AppHost _host; + winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; }; diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index a90b2d5e69f..6055122446a 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -119,5 +119,8 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { ExitProcess(0); } - emperor.WaitForWindows(); + if (emperor.HandleCommandlineArgs()) + { + emperor.WaitForWindows(); + } } From 274d62d8a8a97c777cb94761b0f2a4818bfc2df4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 1 Feb 2023 13:56:40 -0600 Subject: [PATCH 015/122] it's working --- src/cascadia/Remoting/Monarch.cpp | 8 +++++ src/cascadia/Remoting/Monarch.h | 2 ++ src/cascadia/Remoting/Monarch.idl | 2 ++ src/cascadia/Remoting/WindowManager.idl | 1 + src/cascadia/Remoting/WindowManager2.cpp | 35 ++++++++++++++++++- src/cascadia/Remoting/WindowManager2.h | 8 +++++ src/cascadia/Remoting/WindowManager2.idl | 2 ++ .../WindowsTerminal/WindowEmperor.cpp | 14 +++++++- src/cascadia/WindowsTerminal/main.cpp | 3 ++ 9 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 778ff2f6160..f828fd05dbd 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -725,6 +725,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation result->WindowName(targetWindowName); result->ShouldCreateWindow(true); + _RequestNewWindowHandlers(*this, *winrt::make_self(*result, args)); + // If this fails, it'll be logged in the following // TraceLoggingWrite statement, with succeeded=false } @@ -760,6 +762,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto result{ winrt::make_self(true) }; result->Id(windowID); result->WindowName(targetWindowName); + + _RequestNewWindowHandlers(*this, *winrt::make_self(*result, args)); + return *result; } } @@ -774,6 +779,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // In this case, no usable ID was provided. Return { true, nullopt } auto result = winrt::make_self(true); result->WindowName(targetWindowName); + + _RequestNewWindowHandlers(*this, *winrt::make_self(*result, args)); + return *result; } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index bb8fffbe900..6765d0b15ca 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -88,6 +88,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation 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: uint64_t _ourPID; diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index adc29036a9a..1dc3d53f70e 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -76,6 +76,8 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler WindowCreated; event Windows.Foundation.TypedEventHandler WindowClosed; event Windows.Foundation.TypedEventHandler QuitAllRequested; + + event Windows.Foundation.TypedEventHandler RequestNewWindow; }; runtimeclass Monarch : [default] IMonarch diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 2fdfd7e3432..917347f2e9b 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -31,5 +31,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; + }; } diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index bd410014a97..ea8794d8218 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -46,7 +46,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // * If we're running unpackaged: the .winmd must be a sibling of the .exe // * If we're running packaged: the .winmd must be in the package root _monarch = try_create_instance(Monarch_clsid, - CLSCTX_LOCAL_SERVER); + CLSCTX_LOCAL_SERVER); + } + + // Check if we became the king, and if we are, wire up callbacks. + void WindowManager2::_createCallbacks() + { + assert(_monarch); + // Here, we're the king! + // + // This is where you should do any additional setup that might need to be + // done when we become the king. This will be called both for the first + // window, and when the current monarch dies. + + _monarch.WindowCreated({ get_weak(), &WindowManager2::_WindowCreatedHandlers }); + _monarch.WindowClosed({ get_weak(), &WindowManager2::_WindowClosedHandlers }); + _monarch.FindTargetWindowRequested({ this, &WindowManager2::_raiseFindTargetWindowRequested }); + _monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); }); + _monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); }); + _monarch.QuitAllRequested({ get_weak(), &WindowManager2::_QuitAllRequestedHandlers }); + + _monarch.RequestNewWindow({ get_weak(), &WindowManager2::_raiseRequestNewWindow }); + // _BecameMonarchHandlers(*this, nullptr); } void WindowManager2::_registerAsMonarch() @@ -58,6 +79,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation &_registrationHostClass)); } + void WindowManager2::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + { + _FindTargetWindowRequestedHandlers(sender, args); + } + void WindowManager2::_raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args) + { + _RequestNewWindowHandlers(sender, args); + } + Remoting::ProposeCommandlineResult WindowManager2::ProposeCommandline2(const Remoting::CommandlineArgs& args) { bool shouldCreateWindow = false; @@ -105,6 +137,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // getting any windows. _registerAsMonarch(); _createMonarch(); + _createCallbacks(); if (!_monarch) { // TODO! something catastrophically bad happened here. diff --git a/src/cascadia/Remoting/WindowManager2.h b/src/cascadia/Remoting/WindowManager2.h index 34a9f9b84ac..3de1b41883e 100644 --- a/src/cascadia/Remoting/WindowManager2.h +++ b/src/cascadia/Remoting/WindowManager2.h @@ -36,6 +36,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation 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: DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr }; @@ -46,6 +48,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _proposeToMonarch(const Remoting::CommandlineArgs& args, std::optional& givenID, winrt::hstring& givenName); + + void _createCallbacks(); + void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + void _raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); }; } diff --git a/src/cascadia/Remoting/WindowManager2.idl b/src/cascadia/Remoting/WindowManager2.idl index c037d8c1b2d..b111b06294d 100644 --- a/src/cascadia/Remoting/WindowManager2.idl +++ b/src/cascadia/Remoting/WindowManager2.idl @@ -38,5 +38,7 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; + event Windows.Foundation.TypedEventHandler RequestNewWindow; + }; } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 6d3203a5d14..2c4eb1e99c8 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -67,6 +67,10 @@ bool WindowEmperor::HandleCommandlineArgs() if (result.ShouldCreateWindow()) { CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }); + + _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { + CreateNewWindowThread(args); + }); } return result.ShouldCreateWindow(); @@ -95,7 +99,15 @@ void WindowEmperor::WaitForWindows() // one.join(); // two.join(); - Sleep(30000); //30s + // Sleep(30000); //30s + + MSG message; + + while (GetMessage(&message, nullptr, 0, 0)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } } void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index 6055122446a..43934998b9f 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -122,5 +122,8 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) if (emperor.HandleCommandlineArgs()) { emperor.WaitForWindows(); + + + } } From e623299ac6ccaccc5e48532d8ac73d1f64b743e7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 1 Feb 2023 14:12:31 -0600 Subject: [PATCH 016/122] notes --- src/cascadia/Remoting/WindowManager2.cpp | 5 +++-- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 12 +++++++++++- src/cascadia/WindowsTerminal/WindowEmperor.h | 1 - 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index ea8794d8218..84cb71148ef 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -153,8 +153,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // TODO! So, we wanted a new peasant. Cool! // // We need to fill in args.ResultTargetWindow, - // args.ResultTargetWindowName so that we can create the new window - // with those values. + // args.ResultTargetWindowName so that we can create the new + // window with those values. Otherwise, the very first window + // won't obey the given name / ID. shouldCreateWindow = true; auto result = winrt::make_self(shouldCreateWindow); result->Id(); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 2c4eb1e99c8..903641dd813 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -101,8 +101,18 @@ void WindowEmperor::WaitForWindows() // Sleep(30000); //30s - MSG message; + // TODO! This creates a loop that never actually exits right now. It seems + // to get a message wehn another window is activated, but never a WM_CLOSE + // (that makes sense). It keeps running even when the threads all exit, + // which is INTERESTING for sure. + // + // what we should do: + // - Add an event to onarch to indicate that we should exit, because all the + // peasants have exited. + // - We very well may need an HWND_MESSAGE that's connected to the main + // thread, for processing global hotkeys. Consider that in the future too. + MSG message; while (GetMessage(&message, nullptr, 0, 0)) { TranslateMessage(&message); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 16c762a6d0a..046a5ec12f7 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -29,6 +29,5 @@ class WindowEmperor winrt::TerminalApp::App _app; winrt::Microsoft::Terminal::Remoting::WindowManager2 _manager; - // std::vector> _threads; std::vector _threads; }; From 1b4f1efb090cd35abcc4b4b83c2e5e51f6659cea Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 1 Feb 2023 17:08:37 -0600 Subject: [PATCH 017/122] I literally think this is the whole thing --- src/cascadia/TerminalApp/AppLogic.cpp | 7 ++- src/cascadia/TerminalApp/AppLogic.h | 4 ++ src/cascadia/TerminalApp/AppLogic.idl | 2 + src/cascadia/TerminalApp/ContentManager.cpp | 43 +++++++++++++++++++ src/cascadia/TerminalApp/ContentManager.h | 25 +++++++++++ .../TerminalApp/TerminalAppLib.vcxproj | 6 +++ src/cascadia/TerminalApp/TerminalPage.cpp | 8 +++- src/cascadia/TerminalApp/TerminalPage.h | 3 +- src/cascadia/TerminalApp/TerminalPage.idl | 13 +++++- src/cascadia/TerminalApp/TerminalWindow.cpp | 5 ++- src/cascadia/TerminalApp/TerminalWindow.h | 4 +- src/cascadia/TerminalApp/TerminalWindow.idl | 3 +- .../TerminalControl/ControlInteractivity.cpp | 7 +++ .../TerminalControl/ControlInteractivity.h | 4 ++ .../TerminalControl/ControlInteractivity.idl | 2 + src/cascadia/TerminalControl/TermControl.cpp | 7 ++- src/cascadia/TerminalControl/TermControl.h | 2 + src/cascadia/TerminalControl/TermControl.idl | 3 ++ 18 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 src/cascadia/TerminalApp/ContentManager.cpp create mode 100644 src/cascadia/TerminalApp/ContentManager.h diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index ad77ccdc3cf..a277ad7b7fa 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -687,7 +687,7 @@ namespace winrt::TerminalApp::implementation { ReloadSettings(); } - auto window = winrt::make_self(_settings); + auto window = winrt::make_self(_settings, _contentManager); this->SettingsChanged({ window->get_weak(), &implementation::TerminalWindow::UpdateSettingsHandler }); if (_hasSettingsStartupActions) { @@ -695,4 +695,9 @@ namespace winrt::TerminalApp::implementation } return *window; } + + winrt::TerminalApp::ContentManager AppLogic::ContentManager() + { + return _contentManager; + } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index ffe744b8978..17c1782c2d3 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -10,6 +10,7 @@ #include "LanguageProfileNotifier.h" #include "AppCommandlineArgs.h" #include "TerminalWindow.h" +#include "ContentManager.h" #include #include @@ -68,6 +69,7 @@ namespace winrt::TerminalApp::implementation TerminalApp::TerminalWindow CreateNewWindow(); + winrt::TerminalApp::ContentManager ContentManager(); TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); private: @@ -93,6 +95,8 @@ namespace winrt::TerminalApp::implementation winrt::com_ptr _languageProfileNotifier; wil::unique_folder_change_reader_nothrow _reader; + TerminalApp::ContentManager _contentManager{ *winrt::make_self() }; + static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view args, const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 7946403aeed..c1fa2d5ca48 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -27,6 +27,8 @@ namespace TerminalApp void RunAsUwp(); Boolean IsElevated(); + ContentManager ContentManager { get; }; + Boolean HasSettingsStartupActions(); void ReloadSettings(); diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp new file mode 100644 index 00000000000..1b9d5f68e4b --- /dev/null +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "ContentManager.h" +#include "ContentManager.g.cpp" + +#include + +#include "../../types/inc/utils.hpp" + +using namespace winrt::Windows::ApplicationModel; +using namespace winrt::Windows::ApplicationModel::DataTransfer; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::System; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Microsoft::Terminal::Control; +using namespace winrt::Microsoft::Terminal::Settings::Model; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} +namespace winrt::TerminalApp::implementation +{ + ControlInteractivity ContentManager::CreateCore(Microsoft::Terminal::Control::IControlSettings settings, + IControlAppearance unfocusedAppearance, + TerminalConnection::ITerminalConnection connection) + { + auto content = ControlInteractivity{ settings, unfocusedAppearance, connection }; + // winrt::guid g{ ::Microsoft::Console::Utils::CreateGuid() }; + _content.Insert(content.Id(), content); + return content; + } + + ControlInteractivity ContentManager::LookupCore(winrt::guid id) + { + return _content.TryLookup(id); + } +} diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h new file mode 100644 index 00000000000..bd2725cac6e --- /dev/null +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "ContentManager.g.h" + +#include +namespace winrt::TerminalApp::implementation +{ + struct ContentManager : ContentManagerT + { + public: + ContentManager() = default; + Microsoft::Terminal::Control::ControlInteractivity CreateCore(Microsoft::Terminal::Control::IControlSettings settings, + Microsoft::Terminal::Control::IControlAppearance unfocusedAppearance, + Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); + Microsoft::Terminal::Control::ControlInteractivity LookupCore(winrt::guid id); + + private: + Windows::Foundation::Collections::IMap _content{ + winrt::multi_threaded_map() + }; + }; +} diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 7fb88842a1c..6b99ca582f0 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -138,6 +138,9 @@ AppLogic.idl + + TerminalPage.idl + TerminalWindow.idl @@ -234,6 +237,9 @@ AppLogic.idl + + TerminalPage.idl + TerminalWindow.idl diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f272c743d55..f17602c9b63 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -51,10 +51,11 @@ namespace winrt namespace winrt::TerminalApp::implementation { - TerminalPage::TerminalPage() : + TerminalPage::TerminalPage(const TerminalApp::ContentManager& manager) : _tabs{ winrt::single_threaded_observable_vector() }, _mruTabs{ winrt::single_threaded_observable_vector() }, _startupActions{ winrt::single_threaded_vector() }, + _manager{ manager }, _hostingHwnd{} { InitializeComponent(); @@ -2645,7 +2646,10 @@ namespace winrt::TerminalApp::implementation // Do any initialization that needs to apply to _every_ TermControl we // create here. // TermControl will copy the settings out of the settings passed to it. - TermControl term{ settings.DefaultSettings(), settings.UnfocusedSettings(), connection }; + + auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection); + + TermControl term{ content }; // GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now. if (_visible) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 99415eb333c..2f23dff354d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -69,7 +69,7 @@ namespace winrt::TerminalApp::implementation struct TerminalPage : TerminalPageT { public: - TerminalPage(); + TerminalPage(const TerminalApp::ContentManager& manager); // This implements shobjidl's IInitializeWithWindow, but due to a XAML Compiler bug we cannot // put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331 @@ -253,6 +253,7 @@ namespace winrt::TerminalApp::implementation bool _renamerPressedEnter{ false }; TerminalApp::IWindowProperties _WindowProperties{ nullptr }; + TerminalApp::ContentManager _manager{ nullptr }; winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 82abf118666..9ee1fd8b9f9 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -5,6 +5,17 @@ import "IDirectKeyListener.idl"; namespace TerminalApp { + + // See IDialogPresenter and TerminalPage's DialogPresenter for more + // information. + [default_interface] runtimeclass ContentManager + { + Microsoft.Terminal.Control.ControlInteractivity CreateCore(Microsoft.Terminal.Control.IControlSettings settings, + Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance, + Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); + Microsoft.Terminal.Control.ControlInteractivity LookupCore(Guid id); + } + delegate void LastTabClosedEventArgs(); [default_interface] runtimeclass RenameWindowRequestedArgs @@ -28,7 +39,7 @@ namespace TerminalApp [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener { - TerminalPage(); + TerminalPage(ContentManager manager); // XAML bound properties String ApplicationDisplayName { get; }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 75e9c5fc0da..a182d58518c 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -135,7 +135,8 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD namespace winrt::TerminalApp::implementation { - TerminalWindow::TerminalWindow(const CascadiaSettings& settings) : + TerminalWindow::TerminalWindow(const CascadiaSettings& settings, const TerminalApp::ContentManager& manager) : + _manager{ manager }, _settings{ settings } { // For your own sanity, it's better to do setup outside the ctor. @@ -154,7 +155,7 @@ namespace winrt::TerminalApp::implementation // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT TerminalWindow::Initialize(HWND hwnd) { - _root = winrt::make_self(); + _root = winrt::make_self(_manager); _root->WindowProperties(*this); _dialog = ContentDialog{}; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index bde6e1bfac6..9935e8a11e9 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -35,7 +35,7 @@ namespace winrt::TerminalApp::implementation struct TerminalWindow : TerminalWindowT { public: - TerminalWindow(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + TerminalWindow(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::ContentManager& manager); ~TerminalWindow() = default; STDMETHODIMP Initialize(HWND hwnd); @@ -158,6 +158,8 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; + TerminalApp::ContentManager _manager{ nullptr }; + void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); void _ShowLoadWarningsDialog(); bool _IsKeyboardServiceEnabled(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index e827dd7423d..2a63b4dfb20 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -31,8 +31,7 @@ namespace TerminalApp // information. [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, IWindowProperties, Windows.UI.Xaml.Data.INotifyPropertyChanged { - TerminalWindow(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); - + TerminalWindow(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, ContentManager manager); // 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, diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 7eaf4275535..dbb47b6b543 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -44,9 +44,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation _lastMouseClickPos{}, _selectionNeedsToBeCopied{ false } { + _guid = ::Microsoft::Console::Utils::CreateGuid(); + _core = winrt::make_self(settings, unfocusedAppearance, connection); } + winrt::guid ControlInteractivity::Id() + { + return _guid; + } + // Method Description: // - Updates our internal settings. These settings should be // interactivity-specific. Right now, we primarily update _rowsToScroll diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 4cc4df10bdd..f45e91793db 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -85,6 +85,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SetEndSelectionPoint(const Core::Point pixelPosition); bool ManglePathsForWsl(); + winrt::guid Id(); + TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs); @@ -129,6 +131,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::optional::interval> _lastHoveredInterval{ std::nullopt }; + winrt::guid _guid; + unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime); void _updateSystemParameterSettings() noexcept; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index aada01ee0ad..97b050a736b 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -23,6 +23,8 @@ namespace Microsoft.Terminal.Control void GotFocus(); void LostFocus(); + Guid Id { get; }; + InteractivityAutomationPeer OnCreateAutomationPeer(); Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference formats); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 7cfe8cbdc4b..db74370ecb7 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -50,6 +50,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation TermControl::TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) : + TermControl{ winrt::make(settings, unfocusedAppearance, connection) } + { + } + + TermControl::TermControl(Control::ControlInteractivity content) : + _interactivity{ content }, _isInternalScrollBarUpdate{ false }, _autoScrollVelocity{ 0 }, _autoScrollingPointerPoint{ std::nullopt }, @@ -61,7 +67,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation { InitializeComponent(); - _interactivity = winrt::make(settings, unfocusedAppearance, connection); _core = _interactivity.Core(); // These events might all be triggered by the connection, but that diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 590780e7524..79050015335 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -25,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { struct TermControl : TermControlT { + TermControl(Control::ControlInteractivity content); + TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 044bfb0a6b1..9fd43e95f08 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -3,6 +3,7 @@ import "IMouseWheelListener.idl"; import "IControlSettings.idl"; +import "ControlInteractivity.idl"; import "IDirectKeyListener.idl"; import "EventArgs.idl"; import "ICoreState.idl"; @@ -17,6 +18,8 @@ namespace Microsoft.Terminal.Control ICoreState, Windows.UI.Xaml.Data.INotifyPropertyChanged { + TermControl(ControlInteractivity content); + TermControl(IControlSettings settings, IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); From 47336c0982ef556b6d91e49abf9ade89d0924806 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 09:15:53 -0600 Subject: [PATCH 018/122] [ainulindale] Clean tear down the `App` when the process exits (cherry picked from commit 57d1dd435828aebfa2b81e93680da00a89132296) --- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 6 ++++++ src/cascadia/WindowsTerminal/WindowEmperor.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 903641dd813..df2fb245c0a 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -30,6 +30,12 @@ WindowEmperor::WindowEmperor() noexcept : }); } +WindowEmperor::~WindowEmperor() +{ + _app.Close(); + _app = nullptr; +} + void _buildArgsFromCommandline(std::vector& args) { if (auto commandline{ GetCommandLineW() }) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 046a5ec12f7..448fe916165 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -19,6 +19,7 @@ class WindowEmperor { public: WindowEmperor() noexcept; + ~WindowEmperor(); bool ShouldExit(); void WaitForWindows(); From 9924e23dec4398c26bad1edaf2f35a5592512c5e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 13:28:30 -0600 Subject: [PATCH 019/122] [ainulindale] "fix" hot reload Doesn't work with multiple windows open, but doesn't do _nothing_ (cherry picked from commit 427a4a51c5f57a01d010bab23803a0dba1b7c62c) --- src/cascadia/TerminalApp/AppLogic.cpp | 24 +++++-- src/cascadia/TerminalApp/AppLogic.h | 4 +- src/cascadia/TerminalApp/AppLogic.idl | 2 +- .../TerminalApp/SettingsLoadEventArgs.h | 30 ++++++++ src/cascadia/TerminalApp/TerminalWindow.cpp | 69 ++++++++++--------- src/cascadia/TerminalApp/TerminalWindow.h | 13 ++-- src/cascadia/TerminalApp/TerminalWindow.idl | 11 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 4 +- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 9 files changed, 111 insertions(+), 48 deletions(-) create mode 100644 src/cascadia/TerminalApp/SettingsLoadEventArgs.h diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index ad77ccdc3cf..a7075f32f13 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -6,6 +6,7 @@ #include "../inc/WindowingBehavior.h" #include "AppLogic.g.cpp" #include "FindTargetWindowResult.g.cpp" +#include "SettingsLoadEventArgs.h" #include #include @@ -256,10 +257,10 @@ namespace winrt::TerminalApp::implementation return E_INVALIDARG; } - _warnings.clear(); + _warnings.Clear(); for (uint32_t i = 0; i < newSettings.Warnings().Size(); i++) { - _warnings.push_back(newSettings.Warnings().GetAt(i)); + _warnings.Append(newSettings.Warnings().GetAt(i)); } _hasSettingsStartupActions = false; @@ -279,12 +280,12 @@ namespace winrt::TerminalApp::implementation } else { - _warnings.push_back(SettingsLoadWarnings::FailedToParseStartupActions); + _warnings.Append(SettingsLoadWarnings::FailedToParseStartupActions); } } _settings = std::move(newSettings); - hr = _warnings.empty() ? S_OK : S_FALSE; + hr = (_warnings.Size()) == 0 ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) { @@ -486,6 +487,14 @@ namespace winrt::TerminalApp::implementation // const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); // return; + + auto ev = winrt::make_self(true, + static_cast(_settingsLoadedResult), + _settingsLoadExceptionText, + _warnings, + _settings); + _SettingsChangedHandlers(*this, *ev); + return; } } @@ -508,7 +517,12 @@ namespace winrt::TerminalApp::implementation _ApplyStartupTaskStateChange(); _ProcessLazySettingsChanges(); - _SettingsChangedHandlers(*this, _settings); + auto ev = winrt::make_self(!initialLoad, + _settingsLoadedResult, + _settingsLoadExceptionText, + _warnings, + _settings); + _SettingsChangedHandlers(*this, *ev); } // Method Description: diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index ffe744b8978..47743f780ff 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -68,7 +68,7 @@ namespace winrt::TerminalApp::implementation TerminalApp::TerminalWindow CreateNewWindow(); - TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); private: bool _isUwp{ false }; @@ -86,7 +86,7 @@ namespace winrt::TerminalApp::implementation std::shared_ptr> _reloadSettings; til::throttled_func_trailing<> _reloadState; - std::vector _warnings; + winrt::Windows::Foundation::Collections::IVector _warnings{ winrt::multi_threaded_vector() }; // These fields invoke _reloadSettings and must be destroyed before _reloadSettings. // (C++ destroys members in reverse-declaration-order.) diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 7946403aeed..79e0c5b9a1c 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -44,7 +44,7 @@ namespace TerminalApp Windows.Foundation.Collections.IMapView GlobalHotkeys(); - event Windows.Foundation.TypedEventHandler SettingsChanged; + event Windows.Foundation.TypedEventHandler SettingsChanged; } } diff --git a/src/cascadia/TerminalApp/SettingsLoadEventArgs.h b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h new file mode 100644 index 00000000000..e31dd5e2a91 --- /dev/null +++ b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "SettingsLoadEventArgs.g.h" +#include +namespace winrt::TerminalApp::implementation +{ + struct SettingsLoadEventArgs : SettingsLoadEventArgsT + { + WINRT_PROPERTY(bool, Reload, false); + WINRT_PROPERTY(uint64_t, Result, S_OK); + WINRT_PROPERTY(winrt::hstring, ExceptionText, L""); + WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVector, Warnings, nullptr); + WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::CascadiaSettings, NewSettings, nullptr); + + public: + SettingsLoadEventArgs(bool reload, + uint64_t result, + const winrt::hstring& exceptionText, + const winrt::Windows::Foundation::Collections::IVector& warnings, + const Microsoft::Terminal::Settings::Model::CascadiaSettings& newSettings) : + _Reload{ reload }, + _Result{ result }, + _ExceptionText{ exceptionText }, + _Warnings{ warnings }, + _NewSettings{ newSettings } {}; + }; +} diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 75e9c5fc0da..c96f9e2fcc7 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -5,6 +5,7 @@ #include "TerminalWindow.h" #include "../inc/WindowingBehavior.h" #include "TerminalWindow.g.cpp" +#include "SettingsLoadEventArgs.g.cpp" #include #include @@ -409,7 +410,8 @@ namespace winrt::TerminalApp::implementation // - contentKey: The key to use to lookup the content text from our resources. void TerminalWindow::_ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, - HRESULT settingsLoadedResult) + HRESULT settingsLoadedResult, + const winrt::hstring& exceptionText) { auto title = GetLibraryResourceString(titleKey); auto buttonText = RS_(L"Ok"); @@ -428,13 +430,12 @@ namespace winrt::TerminalApp::implementation if (FAILED(settingsLoadedResult)) { - // TODO! _settingsLoadExceptionText needs to get into the TerminalWindow somehow - - // if (!_settingsLoadExceptionText.empty()) - // { - // warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); - // warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - // } + if (!exceptionText.empty()) + { + warningsTextBlock.Inlines().Append(_BuildErrorRun(exceptionText, + winrt::WUX::Application::Current().as<::winrt::TerminalApp::App>().Resources())); + warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + } } // Add a note that we're using the default settings in this case. @@ -459,7 +460,7 @@ namespace winrt::TerminalApp::implementation // validating the settings. // - Only one dialog can be visible at a time. If another dialog is visible // when this is called, nothing happens. See ShowDialog for details - void TerminalWindow::_ShowLoadWarningsDialog() + void TerminalWindow::_ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector& warnings) { auto title = RS_(L"SettingsValidateErrorTitle"); auto buttonText = RS_(L"Ok"); @@ -470,18 +471,16 @@ namespace winrt::TerminalApp::implementation // Make sure the lines of text wrap warningsTextBlock.TextWrapping(TextWrapping::Wrap); - // TODO! warnings need to get into here somehow - // - // for (const auto& warning : _warnings) - // { - // // Try looking up the warning message key for each warning. - // const auto warningText = _GetWarningText(warning); - // if (!warningText.empty()) - // { - // warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); - // warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - // } - // } + for (const auto& warning : warnings) + { + // Try looking up the warning message key for each warning. + const auto warningText = _GetWarningText(warning); + if (!warningText.empty()) + { + warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, winrt::WUX::Application::Current().as<::winrt::TerminalApp::App>().Resources())); + warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + } + } Controls::ContentDialog dialog; dialog.Title(winrt::box_value(title)); @@ -738,20 +737,25 @@ namespace winrt::TerminalApp::implementation _RequestedThemeChangedHandlers(*this, Theme()); } - void TerminalWindow::UpdateSettings(const HRESULT settingsLoadedResult, const CascadiaSettings& settings) + winrt::fire_and_forget TerminalWindow::UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args) { - if (FAILED(settingsLoadedResult)) + co_await wil::resume_foreground(_root->Dispatcher()); + + if (FAILED(args.Result())) { const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, textKey, settingsLoadedResult); - return; + _ShowLoadErrorsDialog(titleKey, + textKey, + gsl::narrow_cast(args.Result()), + args.ExceptionText()); + co_return; } - else if (settingsLoadedResult == S_FALSE) + else if (args.Result() == S_FALSE) { - _ShowLoadWarningsDialog(); + _ShowLoadWarningsDialog(args.Warnings()); } - _settings = settings; + _settings = args.NewSettings(); // Update the settings in TerminalPage _root->SetSettings(_settings, true); _RefreshThemeRoutine(); @@ -1159,13 +1163,10 @@ namespace winrt::TerminalApp::implementation } // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} void TerminalWindow::UpdateSettingsHandler(const winrt::IInspectable& /*sender*/, - const winrt::IInspectable& arg) + const winrt::TerminalApp::SettingsLoadEventArgs& args) { - if (const auto& settings{ arg.try_as() }) - { - this->UpdateSettings(S_OK, settings); - _root->SetSettings(_settings, true); - } + UpdateSettings(args); + // _root->SetSettings(_settings, true); } //////////////////////////////////////////////////////////////////////////// diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index bde6e1bfac6..78643c8986e 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -7,6 +7,7 @@ #include "SystemMenuChangeArgs.g.h" #include "TerminalPage.h" +#include "SettingsLoadEventArgs.h" #include #include @@ -47,7 +48,7 @@ namespace winrt::TerminalApp::implementation void Quit(); - void UpdateSettings(const HRESULT settingsLoadedResult, const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + winrt::fire_and_forget UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args); bool HasCommandlineArguments() const noexcept; // bool HasSettingsStartupActions() const noexcept; @@ -108,7 +109,7 @@ namespace winrt::TerminalApp::implementation void DismissDialog(); Microsoft::Terminal::Settings::Model::Theme Theme(); - void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); + void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& arg); // Normally, WindowName and WindowId would be // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise @@ -158,8 +159,12 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; - void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); - void _ShowLoadWarningsDialog(); + void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, + const winrt::hstring& contentKey, + HRESULT settingsLoadedResult, + const winrt::hstring& exceptionText); + void _ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector& warnings); + bool _IsKeyboardServiceEnabled(); void _RefreshThemeRoutine(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index e827dd7423d..480ecde0d67 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -27,6 +27,17 @@ namespace TerminalApp SystemMenuItemHandler Handler { get; }; }; + [default_interface] runtimeclass SettingsLoadEventArgs + { + Boolean Reload { get; }; + UInt64 Result { get; }; + IVector Warnings { get; }; + String ExceptionText { get; }; + + Microsoft.Terminal.Settings.Model.CascadiaSettings NewSettings { get; }; + + }; + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, IWindowProperties, Windows.UI.Xaml.Data.INotifyPropertyChanged diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0979c27c2a5..a9d38a6b262 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1425,8 +1425,10 @@ void AppHost::_updateTheme() } void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) + const winrt::TerminalApp::SettingsLoadEventArgs& /*args*/) { + // We don't need to call in to windowLogic here - it has its own SettingsChanged handler + _setupGlobalHotkeys(); // TODO! tray icon diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index cc03a258f64..df6aebc1441 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -98,7 +98,7 @@ class AppHost winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + const winrt::TerminalApp::SettingsLoadEventArgs& args); void _IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); From f06e484324f5a00c43ccb2afcdf2f226d8e88400 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 14:48:20 -0600 Subject: [PATCH 020/122] [ainulindale] This I'm confident fixes some of the x-thread errors, but this doesn't fix the crash (cherry picked from commit 700aadcb155624dbd69ee196d0b5a795804a56f1) --- src/cascadia/TerminalApp/TerminalPage.cpp | 4 +++- src/cascadia/TerminalApp/TerminalPage.h | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 10 +++++++--- src/cascadia/TerminalApp/TerminalWindow.h | 2 ++ src/cascadia/TerminalApp/TerminalWindow.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 8 +++++++- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 7 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f272c743d55..badd7bf8f59 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -127,10 +127,12 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) + winrt::fire_and_forget TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) { _settings = settings; + co_await wil::resume_foreground(Dispatcher()); + // Make sure to _UpdateCommandsForPalette before // _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make // sure the KeyChordText of Commands is updated, which needs to diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 99415eb333c..fdbfd5906db 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -75,7 +75,7 @@ namespace winrt::TerminalApp::implementation // put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331 STDMETHODIMP Initialize(HWND hwnd); - void SetSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings, bool needRefreshUI); + winrt::fire_and_forget SetSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings, bool needRefreshUI); void Create(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index c96f9e2fcc7..300dec307aa 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -739,8 +739,15 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalWindow::UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args) { + _settings = args.NewSettings(); + // Update the settings in TerminalPage + _root->SetSettings(_settings, true); + co_await wil::resume_foreground(_root->Dispatcher()); + // Bubble the notification up to the AppHost, now that we've updated our _settings. + _SettingsChangedHandlers(*this, args); + if (FAILED(args.Result())) { const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); @@ -755,9 +762,6 @@ namespace winrt::TerminalApp::implementation { _ShowLoadWarningsDialog(args.Warnings()); } - _settings = args.NewSettings(); - // Update the settings in TerminalPage - _root->SetSettings(_settings, true); _RefreshThemeRoutine(); } void TerminalWindow::_OpenSettingsUI() diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 78643c8986e..bd72cf59a37 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -193,6 +193,8 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); + TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); + #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; #endif diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 480ecde0d67..5992c3bdf3b 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -137,5 +137,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; event Windows.Foundation.TypedEventHandler ShowWindowChanged; + event Windows.Foundation.TypedEventHandler SettingsChanged; } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a9d38a6b262..d691527f6e8 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -448,7 +448,13 @@ void AppHost::Initialize() _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 }); - _revokers.SettingsChanged = _appLogic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); + + // A note: make sure to listen to our _window_'s settings changed, not the + // applogic's. We want to make sure the event has gone through the window + // logic _before_ we handle it, so we can ask the window about it's newest + // properties. + _revokers.SettingsChanged = _windowLogic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); + _revokers.IsQuakeWindowChanged = _windowLogic.IsQuakeWindowChanged(winrt::auto_revoke, { this, &AppHost::_IsQuakeWindowChanged }); _revokers.SummonWindowRequested = _windowLogic.SummonWindowRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowRequested }); _revokers.OpenSystemMenu = _windowLogic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu }); diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index df6aebc1441..3992597ce8e 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -180,7 +180,7 @@ class AppHost winrt::TerminalApp::TerminalWindow::QuitRequested_revoker QuitRequested; winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged; winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged; - winrt::TerminalApp::AppLogic::SettingsChanged_revoker SettingsChanged; + winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged; winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; From 2822c365071128d55e6464a34bf7326bf41b1c26 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 16:57:23 -0600 Subject: [PATCH 021/122] [ainulindale] Expand commands in the AppLogic, not on each page TerminalPage is the thing that ends up expanding iterable Command. It does this largely with copies - it makes a new map, a new vector, copies the Commands over, and does the work there before setting up the cmdpal. Except, it's not making a copy of the Commands, it's making a copy of the vector, with winrt objects all pointing at the Command objects that are ultimately owned by CascadiaSettings. This doesn't matter if there's only one TerminalPage - we'll only ever do that once. If there's many, on different threads, then one tpage will end up expanding the subcommands of one Command while another tpage is ALSO iterating on those subcommands. Hence why I'm getting `hresult_changed_state`s (cherry picked from commit 2122eec18612ee8f06de8c48a3af3445fe6c28cc) --- src/cascadia/TerminalApp/AppLogic.cpp | 3 + src/cascadia/TerminalApp/TerminalPage.cpp | 83 +------------------ src/cascadia/TerminalApp/TerminalPage.h | 4 - .../TerminalSettingsModel/ActionMap.cpp | 75 +++++++++++++++++ .../TerminalSettingsModel/ActionMap.h | 8 ++ .../TerminalSettingsModel/ActionMap.idl | 2 + .../CascadiaSettings.cpp | 5 ++ .../TerminalSettingsModel/CascadiaSettings.h | 2 + .../CascadiaSettings.idl | 2 + .../GlobalAppSettings.cpp | 6 ++ .../TerminalSettingsModel/GlobalAppSettings.h | 3 + 11 files changed, 108 insertions(+), 85 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index a7075f32f13..79bb399add9 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -285,6 +285,9 @@ namespace winrt::TerminalApp::implementation } _settings = std::move(newSettings); + + _settings.ExpandCommands(); + hr = (_warnings.Size()) == 0 ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index badd7bf8f59..7c46429e3c0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -97,36 +97,6 @@ namespace winrt::TerminalApp::implementation return S_OK; } - // Function Description: - // - Recursively check our commands to see if there's a keybinding for - // exactly their action. If there is, label that command with the text - // corresponding to that key chord. - // - Will recurse into nested commands as well. - // Arguments: - // - settings: The settings who's keybindings we should use to look up the key chords from - // - commands: The list of commands to label. - static void _recursiveUpdateCommandKeybindingLabels(CascadiaSettings settings, - IMapView commands) - { - for (const auto& nameAndCmd : commands) - { - const auto& command = nameAndCmd.Value(); - if (command.HasNestedCommands()) - { - _recursiveUpdateCommandKeybindingLabels(settings, command.NestedCommands()); - } - else - { - // If there's a keybinding that's bound to exactly this command, - // then get the keychord and display it as a - // part of the command in the UI. - // We specifically need to do this for nested commands. - const auto keyChord{ settings.ActionMap().GetKeyBindingForAction(command.ActionAndArgs().Action(), command.ActionAndArgs().Args()) }; - command.RegisterKey(keyChord); - } - } - } - winrt::fire_and_forget TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) { _settings = settings; @@ -2974,50 +2944,6 @@ namespace winrt::TerminalApp::implementation } } - // This is a helper to aid in sorting commands by their `Name`s, alphabetically. - static bool _compareSchemeNames(const ColorScheme& lhs, const ColorScheme& rhs) - { - std::wstring leftName{ lhs.Name() }; - std::wstring rightName{ rhs.Name() }; - return leftName.compare(rightName) < 0; - } - - // Method Description: - // - Takes a mapping of names->commands and expands them - // Arguments: - // - - // Return Value: - // - - IMap TerminalPage::_ExpandCommands(IMapView commandsToExpand, - IVectorView profiles, - IMapView schemes) - { - auto warnings{ winrt::single_threaded_vector() }; - - std::vector sortedSchemes; - sortedSchemes.reserve(schemes.Size()); - - for (const auto& nameAndScheme : schemes) - { - sortedSchemes.push_back(nameAndScheme.Value()); - } - std::sort(sortedSchemes.begin(), - sortedSchemes.end(), - _compareSchemeNames); - - auto copyOfCommands = winrt::single_threaded_map(); - for (const auto& nameAndCommand : commandsToExpand) - { - copyOfCommands.Insert(nameAndCommand.Key(), nameAndCommand.Value()); - } - - Command::ExpandCommands(copyOfCommands, - profiles, - { sortedSchemes }, - warnings); - - return copyOfCommands; - } // Method Description: // - Repopulates the list of commands in the command palette with the // current commands in the settings. Also updates the keybinding labels to @@ -3028,15 +2954,10 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_UpdateCommandsForPalette() { - auto copyOfCommands = _ExpandCommands(_settings.GlobalSettings().ActionMap().NameMap(), - _settings.ActiveProfiles().GetView(), - _settings.GlobalSettings().ColorSchemes()); - - _recursiveUpdateCommandKeybindingLabels(_settings, copyOfCommands.GetView()); - // Update the command palette when settings reload + const auto& expanded{ _settings.GlobalSettings().ActionMap().ExpandedCommands() }; auto commandsCollection = winrt::single_threaded_vector(); - for (const auto& nameAndCommand : copyOfCommands) + for (const auto& nameAndCommand : expanded) { commandsCollection.Append(nameAndCommand.Value()); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index fdbfd5906db..f6e37732d09 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -297,10 +297,6 @@ namespace winrt::TerminalApp::implementation void _UpdateCommandsForPalette(); void _SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance); - static winrt::Windows::Foundation::Collections::IMap _ExpandCommands(Windows::Foundation::Collections::IMapView commandsToExpand, - Windows::Foundation::Collections::IVectorView profiles, - Windows::Foundation::Collections::IMapView schemes); - void _DuplicateFocusedTab(); void _DuplicateTab(const TerminalTab& tab); diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index a3a5124a3d0..d61d1fa2e2a 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -854,4 +854,79 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation cmd->ActionAndArgs(action); AddAction(*cmd); } + + void ActionMap::_recursiveUpdateCommandKeybindingLabels() + { + const auto& commands{ _ExpandedMapCache }; + + for (const auto& nameAndCmd : commands) + { + const auto& command = nameAndCmd.Value(); + if (command.HasNestedCommands()) + { + _recursiveUpdateCommandKeybindingLabels(); + } + else + { + // If there's a keybinding that's bound to exactly this command, + // then get the keychord and display it as a + // part of the command in the UI. + // We specifically need to do this for nested commands. + const auto keyChord{ GetKeyBindingForAction(command.ActionAndArgs().Action(), + command.ActionAndArgs().Args()) }; + command.RegisterKey(keyChord); + } + } + } + + // This is a helper to aid in sorting commands by their `Name`s, alphabetically. + static bool _compareSchemeNames(const ColorScheme& lhs, const ColorScheme& rhs) + { + std::wstring leftName{ lhs.Name() }; + std::wstring rightName{ rhs.Name() }; + return leftName.compare(rightName) < 0; + } + + void ActionMap::ExpandCommands(const winrt::Windows::Foundation::Collections::IVectorView& profiles, + const winrt::Windows::Foundation::Collections::IMapView& schemes) + { + // TODO in review - It's a little weird to stash the expanded commands + // into a separate map. Is it possible to just replace the name map with + // the post-expanded commands? + // + // WHILE also making sure that upon re-saving the commands, we don't + // actually serialize the results of the expansion. I don't think it is. + + auto warnings{ winrt::single_threaded_vector() }; + + std::vector sortedSchemes; + sortedSchemes.reserve(schemes.Size()); + + for (const auto& nameAndScheme : schemes) + { + sortedSchemes.push_back(nameAndScheme.Value()); + } + std::sort(sortedSchemes.begin(), + sortedSchemes.end(), + _compareSchemeNames); + + auto copyOfCommands = winrt::single_threaded_map(); + + const auto& commandsToExpand{ NameMap() }; + for (const auto& nameAndCommand : commandsToExpand) + { + copyOfCommands.Insert(nameAndCommand.Key(), nameAndCommand.Value()); + } + + Command::ExpandCommands(copyOfCommands, + profiles, + winrt::param::vector_view{ sortedSchemes }, + warnings); + + _ExpandedMapCache = copyOfCommands; + } + Windows::Foundation::Collections::IMapView ActionMap::ExpandedCommands() + { + return _ExpandedMapCache.GetView(); + } } diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index 6dbd2b04c23..7f71e09f19d 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -75,6 +75,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void DeleteKeyBinding(const Control::KeyChord& keys); void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action); + Windows::Foundation::Collections::IMapView ExpandedCommands(); + void ExpandCommands(const Windows::Foundation::Collections::IVectorView& profiles, + const Windows::Foundation::Collections::IMapView& schemes); + private: std::optional _GetActionByID(const InternalActionID actionID) const; std::optional _GetActionByKeyChordInternal(const Control::KeyChord& keys) const; @@ -90,11 +94,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void _TryUpdateName(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd); void _TryUpdateKeyChord(const Model::Command& cmd, const Model::Command& oldCmd, const Model::Command& consolidatedCmd); + void _recursiveUpdateCommandKeybindingLabels(); + Windows::Foundation::Collections::IMap _AvailableActionsCache{ nullptr }; Windows::Foundation::Collections::IMap _NameMapCache{ nullptr }; Windows::Foundation::Collections::IMap _GlobalHotkeysCache{ nullptr }; Windows::Foundation::Collections::IMap _KeyBindingMapCache{ nullptr }; + Windows::Foundation::Collections::IMap _ExpandedMapCache{ nullptr }; + std::unordered_map _NestedCommands; std::vector _IterableCommands; std::unordered_map _KeyMap; diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.idl b/src/cascadia/TerminalSettingsModel/ActionMap.idl index 806baa17a30..8bbc3b1233c 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.idl +++ b/src/cascadia/TerminalSettingsModel/ActionMap.idl @@ -20,6 +20,8 @@ namespace Microsoft.Terminal.Settings.Model Windows.Foundation.Collections.IMapView NameMap { get; }; Windows.Foundation.Collections.IMapView KeyBindings { get; }; Windows.Foundation.Collections.IMapView GlobalHotkeys { get; }; + + Windows.Foundation.Collections.IMapView ExpandedCommands { get; }; }; [default_interface] runtimeclass ActionMap : IActionMapView diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 42ec1d28725..086ffcec529 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -1214,3 +1214,8 @@ void CascadiaSettings::_validateThemeExists() } } } + +void CascadiaSettings::ExpandCommands() +{ + _globals->ExpandCommands(ActiveProfiles().GetView(), GlobalSettings().ColorSchemes()); +} diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index ac9049b4b12..e0cf1e96202 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -143,6 +143,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::DefaultTerminal CurrentDefaultTerminal() noexcept; void CurrentDefaultTerminal(const Model::DefaultTerminal& terminal); + void ExpandCommands(); + private: static const std::filesystem::path& _settingsPath(); static const std::filesystem::path& _releaseSettingsPath(); diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl index 5a1987dcf3e..58db9f20e87 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl @@ -53,5 +53,7 @@ namespace Microsoft.Terminal.Settings.Model static Boolean IsDefaultTerminalSet { get; }; IObservableVector DefaultTerminals { get; }; DefaultTerminal CurrentDefaultTerminal; + + void ExpandCommands(); } } diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 55ec4d634bf..d6a5aa49e87 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -239,3 +239,9 @@ winrt::Windows::Foundation::Collections::IMapView& profiles, + const winrt::Windows::Foundation::Collections::IMapView& schemes) +{ + _actionMap->ExpandCommands(profiles, schemes); +} diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 4a594948eba..945c032c2bc 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -63,6 +63,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void AddTheme(const Model::Theme& theme); Model::Theme CurrentTheme() noexcept; + void ExpandCommands(const Windows::Foundation::Collections::IVectorView& profiles, + const Windows::Foundation::Collections::IMapView& schemes); + INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L""); #define GLOBAL_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ From 2332f0c0e4c3f2096f930567032b014a12f27b54 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 06:51:29 -0600 Subject: [PATCH 022/122] Don't try to snap on create if we're not prepared to snap 37 TODOs left --- src/cascadia/WindowsTerminal/AppHost.cpp | 12 ++++++------ src/cascadia/WindowsTerminal/IslandWindow.cpp | 11 +++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index d691527f6e8..558130b1109 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -85,11 +85,6 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, std::placeholders::_3); _window->SetCreateCallback(pfn); - _window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::TerminalWindow::CalcSnappedDimension, - _windowLogic, - std::placeholders::_1, - std::placeholders::_2)); - // Event handlers: // MAKE SURE THEY ARE ALL: // * winrt::auto_revoke @@ -470,6 +465,11 @@ void AppHost::Initialize() // while the screen is off. TerminalTrySetAutoCompleteAnimationsWhenOccluded(static_cast<::IUnknown*>(winrt::get_abi(_windowLogic.GetRoot())), true); + _window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::TerminalWindow::CalcSnappedDimension, + _windowLogic, + std::placeholders::_1, + std::placeholders::_2)); + _window->UpdateTitle(_windowLogic.Title()); // Set up the content of the application. If the app has a custom titlebar, @@ -1508,7 +1508,7 @@ void AppHost::_QuitAllRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs&) { // TODO! wat do - // + // //// 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()) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 1b0defb64ee..acc039ae447 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -403,12 +403,15 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam // - nonClientSizeScaled: the exclusive non-client size (already scaled) // Return Value: // - The total dimension -long IslandWindow::_calculateTotalSize(const bool /*isWidth*/, const long clientSize, const long nonClientSize) +long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize) { + if (_pfnSnapDimensionCallback) + { + return gsl::narrow_cast(_pfnSnapDimensionCallback(isWidth, gsl::narrow_cast(clientSize)) + nonClientSize); + } + // We might have been called in WM_CREATE, before we've initialized XAML or + // our page. That's okay. return clientSize + nonClientSize; - // TODO! - // - // return gsl::narrow_cast(_pfnSnapDimensionCallback(isWidth, gsl::narrow_cast(clientSize)) + nonClientSize); } [[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept From 4e7da2ec13a3f9ab31151302917c22cf97df2668 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 06:51:44 -0600 Subject: [PATCH 023/122] I guess this is just dead now 36 TODOs --- src/cascadia/WindowsTerminal/AppHost.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 558130b1109..04d20a026d6 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -476,26 +476,6 @@ void AppHost::Initialize() // set that content as well. _window->SetContent(_windowLogic.GetRoot()); _window->OnAppInitialized(); - - // TODO! is this still an issue? - // - // // BODGY - // // - // // We've got a weird crash that happens terribly inconsistently, but pretty - // // readily on migrie's laptop, only in Debug mode. Apparently, there's some - // // weird ref-counting magic that goes on during teardown, and our - // // Application doesn't get closed quite right, which can cause us to crash - // // into the debugger. This of course, only happens on exit, and happens - // // somewhere in the XamlHost.dll code. - // // - // // Crazily, if we _manually leak the Application_ here, then the crash - // // doesn't happen. This doesn't matter, because we really want the - // // Application to live for _the entire lifetime of the process_, so the only - // // time when this object would actually need to get cleaned up is _during - // // exit_. So we can safely leak this Application object, and have it just - // // get cleaned up normally when our process exits. - // auto a{ _app }; - // ::winrt::detach_abi(a); } // Method Description: From 23c4d4c0f83225062924e2139439b9c8b513a0e6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 08:05:46 -0600 Subject: [PATCH 024/122] Move the initialization of the showHide Throttler after we init the dispatcher. 35 TODOs left --- src/cascadia/WindowsTerminal/AppHost.cpp | 27 ++++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 04d20a026d6..a70f46c6c9c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -123,20 +123,6 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, // { // _BecomeMonarch(nullptr, nullptr); // } - - // TODO! - // // Create a throttled function for updating the window state, to match the - // // one requested by the pty. A 200ms delay was chosen because it's the - // // typical animation timeout in Windows. This does result in a delay between - // // the PTY requesting a change to the window state and the Terminal - // // realizing it, but should mitigate issues where the Terminal and PTY get - // // de-sync'd. - // _showHideWindowThrottler = std::make_shared>( - // winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), - // std::chrono::milliseconds(200), - // [this](const bool show) { - // _window->ShowWindowChanged(show); - // }); } AppHost::~AppHost() @@ -470,6 +456,19 @@ void AppHost::Initialize() std::placeholders::_1, std::placeholders::_2)); + // Create a throttled function for updating the window state, to match the + // one requested by the pty. A 200ms delay was chosen because it's the + // typical animation timeout in Windows. This does result in a delay between + // the PTY requesting a change to the window state and the Terminal + // realizing it, but should mitigate issues where the Terminal and PTY get + // de-sync'd. + _showHideWindowThrottler = std::make_shared>( + winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), + std::chrono::milliseconds(200), + [this](const bool show) { + _window->ShowWindowChanged(show); + }); + _window->UpdateTitle(_windowLogic.Title()); // Set up the content of the application. If the app has a custom titlebar, From 761bd6a6ab3f9a995e474ef239b419b4d71b5464 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 08:07:38 -0600 Subject: [PATCH 025/122] This one was dead code, 34 TODOs left --- src/cascadia/WindowsTerminal/AppHost.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a70f46c6c9c..e0967b9caab 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -138,10 +138,6 @@ AppHost::~AppHost() _showHideWindowThrottler.reset(); _window = nullptr; - /// TODO! - // - // _app.Close(); - // _app = nullptr; } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) From 93e9dc505d4d3b9bdd05fff6b70bc73437d9cc90 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 08:44:49 -0600 Subject: [PATCH 026/122] I guess we need to just hardcode these sizes. Sad, but okay. 33 TODOs --- src/cascadia/TerminalApp/TerminalWindow.cpp | 66 ++++++++++----------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 300dec307aa..6ddaa7de14f 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -613,40 +613,38 @@ namespace winrt::TerminalApp::implementation commandlineSize.height); } - // TODO! - // - // // GH#2061 - If the global setting "Always show tab bar" is - // // set or if "Show tabs in title bar" is set, then we'll need to add - // // the height of the tab bar here. - // if (_settings.GlobalSettings().ShowTabsInTitlebar()) - // { - // // If we're showing the tabs in the titlebar, we need to use a - // // TitlebarControl here to calculate how much space to reserve. - // // - // // We'll create a fake TitlebarControl, and we'll propose an - // // available size to it with Measure(). After Measure() is called, - // // the TitlebarControl's DesiredSize will contain the _unscaled_ - // // size that the titlebar would like to use. We'll use that as part - // // of the height calculation here. - // auto titlebar = TitlebarControl{ static_cast(0) }; - // titlebar.Measure({ SHRT_MAX, SHRT_MAX }); - // proposedSize.Height += (titlebar.DesiredSize().Height) * scale; - // } - // else if (_settings.GlobalSettings().AlwaysShowTabs()) - // { - // // Otherwise, let's use a TabRowControl to calculate how much extra - // // space we'll need. - // // - // // Similarly to above, we'll measure it with an arbitrarily large - // // available space, to make sure we get all the space it wants. - // auto tabControl = TabRowControl(); - // tabControl.Measure({ SHRT_MAX, SHRT_MAX }); - - // // For whatever reason, there's about 10px of unaccounted-for space - // // in the application. I couldn't tell you where these 10px are - // // coming from, but they need to be included in this math. - // proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; - // } + // GH#2061 - If the global setting "Always show tab bar" is + // set or if "Show tabs in title bar" is set, then we'll need to add + // the height of the tab bar here. + if (_settings.GlobalSettings().ShowTabsInTitlebar()) + { + // In the past, we used to actually instantiate a TitlebarControl + // and use Measure() to determine the DesiredSize of the control, to + // reserve exactly what we'd need. + // + // We can't do that anymore, because this is now called _before_ + // we've initilized XAML for this thread. We can't start XAML till + // we have an HWND, and we can't finish creating the window till we + // know how big it should be. + // + // Instead, we'll just hardcode how big the titlebar chould be. If + // the titlebar / tab row ever change size, these numbers will have + // to change accordingly. + + static constexpr auto titlebarHeight = 40; + proposedSize.Height += (titlebarHeight)*scale; + } + else if (_settings.GlobalSettings().AlwaysShowTabs()) + { + // Same comment as above, but with a TabRowControl. + // + // A note from before: For whatever reason, there's about 10px of + // unaccounted-for space in the application. I couldn't tell you + // where these 10px are coming from, but they need to be included in + // this math. + static constexpr auto tabRowHeight = 32; + proposedSize.Height += (tabRowHeight + 10) * scale; + } return proposedSize; } From d5396d110479cd3e9a890c99caf0f45bc011decc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 08:45:05 -0600 Subject: [PATCH 027/122] UWP is dead 32 TODOs --- src/cascadia/TerminalApp/App.cpp | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 1a22cfc5b32..b4019d95227 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -77,24 +77,7 @@ namespace winrt::TerminalApp::implementation /// Details about the launch request and process. void App::OnLaunched(const LaunchActivatedEventArgs& /*e*/) { - // TODO! UWP mode is straight up not supported anymore, yea? - // - // if this is a UWP... it means its our problem to hook up the content to the window here. - //if (_isUwp) - //{ - // auto content = Window::Current().Content(); - // if (content == nullptr) - // { - // auto logic = Logic(); - // logic.RunAsUwp(); // Must set UWP status first, settings might change based on it. - // logic.ReloadSettings(); - // logic.Create(); - - // auto page = logic.GetRoot().as(); - - // Window::Current().Content(page); - // Window::Current().Activate(); - // } - //} + // We used to support a pure UWP version of the Terminal. This method + // was only ever used to do UWP-specific setup of our App. } } From 07ff4183e4f92b1a186be2f5f1fe776bad89e4f4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 09:01:48 -0600 Subject: [PATCH 028/122] Plumb initial load result into the window on startup 30 TODOs left --- src/cascadia/TerminalApp/AppLogic.cpp | 24 +++++++----------- src/cascadia/TerminalApp/TerminalWindow.cpp | 28 ++++++++++----------- src/cascadia/TerminalApp/TerminalWindow.h | 3 ++- src/cascadia/TerminalApp/TerminalWindow.idl | 2 +- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 79bb399add9..d6f0ed87de9 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -482,15 +482,6 @@ namespace winrt::TerminalApp::implementation } else { - // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} - // - // _SettingsChangedHandlers(*this, make_self( _settingsLoadedResult, warnings, _settingsLoadExceptionText, settings})) - - // const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); - // const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); - // return; - auto ev = winrt::make_self(true, static_cast(_settingsLoadedResult), _settingsLoadExceptionText, @@ -508,11 +499,6 @@ namespace winrt::TerminalApp::implementation return; } - // if (_settingsLoadedResult == S_FALSE) - // { - // _ShowLoadWarningsDialog(); - // } - // Here, we successfully reloaded the settings, and created a new // TerminalSettings object. @@ -704,7 +690,15 @@ namespace winrt::TerminalApp::implementation { ReloadSettings(); } - auto window = winrt::make_self(_settings); + + auto ev = winrt::make_self(false, + _settingsLoadedResult, + _settingsLoadExceptionText, + _warnings, + _settings); + + auto window = winrt::make_self(*ev); + this->SettingsChanged({ window->get_weak(), &implementation::TerminalWindow::UpdateSettingsHandler }); if (_hasSettingsStartupActions) { diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 6ddaa7de14f..c30a96ba26b 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -136,8 +136,9 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD namespace winrt::TerminalApp::implementation { - TerminalWindow::TerminalWindow(const CascadiaSettings& settings) : - _settings{ settings } + TerminalWindow::TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult) : + _settings{ settingsLoadedResult.NewSettings() }, + _initialLoadResult{ settingsLoadedResult } { // 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, @@ -511,18 +512,17 @@ namespace winrt::TerminalApp::implementation } } - // TODO! Yea, more of "how do we get _settingsLoadedResult / warnings / error" into here? - // - // if (FAILED(_settingsLoadedResult)) - // { - // const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle"); - // const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText"); - // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); - // } - // else if (_settingsLoadedResult == S_FALSE) - // { - // _ShowLoadWarningsDialog(); - // } + const auto& settingsLoadedResult = gsl::narrow_cast(_initialLoadResult.Result()); + if (FAILED(settingsLoadedResult)) + { + const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText"); + _ShowLoadErrorsDialog(titleKey, textKey, settingsLoadedResult, _initialLoadResult.ExceptionText()); + } + else if (settingsLoadedResult == S_FALSE) + { + _ShowLoadWarningsDialog(_initialLoadResult.Warnings()); + } } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index bd72cf59a37..99a043a35a0 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -36,7 +36,7 @@ namespace winrt::TerminalApp::implementation struct TerminalWindow : TerminalWindowT { public: - TerminalWindow(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult); ~TerminalWindow() = default; STDMETHODIMP Initialize(HWND hwnd); @@ -158,6 +158,7 @@ namespace winrt::TerminalApp::implementation uint64_t _numOpenWindows{ 0 }; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; + TerminalApp::SettingsLoadEventArgs _initialLoadResult{ nullptr }; void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 5992c3bdf3b..88878d0eba4 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -42,7 +42,7 @@ namespace TerminalApp // information. [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, IWindowProperties, Windows.UI.Xaml.Data.INotifyPropertyChanged { - TerminalWindow(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); + TerminalWindow(SettingsLoadEventArgs result); // For your own sanity, it's better to do setup outside the ctor. From 40fdbc1536b6e31f0735f79be3379ef160cbe6a3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 09:02:32 -0600 Subject: [PATCH 029/122] Hey this comment was TODOne, 29 TODOs left --- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index e0967b9caab..849ef8ae4de 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -488,7 +488,7 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* { _window->UpdateTitle(newTitle); } - _windowManager2.UpdateActiveTabTitle(newTitle, _peasant); // TODO! this probably needs a peasant + _windowManager2.UpdateActiveTabTitle(newTitle, _peasant); } // Method Description: From 4db381e2cb25156589bc1ef395286fa74d665d98 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 09:10:09 -0600 Subject: [PATCH 030/122] not totally happy with this, but I understand why. 29 TODOs left --- src/cascadia/TerminalApp/AppLogic.cpp | 17 ++++++++------ src/cascadia/TerminalApp/TerminalWindow.cpp | 25 +++------------------ 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index d6f0ed87de9..860781b4f25 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -32,13 +32,19 @@ namespace winrt } //////////////////////////////////////////////////////////////////////////////// -// TODO! This section probably should be in TerminalWindow with the warnings - -// clang-format off -static const std::array settingsLoadErrorsLabels { +// Error message handling. This is in this file rather than with the warnings in +// TerminalWindow, becuase the error text might also just be a serialization +// error message. So AppLogic needs to know the actual text of the error. + +// !!! IMPORTANT !!! +// Make sure that these keys are in the same order as the +// SettingsLoadWarnings/Errors enum is! +static const std::array settingsLoadErrorsLabels{ USES_RESOURCE(L"NoProfilesText"), USES_RESOURCE(L"AllProfilesHiddenText") }; +static_assert(settingsLoadErrorsLabels.size() == static_cast(SettingsLoadErrors::ERRORS_SIZE)); + template winrt::hstring _GetMessageText(uint32_t index, const T& keys) { @@ -60,9 +66,6 @@ static winrt::hstring _GetErrorText(SettingsLoadErrors error) { return _GetMessageText(static_cast(error), settingsLoadErrorsLabels); } - -// clang-format on - //////////////////////////////////////////////////////////////////////////////// static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask"; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index c30a96ba26b..0dd2b59c7a6 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -31,11 +31,10 @@ namespace winrt using IInspectable = Windows::Foundation::IInspectable; } -// clang-format off // !!! IMPORTANT !!! // Make sure that these keys are in the same order as the // SettingsLoadWarnings/Errors enum is! -static const std::array settingsLoadWarningsLabels { +static const std::array settingsLoadWarningsLabels{ USES_RESOURCE(L"MissingDefaultProfileText"), USES_RESOURCE(L"DuplicateProfileText"), USES_RESOURCE(L"UnknownColorSchemeText"), @@ -53,14 +52,9 @@ static const std::array settingsLoadWarningsLabels { USES_RESOURCE(L"UnknownTheme"), USES_RESOURCE(L"DuplicateRemainingProfilesEntry"), }; -static const std::array settingsLoadErrorsLabels { - USES_RESOURCE(L"NoProfilesText"), - USES_RESOURCE(L"AllProfilesHiddenText") -}; -// clang-format on - static_assert(settingsLoadWarningsLabels.size() == static_cast(SettingsLoadWarnings::WARNINGS_SIZE)); -static_assert(settingsLoadErrorsLabels.size() == static_cast(SettingsLoadErrors::ERRORS_SIZE)); + +// Errors are definted in AppLogic.cpp // Function Description: // - General-purpose helper for looking up a localized string for a @@ -96,19 +90,6 @@ static winrt::hstring _GetWarningText(SettingsLoadWarnings warning) return _GetMessageText(static_cast(warning), settingsLoadWarningsLabels); } -// // Function Description: -// // - Gets the text from our ResourceDictionary for the given -// // SettingsLoadError. If there is no such text, we'll return nullptr. -// // - The warning should have an entry in settingsLoadErrorsLabels. -// // Arguments: -// // - error: the SettingsLoadErrors value to get the localized text for. -// // Return Value: -// // - localized text for the given error -// static winrt::hstring _GetErrorText(SettingsLoadErrors error) -// { -// return _GetMessageText(static_cast(error), settingsLoadErrorsLabels); -// } - // Function Description: // - Creates a Run of text to display an error message. The text is yellow or // red for dark/light theme, respectively. From 64257d830adf19c3487d775ebea31bb8baa3b507 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 09:31:58 -0600 Subject: [PATCH 031/122] This straight up isn't even used in main anymore, 26 TODOs left --- src/cascadia/TerminalApp/AppLogic.cpp | 13 ------------- src/cascadia/TerminalApp/AppLogic.h | 2 -- src/cascadia/TerminalApp/AppLogic.idl | 2 -- src/cascadia/TerminalApp/TerminalPage.cpp | 7 ------- src/cascadia/WindowsTerminal/AppHost.cpp | 6 ------ src/cascadia/WindowsTerminal/AppHost.h | 1 - 6 files changed, 31 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 860781b4f25..3818b44f861 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -524,19 +524,6 @@ namespace winrt::TerminalApp::implementation return _settings; } - // Method Description: - // - Triggers the setup of the listener for incoming console connections - // from the operating system. - // Arguments: - // - - // Return Value: - // - - void AppLogic::SetInboundListener() - { - // TODO! - // _root->SetInboundListener(false); - } - bool AppLogic::ShouldImmediatelyHandoffToElevated() { // TODO! Merge that code in here. diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 47743f780ff..83cfca4ee87 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -60,8 +60,6 @@ namespace winrt::TerminalApp::implementation bool ShouldImmediatelyHandoffToElevated(); void HandoffToElevated(); - void SetInboundListener(); - Windows::Foundation::Collections::IMapView GlobalHotkeys(); Microsoft::Terminal::Settings::Model::Theme Theme(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 79e0c5b9a1c..481c793ea40 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -31,8 +31,6 @@ namespace TerminalApp void ReloadSettings(); - void SetInboundListener(); - Microsoft.Terminal.Settings.Model.Theme Theme { get; }; FindTargetWindowResult FindTargetWindow(String[] args); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 7c46429e3c0..5b2945140fc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -593,8 +593,6 @@ namespace winrt::TerminalApp::implementation const bool initial, const winrt::hstring cwd) { - const auto paramFistSize = actions.Size(); - auto weakThis{ get_weak() }; // Handle it on a subsequent pass of the UI thread. @@ -625,11 +623,6 @@ namespace winrt::TerminalApp::implementation if (auto page{ weakThis.get() }) { - const auto memberSize = page->_startupActions.Size(); - const auto paramSecondSize = actions.Size(); - memberSize; - paramFistSize; - paramSecondSize; for (const auto& action : actions) { if (auto page{ weakThis.get() }) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 849ef8ae4de..11413606132 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1065,12 +1065,6 @@ winrt::fire_and_forget AppHost::_WindowActivated(bool activated) // } // } -// TODO! this looks like something that belongs elsewhere -void AppHost::_listenForInboundConnections() -{ - _appLogic.SetInboundListener(); -} - winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { if (_windowLogic.GetRoot() == nullptr) diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 3992597ce8e..e2ad0a97046 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -94,7 +94,6 @@ class AppHost bool _LazyLoadDesktopManager(); - void _listenForInboundConnections(); winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, From 3026922e590d5b10581fac26c2708309c9c4f480 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 09:57:45 -0600 Subject: [PATCH 032/122] Fix the window name not updating as it changed. Added some notes, so, 26 TODOs left --- src/cascadia/TerminalApp/TerminalPage.cpp | 12 ------------ src/cascadia/TerminalApp/TerminalPage.h | 14 +++++--------- src/cascadia/TerminalApp/TerminalPage.idl | 5 +++++ src/cascadia/TerminalApp/TerminalPage.xaml | 8 ++++---- src/cascadia/TerminalApp/TerminalWindow.cpp | 11 ++++++++--- src/cascadia/TerminalApp/TerminalWindow.h | 5 +---- src/cascadia/TerminalApp/TerminalWindow.idl | 2 -- 7 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 5b2945140fc..3844f1156ab 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4286,18 +4286,6 @@ namespace winrt::TerminalApp::implementation void TerminalPage::WindowProperties(const TerminalApp::IWindowProperties& props) { _WindowProperties = props; - // if (const auto& observable{ props.try_as() }) - // { - // observable.PropertyChanged([weakThis = get_weak()](auto& /*sender*/, auto& e) { - // if (auto page{ weakThis.get() }) - // { - // if (e.PropertyName() == L"WindowName") - // { - // page->_windowNameChanged(); - // } - // } - // }); - // } } winrt::fire_and_forget TerminalPage::WindowNameChanged() diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index f6e37732d09..cee4c6f8f45 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -133,19 +133,15 @@ namespace winrt::TerminalApp::implementation const bool initial, const winrt::hstring cwd = L""); - // Normally, WindowName and WindowId would be - // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise - // WindowNameForDisplay and WindowIdForDisplay instead - // winrt::hstring WindowName() const noexcept; - // winrt::fire_and_forget WindowName(const winrt::hstring& value); - // uint64_t WindowId() const noexcept; - // void WindowId(const uint64_t& value); + // For the sake of XAML binding: + winrt::hstring WindowName() const noexcept { return _WindowProperties.WindowName(); }; + uint64_t WindowId() const noexcept { return _WindowProperties.WindowId(); }; + winrt::hstring WindowIdForDisplay() const noexcept { return _WindowProperties.WindowIdForDisplay(); }; + winrt::hstring WindowNameForDisplay() const noexcept { return _WindowProperties.WindowNameForDisplay(); }; void SetNumberOfOpenWindows(const uint64_t value); void SetPersistedLayoutIdx(const uint32_t value); - // winrt::hstring WindowIdForDisplay() const noexcept; - // winrt::hstring WindowNameForDisplay() const noexcept; // bool IsQuakeWindow() const noexcept; bool IsElevated() const noexcept; diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 82abf118666..9a2cfef292c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -54,6 +54,11 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); + String WindowName { get; }; + UInt64 WindowId { get; }; + String WindowNameForDisplay { get; }; + String WindowIdForDisplay { get; }; + event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler LastTabClosed; event Windows.Foundation.TypedEventHandler SetTitleBarContent; diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 38d7c0f69f7..3434cd868ee 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -201,10 +201,10 @@ tracked by MUX#4382 --> + Subtitle="{x:Bind WindowNameForDisplay, Mode=OneWay}" /> + Text="{x:Bind WindowName, Mode=OneWay}" /> diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 0dd2b59c7a6..0549c108b53 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1130,6 +1130,7 @@ namespace winrt::TerminalApp::implementation } } + // TODO! // bool TerminalWindow::IsQuakeWindow() const noexcept // { // return _root->IsQuakeWindow(); @@ -1164,13 +1165,16 @@ namespace winrt::TerminalApp::implementation void TerminalWindow::WindowName(const winrt::hstring& value) { + // TODO! // const auto oldIsQuakeMode = IsQuakeWindow(); const auto changed = _WindowName != value; if (changed) { _WindowName = value; if (_root) - _root->WindowNameChanged(); // TODO! is bodge + { + _root->WindowNameChanged(); + } } } @@ -1187,8 +1191,9 @@ namespace winrt::TerminalApp::implementation { _WindowId = value; if (_root) - _root->WindowNameChanged(); // TODO! is bodge - // _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); + { + _root->WindowNameChanged(); + } } } diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 99a043a35a0..61ca2367767 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -70,10 +70,7 @@ namespace winrt::TerminalApp::implementation void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); void IdentifyWindow(); void RenameFailed(); - // winrt::hstring WindowName(); - // void WindowName(const winrt::hstring& name); - // uint64_t WindowId(); - // void WindowId(const uint64_t& id); + void SetPersistedLayoutIdx(const uint32_t idx); void SetNumberOfOpenWindows(const uint64_t num); // bool IsQuakeWindow() const noexcept; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 88878d0eba4..be814c60166 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -77,8 +77,6 @@ namespace TerminalApp Boolean AutoHideWindow { get; }; void IdentifyWindow(); - // String WindowName; - // UInt64 WindowId; void SetPersistedLayoutIdx(UInt32 idx); void SetNumberOfOpenWindows(UInt64 num); void RenameFailed(); From 84e228f1fecf37f9147747d37ba2ef280b23e176 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 11:05:43 -0600 Subject: [PATCH 033/122] Start to move window restoration into the Emperor I think I have a vision for it now. 25 TODOs left. --- src/cascadia/TerminalApp/AppLogic.cpp | 4 +- src/cascadia/TerminalApp/TerminalPage.cpp | 3 + src/cascadia/WindowsTerminal/AppHost.cpp | 10 +- src/cascadia/WindowsTerminal/AppHost.h | 6 +- .../WindowsTerminal/WindowEmperor.cpp | 135 +++++++++++++++++- src/cascadia/WindowsTerminal/WindowEmperor.h | 9 ++ src/cascadia/WindowsTerminal/WindowThread.cpp | 5 + src/cascadia/WindowsTerminal/WindowThread.h | 2 + 8 files changed, 161 insertions(+), 13 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 3818b44f861..d9fee50c4c0 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -191,7 +191,9 @@ namespace winrt::TerminalApp::implementation _settings.GlobalSettings().ShowTabsInTitlebar(false); } - // TODO! These used to be in `_root->Initialized`. Where do they belong now? + // These used to be in `TerminalPage::Initialized`, so that they started + // _after_ the Terminal window was started and displayed. These could + // theoretically move there again too. { // Both LoadSettings and ReloadSettings are supposed to call this function, // but LoadSettings skips it, so that the UI starts up faster. diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 3844f1156ab..60d5019567c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3783,6 +3783,9 @@ namespace winrt::TerminalApp::implementation void TerminalPage::SetNumberOfOpenWindows(const uint64_t num) { + // This is used in TerminalPage::_RemoveTab, when we close a tab. If we + // close the last tab, and there's only one window open, then we will + // call to persist _no_ state. _numOpenWindows = num; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 11413606132..62c3be16508 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -514,10 +514,7 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se // // We don't want to try to save layouts if we are about to close. // _getWindowLayoutThrottler.reset(); // _windowManager2.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); - // We also don't need to update any of our bookkeeping on how many - // windows are open. - _windowManager2.WindowCreated(_WindowCreatedToken); - _windowManager2.WindowClosed(_WindowClosedToken); + // 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 @@ -1677,3 +1674,8 @@ void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspect } } } + +winrt::TerminalApp::TerminalWindow AppHost::Logic() +{ + return _windowLogic; +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index e2ad0a97046..def0552e400 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -24,6 +24,8 @@ class AppHost bool HasWindow(); + winrt::TerminalApp::TerminalWindow Logic(); + private: std::unique_ptr _window; @@ -145,8 +147,8 @@ class AppHost winrt::event_token _ShowNotificationIconContextMenuToken; winrt::event_token _NotificationIconMenuItemSelectedToken; winrt::event_token _GetWindowLayoutRequestedToken; - winrt::event_token _WindowCreatedToken; - winrt::event_token _WindowClosedToken; + // winrt::event_token _WindowCreatedToken; + // winrt::event_token _WindowClosedToken; // 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 diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index df2fb245c0a..8edec6585b1 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -65,7 +65,7 @@ bool WindowEmperor::HandleCommandlineArgs() auto cwd{ wil::GetCurrentDirectoryW() }; Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; - // _windowManager.ProposeCommandline(eventArgs); + const auto result = _manager.ProposeCommandline2(eventArgs); // TODO! createWindow is false in cases like wt --help. Figure that out. @@ -77,6 +77,8 @@ bool WindowEmperor::HandleCommandlineArgs() _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { CreateNewWindowThread(args); }); + _becomeMonarch(); + // _attemptWindowRestore(); } return result.ShouldCreateWindow(); @@ -113,7 +115,7 @@ void WindowEmperor::WaitForWindows() // which is INTERESTING for sure. // // what we should do: - // - Add an event to onarch to indicate that we should exit, because all the + // - Add an event to Monarch to indicate that we should exit, because all the // peasants have exited. // - We very well may need an HWND_MESSAGE that's connected to the main // thread, for processing global hotkeys. Consider that in the future too. @@ -130,8 +132,129 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) { Remoting::Peasant peasant{ _manager.CreateAPeasant(args) }; - _threads.emplace_back([this, args, peasant]() { - WindowThread foo{ _app.Logic(), args, _manager, peasant }; - return foo.WindowProc(); - }); + auto func = [this, args, peasant]() { + auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; + _windows.push_back(window); + return window->WindowProc(); + }; + + _threads.emplace_back(func); +} + +void WindowEmperor::_becomeMonarch() +{ + //////////////////////////////////////////////////////////////////////////// + // _setupGlobalHotkeys(); + + //////////////////////////////////////////////////////////////////////////// + + // if (_windowManager2.DoesQuakeWindowExist() || + // _window->IsQuakeWindow() || + // (_windowLogic.GetAlwaysShowNotificationIcon() || _windowLogic.GetMinimizeToNotificationArea())) + // { + // _CreateNotificationIcon(); + // } + + // // These events are coming from peasants that become or un-become quake windows. + // _revokers.ShowNotificationIconRequested = _windowManager2.ShowNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_ShowNotificationIconRequested }); + // _revokers.HideNotificationIconRequested = _windowManager2.HideNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_HideNotificationIconRequested }); + + //////////////////////////////////////////////////////////////////////////// + + // // Set the number of open windows (so we know if we are the last window) + // // and subscribe for updates if there are any changes to that number. + // _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); + + _WindowCreatedToken = _manager.WindowCreated({ this, &WindowEmperor::_numberOfWindowsChanged }); + _WindowClosedToken = _manager.WindowClosed({ 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 = _windowManager2.QuitAllRequested(winrt::auto_revoke, { this, &AppHost::_QuitAllRequested }); + + //////////////////////////////////////////////////////////////////////////// + + // // The monarch should be monitoring if it should save the window layout. + // if (!_getWindowLayoutThrottler.has_value()) + // { + // // We want at least some delay to prevent the first save from overwriting + // // the data as we try load windows initially. + // _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()(); + } + + const auto& numWindows{ _manager.GetNumberOfPeasants() }; + for (const auto& _windowThread : _windows) + { + _windowThread->Logic().SetNumberOfOpenWindows(numWindows); + } +} + +// void WindowEmperor::_attemptWindowRestore() +// { +// // At this point, we know there's exactly one peasant. +// // * WindowLogic / TerminalPage should start with htat value initialized to 1 +// // * emperor should listen for windowManager.NumberOfOpenWindowsChanged and then propogate that to all its WindowThreads +// // * we should store WindowThread ptrs in the vector, not std::thread's you idiot +// // * WindowThread should own the thread, don't you think OH but do we want thre WindowThread to live on main? or on the std::thread - probably the second + +// const auto numPeasants = _windowManager2.GetNumberOfPeasants(); +// const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + +// if (_windowLogic.ShouldUsePersistedLayout() && +// 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 (numPeasants == 1 && +// !_windowLogic.HasCommandlineArguments() && +// !_appLogic.HasSettingsStartupActions()) +// { +// _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[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 +// )); +// } +// } +// _windowLogic.SetNumberOfOpenWindows(numPeasants); +// } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 448fe916165..2ec892f7300 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -30,5 +30,14 @@ class WindowEmperor winrt::TerminalApp::App _app; winrt::Microsoft::Terminal::Remoting::WindowManager2 _manager; + std::vector> _windows; std::vector _threads; + + std::optional> _getWindowLayoutThrottler; + + winrt::event_token _WindowCreatedToken; + winrt::event_token _WindowClosedToken; + + void _becomeMonarch(); + void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); }; diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 1ef44717fae..50fcdd4f19f 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -13,6 +13,11 @@ WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic, { } +winrt::TerminalApp::TerminalWindow WindowThread::Logic() +{ + return _host.Logic(); +} + static bool _messageIsF7Keypress(const MSG& message) { return (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN) && message.wParam == VK_F7; diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 2f5ff8027da..d97a9dfbc43 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -13,6 +13,8 @@ class WindowThread winrt::Microsoft::Terminal::Remoting::Peasant peasant); int WindowProc(); + winrt::TerminalApp::TerminalWindow Logic(); + private: ::AppHost _host; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; From dc1ae9a895fc83011f4dd846042c1f8acd7450e5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 12:08:28 -0600 Subject: [PATCH 034/122] Starting to move these things to the right places --- src/cascadia/TerminalApp/AppLogic.cpp | 20 +++++ src/cascadia/TerminalApp/AppLogic.h | 3 + src/cascadia/TerminalApp/AppLogic.idl | 3 + src/cascadia/TerminalApp/TerminalWindow.cpp | 12 +-- src/cascadia/TerminalApp/TerminalWindow.h | 4 +- src/cascadia/TerminalApp/TerminalWindow.idl | 4 +- .../WindowsTerminal/WindowEmperor.cpp | 77 +++++++++++++++++-- src/cascadia/WindowsTerminal/WindowEmperor.h | 3 + 8 files changed, 108 insertions(+), 18 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index d9fee50c4c0..8779c80a5f3 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -698,4 +698,24 @@ namespace winrt::TerminalApp::implementation } return *window; } + + bool AppLogic::ShouldUsePersistedLayout() const + { + return _settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + } + 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))); + } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 83cfca4ee87..cfb15fbea4f 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -54,6 +54,9 @@ namespace winrt::TerminalApp::implementation bool HasSettingsStartupActions() const noexcept; + bool ShouldUsePersistedLayout() const; + void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); + [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; TerminalApp::FindTargetWindowResult FindTargetWindow(array_view actions); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 481c793ea40..8121d4dad5d 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -29,6 +29,9 @@ namespace TerminalApp Boolean HasSettingsStartupActions(); + Boolean ShouldUsePersistedLayout(); + void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); + void ReloadSettings(); Microsoft.Terminal.Settings.Model.Theme Theme { get; }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 0549c108b53..64997fa4fe9 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1040,12 +1040,12 @@ namespace winrt::TerminalApp::implementation //////////////////////////////////////////////////////////////////////////// - bool TerminalWindow::ShouldUsePersistedLayout() - { - return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; - } + // bool TerminalWindow::ShouldUsePersistedLayout() + // { + // return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; + // } - void TerminalWindow::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) + /*void TerminalWindow::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) { std::vector converted; converted.reserve(layouts.Size()); @@ -1059,7 +1059,7 @@ namespace winrt::TerminalApp::implementation } ApplicationState::SharedInstance().PersistedWindowLayouts(winrt::single_threaded_vector(std::move(converted))); - } + }*/ hstring TerminalWindow::GetWindowLayoutJson(LaunchPosition position) { diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 61ca2367767..c800480a35a 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -64,10 +64,10 @@ namespace winrt::TerminalApp::implementation bool AlwaysOnTop() const; bool AutoHideWindow(); - bool ShouldUsePersistedLayout(); + // bool ShouldUsePersistedLayout(); hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); - void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); + // void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); void IdentifyWindow(); void RenameFailed(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index be814c60166..ad635e7efd6 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -100,11 +100,11 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); - Boolean ShouldUsePersistedLayout(); + // Boolean ShouldUsePersistedLayout(); // Boolean ShouldImmediatelyHandoffToElevated(); // void HandoffToElevated(); String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); - void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); + // void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); Boolean GetMinimizeToNotificationArea(); Boolean GetAlwaysShowNotificationIcon(); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 8edec6585b1..a9a5c91c69f 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -16,6 +16,7 @@ using namespace winrt; using namespace winrt::Microsoft::Terminal; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; +using namespace std::chrono_literals; WindowEmperor::WindowEmperor() noexcept : _app{} @@ -176,14 +177,10 @@ void WindowEmperor::_becomeMonarch() //////////////////////////////////////////////////////////////////////////// - // // The monarch should be monitoring if it should save the window layout. - // if (!_getWindowLayoutThrottler.has_value()) - // { - // // We want at least some delay to prevent the first save from overwriting - // // the data as we try load windows initially. - // _getWindowLayoutThrottler.emplace(std::move(std::chrono::seconds(10)), std::move([this]() { _SaveWindowLayoutsRepeat(); })); - // _getWindowLayoutThrottler.value()(); - // } + // 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 @@ -258,3 +255,67 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II // } // _windowLogic.SetNumberOfOpenWindows(numPeasants); // } + +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 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()(); + } +} diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 2ec892f7300..3a19d452ccc 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -40,4 +40,7 @@ class WindowEmperor void _becomeMonarch(); void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); + + winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); + winrt::fire_and_forget _SaveWindowLayoutsRepeat(); }; From 8bb839113a4e2539c122e4ba97cabea65abfd77f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 12:42:12 -0600 Subject: [PATCH 035/122] move more window persist code around. It doesn't save or restore, but it does seem to not crash. 24 TODOs left --- src/cascadia/TerminalApp/AppLogic.cpp | 2 +- src/cascadia/TerminalApp/TabManagement.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 75 ++++------- src/cascadia/TerminalApp/TerminalPage.h | 6 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 104 +++++++-------- src/cascadia/TerminalApp/TerminalWindow.h | 9 +- .../GlobalAppSettings.cpp | 5 + .../TerminalSettingsModel/GlobalAppSettings.h | 2 + .../GlobalAppSettings.idl | 2 + src/cascadia/WindowsTerminal/AppHost.cpp | 118 ------------------ 10 files changed, 90 insertions(+), 235 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 8779c80a5f3..59fd00242a2 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -701,7 +701,7 @@ namespace winrt::TerminalApp::implementation bool AppLogic::ShouldUsePersistedLayout() const { - return _settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + return _settings.GlobalSettings().ShouldUsePersistedLayout(); } void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) { diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 7acd7d3f349..0b4c54d1e6f 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -523,7 +523,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. - if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + if (!_maintainStateOnTabClose && _settings.GlobalSettings().ShouldUsePersistedLayout() && _numOpenWindows == 1) { auto state = ApplicationState::SharedInstance(); state.PersistedWindowLayouts(nullptr); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 60d5019567c..d36a280ab05 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -293,17 +293,17 @@ namespace winrt::TerminalApp::implementation ShowSetAsDefaultInfoBar(); } - // Method Description; - // - Checks if the current terminal window should load or save its layout information. - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - true if the ApplicationState should be used. - bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const - { - return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; - } + // // Method Description; + // // - Checks if the current terminal window should load or save its layout information. + // // Arguments: + // // - settings: The settings to use as this may be called before the page is + // // fully initialized. + // // Return Value: + // // - true if the ApplicationState should be used. + // bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const + // { + // return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + // } // Method Description: // - This is a bit of trickiness: If we're running unelevated, and the user @@ -416,32 +416,6 @@ namespace winrt::TerminalApp::implementation } } - // Method Description; - // - Checks if the current window is configured to load a particular layout - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - non-null if there is a particular saved layout to use - std::optional TerminalPage::LoadPersistedLayoutIdx(CascadiaSettings& settings) const - { - return ShouldUsePersistedLayout(settings) ? _loadFromPersistedLayoutIdx : std::nullopt; - } - - WindowLayout TerminalPage::LoadPersistedLayout(CascadiaSettings& settings) const - { - if (const auto idx = LoadPersistedLayoutIdx(settings)) - { - const auto i = idx.value(); - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (layouts && layouts.Size() > i) - { - return layouts.GetAt(i); - } - } - return nullptr; - } - winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e) { Windows::Foundation::Collections::IVectorView items; @@ -525,15 +499,17 @@ namespace winrt::TerminalApp::implementation { _startupState = StartupState::InStartup; - // If we are provided with an index, the cases where we have - // commandline args and startup actions are already handled. - if (const auto layout = LoadPersistedLayout(_settings)) - { - if (layout.TabLayout().Size() > 0) - { - _startupActions = layout.TabLayout(); - } - } + // TODO! This should be moved into TerminalWindow, and loaded into the page _long_ before the first layout. That way this method can just handle _startupActions, whether they're persisted layout or commandline args. + // + //// If we are provided with an index, the cases where we have + //// commandline args and startup actions are already handled. + //if (const auto layout = LoadPersistedLayout(_settings)) + //{ + // if (layout.TabLayout().Size() > 0) + // { + // _startupActions = layout.TabLayout(); + // } + //} ProcessStartupActions(_startupActions, true); @@ -1866,7 +1842,7 @@ namespace winrt::TerminalApp::implementation } } - if (ShouldUsePersistedLayout(_settings)) + 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 @@ -3776,11 +3752,6 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx) - { - _loadFromPersistedLayoutIdx = idx; - } - void TerminalPage::SetNumberOfOpenWindows(const uint64_t num) { // This is used in TerminalPage::_RemoveTab, when we close a tab. If we diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index cee4c6f8f45..8ee040a6685 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -79,11 +79,10 @@ namespace winrt::TerminalApp::implementation void Create(); - bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + // bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - std::optional LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; - winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; + Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout(); winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e); @@ -140,7 +139,6 @@ namespace winrt::TerminalApp::implementation winrt::hstring WindowNameForDisplay() const noexcept { return _WindowProperties.WindowNameForDisplay(); }; void SetNumberOfOpenWindows(const uint64_t value); - void SetPersistedLayoutIdx(const uint32_t value); // bool IsQuakeWindow() const noexcept; bool IsElevated() const noexcept; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 64997fa4fe9..0b91c2ef566 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -146,7 +146,7 @@ namespace winrt::TerminalApp::implementation // don't have a page yet. if (const auto idx = _appArgs.GetPersistedLayoutIdx()) { - _root->SetPersistedLayoutIdx(idx.value()); + SetPersistedLayoutIdx(idx.value()); } _root->SetStartupActions(_appArgs.GetStartupActions()); @@ -569,7 +569,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Size proposedSize{}; const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = LoadPersistedLayout()) { if (layout.InitialSize()) { @@ -645,7 +645,7 @@ namespace winrt::TerminalApp::implementation // commandline, then use that to override the value from the settings. const auto valueFromSettings = _settings.GlobalSettings().LaunchMode(); const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode(); - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = LoadPersistedLayout()) { if (layout.LaunchMode()) { @@ -671,7 +671,7 @@ namespace winrt::TerminalApp::implementation { auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = LoadPersistedLayout()) { if (layout.InitialPosition()) { @@ -858,7 +858,7 @@ namespace winrt::TerminalApp::implementation { // If persisted layout is enabled and we are the last window closing // we should save our state. - if (_root->ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + if (_settings.GlobalSettings().ShouldUsePersistedLayout() && _numOpenWindows == 1) { if (const auto layout = _root->GetWindowLayout()) { @@ -1040,27 +1040,6 @@ namespace winrt::TerminalApp::implementation //////////////////////////////////////////////////////////////////////////// - // bool TerminalWindow::ShouldUsePersistedLayout() - // { - // return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; - // } - - /*void TerminalWindow::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))); - }*/ - hstring TerminalWindow::GetWindowLayoutJson(LaunchPosition position) { if (_root != nullptr) @@ -1074,43 +1053,35 @@ namespace winrt::TerminalApp::implementation return L""; } - void TerminalWindow::IdentifyWindow() + void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) { - if (_root) - { - _root->IdentifyWindow(); - } + _loadFromPersistedLayoutIdx = idx; } - // winrt::hstring TerminalWindow::WindowName() - // { - // return _root ? _root->WindowName() : L""; - // } - // void TerminalWindow::WindowName(const winrt::hstring& name) - // { - // if (_root) - // { - // _root->WindowName(name); - // } - // } - // uint64_t TerminalWindow::WindowId() - // { - // return _root ? _root->WindowId() : 0; - // } - // void TerminalWindow::WindowId(const uint64_t& id) - // { - // if (_root) - // { - // _root->WindowId(id); - // } - // } + // Method Description; + // - Checks if the current window is configured to load a particular layout + // Arguments: + // - settings: The settings to use as this may be called before the page is + // fully initialized. + // Return Value: + // - non-null if there is a particular saved layout to use + std::optional TerminalWindow::LoadPersistedLayoutIdx() const + { + return _settings.GlobalSettings().ShouldUsePersistedLayout() ? _loadFromPersistedLayoutIdx : std::nullopt; + } - void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) + WindowLayout TerminalWindow::LoadPersistedLayout() const { - if (_root) + if (const auto idx = LoadPersistedLayoutIdx()) { - _root->SetPersistedLayoutIdx(idx); + const auto i = idx.value(); + const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + if (layouts && layouts.Size() > i) + { + return layouts.GetAt(i); + } } + return nullptr; } void TerminalWindow::SetNumberOfOpenWindows(const uint64_t num) @@ -1122,6 +1093,27 @@ namespace winrt::TerminalApp::implementation } } + // // Method Description; + // // - Checks if the current terminal window should load or save its layout information. + // // Arguments: + // // - settings: The settings to use as this may be called before the page is + // // fully initialized. + // // Return Value: + // // - true if the ApplicationState should be used. + // bool TerminalWindow::ShouldUsePersistedLayout() const + // { + // return _settings.GlobalSettings().ShouldUsePersistedLayout(); + // } + //////////////////////////////////////////////////////////////////////////// + + void TerminalWindow::IdentifyWindow() + { + if (_root) + { + _root->IdentifyWindow(); + } + } + void TerminalWindow::RenameFailed() { if (_root) diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index c800480a35a..2b1545b7877 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -64,15 +64,17 @@ namespace winrt::TerminalApp::implementation bool AlwaysOnTop() const; bool AutoHideWindow(); - // bool ShouldUsePersistedLayout(); - hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); - // void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); + void IdentifyWindow(); void RenameFailed(); + std::optional LoadPersistedLayoutIdx() const; + winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout() const; void SetPersistedLayoutIdx(const uint32_t idx); void SetNumberOfOpenWindows(const uint64_t num); + bool ShouldUsePersistedLayout() const; + // bool IsQuakeWindow() const noexcept; void RequestExitFullscreen(); @@ -153,6 +155,7 @@ namespace winrt::TerminalApp::implementation uint64_t _WindowId{ 0 }; uint64_t _numOpenWindows{ 0 }; + std::optional _loadFromPersistedLayoutIdx{}; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; TerminalApp::SettingsLoadEventArgs _initialLoadResult{ nullptr }; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index d6a5aa49e87..eb166de9399 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -245,3 +245,8 @@ void GlobalAppSettings::ExpandCommands(const winrt::Windows::Foundation::Collect { _actionMap->ExpandCommands(profiles, schemes); } + +bool GlobalAppSettings::ShouldUsePersistedLayout() const +{ + return FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; +} diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 945c032c2bc..b14134167ee 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -63,6 +63,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void AddTheme(const Model::Theme& theme); Model::Theme CurrentTheme() noexcept; + bool ShouldUsePersistedLayout() const; + void ExpandCommands(const Windows::Foundation::Collections::IVectorView& profiles, const Windows::Foundation::Collections::IMapView& schemes); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 4ee2b0bf9e6..eb5fa11e041 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -108,5 +108,7 @@ namespace Microsoft.Terminal.Settings.Model void AddTheme(Theme theme); INHERITABLE_SETTING(ThemePair, Theme); Theme CurrentTheme { get; }; + + Boolean ShouldUsePersistedLayout(); } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 62c3be16508..0b9c8a782df 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -116,13 +116,6 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, // // swap what context they are in to the ui thread to get the actual layout. // args.WindowLayoutJsonAsync(_GetWindowLayoutAsync()); // }); - - // TODO! Where are we putting the monarch stuff now? - // _revokers.BecameMonarch = _windowManager2.BecameMonarch(winrt::auto_revoke, { this, &AppHost::_BecomeMonarch }); - // if (_windowManager2.IsMonarch()) - // { - // _BecomeMonarch(nullptr, nullptr); - // } } AppHost::~AppHost() @@ -515,7 +508,6 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se // _getWindowLayoutThrottler.reset(); // _windowManager2.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); - // 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. @@ -870,55 +862,6 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable send _windowLogic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } -// TODO! -// // 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::apartment_context peasant_thread; - -// winrt::hstring layoutJson = L""; -// // Use the main thread since we are accessing controls. -// co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); -// try -// { -// const auto pos = _GetWindowLaunchPosition(); -// layoutJson = _windowLogic.GetWindowLayoutJson(pos); -// } -// CATCH_LOG() - -// // go back to give the result to the peasant. -// co_await peasant_thread; - -// co_return layoutJson; -// } - -// // Method Description: -// // - Event handler for the WindowManager::FindTargetWindowRequested event. The -// // manager will ask us how to figure out what the target window is for a set -// // of commandline arguments. We'll take those arguments, and ask AppLogic to -// // parse them for us. We'll then set ResultTargetWindow in the given args, so -// // the sender can use that result. -// // Arguments: -// // - args: the bundle of a commandline and working directory to find the correct target window for. -// // Return Value: -// // - -// void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, -// const Remoting::FindTargetWindowArgs& args) -// { -// const auto targetWindow = _appLogic.FindTargetWindow(args.Args().Commandline()); -// args.ResultTargetWindow(targetWindow.WindowId()); -// args.ResultTargetWindowName(targetWindow.WindowName()); -// } - winrt::fire_and_forget AppHost::_WindowActivated(bool activated) { _windowLogic.WindowActivated(activated); @@ -1001,67 +944,6 @@ winrt::fire_and_forget AppHost::_WindowActivated(bool activated) // } // } -// winrt::Windows::Foundation::IAsyncAction AppHost::_SaveWindowLayouts() -// { -// // Make sure we run on a background thread to not block anything. -// co_await winrt::resume_background(); - -// if (_windowLogic.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 = _windowManager2.GetAllWindowLayouts(); -// TraceLoggingWrite(g_hWindowsTerminalProvider, -// "AppHost_SaveWindowLayouts_Save", -// TraceLoggingDescription("Logged when writing window state"), -// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), -// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); -// _windowLogic.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 AppHost::_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 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()(); -// } -// } - winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { if (_windowLogic.GetRoot() == nullptr) From 0f4c4d8eefb977e5f3c063a0f3a39d3948edb29e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 12:56:22 -0600 Subject: [PATCH 036/122] It persists, but it doesn't restore yet. 21 TODOs left --- src/cascadia/Remoting/WindowManager2.cpp | 3 +- src/cascadia/WindowsTerminal/AppHost.cpp | 70 +++++++++++++----------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index 84cb71148ef..d9fea3b5ee5 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -351,8 +351,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.AddPeasant(*p); - // TODO! - // _peasant.GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers }); + p->GetWindowLayoutRequested({ get_weak(), &WindowManager2::_GetWindowLayoutRequestedHandlers }); TraceLoggingWrite(g_hRemotingProvider, "WindowManager_CreateOurPeasant", diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0b9c8a782df..5033c4062a4 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -110,12 +110,12 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, _window->MakeWindow(); - // TODO! - // _GetWindowLayoutRequestedToken = _windowManager2.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::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()); - // }); + _GetWindowLayoutRequestedToken = _windowManager2.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()); + }); } AppHost::~AppHost() @@ -160,28 +160,6 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& } } -// void _buildArgsFromCommandline(std::vector& args) -// { -// if (auto commandline{ GetCommandLineW() }) -// { -// auto argc = 0; - -// // Get the argv, and turn them into a hstring array to pass to the app. -// wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; -// if (argv) -// { -// for (auto& elem : wil::make_range(argv.get(), argc)) -// { -// args.emplace_back(elem); -// } -// } -// } -// if (args.empty()) -// { -// args.emplace_back(L"wt.exe"); -// } -// } - // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to // the WindowManager, to ask if we should become a window process. @@ -503,10 +481,8 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se // _HideNotificationIconRequested(nullptr, nullptr); // } - // TODO! - // // We don't want to try to save layouts if we are about to close. - // _getWindowLayoutThrottler.reset(); - // _windowManager2.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); + // We don't want to try to save layouts if we are about to close. + _windowManager2.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); // 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 @@ -888,6 +864,36 @@ winrt::fire_and_forget AppHost::_WindowActivated(bool activated) } } +// 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::apartment_context peasant_thread; + + winrt::hstring layoutJson = L""; + // Use the main thread since we are accessing controls. + co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); + try + { + const auto pos = _GetWindowLaunchPosition(); + layoutJson = _windowLogic.GetWindowLayoutJson(pos); + } + CATCH_LOG() + + // go back to give the result to the peasant. + co_await peasant_thread; + + co_return layoutJson; +} + // void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, // const winrt::Windows::Foundation::IInspectable& /*args*/) // { From a5a99303540e06a9a4820cdac4a6c06bb75a357d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 13:25:41 -0600 Subject: [PATCH 037/122] Hey look I brought two windows back into existence!... They weren't from the persisted JSON, but they did come back 21 TODOs left --- src/cascadia/TerminalApp/TerminalWindow.h | 2 +- .../WindowsTerminal/WindowEmperor.cpp | 134 ++++++++++-------- src/cascadia/WindowsTerminal/WindowEmperor.h | 3 +- 3 files changed, 76 insertions(+), 63 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 2b1545b7877..8119ca45d56 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -154,7 +154,7 @@ namespace winrt::TerminalApp::implementation winrt::hstring _WindowName{}; uint64_t _WindowId{ 0 }; - uint64_t _numOpenWindows{ 0 }; + uint64_t _numOpenWindows{ 1 }; std::optional _loadFromPersistedLayoutIdx{}; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index a9a5c91c69f..63c965b5058 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -14,6 +14,7 @@ using namespace winrt; using namespace winrt::Microsoft::Terminal; +using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; using namespace std::chrono_literals; @@ -73,13 +74,13 @@ bool WindowEmperor::HandleCommandlineArgs() if (result.ShouldCreateWindow()) { - CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }); + CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }, true); _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { - CreateNewWindowThread(args); + CreateNewWindowThread(args, false); }); _becomeMonarch(); - // _attemptWindowRestore(); + _attemptWindowRestore(eventArgs); } return result.ShouldCreateWindow(); @@ -129,13 +130,30 @@ void WindowEmperor::WaitForWindows() } } -void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) +void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, const bool firstWindow) { Remoting::Peasant peasant{ _manager.CreateAPeasant(args) }; - auto func = [this, args, peasant]() { + auto func = [this, args, peasant, firstWindow]() { auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; _windows.push_back(window); + + if (firstWindow) + { + const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + if (_app.Logic().ShouldUsePersistedLayout() && + layouts && + layouts.Size() > 0) + { + if ( + !window->Logic().HasCommandlineArguments() && + !_app.Logic().HasSettingsStartupActions()) + { + window->Logic().SetPersistedLayoutIdx(0); + } + } + } + return window->WindowProc(); }; @@ -199,62 +217,56 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II } } -// void WindowEmperor::_attemptWindowRestore() -// { -// // At this point, we know there's exactly one peasant. -// // * WindowLogic / TerminalPage should start with htat value initialized to 1 -// // * emperor should listen for windowManager.NumberOfOpenWindowsChanged and then propogate that to all its WindowThreads -// // * we should store WindowThread ptrs in the vector, not std::thread's you idiot -// // * WindowThread should own the thread, don't you think OH but do we want thre WindowThread to live on main? or on the std::thread - probably the second - -// const auto numPeasants = _windowManager2.GetNumberOfPeasants(); -// const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - -// if (_windowLogic.ShouldUsePersistedLayout() && -// 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 (numPeasants == 1 && -// !_windowLogic.HasCommandlineArguments() && -// !_appLogic.HasSettingsStartupActions()) -// { -// _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[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 -// )); -// } -// } -// _windowLogic.SetNumberOfOpenWindows(numPeasants); -// } +void WindowEmperor::_attemptWindowRestore(const Remoting::CommandlineArgs& args) +{ + // At this point, we know there's exactly one peasant. + // * WindowLogic / TerminalPage should start with htat value initialized to 1 + // * emperor should listen for windowManager.NumberOfOpenWindowsChanged and then propogate that to all its WindowThreads + // * we should store WindowThread ptrs in the vector, not std::thread's you idiot + // * WindowThread should own the thread, don't you think OH but do we want thre WindowThread to live on main? or on the std::thread - probably the second + + // const auto numPeasants = _windowManager2.GetNumberOfPeasants(); + const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + + if (_app.Logic().ShouldUsePersistedLayout() && + layouts && + layouts.Size() > 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. + + // _windowLogic.HasCommandlineArguments === args.size() > 1 + uint32_t startIdx = args.Commandline().size() > 1 ? 1 : 0; + + // 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 + )); + } + } + // _windowLogic.SetNumberOfOpenWindows(numPeasants); +} winrt::Windows::Foundation::IAsyncAction WindowEmperor::_SaveWindowLayouts() { diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 3a19d452ccc..d9928353d2b 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -24,7 +24,7 @@ class WindowEmperor void WaitForWindows(); bool HandleCommandlineArgs(); - void CreateNewWindowThread(winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args); + void CreateNewWindowThread(winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, const bool firstWindow); private: winrt::TerminalApp::App _app; @@ -43,4 +43,5 @@ class WindowEmperor winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); winrt::fire_and_forget _SaveWindowLayoutsRepeat(); + void _attemptWindowRestore(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); }; From 6e6d14e0dcdb8adbf30282db7fc0477425175415 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 15:29:04 -0600 Subject: [PATCH 038/122] We're getting closer to loading up the previous state, but it doesn't always work as expected 20 TODOs left --- src/cascadia/TerminalApp/TerminalPage.cpp | 12 ------- src/cascadia/TerminalApp/TerminalWindow.cpp | 36 +++++++++++++++++-- .../WindowsTerminal/WindowEmperor.cpp | 2 +- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d36a280ab05..494b6f14f93 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -499,18 +499,6 @@ namespace winrt::TerminalApp::implementation { _startupState = StartupState::InStartup; - // TODO! This should be moved into TerminalWindow, and loaded into the page _long_ before the first layout. That way this method can just handle _startupActions, whether they're persisted layout or commandline args. - // - //// If we are provided with an index, the cases where we have - //// commandline args and startup actions are already handled. - //if (const auto layout = LoadPersistedLayout(_settings)) - //{ - // if (layout.TabLayout().Size() > 0) - // { - // _startupActions = layout.TabLayout(); - // } - //} - ProcessStartupActions(_startupActions, true); // If we were told that the COM server needs to be started to listen for incoming diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 0b91c2ef566..241bc620a31 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -144,11 +144,26 @@ namespace winrt::TerminalApp::implementation // Pass commandline args into the TerminalPage. // * we first got these in SetStartupCommandline, but at that time, we // don't have a page yet. - if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + auto foundLayout = false; + + if (const auto& layout = LoadPersistedLayout()) { - SetPersistedLayoutIdx(idx.value()); + if (layout.TabLayout().Size() > 0) + { + // _startupActions = layout.TabLayout(); + std::vector actions; + for (const auto& a : layout.TabLayout()) + { + actions.emplace_back(a); + } + _root->SetStartupActions(actions); + foundLayout = true; + } + } + if (!foundLayout) + { + _root->SetStartupActions(_appArgs.GetStartupActions()); } - _root->SetStartupActions(_appArgs.GetStartupActions()); // Check if we were started as a COM server for inbound connections of console sessions // coming out of the operating system default application feature. If so, @@ -969,6 +984,9 @@ namespace winrt::TerminalApp::implementation // or 0. (see TerminalWindow::_ParseArgs) int32_t TerminalWindow::SetStartupCommandline(array_view args) { + // This is called in Apphost::ctor(), before we've created the window + // (or called TerminalWindow::Initialize) + const auto result = _appArgs.ParseArgs(args); if (result == 0) { @@ -981,6 +999,18 @@ namespace winrt::TerminalApp::implementation // Instead, we'll handle that in Initialize, when we first instantiate the page. } + if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + { + SetPersistedLayoutIdx(idx.value()); + } + else + { + if (_settings.GlobalSettings().ShouldUsePersistedLayout() && + args.size() <= 1) + { + SetPersistedLayoutIdx(0); + } + } return result; } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 63c965b5058..aa42603ec65 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -240,7 +240,7 @@ void WindowEmperor::_attemptWindowRestore(const Remoting::CommandlineArgs& args) // The 2nd+ layout will always get a new window. // _windowLogic.HasCommandlineArguments === args.size() > 1 - uint32_t startIdx = args.Commandline().size() > 1 ? 1 : 0; + uint32_t startIdx = args.Commandline().size() > 1 ? 0 : 1; // Create new windows for each of the other saved layouts. for (const auto size = layouts.Size(); startIdx < size; startIdx += 1) From 3fb8e8cac3f1a408eb49591d78f2caddd4833653 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 17:13:15 -0600 Subject: [PATCH 039/122] Hey this worked! The trick is, that if you do this while debugging, the AppState will get persisted as you're stepping through, which can make debugging this really tricky --- src/cascadia/TerminalApp/TerminalWindow.cpp | 16 +-- src/cascadia/WindowsTerminal/AppHost.cpp | 101 +++++++++--------- .../WindowsTerminal/WindowEmperor.cpp | 32 +++--- 3 files changed, 74 insertions(+), 75 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 241bc620a31..d3007d03604 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1003,14 +1003,14 @@ namespace winrt::TerminalApp::implementation { SetPersistedLayoutIdx(idx.value()); } - else - { - if (_settings.GlobalSettings().ShouldUsePersistedLayout() && - args.size() <= 1) - { - SetPersistedLayoutIdx(0); - } - } + // else + // { + // if (_settings.GlobalSettings().ShouldUsePersistedLayout() && + // args.size() <= 1) + // { + // SetPersistedLayoutIdx(0); + // } + // } return result; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 5033c4062a4..528e11879e2 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -198,7 +198,8 @@ void AppHost::_HandleCommandlineArgs(/*const winrt::Microsoft::Terminal::Remotin if (_peasant) { - if (auto args{ _peasant.InitialArgs() }) + const auto& args{ _peasant.InitialArgs() }; + if (args) { const auto result = _windowLogic.SetStartupCommandline(args.Commandline()); const auto message = _windowLogic.ParseCommandlineMessage(); @@ -248,56 +249,54 @@ void AppHost::_HandleCommandlineArgs(/*const winrt::Microsoft::Terminal::Remotin _revokers.peasantDisplayWindowIdRequested = _peasant.DisplayWindowIdRequested(winrt::auto_revoke, { this, &AppHost::_DisplayWindowId }); _revokers.peasantQuitRequested = _peasant.QuitRequested(winrt::auto_revoke, { this, &AppHost::_QuitRequested }); - // TODO! - // // We need this property to be set before we get the InitialSize/Position - // // and BecameMonarch which normally sets it is only run after the window - // // is created. - // if (_windowManager2.IsMonarch()) - // { - // const auto numPeasants = _windowManager2.GetNumberOfPeasants(); - // const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - // if (_windowLogic.ShouldUsePersistedLayout() && - // 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 (numPeasants == 1 && !_windowLogic.HasCommandlineArguments() && !_appLogic.HasSettingsStartupActions()) - // { - // _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[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 - // )); - // } - // } - // _windowLogic.SetNumberOfOpenWindows(numPeasants); - // } + const auto numPeasants = _windowManager2.GetNumberOfPeasants(); + if (numPeasants == 1) + { + const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + if (_appLogic.ShouldUsePersistedLayout() && + 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()) + { + _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 + )); + } + } + } + _windowLogic.SetNumberOfOpenWindows(numPeasants); + _windowLogic.WindowName(_peasant.WindowName()); _windowLogic.WindowId(_peasant.GetID()); } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index aa42603ec65..cec7307b252 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -80,7 +80,7 @@ bool WindowEmperor::HandleCommandlineArgs() CreateNewWindowThread(args, false); }); _becomeMonarch(); - _attemptWindowRestore(eventArgs); + // _attemptWindowRestore(eventArgs); } return result.ShouldCreateWindow(); @@ -138,21 +138,21 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; _windows.push_back(window); - if (firstWindow) - { - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (_app.Logic().ShouldUsePersistedLayout() && - layouts && - layouts.Size() > 0) - { - if ( - !window->Logic().HasCommandlineArguments() && - !_app.Logic().HasSettingsStartupActions()) - { - window->Logic().SetPersistedLayoutIdx(0); - } - } - } + // if (firstWindow) + // { + // const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + // if (_app.Logic().ShouldUsePersistedLayout() && + // layouts && + // layouts.Size() > 0) + // { + // if ( + // !window->Logic().HasCommandlineArguments() && + // !_app.Logic().HasSettingsStartupActions()) + // { + // window->Logic().SetPersistedLayoutIdx(0); + // } + // } + // } return window->WindowProc(); }; From 4d5f6d27fb4353815381f36364cfb3551045b615 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Feb 2023 17:26:33 -0600 Subject: [PATCH 040/122] cleanup. 18 TODOs remain --- src/cascadia/TerminalApp/TerminalPage.cpp | 12 ---- src/cascadia/TerminalApp/TerminalWindow.cpp | 44 +++--------- src/cascadia/WindowsTerminal/AppHost.cpp | 20 ++---- .../WindowsTerminal/WindowEmperor.cpp | 70 +------------------ 4 files changed, 16 insertions(+), 130 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 494b6f14f93..03d2df9c490 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -293,18 +293,6 @@ namespace winrt::TerminalApp::implementation ShowSetAsDefaultInfoBar(); } - // // Method Description; - // // - Checks if the current terminal window should load or save its layout information. - // // Arguments: - // // - settings: The settings to use as this may be called before the page is - // // fully initialized. - // // Return Value: - // // - true if the ApplicationState should be used. - // bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const - // { - // return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; - // } - // Method Description: // - This is a bit of trickiness: If we're running unelevated, and the user // passed in only --elevate actions, the we don't _actually_ want to diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index d3007d03604..05e68943bae 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -141,16 +141,13 @@ namespace winrt::TerminalApp::implementation _root->WindowProperties(*this); _dialog = ContentDialog{}; - // Pass commandline args into the TerminalPage. - // * we first got these in SetStartupCommandline, but at that time, we - // don't have a page yet. + // Pass commandline args into the TerminalPage. If we were supposed to + // load from a persisted layout, do that instead. auto foundLayout = false; - if (const auto& layout = LoadPersistedLayout()) { if (layout.TabLayout().Size() > 0) { - // _startupActions = layout.TabLayout(); std::vector actions; for (const auto& a : layout.TabLayout()) { @@ -269,7 +266,9 @@ namespace winrt::TerminalApp::implementation _RefreshThemeRoutine(); - auto args = winrt::make_self(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &TerminalWindow::_OpenSettingsUI)); + auto args = winrt::make_self(RS_(L"SettingsMenuItem"), + SystemMenuChangeAction::Add, + SystemMenuItemHandler(this, &TerminalWindow::_OpenSettingsUI)); _SystemMenuChangeRequestedHandlers(*this, *args); TraceLoggingWrite( @@ -334,7 +333,7 @@ namespace winrt::TerminalApp::implementation // - dialog: the dialog object that is going to show up // Return value: // - an IAsyncOperation with the dialog result - winrt::Windows::Foundation::IAsyncOperation TerminalWindow::ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog) + winrt::Windows::Foundation::IAsyncOperation TerminalWindow::ShowDialog(winrt::WUX::Controls::ContentDialog dialog) { // DON'T release this lock in a wil::scope_exit. The scope_exit will get // called when we await, which is not what we want. @@ -949,12 +948,7 @@ namespace winrt::TerminalApp::implementation } //////////////////////////////////////////////////////////////////////////// - - // bool TerminalWindow::HasSettingsStartupActions() const noexcept - // { - // return _hasSettingsStartupActions; - // } - void TerminalWindow::SetSettingsStartupArgs(const std::vector& actions) + void TerminalWindow::SetSettingsStartupArgs(const std::vector& actions) { for (const auto& action : actions) { @@ -999,18 +993,11 @@ namespace winrt::TerminalApp::implementation // Instead, we'll handle that in Initialize, when we first instantiate the page. } + // If we have a -s param passed to us to load a saved layout, cache that now. if (const auto idx = _appArgs.GetPersistedLayoutIdx()) { SetPersistedLayoutIdx(idx.value()); } - // else - // { - // if (_settings.GlobalSettings().ShouldUsePersistedLayout() && - // args.size() <= 1) - // { - // SetPersistedLayoutIdx(0); - // } - // } return result; } @@ -1122,18 +1109,6 @@ namespace winrt::TerminalApp::implementation _root->SetNumberOfOpenWindows(num); } } - - // // Method Description; - // // - Checks if the current terminal window should load or save its layout information. - // // Arguments: - // // - settings: The settings to use as this may be called before the page is - // // fully initialized. - // // Return Value: - // // - true if the ApplicationState should be used. - // bool TerminalWindow::ShouldUsePersistedLayout() const - // { - // return _settings.GlobalSettings().ShouldUsePersistedLayout(); - // } //////////////////////////////////////////////////////////////////////////// void TerminalWindow::IdentifyWindow() @@ -1167,12 +1142,11 @@ namespace winrt::TerminalApp::implementation { return _settings.GlobalSettings().AutoHideWindow(); } - // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} + void TerminalWindow::UpdateSettingsHandler(const winrt::IInspectable& /*sender*/, const winrt::TerminalApp::SettingsLoadEventArgs& args) { UpdateSettings(args); - // _root->SetSettings(_settings, true); } //////////////////////////////////////////////////////////////////////////// diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 528e11879e2..a6153c507ae 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -177,21 +177,8 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& // - // Return Value: // - -void AppHost::_HandleCommandlineArgs(/*const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args*/) +void AppHost::_HandleCommandlineArgs() { - // std::vector args; - // // _buildArgsFromCommandline(args); - // auto cwd{ wil::GetCurrentDirectoryW() }; - - // Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; - // _windowManager2.ProposeCommandline(eventArgs); - - // _shouldCreateWindow = _windowManager2.ShouldCreateWindow(); - // if (!_shouldCreateWindow) - // { - // return; - // } - // We did want to make a window, so let's instantiate it here. // We don't have XAML yet, but we do have other stuff. _windowLogic = _appLogic.CreateNewWindow(); @@ -249,6 +236,11 @@ void AppHost::_HandleCommandlineArgs(/*const winrt::Microsoft::Terminal::Remotin _revokers.peasantDisplayWindowIdRequested = _peasant.DisplayWindowIdRequested(winrt::auto_revoke, { this, &AppHost::_DisplayWindowId }); _revokers.peasantQuitRequested = _peasant.QuitRequested(winrt::auto_revoke, { this, &AppHost::_QuitRequested }); + // This is logic that almost seems like it belongs on the WindowEmperor. + // It probably does. However, it needs to muck with our own window so + // much, that there was no reasonable way of moving this. Moving it also + // seemed to reorder bits of init so much that everything broke. So + // we'll leave it here. const auto numPeasants = _windowManager2.GetNumberOfPeasants(); if (numPeasants == 1) { diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index cec7307b252..117b8b34619 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -79,8 +79,8 @@ bool WindowEmperor::HandleCommandlineArgs() _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { CreateNewWindowThread(args, false); }); + _becomeMonarch(); - // _attemptWindowRestore(eventArgs); } return result.ShouldCreateWindow(); @@ -137,23 +137,6 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co auto func = [this, args, peasant, firstWindow]() { auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; _windows.push_back(window); - - // if (firstWindow) - // { - // const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - // if (_app.Logic().ShouldUsePersistedLayout() && - // layouts && - // layouts.Size() > 0) - // { - // if ( - // !window->Logic().HasCommandlineArguments() && - // !_app.Logic().HasSettingsStartupActions()) - // { - // window->Logic().SetPersistedLayoutIdx(0); - // } - // } - // } - return window->WindowProc(); }; @@ -217,57 +200,6 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II } } -void WindowEmperor::_attemptWindowRestore(const Remoting::CommandlineArgs& args) -{ - // At this point, we know there's exactly one peasant. - // * WindowLogic / TerminalPage should start with htat value initialized to 1 - // * emperor should listen for windowManager.NumberOfOpenWindowsChanged and then propogate that to all its WindowThreads - // * we should store WindowThread ptrs in the vector, not std::thread's you idiot - // * WindowThread should own the thread, don't you think OH but do we want thre WindowThread to live on main? or on the std::thread - probably the second - - // const auto numPeasants = _windowManager2.GetNumberOfPeasants(); - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - - if (_app.Logic().ShouldUsePersistedLayout() && - layouts && - layouts.Size() > 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. - - // _windowLogic.HasCommandlineArguments === args.size() > 1 - uint32_t startIdx = args.Commandline().size() > 1 ? 0 : 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 - )); - } - } - // _windowLogic.SetNumberOfOpenWindows(numPeasants); -} - winrt::Windows::Foundation::IAsyncAction WindowEmperor::_SaveWindowLayouts() { // Make sure we run on a background thread to not block anything. From a7379ca8e92edfd71bb12b03aa0fe95d82d0e7c6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 10:51:50 -0600 Subject: [PATCH 041/122] It so SO works --- src/cascadia/WindowsTerminal/AppHost.cpp | 202 +++++++-------- src/cascadia/WindowsTerminal/AppHost.h | 6 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 126 +++++----- src/cascadia/WindowsTerminal/IslandWindow.h | 2 +- .../WindowsTerminal/WindowEmperor.cpp | 238 +++++++++++++++++- src/cascadia/WindowsTerminal/WindowEmperor.h | 16 +- 6 files changed, 420 insertions(+), 170 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a6153c507ae..b11d7a62f05 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -102,7 +102,7 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, _window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled }); _window->WindowActivated({ this, &AppHost::_WindowActivated }); _window->WindowMoved({ this, &AppHost::_WindowMoved }); - _window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed }); + _window->ShouldExitFullscreen({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen }); _window->SetAlwaysOnTop(_windowLogic.GetInitialAlwaysOnTop()); @@ -941,112 +941,112 @@ winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowL // } // } -winrt::fire_and_forget AppHost::_setupGlobalHotkeys() -{ - if (_windowLogic.GetRoot() == nullptr) - { - co_return; - } - // The hotkey MUST be registered on the main thread. It will fail otherwise! - co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); +// winrt::fire_and_forget AppHost::_setupGlobalHotkeys() +// { +// if (_windowLogic.GetRoot() == nullptr) +// { +// co_return; +// } +// // The hotkey MUST be registered on the main thread. It will fail otherwise! +// co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); - if (!_window) - { - // MSFT:36797001 There's a surprising number of hits of this callback - // getting triggered during teardown. As a best practice, we really - // should make sure _window exists before accessing it on any coroutine. - // We might be getting called back after the app already began getting - // cleaned up. - co_return; - } - // Unregister all previously registered hotkeys. - // - // RegisterHotKey(), will not unregister hotkeys automatically. - // If a hotkey with a given HWND and ID combination already exists - // then a duplicate one will be added, which we don't want. - // (Additionally we want to remove hotkeys that were removed from the settings.) - for (auto i = 0, count = gsl::narrow_cast(_hotkeys.size()); i < count; ++i) - { - _window->UnregisterHotKey(i); - } +// if (!_window) +// { +// // MSFT:36797001 There's a surprising number of hits of this callback +// // getting triggered during teardown. As a best practice, we really +// // should make sure _window exists before accessing it on any coroutine. +// // We might be getting called back after the app already began getting +// // cleaned up. +// co_return; +// } +// // Unregister all previously registered hotkeys. +// // +// // RegisterHotKey(), will not unregister hotkeys automatically. +// // If a hotkey with a given HWND and ID combination already exists +// // then a duplicate one will be added, which we don't want. +// // (Additionally we want to remove hotkeys that were removed from the settings.) +// for (auto i = 0, count = gsl::narrow_cast(_hotkeys.size()); i < count; ++i) +// { +// _window->UnregisterHotKey(i); +// } - _hotkeys.clear(); +// _hotkeys.clear(); - // Re-register all current hotkeys. - for (const auto& [keyChord, cmd] : _appLogic.GlobalHotkeys()) - { - if (auto summonArgs = cmd.ActionAndArgs().Args().try_as()) - { - auto index = gsl::narrow_cast(_hotkeys.size()); - const auto succeeded = _window->RegisterHotKey(index, keyChord); - - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_setupGlobalHotkey", - TraceLoggingDescription("Emitted when setting a single hotkey"), - TraceLoggingInt64(index, "index", "the index of the hotkey to add"), - TraceLoggingWideString(cmd.Name().c_str(), "name", "the name of the command"), - TraceLoggingBoolean(succeeded, "succeeded", "true if we succeeded"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _hotkeys.emplace_back(summonArgs); - } - } -} +// // Re-register all current hotkeys. +// for (const auto& [keyChord, cmd] : _appLogic.GlobalHotkeys()) +// { +// if (auto summonArgs = cmd.ActionAndArgs().Args().try_as()) +// { +// auto index = gsl::narrow_cast(_hotkeys.size()); +// const auto succeeded = _window->RegisterHotKey(index, keyChord); + +// TraceLoggingWrite(g_hWindowsTerminalProvider, +// "AppHost_setupGlobalHotkey", +// TraceLoggingDescription("Emitted when setting a single hotkey"), +// TraceLoggingInt64(index, "index", "the index of the hotkey to add"), +// TraceLoggingWideString(cmd.Name().c_str(), "name", "the name of the command"), +// TraceLoggingBoolean(succeeded, "succeeded", "true if we succeeded"), +// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), +// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); +// _hotkeys.emplace_back(summonArgs); +// } +// } +// } -// Method Description: -// - Called whenever a registered hotkey is pressed. We'll look up the -// GlobalSummonArgs for the specified hotkey, then dispatch a call to the -// Monarch with the selection information. -// - If the monarch finds a match for the window name (or no name was provided), -// it'll set FoundMatch=true. -// - If FoundMatch is false, and a name was provided, then we should create a -// new window with the given name. -// Arguments: -// - hotkeyIndex: the index of the entry in _hotkeys that was pressed. -// Return Value: -// - -void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) -{ - if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) - { - return; - } +// // Method Description: +// // - Called whenever a registered hotkey is pressed. We'll look up the +// // GlobalSummonArgs for the specified hotkey, then dispatch a call to the +// // Monarch with the selection information. +// // - If the monarch finds a match for the window name (or no name was provided), +// // it'll set FoundMatch=true. +// // - If FoundMatch is false, and a name was provided, then we should create a +// // new window with the given name. +// // Arguments: +// // - hotkeyIndex: the index of the entry in _hotkeys that was pressed. +// // Return Value: +// // - +// void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) +// { +// if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) +// { +// return; +// } - const auto& summonArgs = til::at(_hotkeys, hotkeyIndex); - Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() }; +// const auto& summonArgs = til::at(_hotkeys, hotkeyIndex); +// Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() }; - // desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false - // desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false - // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true - args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); - args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); - args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); - args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); +// // desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false +// // desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false +// // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true +// args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); +// args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); +// args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); +// args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); - switch (summonArgs.Monitor()) - { - case Settings::Model::MonitorBehavior::Any: - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); - break; - case Settings::Model::MonitorBehavior::ToCurrent: - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToCurrent); - break; - case Settings::Model::MonitorBehavior::ToMouse: - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToMouse); - break; - } +// switch (summonArgs.Monitor()) +// { +// case Settings::Model::MonitorBehavior::Any: +// args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); +// break; +// case Settings::Model::MonitorBehavior::ToCurrent: +// args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToCurrent); +// break; +// case Settings::Model::MonitorBehavior::ToMouse: +// args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToMouse); +// break; +// } - _windowManager2.SummonWindow(args); - if (args.FoundMatch()) - { - // Excellent, the window was found. We have nothing else to do here. - } - else - { - // We should make the window ourselves. - _createNewTerminalWindow(summonArgs); - } -} +// _windowManager2.SummonWindow(args); +// if (args.FoundMatch()) +// { +// // Excellent, the window was found. We have nothing else to do here. +// } +// else +// { +// // We should make the window ourselves. +// _createNewTerminalWindow(summonArgs); +// } +// } // Method Description: // - Called when the monarch failed to summon a window for a given set of @@ -1279,8 +1279,8 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta const winrt::TerminalApp::SettingsLoadEventArgs& /*args*/) { // We don't need to call in to windowLogic here - it has its own SettingsChanged handler - - _setupGlobalHotkeys(); + // TODO! this need to be replicated in Emperor + // _setupGlobalHotkeys(); // TODO! tray icon // diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index def0552e400..5c573f50642 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -36,7 +36,7 @@ class AppHost winrt::Microsoft::Terminal::Remoting::WindowManager2 _windowManager2{ nullptr }; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; - std::vector _hotkeys; + // std::vector _hotkeys; winrt::com_ptr _desktopManager{ nullptr }; bool _shouldCreateWindow{ false }; @@ -81,7 +81,7 @@ class AppHost // void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, // const winrt::Windows::Foundation::IInspectable& args); - void _GlobalHotkeyPressed(const long hotkeyIndex); + // void _GlobalHotkeyPressed(const long hotkeyIndex); void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args); @@ -96,7 +96,7 @@ class AppHost bool _LazyLoadDesktopManager(); - winrt::fire_and_forget _setupGlobalHotkeys(); + // winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& args); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index acc039ae447..0d6958b2a45 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -418,11 +418,11 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { switch (message) { - case WM_HOTKEY: - { - _HotkeyPressedHandlers(static_cast(wparam)); - return 0; - } + // case WM_HOTKEY: + // { + // _HotkeyPressedHandlers(static_cast(wparam)); + // return 0; + // } case WM_GETMINMAXINFO: { _OnGetMinMaxInfo(wparam, lparam); @@ -1264,64 +1264,64 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) } } -// Method Description: -// - Call UnregisterHotKey once for each previously registered hotkey. -// Return Value: -// - -void IslandWindow::UnregisterHotKey(const int index) noexcept -{ - TraceLoggingWrite( - g_hWindowsTerminalProvider, - "UnregisterHotKey", - TraceLoggingDescription("Emitted when clearing previously set hotkeys"), - TraceLoggingInt64(index, "index", "the index of the hotkey to remove"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - LOG_IF_WIN32_BOOL_FALSE(::UnregisterHotKey(_window.get(), index)); -} - -// Method Description: -// - Call RegisterHotKey to attempt to register that keybinding as a global hotkey. -// - When these keys are pressed, we'll get a WM_HOTKEY message with the payload -// containing the index we registered here. -// - Call UnregisterHotKey() before registering your hotkeys. -// See: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey#remarks -// Arguments: -// - hotkey: The key-combination to register. -// Return Value: -// - -bool IslandWindow::RegisterHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept -{ - const auto vkey = hotkey.Vkey(); - auto hotkeyFlags = MOD_NOREPEAT; - { - const auto modifiers = hotkey.Modifiers(); - WI_SetFlagIf(hotkeyFlags, MOD_WIN, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Windows)); - WI_SetFlagIf(hotkeyFlags, MOD_ALT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Menu)); - WI_SetFlagIf(hotkeyFlags, MOD_CONTROL, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Control)); - WI_SetFlagIf(hotkeyFlags, MOD_SHIFT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Shift)); - } - - // TODO GH#8888: We should display a warning of some kind if this fails. - // This can fail if something else already bound this hotkey. - const auto result = ::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey); - - TraceLoggingWrite(g_hWindowsTerminalProvider, - "RegisterHotKey", - TraceLoggingDescription("Emitted when setting hotkeys"), - TraceLoggingInt64(index, "index", "the index of the hotkey to add"), - TraceLoggingUInt64(vkey, "vkey", "the key"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_WIN), "win", "is WIN in the modifiers"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_ALT), "alt", "is ALT in the modifiers"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_CONTROL), "control", "is CONTROL in the modifiers"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_SHIFT), "shift", "is SHIFT in the modifiers"), - TraceLoggingBool(result, "succeeded", "true if we succeeded"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - return result; -} +// // Method Description: +// // - Call UnregisterHotKey once for each previously registered hotkey. +// // Return Value: +// // - +// void IslandWindow::UnregisterHotKey(const int index) noexcept +// { +// TraceLoggingWrite( +// g_hWindowsTerminalProvider, +// "UnregisterHotKey", +// TraceLoggingDescription("Emitted when clearing previously set hotkeys"), +// TraceLoggingInt64(index, "index", "the index of the hotkey to remove"), +// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), +// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + +// LOG_IF_WIN32_BOOL_FALSE(::UnregisterHotKey(_window.get(), index)); +// } + +// // Method Description: +// // - Call RegisterHotKey to attempt to register that keybinding as a global hotkey. +// // - When these keys are pressed, we'll get a WM_HOTKEY message with the payload +// // containing the index we registered here. +// // - Call UnregisterHotKey() before registering your hotkeys. +// // See: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey#remarks +// // Arguments: +// // - hotkey: The key-combination to register. +// // Return Value: +// // - +// bool IslandWindow::RegisterHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept +// { +// const auto vkey = hotkey.Vkey(); +// auto hotkeyFlags = MOD_NOREPEAT; +// { +// const auto modifiers = hotkey.Modifiers(); +// WI_SetFlagIf(hotkeyFlags, MOD_WIN, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Windows)); +// WI_SetFlagIf(hotkeyFlags, MOD_ALT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Menu)); +// WI_SetFlagIf(hotkeyFlags, MOD_CONTROL, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Control)); +// WI_SetFlagIf(hotkeyFlags, MOD_SHIFT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Shift)); +// } + +// // TODO GH#8888: We should display a warning of some kind if this fails. +// // This can fail if something else already bound this hotkey. +// const auto result = ::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey); + +// TraceLoggingWrite(g_hWindowsTerminalProvider, +// "RegisterHotKey", +// TraceLoggingDescription("Emitted when setting hotkeys"), +// TraceLoggingInt64(index, "index", "the index of the hotkey to add"), +// TraceLoggingUInt64(vkey, "vkey", "the key"), +// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_WIN), "win", "is WIN in the modifiers"), +// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_ALT), "alt", "is ALT in the modifiers"), +// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_CONTROL), "control", "is CONTROL in the modifiers"), +// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_SHIFT), "shift", "is SHIFT in the modifiers"), +// TraceLoggingBool(result, "succeeded", "true if we succeeded"), +// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), +// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + +// return result; +// } // Method Description: // - Summon the window, or possibly dismiss it. If toggleVisibility is true, diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 1fe0c4e60cb..2107fa5f2a0 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -73,7 +73,7 @@ class IslandWindow : WINRT_CALLBACK(WindowCloseButtonClicked, winrt::delegate<>); WINRT_CALLBACK(MouseScrolled, winrt::delegate); WINRT_CALLBACK(WindowActivated, winrt::delegate); - WINRT_CALLBACK(HotkeyPressed, winrt::delegate); + // WINRT_CALLBACK(HotkeyPressed, winrt::delegate); WINRT_CALLBACK(NotifyNotificationIconPressed, winrt::delegate); WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate); WINRT_CALLBACK(NotifyShowNotificationIconContextMenu, winrt::delegate); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 117b8b34619..453ea6028ba 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -12,12 +12,18 @@ #include "../../types/inc/utils.hpp" +#include "resource.h" + using namespace winrt; using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; using namespace std::chrono_literals; +using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; + +#define TERMINAL_MESSAGE_CLASS_NAME L"TERMINAL_MESSAGE_CLASS" +extern "C" IMAGE_DOS_HEADER __ImageBase; WindowEmperor::WindowEmperor() noexcept : _app{} @@ -30,6 +36,8 @@ WindowEmperor::WindowEmperor() noexcept : findWindowArgs.ResultTargetWindowName(targetWindow.WindowName()); } }); + + _dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread(); } WindowEmperor::~WindowEmperor() @@ -128,6 +136,8 @@ void WindowEmperor::WaitForWindows() TranslateMessage(&message); DispatchMessage(&message); } + + // _threads.clear(); } void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, const bool firstWindow) @@ -141,12 +151,16 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co }; _threads.emplace_back(func); + + LOG_IF_FAILED(SetThreadDescription(_threads.back().native_handle(), L"Window Thread")); } void WindowEmperor::_becomeMonarch() { + _createMessageWindow(); + //////////////////////////////////////////////////////////////////////////// - // _setupGlobalHotkeys(); + _setupGlobalHotkeys(); //////////////////////////////////////////////////////////////////////////// @@ -198,6 +212,13 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II { _windowThread->Logic().SetNumberOfOpenWindows(numWindows); } + // TODO! apparently, we crash when whe actually post a quit, handle it, and + // then leak all our threads. That's not good, but also, this should only + // happen once our threads all exited. So figure that out. + if (numWindows == 0) + { + // _close(); + } } winrt::Windows::Foundation::IAsyncAction WindowEmperor::_SaveWindowLayouts() @@ -263,3 +284,218 @@ winrt::fire_and_forget WindowEmperor::_SaveWindowLayoutsRepeat() _getWindowLayoutThrottler.value()(); } } + +static WindowEmperor* GetThisFromHandle(HWND const window) noexcept +{ + const auto data = GetWindowLongPtr(window, GWLP_USERDATA); + return reinterpret_cast(data); +} +[[nodiscard]] static LRESULT __stdcall MessageWndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept +{ + WINRT_ASSERT(window); + + if (WM_NCCREATE == message) + { + auto cs = reinterpret_cast(lparam); + WindowEmperor* that = static_cast(cs->lpCreateParams); + WINRT_ASSERT(that); + WINRT_ASSERT(!that->_window); + that->_window = wil::unique_hwnd(window); + SetWindowLongPtr(that->_window.get(), GWLP_USERDATA, reinterpret_cast(that)); + + // return that->OnNcCreate(wparam, lparam); + } + else if (WindowEmperor* that = GetThisFromHandle(window)) + { + return that->MessageHandler(message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} +void WindowEmperor::_createMessageWindow() +{ + WNDCLASS wc{}; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hInstance = reinterpret_cast(&__ImageBase); + wc.lpszClassName = TERMINAL_MESSAGE_CLASS_NAME; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = MessageWndProc; + wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON)); + RegisterClass(&wc); + WINRT_ASSERT(!_window); + + WINRT_VERIFY(CreateWindow(wc.lpszClassName, + L"Windows Terminal", + 0, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + HWND_MESSAGE, + nullptr, + wc.hInstance, + this)); +} + +LRESULT WindowEmperor::MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept +{ + switch (message) + { + case WM_HOTKEY: + { + _hotkeyPressed(static_cast(wParam)); + return 0; + } + } + return DefWindowProc(_window.get(), message, wParam, lParam); +} + +winrt::fire_and_forget WindowEmperor::_close() +{ + // Important! Switch back to the main thread for the emperor. That way, the + // quit will go to the emperor's message pump. + co_await wil::resume_foreground(_dispatcher); + PostQuitMessage(0); +} + +void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) +{ + if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) + { + return; + } + + const auto& summonArgs = til::at(_hotkeys, hotkeyIndex); + Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() }; + + // desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false + // desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false + // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true + args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); + args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); + args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); + args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); + + switch (summonArgs.Monitor()) + { + case Settings::Model::MonitorBehavior::Any: + args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); + break; + case Settings::Model::MonitorBehavior::ToCurrent: + args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToCurrent); + break; + case Settings::Model::MonitorBehavior::ToMouse: + args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToMouse); + break; + } + + _manager.SummonWindow(args); + if (args.FoundMatch()) + { + // Excellent, the window was found. We have nothing else to do here. + } + else + { + // We should make the window ourselves. + // TODO! + // _createNewTerminalWindow(summonArgs); + } +} + +bool WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept +{ + const auto vkey = hotkey.Vkey(); + auto hotkeyFlags = MOD_NOREPEAT; + { + const auto modifiers = hotkey.Modifiers(); + WI_SetFlagIf(hotkeyFlags, MOD_WIN, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Windows)); + WI_SetFlagIf(hotkeyFlags, MOD_ALT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Menu)); + WI_SetFlagIf(hotkeyFlags, MOD_CONTROL, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Control)); + WI_SetFlagIf(hotkeyFlags, MOD_SHIFT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Shift)); + } + + // TODO GH#8888: We should display a warning of some kind if this fails. + // This can fail if something else already bound this hotkey. + const auto result = ::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey); + // const auto result = ::RegisterHotKey(nullptr, index, hotkeyFlags, vkey); + LOG_LAST_ERROR_IF(!result); + TraceLoggingWrite(g_hWindowsTerminalProvider, + "RegisterHotKey", + TraceLoggingDescription("Emitted when setting hotkeys"), + TraceLoggingInt64(index, "index", "the index of the hotkey to add"), + TraceLoggingUInt64(vkey, "vkey", "the key"), + TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_WIN), "win", "is WIN in the modifiers"), + TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_ALT), "alt", "is ALT in the modifiers"), + TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_CONTROL), "control", "is CONTROL in the modifiers"), + TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_SHIFT), "shift", "is SHIFT in the modifiers"), + TraceLoggingBool(result, "succeeded", "true if we succeeded"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + return result; +} + +// Method Description: +// - Call UnregisterHotKey once for each previously registered hotkey. +// Return Value: +// - +void WindowEmperor::_unregisterHotKey(const int index) noexcept +{ + TraceLoggingWrite( + g_hWindowsTerminalProvider, + "UnregisterHotKey", + TraceLoggingDescription("Emitted when clearing previously set hotkeys"), + TraceLoggingInt64(index, "index", "the index of the hotkey to remove"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + LOG_IF_WIN32_BOOL_FALSE(::UnregisterHotKey(_window.get(), index)); +} + +winrt::fire_and_forget WindowEmperor::_setupGlobalHotkeys() +{ + // The hotkey MUST be registered on the main thread. It will fail otherwise! + co_await wil::resume_foreground(_dispatcher); + + if (!_window) + { + // MSFT:36797001 There's a surprising number of hits of this callback + // getting triggered during teardown. As a best practice, we really + // should make sure _window exists before accessing it on any coroutine. + // We might be getting called back after the app already began getting + // cleaned up. + co_return; + } + // Unregister all previously registered hotkeys. + // + // RegisterHotKey(), will not unregister hotkeys automatically. + // If a hotkey with a given HWND and ID combination already exists + // then a duplicate one will be added, which we don't want. + // (Additionally we want to remove hotkeys that were removed from the settings.) + for (auto i = 0, count = gsl::narrow_cast(_hotkeys.size()); i < count; ++i) + { + _unregisterHotKey(i); + } + + _hotkeys.clear(); + + // Re-register all current hotkeys. + for (const auto& [keyChord, cmd] : _app.Logic().GlobalHotkeys()) + { + if (auto summonArgs = cmd.ActionAndArgs().Args().try_as()) + { + auto index = gsl::narrow_cast(_hotkeys.size()); + const auto succeeded = _registerHotKey(index, keyChord); + + TraceLoggingWrite(g_hWindowsTerminalProvider, + "AppHost_setupGlobalHotkey", + TraceLoggingDescription("Emitted when setting a single hotkey"), + TraceLoggingInt64(index, "index", "the index of the hotkey to add"), + TraceLoggingWideString(cmd.Name().c_str(), "name", "the name of the command"), + TraceLoggingBoolean(succeeded, "succeeded", "true if we succeeded"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + _hotkeys.emplace_back(summonArgs); + } + } +} diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index d9928353d2b..71829ffb81c 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -26,8 +26,12 @@ class WindowEmperor bool HandleCommandlineArgs(); void CreateNewWindowThread(winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, const bool firstWindow); + LRESULT MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept; + wil::unique_hwnd _window; + private: winrt::TerminalApp::App _app; + winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr }; winrt::Microsoft::Terminal::Remoting::WindowManager2 _manager; std::vector> _windows; @@ -38,10 +42,20 @@ class WindowEmperor winrt::event_token _WindowCreatedToken; winrt::event_token _WindowClosedToken; + std::vector _hotkeys; + void _becomeMonarch(); void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); winrt::fire_and_forget _SaveWindowLayoutsRepeat(); - void _attemptWindowRestore(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + + void _createMessageWindow(); + + void _hotkeyPressed(const long hotkeyIndex); + bool _registerHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept; + void _unregisterHotKey(const int index) noexcept; + winrt::fire_and_forget _setupGlobalHotkeys(); + + winrt::fire_and_forget _close(); }; From 0b79e81ae90267ac18f256150dba497543955113 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 11:03:10 -0600 Subject: [PATCH 042/122] Summoning works, except for making new windows, but DANG --- src/cascadia/WindowsTerminal/AppHost.cpp | 184 ------------------ src/cascadia/WindowsTerminal/AppHost.h | 12 -- src/cascadia/WindowsTerminal/IslandWindow.cpp | 64 ------ src/cascadia/WindowsTerminal/IslandWindow.h | 4 - .../WindowsTerminal/WindowEmperor.cpp | 12 +- 5 files changed, 8 insertions(+), 268 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index b11d7a62f05..a5ef9c9b107 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -32,33 +32,12 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, const Remoting::WindowManager2& manager, const Remoting::Peasant& peasant) noexcept : - // _app{}, _windowManager2{ manager }, _peasant{ peasant }, _appLogic{ logic }, // don't make one, we're going to take a ref on app's _windowLogic{ nullptr }, _window{ nullptr } -// _getWindowLayoutThrottler{} // this will get set if we become the monarch { - // _appLogic = _app.Logic(); // get a ref to app's logic - - // // Inform the WindowManager that it can use us to find the target window for - // // a set of commandline args. This needs to be done before - // // _HandleCommandlineArgs, because WE might end up being the monarch. That - // // would mean we'd need to be responsible for looking that up. - // _windowManager2.FindTargetWindowRequested({ this, &AppHost::_FindTargetWindow }); - - // // If there were commandline args to our process, try and process them here. - // // Do this before AppLogic::Create, otherwise this will have no effect. - // // - // // This will send our commandline to the Monarch, to ask if we should make a - // // new window or not. If not, exit immediately. - // _HandleCommandlineArgs(); - // if (!_shouldCreateWindow) - // { - // return; - // } - _HandleCommandlineArgs(); // _HandleCommandlineArgs will create a _windowLogic @@ -885,169 +864,6 @@ winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowL co_return layoutJson; } -// void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, -// const winrt::Windows::Foundation::IInspectable& /*args*/) -// { -// // MSFT:35726322 -// // -// // Although we're manually revoking the event handler now in the dtor before -// // we null out the window, let's be extra careful and check JUST IN CASE. -// if (_window == nullptr) -// { -// return; -// } - -// _setupGlobalHotkeys(); - -// if (_windowManager2.DoesQuakeWindowExist() || -// _window->IsQuakeWindow() || -// (_windowLogic.GetAlwaysShowNotificationIcon() || _windowLogic.GetMinimizeToNotificationArea())) -// { -// _CreateNotificationIcon(); -// } - -// // Set the number of open windows (so we know if we are the last window) -// // and subscribe for updates if there are any changes to that number. -// _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); - -// _WindowCreatedToken = _windowManager2.WindowCreated([this](auto&&, auto&&) { -// if (_getWindowLayoutThrottler) { -// _getWindowLayoutThrottler.value()(); -// } -// _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); }); - -// _WindowClosedToken = _windowManager2.WindowClosed([this](auto&&, auto&&) { -// if (_getWindowLayoutThrottler) -// { -// _getWindowLayoutThrottler.value()(); -// } -// _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); -// }); - -// // These events are coming from peasants that become or un-become quake windows. -// _revokers.ShowNotificationIconRequested = _windowManager2.ShowNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_ShowNotificationIconRequested }); -// _revokers.HideNotificationIconRequested = _windowManager2.HideNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_HideNotificationIconRequested }); -// // If the monarch receives a QuitAll event it will signal this event to be -// // ran before each peasant is closed. -// _revokers.QuitAllRequested = _windowManager2.QuitAllRequested(winrt::auto_revoke, { this, &AppHost::_QuitAllRequested }); - -// // The monarch should be monitoring if it should save the window layout. -// if (!_getWindowLayoutThrottler.has_value()) -// { -// // We want at least some delay to prevent the first save from overwriting -// // the data as we try load windows initially. -// _getWindowLayoutThrottler.emplace(std::move(std::chrono::seconds(10)), std::move([this]() { _SaveWindowLayoutsRepeat(); })); -// _getWindowLayoutThrottler.value()(); -// } -// } - -// winrt::fire_and_forget AppHost::_setupGlobalHotkeys() -// { -// if (_windowLogic.GetRoot() == nullptr) -// { -// co_return; -// } -// // The hotkey MUST be registered on the main thread. It will fail otherwise! -// co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); - -// if (!_window) -// { -// // MSFT:36797001 There's a surprising number of hits of this callback -// // getting triggered during teardown. As a best practice, we really -// // should make sure _window exists before accessing it on any coroutine. -// // We might be getting called back after the app already began getting -// // cleaned up. -// co_return; -// } -// // Unregister all previously registered hotkeys. -// // -// // RegisterHotKey(), will not unregister hotkeys automatically. -// // If a hotkey with a given HWND and ID combination already exists -// // then a duplicate one will be added, which we don't want. -// // (Additionally we want to remove hotkeys that were removed from the settings.) -// for (auto i = 0, count = gsl::narrow_cast(_hotkeys.size()); i < count; ++i) -// { -// _window->UnregisterHotKey(i); -// } - -// _hotkeys.clear(); - -// // Re-register all current hotkeys. -// for (const auto& [keyChord, cmd] : _appLogic.GlobalHotkeys()) -// { -// if (auto summonArgs = cmd.ActionAndArgs().Args().try_as()) -// { -// auto index = gsl::narrow_cast(_hotkeys.size()); -// const auto succeeded = _window->RegisterHotKey(index, keyChord); - -// TraceLoggingWrite(g_hWindowsTerminalProvider, -// "AppHost_setupGlobalHotkey", -// TraceLoggingDescription("Emitted when setting a single hotkey"), -// TraceLoggingInt64(index, "index", "the index of the hotkey to add"), -// TraceLoggingWideString(cmd.Name().c_str(), "name", "the name of the command"), -// TraceLoggingBoolean(succeeded, "succeeded", "true if we succeeded"), -// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), -// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); -// _hotkeys.emplace_back(summonArgs); -// } -// } -// } - -// // Method Description: -// // - Called whenever a registered hotkey is pressed. We'll look up the -// // GlobalSummonArgs for the specified hotkey, then dispatch a call to the -// // Monarch with the selection information. -// // - If the monarch finds a match for the window name (or no name was provided), -// // it'll set FoundMatch=true. -// // - If FoundMatch is false, and a name was provided, then we should create a -// // new window with the given name. -// // Arguments: -// // - hotkeyIndex: the index of the entry in _hotkeys that was pressed. -// // Return Value: -// // - -// void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) -// { -// if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) -// { -// return; -// } - -// const auto& summonArgs = til::at(_hotkeys, hotkeyIndex); -// Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() }; - -// // desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false -// // desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false -// // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true -// args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); -// args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); -// args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); -// args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); - -// switch (summonArgs.Monitor()) -// { -// case Settings::Model::MonitorBehavior::Any: -// args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); -// break; -// case Settings::Model::MonitorBehavior::ToCurrent: -// args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToCurrent); -// break; -// case Settings::Model::MonitorBehavior::ToMouse: -// args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToMouse); -// break; -// } - -// _windowManager2.SummonWindow(args); -// if (args.FoundMatch()) -// { -// // Excellent, the window was found. We have nothing else to do here. -// } -// else -// { -// // We should make the window ourselves. -// _createNewTerminalWindow(summonArgs); -// } -// } - // Method Description: // - Called when the monarch failed to summon a window for a given set of // SummonWindowSelectionArgs. In this case, we should create the specified diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 5c573f50642..f82296ce855 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -29,23 +29,18 @@ class AppHost private: std::unique_ptr _window; - // winrt::TerminalApp::App _app; winrt::TerminalApp::AppLogic _appLogic; winrt::TerminalApp::TerminalWindow _windowLogic; winrt::Microsoft::Terminal::Remoting::WindowManager2 _windowManager2{ nullptr }; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; - // std::vector _hotkeys; winrt::com_ptr _desktopManager{ nullptr }; bool _shouldCreateWindow{ false }; bool _useNonClientArea{ false }; - // std::optional> _getWindowLayoutThrottler; std::shared_ptr> _showHideWindowThrottler; - // winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); - // winrt::fire_and_forget _SaveWindowLayoutsRepeat(); void _preInit(); @@ -76,12 +71,6 @@ class AppHost winrt::Windows::Foundation::IAsyncOperation _GetWindowLayoutAsync(); - // void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, - // const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); - - // void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, - // const winrt::Windows::Foundation::IInspectable& args); - // void _GlobalHotkeyPressed(const long hotkeyIndex); void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args); @@ -96,7 +85,6 @@ class AppHost bool _LazyLoadDesktopManager(); - // winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& args); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 0d6958b2a45..d1b4a24abdc 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -418,11 +418,6 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { switch (message) { - // case WM_HOTKEY: - // { - // _HotkeyPressedHandlers(static_cast(wparam)); - // return 0; - // } case WM_GETMINMAXINFO: { _OnGetMinMaxInfo(wparam, lparam); @@ -1264,65 +1259,6 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) } } -// // Method Description: -// // - Call UnregisterHotKey once for each previously registered hotkey. -// // Return Value: -// // - -// void IslandWindow::UnregisterHotKey(const int index) noexcept -// { -// TraceLoggingWrite( -// g_hWindowsTerminalProvider, -// "UnregisterHotKey", -// TraceLoggingDescription("Emitted when clearing previously set hotkeys"), -// TraceLoggingInt64(index, "index", "the index of the hotkey to remove"), -// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), -// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - -// LOG_IF_WIN32_BOOL_FALSE(::UnregisterHotKey(_window.get(), index)); -// } - -// // Method Description: -// // - Call RegisterHotKey to attempt to register that keybinding as a global hotkey. -// // - When these keys are pressed, we'll get a WM_HOTKEY message with the payload -// // containing the index we registered here. -// // - Call UnregisterHotKey() before registering your hotkeys. -// // See: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey#remarks -// // Arguments: -// // - hotkey: The key-combination to register. -// // Return Value: -// // - -// bool IslandWindow::RegisterHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept -// { -// const auto vkey = hotkey.Vkey(); -// auto hotkeyFlags = MOD_NOREPEAT; -// { -// const auto modifiers = hotkey.Modifiers(); -// WI_SetFlagIf(hotkeyFlags, MOD_WIN, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Windows)); -// WI_SetFlagIf(hotkeyFlags, MOD_ALT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Menu)); -// WI_SetFlagIf(hotkeyFlags, MOD_CONTROL, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Control)); -// WI_SetFlagIf(hotkeyFlags, MOD_SHIFT, WI_IsFlagSet(modifiers, VirtualKeyModifiers::Shift)); -// } - -// // TODO GH#8888: We should display a warning of some kind if this fails. -// // This can fail if something else already bound this hotkey. -// const auto result = ::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey); - -// TraceLoggingWrite(g_hWindowsTerminalProvider, -// "RegisterHotKey", -// TraceLoggingDescription("Emitted when setting hotkeys"), -// TraceLoggingInt64(index, "index", "the index of the hotkey to add"), -// TraceLoggingUInt64(vkey, "vkey", "the key"), -// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_WIN), "win", "is WIN in the modifiers"), -// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_ALT), "alt", "is ALT in the modifiers"), -// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_CONTROL), "control", "is CONTROL in the modifiers"), -// TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_SHIFT), "shift", "is SHIFT in the modifiers"), -// TraceLoggingBool(result, "succeeded", "true if we succeeded"), -// TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), -// TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - -// return result; -// } - // Method Description: // - Summon the window, or possibly dismiss it. If toggleVisibility is true, // then we'll dismiss (minimize) the window if it's currently active. diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 2107fa5f2a0..eb412a0f445 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -50,9 +50,6 @@ class IslandWindow : void FlashTaskbar(); void SetTaskbarProgress(const size_t state, const size_t progress); - void UnregisterHotKey(const int index) noexcept; - bool RegisterHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept; - winrt::fire_and_forget SummonWindow(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args); bool IsQuakeWindow() const noexcept; @@ -73,7 +70,6 @@ class IslandWindow : WINRT_CALLBACK(WindowCloseButtonClicked, winrt::delegate<>); WINRT_CALLBACK(MouseScrolled, winrt::delegate); WINRT_CALLBACK(WindowActivated, winrt::delegate); - // WINRT_CALLBACK(HotkeyPressed, winrt::delegate); WINRT_CALLBACK(NotifyNotificationIconPressed, winrt::delegate); WINRT_CALLBACK(NotifyWindowHidden, winrt::delegate); WINRT_CALLBACK(NotifyShowNotificationIconContextMenu, winrt::delegate); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 453ea6028ba..15b0c129769 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -120,7 +120,7 @@ void WindowEmperor::WaitForWindows() // Sleep(30000); //30s // TODO! This creates a loop that never actually exits right now. It seems - // to get a message wehn another window is activated, but never a WM_CLOSE + // to get a message when another window is activated, but never a WM_CLOSE // (that makes sense). It keeps running even when the threads all exit, // which is INTERESTING for sure. // @@ -162,6 +162,13 @@ void WindowEmperor::_becomeMonarch() //////////////////////////////////////////////////////////////////////////// _setupGlobalHotkeys(); + _app.Logic().SettingsChanged([this](auto&&, const TerminalApp::SettingsLoadEventArgs& args) { + if (SUCCEEDED(args.Result())) + { + _setupGlobalHotkeys(); + } + }); + //////////////////////////////////////////////////////////////////////////// // if (_windowManager2.DoesQuakeWindowExist() || @@ -302,8 +309,6 @@ static WindowEmperor* GetThisFromHandle(HWND const window) noexcept WINRT_ASSERT(!that->_window); that->_window = wil::unique_hwnd(window); SetWindowLongPtr(that->_window.get(), GWLP_USERDATA, reinterpret_cast(that)); - - // return that->OnNcCreate(wparam, lparam); } else if (WindowEmperor* that = GetThisFromHandle(window)) { @@ -417,7 +422,6 @@ bool WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Ter // TODO GH#8888: We should display a warning of some kind if this fails. // This can fail if something else already bound this hotkey. const auto result = ::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey); - // const auto result = ::RegisterHotKey(nullptr, index, hotkeyFlags, vkey); LOG_LAST_ERROR_IF(!result); TraceLoggingWrite(g_hWindowsTerminalProvider, "RegisterHotKey", From 950ce6c4fce97ae3b233dd8d4204a86e23ca6795 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 13:06:06 -0600 Subject: [PATCH 043/122] make pane brushes a member variable, which is good enough (cherry picked from commit 7bad8c9642662cae1a12329f07029f6937ea8ef4) --- src/cascadia/TerminalApp/Pane.cpp | 21 +++++++++------------ src/cascadia/TerminalApp/Pane.h | 6 +++--- src/cascadia/TerminalApp/TerminalPage.cpp | 13 +++++++++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 47dc1a36271..2e7b474b5fa 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -33,9 +33,6 @@ static const int CombinedPaneBorderSize = 2 * PaneBorderSize; static const int AnimationDurationInMilliseconds = 200; static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Windows::Foundation::TimeSpan(std::chrono::milliseconds(AnimationDurationInMilliseconds))); -winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr }; -winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr }; - Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) : _control{ control }, _lastActive{ lastFocused }, @@ -83,7 +80,7 @@ Pane::Pane(std::shared_ptr first, // Use the unfocused border color as the pane background, so an actual color // appears behind panes as we animate them sliding in. - _root.Background(s_unfocusedBorderBrush); + _root.Background(_unfocusedBorderBrush); _root.Children().Append(_borderFirst); _root.Children().Append(_borderSecond); @@ -1396,8 +1393,8 @@ void Pane::UpdateVisuals() { _UpdateBorders(); } - _borderFirst.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); - _borderSecond.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); + _borderFirst.BorderBrush(_lastActive ? _focusedBorderBrush : _unfocusedBorderBrush); + _borderSecond.BorderBrush(_lastActive ? _focusedBorderBrush : _unfocusedBorderBrush); } // Method Description: @@ -1849,7 +1846,7 @@ winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst) Controls::Grid dummyGrid; // GH#603 - we can safely add a BG here, as the control is gone right // away, to fill the space as the rest of the pane expands. - dummyGrid.Background(s_unfocusedBorderBrush); + dummyGrid.Background(_unfocusedBorderBrush); // It should be the size of the closed pane. dummyGrid.Width(removedOriginalSize.Width); dummyGrid.Height(removedOriginalSize.Height); @@ -2127,7 +2124,7 @@ void Pane::_SetupEntranceAnimation() // * If we give the parent (us) root BG a color, then a transparent pane // will flash opaque during the animation, then back to transparent, which // looks bad. - _secondChild->_root.Background(s_unfocusedBorderBrush); + _secondChild->_root.Background(_unfocusedBorderBrush); const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast(totalSize)); @@ -3112,14 +3109,14 @@ void Pane::SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requeste // Transparent as the color, so we don't do this process again on // the next pane (by leaving s_focusedBorderBrush nullptr) auto actualColor = winrt::unbox_value_or(colorFromResources, Colors::Black()); - s_focusedBorderBrush = SolidColorBrush(actualColor); + _focusedBorderBrush = SolidColorBrush(actualColor); } else { // DON'T use Transparent here - if it's "Transparent", then it won't // be able to hittest for clicks, and then clicking on the border // will eat focus. - s_focusedBorderBrush = SolidColorBrush{ Colors::Black() }; + _focusedBorderBrush = SolidColorBrush{ Colors::Black() }; } const auto unfocusedBorderBrushKey = winrt::box_value(L"UnfocusedBorderBrush"); @@ -3129,14 +3126,14 @@ void Pane::SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requeste // the requestedTheme, not just the value from the resources (which // might not respect the settings' requested theme) auto obj = ThemeLookup(res, requestedTheme, unfocusedBorderBrushKey); - s_unfocusedBorderBrush = obj.try_as(); + _unfocusedBorderBrush = obj.try_as(); } else { // DON'T use Transparent here - if it's "Transparent", then it won't // be able to hittest for clicks, and then clicking on the border // will eat focus. - s_unfocusedBorderBrush = SolidColorBrush{ Colors::Black() }; + _unfocusedBorderBrush = SolidColorBrush{ Colors::Black() }; } } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 4719d1c4b47..28a6aafef41 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -136,7 +136,7 @@ class Pane : public std::enable_shared_from_this bool ContainsReadOnly() const; - static void SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); + void SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); // Method Description: // - A helper method for ad-hoc recursion on a pane tree. Walks the pane @@ -217,8 +217,8 @@ class Pane : public std::enable_shared_from_this winrt::Windows::UI::Xaml::Controls::Grid _root{}; winrt::Windows::UI::Xaml::Controls::Border _borderFirst{}; winrt::Windows::UI::Xaml::Controls::Border _borderSecond{}; - static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush; - static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush; + winrt::Windows::UI::Xaml::Media::SolidColorBrush _focusedBorderBrush; + winrt::Windows::UI::Xaml::Media::SolidColorBrush _unfocusedBorderBrush; #pragma region Properties that need to be transferred between child / parent panes upon splitting / closing std::shared_ptr _firstChild{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 03d2df9c490..e3221b9fcb9 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4104,15 +4104,20 @@ namespace winrt::TerminalApp::implementation auto requestedTheme{ theme.RequestedTheme() }; { - // Update the brushes that Pane's use... - Pane::SetupResources(requestedTheme); - // ... then trigger a visual update for all the pane borders to - // apply the new ones. for (const auto& tab : _tabs) { if (auto terminalTab{ _GetTerminalTabImpl(tab) }) { terminalTab->GetRootPane()->WalkTree([&](auto&& pane) { + // TODO, but of middling priority. We probably shouldn't + // SetupResources on _every_ pane. We can probably call + // that on the root, and then have that backchannel to + // update the whole tree. + + // Update the brushes that Pane's use... + pane->SetupResources(requestedTheme); + // ... then trigger a visual update for all the pane borders to + // apply the new ones. pane->UpdateVisuals(); }); } From 055da357b1bb06e901ed11a11e955fd3f2f14d1b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 12:33:43 -0600 Subject: [PATCH 044/122] Readd ShouldImmediatelyHandoffToElevated 17 TODOs remain --- src/cascadia/TerminalApp/AppLogic.cpp | 19 ------------- src/cascadia/TerminalApp/AppLogic.h | 2 -- src/cascadia/TerminalApp/AppLogic.idl | 3 --- src/cascadia/TerminalApp/TerminalWindow.cpp | 30 +++++++++++++++++++++ src/cascadia/TerminalApp/TerminalWindow.h | 3 +++ src/cascadia/TerminalApp/TerminalWindow.idl | 3 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 28 +++++++++---------- 7 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 59fd00242a2..2105af24279 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -526,25 +526,6 @@ namespace winrt::TerminalApp::implementation return _settings; } - bool AppLogic::ShouldImmediatelyHandoffToElevated() - { - // TODO! Merge that code in here. - // * Probably need to pass in the startupActions that the first window is being started with - // * Or like, a reference to the first TerminalWindow object, or something - - // return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; - return false; - } - void AppLogic::HandoffToElevated() - { - // TODO! Merge that code in here. - - // if (_root) - // { - // _root->HandoffToElevated(_settings); - // } - } - // Method Description: // - Parse the given commandline args in an attempt to find the specified // window. The rest of the args are ignored for now (they'll be handled diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index cfb15fbea4f..759cfb09500 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -60,8 +60,6 @@ namespace winrt::TerminalApp::implementation [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; TerminalApp::FindTargetWindowResult FindTargetWindow(array_view actions); - bool ShouldImmediatelyHandoffToElevated(); - void HandoffToElevated(); Windows::Foundation::Collections::IMapView GlobalHotkeys(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 8121d4dad5d..a5db001de67 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -40,9 +40,6 @@ namespace TerminalApp TerminalWindow CreateNewWindow(); - Boolean ShouldImmediatelyHandoffToElevated(); - void HandoffToElevated(); - Windows.Foundation.Collections.IMapView GlobalHotkeys(); event Windows.Foundation.TypedEventHandler SettingsChanged; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 05e68943bae..b1130e9b1b5 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1224,4 +1224,34 @@ namespace winrt::TerminalApp::implementation return WindowName() == QuakeWindowName; } //////////////////////////////////////////////////////////////////////////// + + bool TerminalWindow::ShouldImmediatelyHandoffToElevated() + { + return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; + } + + // Method Description: + // - Escape hatch for immediately dispatching requests to elevated windows + // when first launched. At this point in startup, the window doesn't exist + // yet, XAML hasn't been started, but we need to dispatch these actions. + // We can't just go through ProcessStartupActions, because that processes + // the actions async using the XAML dispatcher (which doesn't exist yet) + // - DON'T CALL THIS if you haven't already checked + // ShouldImmediatelyHandoffToElevated. If you're thinking about calling + // this outside of the one place it's used, that's probably the wrong + // solution. + // Arguments: + // - settings: the settings we should use for dispatching these actions. At + // this point in startup, we hadn't otherwise been initialized with these, + // so use them now. + // Return Value: + // - + void TerminalWindow::HandoffToElevated() + { + if (_root) + { + _root->HandoffToElevated(_settings); + return; + } + } }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 8119ca45d56..b7958e5694d 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -58,6 +58,9 @@ namespace winrt::TerminalApp::implementation winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); + bool ShouldImmediatelyHandoffToElevated(); + void HandoffToElevated(); + bool FocusMode() const; bool Fullscreen() const; void Maximized(bool newMaximized); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index ad635e7efd6..791625512d6 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -65,6 +65,9 @@ namespace TerminalApp String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; + Boolean ShouldImmediatelyHandoffToElevated(); + void HandoffToElevated(); + void Quit(); Windows.UI.Xaml.UIElement GetRoot(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a5ef9c9b107..7733e2e343f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -188,21 +188,19 @@ void AppHost::_HandleCommandlineArgs() } } - // TODO! + // This is a fix for GH#12190 and hopefully GH#12169. // - // // This is a fix for GH#12190 and hopefully GH#12169. - // // - // // If the commandline we were provided is going to result in us only - // // opening elevated terminal instances, then we need to not even create - // // the window at all here. In that case, we're going through this - // // special escape hatch to dispatch all the calls to elevate-shim, and - // // then we're going to exit immediately. - // if (_appLogic.ShouldImmediatelyHandoffToElevated()) - // { - // _shouldCreateWindow = false; - // _appLogic.HandoffToElevated(); - // return; - // } + // If the commandline we were provided is going to result in us only + // opening elevated terminal instances, then we need to not even create + // the window at all here. In that case, we're going through this + // special escape hatch to dispatch all the calls to elevate-shim, and + // then we're going to exit immediately. + if (_windowLogic.ShouldImmediatelyHandoffToElevated()) + { + _shouldCreateWindow = false; + _windowLogic.HandoffToElevated(); + return; + } // After handling the initial args, hookup the callback for handling // future commandline invocations. When our peasant is told to execute a @@ -1095,8 +1093,6 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta const winrt::TerminalApp::SettingsLoadEventArgs& /*args*/) { // We don't need to call in to windowLogic here - it has its own SettingsChanged handler - // TODO! this need to be replicated in Emperor - // _setupGlobalHotkeys(); // TODO! tray icon // From b0726c20573d4e3e31710c9956ebc5a3affcd2e7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 12:54:23 -0600 Subject: [PATCH 045/122] Handle Quit actions 16 TODOs --- src/cascadia/Remoting/WindowManager2.cpp | 1 - src/cascadia/WindowsTerminal/AppHost.cpp | 19 ++------------- .../WindowsTerminal/WindowEmperor.cpp | 24 ++++++++++++++++--- src/cascadia/WindowsTerminal/WindowEmperor.h | 9 +++++++ 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index d9fea3b5ee5..ab3d3d660b4 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -67,7 +67,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.QuitAllRequested({ get_weak(), &WindowManager2::_QuitAllRequestedHandlers }); _monarch.RequestNewWindow({ get_weak(), &WindowManager2::_raiseRequestNewWindow }); - // _BecameMonarchHandlers(*this, nullptr); } void WindowManager2::_registerAsMonarch() diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 7733e2e343f..4add5703774 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1146,6 +1146,7 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab _window->IsQuakeWindow(_windowLogic.IsQuakeWindow()); } +// Raised from our Peasant. We handle by propogating the call to our terminal window. winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { @@ -1155,29 +1156,13 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation: _windowLogic.Quit(); } +// 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&) { _windowManager2.RequestQuitAll(_peasant); } -void AppHost::_QuitAllRequested(const winrt::Windows::Foundation::IInspectable&, - const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs&) -{ - // TODO! wat do - // - //// 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()); -} - void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Microsoft::Terminal::Control::ShowWindowArgs& args) { diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 15b0c129769..6d9aea73b61 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -193,9 +193,9 @@ void WindowEmperor::_becomeMonarch() //////////////////////////////////////////////////////////////////////////// - // // If the monarch receives a QuitAll event it will signal this event to be - // // ran before each peasant is closed. - // _revokers.QuitAllRequested = _windowManager2.QuitAllRequested(winrt::auto_revoke, { this, &AppHost::_QuitAllRequested }); + // 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 }); //////////////////////////////////////////////////////////////////////////// @@ -228,6 +228,24 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II } } +// Raised from our windowManager (on behalf of the monarch). We respond by +// giving the monarch an async fuction 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) +{ + // 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()); +} + winrt::Windows::Foundation::IAsyncAction WindowEmperor::_SaveWindowLayouts() { // Make sure we run on a background thread to not block anything. diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 71829ffb81c..842b6951e5f 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -46,6 +46,8 @@ class WindowEmperor 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::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); winrt::fire_and_forget _SaveWindowLayoutsRepeat(); @@ -58,4 +60,11 @@ class WindowEmperor winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _close(); + + struct Revokers + { + winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; + winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; + winrt::Microsoft::Terminal::Remoting::WindowManager2::QuitAllRequested_revoker QuitAllRequested; + } _revokers{}; }; From e214624e1f6d530e05c223272f08518dd41f92c8 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 13:52:44 -0600 Subject: [PATCH 046/122] Re-add support for the notification icon. 14 TODOs left --- src/cascadia/TerminalApp/TerminalWindow.cpp | 6 + src/cascadia/TerminalApp/TerminalWindow.h | 2 + src/cascadia/TerminalApp/TerminalWindow.idl | 6 +- src/cascadia/WindowsTerminal/AppHost.cpp | 129 ++--------------- src/cascadia/WindowsTerminal/AppHost.h | 12 -- src/cascadia/WindowsTerminal/IslandWindow.cpp | 36 +---- .../WindowsTerminal/NotificationIcon.h | 1 + .../WindowsTerminal/WindowEmperor.cpp | 134 ++++++++++++++++++ src/cascadia/WindowsTerminal/WindowEmperor.h | 8 ++ 9 files changed, 168 insertions(+), 166 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index b1130e9b1b5..3b58b7d109a 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -310,6 +310,12 @@ namespace winrt::TerminalApp::implementation { return _settings.GlobalSettings().AlwaysShowNotificationIcon(); } + bool TerminalWindow::RequestsTrayIcon() + { + return _settings.GlobalSettings().AlwaysShowNotificationIcon() || + _settings.GlobalSettings().MinimizeToNotificationArea() || + IsQuakeWindow(); + } bool TerminalWindow::GetShowTitleInTitlebar() { diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index b7958e5694d..09fcb4dd524 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -105,6 +105,8 @@ namespace winrt::TerminalApp::implementation bool GetMinimizeToNotificationArea(); bool GetAlwaysShowNotificationIcon(); + bool RequestsTrayIcon(); + bool GetShowTitleInTitlebar(); winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 791625512d6..fc1270641a9 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -44,7 +44,6 @@ namespace TerminalApp { TerminalWindow(SettingsLoadEventArgs result); - // 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, // then it might look like TermApp just failed to activate, which will @@ -103,14 +102,11 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); - // Boolean ShouldUsePersistedLayout(); - // Boolean ShouldImmediatelyHandoffToElevated(); - // void HandoffToElevated(); String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); - // void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); Boolean GetMinimizeToNotificationArea(); Boolean GetAlwaysShowNotificationIcon(); + Boolean RequestsTrayIcon(); Boolean GetShowTitleInTitlebar(); // See IDialogPresenter and TerminalPage's DialogPresenter for more diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 4add5703774..192ec2fbe95 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1094,33 +1094,6 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta { // We don't need to call in to windowLogic here - it has its own SettingsChanged handler - // TODO! tray icon - // - // // If we're monarch, we need to check some conditions to show the notification icon. - // // If there's a Quake window somewhere, we'll want to keep the notification icon. - // // There's two settings - MinimizeToNotificationArea and AlwaysShowNotificationIcon. If either - // // one of them are true, we want to make sure there's a notification icon. - // // If both are false, we want to remove our icon from the notification area. - // // When we remove our icon from the notification area, we'll also want to re-summon - // // any hidden windows, but right now we're not keeping track of who's hidden, - // // so just summon them all. Tracking the work to do a "summon all minimized" in - // // GH#10448 - // if (_windowManager2.IsMonarch()) - // { - // if (!_windowManager2.DoesQuakeWindowExist()) - // { - // if (!_notificationIcon && (_windowLogic.GetMinimizeToNotificationArea() || _windowLogic.GetAlwaysShowNotificationIcon())) - // { - // _CreateNotificationIcon(); - // } - // else if (_notificationIcon && !_windowLogic.GetMinimizeToNotificationArea() && !_windowLogic.GetAlwaysShowNotificationIcon()) - // { - // _windowManager2.SummonAllWindows(); - // _DestroyNotificationIcon(); - // } - // } - // } - _window->SetMinimizeToNotificationAreaBehavior(_windowLogic.GetMinimizeToNotificationArea()); _window->SetAutoHideWindow(_windowLogic.AutoHideWindow()); _updateTheme(); @@ -1129,19 +1102,21 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - // We want the quake window to be accessible through the notification icon. - // This means if there's a quake window _somewhere_, we want the notification icon - // to show regardless of the notification icon settings. - // This also means we'll need to destroy the notification icon if it was created - // specifically for the quake window. If not, it should not be destroyed. - if (!_window->IsQuakeWindow() && _windowLogic.IsQuakeWindow()) - { - _ShowNotificationIconRequested(nullptr, nullptr); - } - else if (_window->IsQuakeWindow() && !_windowLogic.IsQuakeWindow()) - { - _HideNotificationIconRequested(nullptr, nullptr); - } + // // We want the quake window to be accessible through the notification icon. + // // This means if there's a quake window _somewhere_, we want the notification icon + // // to show regardless of the notification icon settings. + // // This also means we'll need to destroy the notification icon if it was created + // // specifically for the quake window. If not, it should not be destroyed. + // if (!_window->IsQuakeWindow() && _windowLogic.IsQuakeWindow()) + // { + // _ShowNotificationIconRequested(nullptr, nullptr); + // } + // else if (_window->IsQuakeWindow() && !_windowLogic.IsQuakeWindow()) + // { + // _HideNotificationIconRequested(nullptr, nullptr); + // } + + // TODO! I think we need the emperor to listen to windowLogic's IsQuakeWindowChanged event, to replicate this _window->IsQuakeWindow(_windowLogic.IsQuakeWindow()); } @@ -1214,80 +1189,6 @@ void AppHost::_SystemMenuChangeRequested(const winrt::Windows::Foundation::IInsp } } -// Method Description: -// - Creates a Notification Icon and hooks up its handlers -// Arguments: -// - -// Return Value: -// - -void AppHost::_CreateNotificationIcon() -{ - _notificationIcon = std::make_unique(_window->GetHandle()); - - // Hookup the handlers, save the tokens for revoking if settings change. - _ReAddNotificationIconToken = _window->NotifyReAddNotificationIcon([this]() { _notificationIcon->ReAddNotificationIcon(); }); - _NotificationIconPressedToken = _window->NotifyNotificationIconPressed([this]() { _notificationIcon->NotificationIconPressed(); }); - _ShowNotificationIconContextMenuToken = _window->NotifyShowNotificationIconContextMenu([this](til::point coord) { _notificationIcon->ShowContextMenu(coord, _windowManager2.GetPeasantInfos()); }); - _NotificationIconMenuItemSelectedToken = _window->NotifyNotificationIconMenuItemSelected([this](HMENU hm, UINT idx) { _notificationIcon->MenuItemSelected(hm, idx); }); - _notificationIcon->SummonWindowRequested([this](auto& args) { _windowManager2.SummonWindow(args); }); -} - -// Method Description: -// - Deletes our notification icon if we have one. -// Arguments: -// - -// Return Value: -// - -void AppHost::_DestroyNotificationIcon() -{ - _window->NotifyReAddNotificationIcon(_ReAddNotificationIconToken); - _window->NotifyNotificationIconPressed(_NotificationIconPressedToken); - _window->NotifyShowNotificationIconContextMenu(_ShowNotificationIconContextMenuToken); - _window->NotifyNotificationIconMenuItemSelected(_NotificationIconMenuItemSelectedToken); - - _notificationIcon->RemoveIconFromNotificationArea(); - _notificationIcon = nullptr; -} - -void AppHost::_ShowNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) -{ - // TODO! tray icon - // - // if (_windowManager2.IsMonarch()) - // { - // if (!_notificationIcon) - // { - // _CreateNotificationIcon(); - // } - // } - // else - // { - // _windowManager2.RequestShowNotificationIcon(); - // } -} - -void AppHost::_HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) -{ - // TODO! tray icon - // - // if (_windowManager2.IsMonarch()) - // { - // // Destroy it only if our settings allow it - // if (_notificationIcon && - // !_windowLogic.GetAlwaysShowNotificationIcon() && - // !_windowLogic.GetMinimizeToNotificationArea()) - // { - // _DestroyNotificationIcon(); - // } - // } - // else - // { - // _windowManager2.RequestHideNotificationIcon(); - // } -} - // Method Description: // - BODGY workaround for GH#9320. When the window moves, dismiss all the popups // in the UI tree. Xaml Islands unfortunately doesn't do this for us, see diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index f82296ce855..9de8144d752 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -115,13 +115,6 @@ class AppHost void _ShowWindowChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs& args); - void _CreateNotificationIcon(); - void _DestroyNotificationIcon(); - void _ShowNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); - void _HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); - void _updateTheme(); void _PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, @@ -129,11 +122,6 @@ class AppHost void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); - std::unique_ptr _notificationIcon; - winrt::event_token _ReAddNotificationIconToken; - winrt::event_token _NotificationIconPressedToken; - winrt::event_token _ShowNotificationIconContextMenuToken; - winrt::event_token _NotificationIconMenuItemSelectedToken; winrt::event_token _GetWindowLayoutRequestedToken; // winrt::event_token _WindowCreatedToken; // winrt::event_token _WindowClosedToken; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index d1b4a24abdc..2e858f998bd 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -27,7 +27,7 @@ using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; #define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS" #define IDM_SYSTEM_MENU_BEGIN 0x1000 -const UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated"); +// const UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated"); IslandWindow::IslandWindow() noexcept : _interopWindowHandle{ nullptr }, @@ -633,30 +633,6 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize } break; } - case CM_NOTIFY_FROM_NOTIFICATION_AREA: - { - switch (LOWORD(lparam)) - { - case NIN_SELECT: - case NIN_KEYSELECT: - { - _NotifyNotificationIconPressedHandlers(); - return 0; - } - case WM_CONTEXTMENU: - { - const til::point eventPoint{ GET_X_LPARAM(wparam), GET_Y_LPARAM(wparam) }; - _NotifyShowNotificationIconContextMenuHandlers(eventPoint); - return 0; - } - } - break; - } - case WM_MENUCOMMAND: - { - _NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam); - return 0; - } case WM_SYSCOMMAND: { // the low 4 bits contain additional information (that we don't care about) @@ -733,16 +709,6 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize _AutomaticShutdownRequestedHandlers(); return true; } - default: - // We'll want to receive this message when explorer.exe restarts - // so that we can re-add our icon to the notification area. - // This unfortunately isn't a switch case because we register the - // message at runtime. - if (message == WM_TASKBARCREATED) - { - _NotifyReAddNotificationIconHandlers(); - return 0; - } } // TODO: handle messages here... diff --git a/src/cascadia/WindowsTerminal/NotificationIcon.h b/src/cascadia/WindowsTerminal/NotificationIcon.h index cc71a88b41f..3277a1e845f 100644 --- a/src/cascadia/WindowsTerminal/NotificationIcon.h +++ b/src/cascadia/WindowsTerminal/NotificationIcon.h @@ -3,6 +3,7 @@ #include "pch.h" +#pragma once // This enumerates all the possible actions // that our notification icon context menu could do. enum class NotificationIconMenuItemAction diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 6d9aea73b61..1a983b4946d 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -13,6 +13,7 @@ #include "../../types/inc/utils.hpp" #include "resource.h" +#include "NotificationIcon.h" using namespace winrt; using namespace winrt::Microsoft::Terminal; @@ -25,6 +26,8 @@ using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; #define TERMINAL_MESSAGE_CLASS_NAME L"TERMINAL_MESSAGE_CLASS" extern "C" IMAGE_DOS_HEADER __ImageBase; +const UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated"); + WindowEmperor::WindowEmperor() noexcept : _app{} { @@ -166,6 +169,8 @@ void WindowEmperor::_becomeMonarch() if (SUCCEEDED(args.Result())) { _setupGlobalHotkeys(); + + _checkWindowsForNotificationIcon(); } }); @@ -246,6 +251,8 @@ void WindowEmperor::_quitAllRequested(const winrt::Windows::Foundation::IInspect args.BeforeQuitAllAction(_SaveWindowLayouts()); } +#pragma region LayoutPersistence + winrt::Windows::Foundation::IAsyncAction WindowEmperor::_SaveWindowLayouts() { // Make sure we run on a background thread to not block anything. @@ -309,6 +316,9 @@ winrt::fire_and_forget WindowEmperor::_SaveWindowLayoutsRepeat() _getWindowLayoutThrottler.value()(); } } +#pragma endregion + +#pragma region WindowProc static WindowEmperor* GetThisFromHandle(HWND const window) noexcept { @@ -369,6 +379,46 @@ LRESULT WindowEmperor::MessageHandler(UINT const message, WPARAM const wParam, L _hotkeyPressed(static_cast(wParam)); return 0; } + case CM_NOTIFY_FROM_NOTIFICATION_AREA: + { + switch (LOWORD(lParam)) + { + case NIN_SELECT: + case NIN_KEYSELECT: + { + // _NotifyNotificationIconPressedHandlers(); + _notificationIcon->NotificationIconPressed(); + return 0; + } + case WM_CONTEXTMENU: + { + const til::point eventPoint{ GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam) }; + // _NotifyShowNotificationIconContextMenuHandlers(eventPoint); + _notificationIcon->ShowContextMenu(eventPoint, _manager.GetPeasantInfos()); + return 0; + } + } + break; + } + case WM_MENUCOMMAND: + { + // _NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam); + _notificationIcon->MenuItemSelected((HMENU)lParam, (UINT)wParam); + return 0; + } + default: + { + // We'll want to receive this message when explorer.exe restarts + // so that we can re-add our icon to the notification area. + // This unfortunately isn't a switch case because we register the + // message at runtime. + if (message == WM_TASKBARCREATED) + { + // _NotifyReAddNotificationIconHandlers(); + _notificationIcon->ReAddNotificationIcon(); + return 0; + } + } } return DefWindowProc(_window.get(), message, wParam, lParam); } @@ -381,6 +431,8 @@ winrt::fire_and_forget WindowEmperor::_close() PostQuitMessage(0); } +#pragma endregion +#pragma region GlobalHotkeys void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) { if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) @@ -521,3 +573,85 @@ winrt::fire_and_forget WindowEmperor::_setupGlobalHotkeys() } } } + +#pragma endregion +//////////////////////////////////////////////////////////////////////////////// +#pragma region NotificationIcon +// Method Description: +// - Creates a Notification Icon and hooks up its handlers +// Arguments: +// - +// Return Value: +// - +void WindowEmperor::_createNotificationIcon() +{ + _notificationIcon = std::make_unique(_window.get()); + _notificationIcon->SummonWindowRequested([this](auto& args) { _manager.SummonWindow(args); }); +} + +// Method Description: +// - Deletes our notification icon if we have one. +// Arguments: +// - +// Return Value: +// - +void WindowEmperor::_destroyNotificationIcon() +{ + _notificationIcon->RemoveIconFromNotificationArea(); + _notificationIcon = nullptr; +} + +void WindowEmperor::_checkWindowsForNotificationIcon() +{ + // We need to check some conditions to show the notification icon. + // + // * If there's a Quake window somewhere, we'll want to keep the + // notification icon. + // * There's two settings - MinimizeToNotificationArea and + // AlwaysShowNotificationIcon. If either one of them are true, we want to + // make sure there's a notification icon. + // + // If both are false, we want to remove our icon from the notification area. + // When we remove our icon from the notification area, we'll also want to + // re-summon any hidden windows, but right now we're not keeping track of + // who's hidden, so just summon them all. Tracking the work to do a "summon + // all minimized" in GH#10448 + + bool needsIcon = false; + for (const auto& _windowThread : _windows) + { + needsIcon |= _windowThread->Logic().RequestsTrayIcon(); + } + + if (needsIcon) + { + _showNotificationIconRequested(); + } + else + { + _hideNotificationIconRequested(); + } +} + +void WindowEmperor::_showNotificationIconRequested() +{ + if (!_notificationIcon) + { + _createNotificationIcon(); + } +} + +void WindowEmperor::_hideNotificationIconRequested() +{ + // Destroy it only if our settings allow it + if (_notificationIcon) + { + // If we no longer want the tray icon, but we did have one, then quick + // re-summon all our windows, so they don't get lost when the icon + // disappears forever. + _manager.SummonAllWindows(); + + _destroyNotificationIcon(); + } +} +#pragma endregion diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 842b6951e5f..f59fbaaed2f 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -44,6 +44,8 @@ class WindowEmperor std::vector _hotkeys; + std::unique_ptr _notificationIcon; + void _becomeMonarch(); void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); void _quitAllRequested(const winrt::Windows::Foundation::IInspectable&, @@ -61,6 +63,12 @@ class WindowEmperor winrt::fire_and_forget _close(); + void _createNotificationIcon(); + void _destroyNotificationIcon(); + void _checkWindowsForNotificationIcon(); + void _showNotificationIconRequested(); + void _hideNotificationIconRequested(); + struct Revokers { winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; From c69f0bc4444464dbc8e8f8c22cef7a5e7f98e807 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 14:16:27 -0600 Subject: [PATCH 047/122] Quake logic seems to work again. Down to just 10 TODOs left --- src/cascadia/TerminalApp/TerminalPage.cpp | 15 ----- src/cascadia/TerminalApp/TerminalPage.h | 25 +-------- src/cascadia/TerminalApp/TerminalPage.idl | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 56 ++++++++++--------- src/cascadia/TerminalApp/TerminalWindow.h | 5 +- src/cascadia/TerminalApp/TerminalWindow.idl | 2 - src/cascadia/WindowsTerminal/AppHost.cpp | 27 +-------- .../WindowsTerminal/WindowEmperor.cpp | 27 ++++----- 8 files changed, 51 insertions(+), 108 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e3221b9fcb9..d473669d0a2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4255,27 +4255,12 @@ namespace winrt::TerminalApp::implementation _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); - // if (changed) - // { // DON'T display the confirmation if this is the name we were // given on startup! if (page->_startupState == StartupState::Initialized) { page->IdentifyWindow(); - - // TODO! This is wacky. Reconcile with oldIsQuakeMode in TerminalWindow::WindowName - - // // If we're entering quake mode, or leaving it - // if (IsQuakeWindow() != oldIsQuakeMode) - // { - // // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode - // // If we're entering Quake Mode from Focus Mode, then this will do nothing - // // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing - // SetFocusMode(true); - // _IsQuakeWindowChangedHandlers(*this, nullptr); - // } } - // } } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 8ee040a6685..7f2d0e13b4f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -50,22 +50,6 @@ namespace winrt::TerminalApp::implementation _ProposedName{ name } {}; }; - // struct WindowProperties : WindowPropertiesT - // { - // // Normally, WindowName and WindowId would be - // // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise - // // WindowNameForDisplay and WindowIdForDisplay instead - // winrt::hstring WindowName() const noexcept; - // winrt::fire_and_forget WindowName(const winrt::hstring& value); - // uint64_t WindowId() const noexcept; - // void WindowId(const uint64_t& value); - // winrt::hstring WindowIdForDisplay() const noexcept; - // winrt::hstring WindowNameForDisplay() const noexcept; - // bool IsQuakeWindow() const noexcept; - - // public: - // }; - struct TerminalPage : TerminalPageT { public: @@ -79,7 +63,6 @@ namespace winrt::TerminalApp::implementation void Create(); - // bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); @@ -140,7 +123,6 @@ namespace winrt::TerminalApp::implementation void SetNumberOfOpenWindows(const uint64_t value); - // bool IsQuakeWindow() const noexcept; bool IsElevated() const noexcept; void OpenSettingsUI(); @@ -152,8 +134,6 @@ namespace winrt::TerminalApp::implementation void WindowProperties(const TerminalApp::IWindowProperties& props); winrt::fire_and_forget WindowNameChanged(); - // WINRT_PROPERTY(TerminalApp::IWindowProperties, WindowProperties, nullptr); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -169,7 +149,7 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(Initialized, IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs); TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable); TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs); - TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable); + TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable); TYPED_EVENT(CloseRequested, IInspectable, IInspectable); TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable); @@ -208,8 +188,7 @@ namespace winrt::TerminalApp::implementation bool _isFullscreen{ false }; bool _isMaximized{ false }; bool _isAlwaysOnTop{ false }; - // winrt::hstring _WindowName{}; - // uint64_t _WindowId{ 0 }; + std::optional _loadFromPersistedLayoutIdx{}; uint64_t _numOpenWindows{ 0 }; diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 9a2cfef292c..38ab490162e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -69,7 +69,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; event Windows.Foundation.TypedEventHandler RenameWindowRequested; - event Windows.Foundation.TypedEventHandler IsQuakeWindowChanged; + event Windows.Foundation.TypedEventHandler SummonWindowRequested; event Windows.Foundation.TypedEventHandler CloseRequested; event Windows.Foundation.TypedEventHandler OpenSystemMenu; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 3b58b7d109a..d5554082952 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1116,29 +1116,6 @@ namespace winrt::TerminalApp::implementation } } //////////////////////////////////////////////////////////////////////////// - - void TerminalWindow::IdentifyWindow() - { - if (_root) - { - _root->IdentifyWindow(); - } - } - - void TerminalWindow::RenameFailed() - { - if (_root) - { - _root->RenameFailed(); - } - } - - // TODO! - // bool TerminalWindow::IsQuakeWindow() const noexcept - // { - // return _root->IsQuakeWindow(); - // } - void TerminalWindow::RequestExitFullscreen() { _root->SetFullscreen(false); @@ -1157,6 +1134,22 @@ namespace winrt::TerminalApp::implementation //////////////////////////////////////////////////////////////////////////// + void TerminalWindow::IdentifyWindow() + { + if (_root) + { + _root->IdentifyWindow(); + } + } + + void TerminalWindow::RenameFailed() + { + if (_root) + { + _root->RenameFailed(); + } + } + // WindowName is a otherwise generic WINRT_OBSERVABLE_PROPERTY, but it needs // to raise a PropertyChanged for WindowNameForDisplay, instead of // WindowName. @@ -1167,8 +1160,8 @@ namespace winrt::TerminalApp::implementation void TerminalWindow::WindowName(const winrt::hstring& value) { - // TODO! - // const auto oldIsQuakeMode = IsQuakeWindow(); + const auto oldIsQuakeMode = IsQuakeWindow(); + const auto changed = _WindowName != value; if (changed) { @@ -1176,6 +1169,16 @@ namespace winrt::TerminalApp::implementation if (_root) { _root->WindowNameChanged(); + + // If we're entering quake mode, or leaving it + if (IsQuakeWindow() != oldIsQuakeMode) + { + // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode + // If we're entering Quake Mode from Focus Mode, then this will do nothing + // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing + _root->SetFocusMode(true); + _IsQuakeWindowChangedHandlers(*this, nullptr); + } } } } @@ -1229,6 +1232,7 @@ namespace winrt::TerminalApp::implementation { return WindowName() == QuakeWindowName; } + //////////////////////////////////////////////////////////////////////////// bool TerminalWindow::ShouldImmediatelyHandoffToElevated() @@ -1260,4 +1264,6 @@ namespace winrt::TerminalApp::implementation return; } } + + //////////////////////////////////////////////////////////////////////////// }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 09fcb4dd524..719d87814ce 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -51,7 +51,6 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args); bool HasCommandlineArguments() const noexcept; - // bool HasSettingsStartupActions() const noexcept; int32_t SetStartupCommandline(array_view actions); int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); void SetSettingsStartupArgs(const std::vector& actions); @@ -78,7 +77,6 @@ namespace winrt::TerminalApp::implementation void SetNumberOfOpenWindows(const uint64_t num); bool ShouldUsePersistedLayout() const; - // bool IsQuakeWindow() const noexcept; void RequestExitFullscreen(); Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); @@ -190,13 +188,14 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress); FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested); FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested); - FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged); FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested); FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested); FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu); FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested); FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged); + TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable); + TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index fc1270641a9..ff23cb76dbe 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -56,7 +56,6 @@ namespace TerminalApp Boolean IsElevated(); Boolean HasCommandlineArguments(); - // Boolean HasSettingsStartupActions(); Int32 SetStartupCommandline(String[] commands); @@ -83,7 +82,6 @@ namespace TerminalApp void SetNumberOfOpenWindows(UInt64 num); void RenameFailed(); void RequestExitFullscreen(); - // Boolean IsQuakeWindow(); Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); Boolean CenterOnLaunch { get; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 192ec2fbe95..eded913c4c8 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -221,6 +221,7 @@ void AppHost::_HandleCommandlineArgs() const auto numPeasants = _windowManager2.GetNumberOfPeasants(); if (numPeasants == 1) { + // TODO! this is vaugely off by one. Not sure, but if you restore 2 windows, you seem to get two copies of the second. Yikes. const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); if (_appLogic.ShouldUsePersistedLayout() && layouts && @@ -439,16 +440,6 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* // - void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/) { - // TODO! - // if (_windowManager2.IsMonarch() && _notificationIcon) - // { - // _DestroyNotificationIcon(); - // } - // else if (_window->IsQuakeWindow()) - // { - // _HideNotificationIconRequested(nullptr, nullptr); - // } - // We don't want to try to save layouts if we are about to close. _windowManager2.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); @@ -1102,22 +1093,6 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - // // We want the quake window to be accessible through the notification icon. - // // This means if there's a quake window _somewhere_, we want the notification icon - // // to show regardless of the notification icon settings. - // // This also means we'll need to destroy the notification icon if it was created - // // specifically for the quake window. If not, it should not be destroyed. - // if (!_window->IsQuakeWindow() && _windowLogic.IsQuakeWindow()) - // { - // _ShowNotificationIconRequested(nullptr, nullptr); - // } - // else if (_window->IsQuakeWindow() && !_windowLogic.IsQuakeWindow()) - // { - // _HideNotificationIconRequested(nullptr, nullptr); - // } - - // TODO! I think we need the emperor to listen to windowLogic's IsQuakeWindowChanged event, to replicate this - _window->IsQuakeWindow(_windowLogic.IsQuakeWindow()); } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 1a983b4946d..678ef2b0e1c 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -150,6 +150,12 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co auto func = [this, args, peasant, firstWindow]() { auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; _windows.push_back(window); + + window->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { + co_await wil::resume_foreground(this->_dispatcher); + this->_checkWindowsForNotificationIcon(); + }); + return window->WindowProc(); }; @@ -176,22 +182,12 @@ void WindowEmperor::_becomeMonarch() //////////////////////////////////////////////////////////////////////////// - // if (_windowManager2.DoesQuakeWindowExist() || - // _window->IsQuakeWindow() || - // (_windowLogic.GetAlwaysShowNotificationIcon() || _windowLogic.GetMinimizeToNotificationArea())) - // { - // _CreateNotificationIcon(); - // } - - // // These events are coming from peasants that become or un-become quake windows. - // _revokers.ShowNotificationIconRequested = _windowManager2.ShowNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_ShowNotificationIconRequested }); - // _revokers.HideNotificationIconRequested = _windowManager2.HideNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_HideNotificationIconRequested }); + _checkWindowsForNotificationIcon(); //////////////////////////////////////////////////////////////////////////// - // // Set the number of open windows (so we know if we are the last window) - // // and subscribe for updates if there are any changes to that number. - // _windowLogic.SetNumberOfOpenWindows(_windowManager2.GetNumberOfPeasants()); + // Set the number of open windows (so we know if we are the last window) + // and subscribe for updates if there are any changes to that number. _WindowCreatedToken = _manager.WindowCreated({ this, &WindowEmperor::_numberOfWindowsChanged }); _WindowClosedToken = _manager.WindowClosed({ this, &WindowEmperor::_numberOfWindowsChanged }); @@ -224,6 +220,11 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II { _windowThread->Logic().SetNumberOfOpenWindows(numWindows); } + + // If we closed out the quake window, and don't otherwise need the tray + // icon, let's get rid of it. + _checkWindowsForNotificationIcon(); + // TODO! apparently, we crash when whe actually post a quit, handle it, and // then leak all our threads. That's not good, but also, this should only // happen once our threads all exited. So figure that out. From 7e91bdb28999ef7e2b3cb3672ff90f4665c06c49 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 16:39:07 -0600 Subject: [PATCH 048/122] This actually seems to make the lifetime management worse - we just dtor the WindowEmperor randomly now, which is no good --- .../WindowsTerminal/WindowEmperor.cpp | 24 +++++++++++++++---- src/cascadia/WindowsTerminal/WindowThread.cpp | 7 ++++++ src/cascadia/WindowsTerminal/WindowThread.h | 3 +++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 678ef2b0e1c..08ca26483d6 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -155,13 +155,27 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); - - return window->WindowProc(); + window->Exited([this, peasant]() { + // find the window in _windows who's peasant's Id matches the peasant's Id + // and remove it + _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [&](const auto& w) { + return w->Peasant().GetID() == peasant.GetID(); + }), + _windows.end()); + if (_windows.size() == 0) + { + _close(); + } + }); + auto result = window->WindowProc(); + return result; }; + // _threads.emplace_back(func); + // LOG_IF_FAILED(SetThreadDescription(_threads.back().native_handle(), L"Window Thread")); - _threads.emplace_back(func); - - LOG_IF_FAILED(SetThreadDescription(_threads.back().native_handle(), L"Window Thread")); + std::thread myWindowThread{ func }; + LOG_IF_FAILED(SetThreadDescription(myWindowThread.native_handle(), L"Window Thread")); + myWindowThread.detach(); } void WindowEmperor::_becomeMonarch() diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 50fcdd4f19f..2e910707bb4 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -85,5 +85,12 @@ int WindowThread::WindowProc() TranslateMessage(&message); DispatchMessage(&message); } + + _ExitedHandlers(); return 0; } + +winrt::Microsoft::Terminal::Remoting::Peasant WindowThread::Peasant() +{ + return _peasant; +} diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index d97a9dfbc43..501665e2d30 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -14,6 +14,9 @@ class WindowThread int WindowProc(); winrt::TerminalApp::TerminalWindow Logic(); + winrt::Microsoft::Terminal::Remoting::Peasant Peasant(); + + WINRT_CALLBACK(Exited, winrt::delegate<>); private: ::AppHost _host; From 0395dc40a38b5d2d1628b9a3c249d6902bc7b6eb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Feb 2023 16:39:15 -0600 Subject: [PATCH 049/122] Revert "This actually seems to make the lifetime management worse - we just dtor the WindowEmperor randomly now, which is no good" This reverts commit 7e91bdb28999ef7e2b3cb3672ff90f4665c06c49. --- .../WindowsTerminal/WindowEmperor.cpp | 24 ++++--------------- src/cascadia/WindowsTerminal/WindowThread.cpp | 7 ------ src/cascadia/WindowsTerminal/WindowThread.h | 3 --- 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 08ca26483d6..678ef2b0e1c 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -155,27 +155,13 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); - window->Exited([this, peasant]() { - // find the window in _windows who's peasant's Id matches the peasant's Id - // and remove it - _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [&](const auto& w) { - return w->Peasant().GetID() == peasant.GetID(); - }), - _windows.end()); - if (_windows.size() == 0) - { - _close(); - } - }); - auto result = window->WindowProc(); - return result; + + return window->WindowProc(); }; - // _threads.emplace_back(func); - // LOG_IF_FAILED(SetThreadDescription(_threads.back().native_handle(), L"Window Thread")); - std::thread myWindowThread{ func }; - LOG_IF_FAILED(SetThreadDescription(myWindowThread.native_handle(), L"Window Thread")); - myWindowThread.detach(); + _threads.emplace_back(func); + + LOG_IF_FAILED(SetThreadDescription(_threads.back().native_handle(), L"Window Thread")); } void WindowEmperor::_becomeMonarch() diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 2e910707bb4..50fcdd4f19f 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -85,12 +85,5 @@ int WindowThread::WindowProc() TranslateMessage(&message); DispatchMessage(&message); } - - _ExitedHandlers(); return 0; } - -winrt::Microsoft::Terminal::Remoting::Peasant WindowThread::Peasant() -{ - return _peasant; -} diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 501665e2d30..d97a9dfbc43 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -14,9 +14,6 @@ class WindowThread int WindowProc(); winrt::TerminalApp::TerminalWindow Logic(); - winrt::Microsoft::Terminal::Remoting::Peasant Peasant(); - - WINRT_CALLBACK(Exited, winrt::delegate<>); private: ::AppHost _host; From f904e5d22d415058293d20229e71c5233b2e3384 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Feb 2023 09:48:08 -0600 Subject: [PATCH 050/122] I give up and am moving on to the next TODOs. 8 TODOs remain --- .../WindowsTerminal/WindowEmperor.cpp | 87 +++++++++---------- src/cascadia/WindowsTerminal/WindowEmperor.h | 1 - src/cascadia/WindowsTerminal/WindowThread.cpp | 42 +++++++-- src/cascadia/WindowsTerminal/WindowThread.h | 13 ++- src/cascadia/WindowsTerminal/main.cpp | 7 -- 5 files changed, 90 insertions(+), 60 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 678ef2b0e1c..e42185201cb 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -15,6 +15,12 @@ #include "resource.h" #include "NotificationIcon.h" +// This was an enormous dead end. +// #include +// #include +// typedef GUID DXGI_DEBUG_ID; +// const DXGI_DEBUG_ID DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 }; + using namespace winrt; using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings::Model; @@ -45,6 +51,13 @@ WindowEmperor::WindowEmperor() noexcept : WindowEmperor::~WindowEmperor() { + // This was an enormous dead end. + // if (wil::com_ptr debug; SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(debug.addressof())))) + // { + // //debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL); + // debug->DisableLeakTrackingForThread(); + // } + _app.Close(); _app = nullptr; } @@ -97,71 +110,55 @@ bool WindowEmperor::HandleCommandlineArgs() return result.ShouldCreateWindow(); } -bool WindowEmperor::ShouldExit() -{ - // TODO! - return false; -} - void WindowEmperor::WaitForWindows() { - // std::thread one{ [this]() { - // WindowThread foo{ _app.Logic() }; - // return foo.WindowProc(); - // } }; - - // Sleep(2000); - - // std::thread two{ [this]() { - // WindowThread foo{ _app.Logic() }; - // return foo.WindowProc(); - // } }; - - // one.join(); - // two.join(); - - // Sleep(30000); //30s - - // TODO! This creates a loop that never actually exits right now. It seems - // to get a message when another window is activated, but never a WM_CLOSE - // (that makes sense). It keeps running even when the threads all exit, - // which is INTERESTING for sure. - // - // what we should do: - // - Add an event to Monarch to indicate that we should exit, because all the - // peasants have exited. - // - We very well may need an HWND_MESSAGE that's connected to the main - // thread, for processing global hotkeys. Consider that in the future too. - MSG message; while (GetMessage(&message, nullptr, 0, 0)) { TranslateMessage(&message); DispatchMessage(&message); } - - // _threads.clear(); } -void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, const bool firstWindow) +void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, const bool /*firstWindow*/) { Remoting::Peasant peasant{ _manager.CreateAPeasant(args) }; - auto func = [this, args, peasant, firstWindow]() { - auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; - _windows.push_back(window); + auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; - window->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { + window->Started([this, sender=window]() -> winrt::fire_and_forget { + // Add a callback to the window's logic to let us know when the window's + // quake mode state changes. We'll use this to check if we need to add + // or remove the notification icon. + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); - return window->WindowProc(); - }; + // These come in on the sender's thread. Move back to our thread. + co_await wil::resume_foreground(_dispatcher); + + _windows.push_back(std::move(sender)); + }); + + window->Exited([this](uint64_t senderID) -> winrt::fire_and_forget { + // These come in on the sender's thread. Move back to our thread. + co_await wil::resume_foreground(_dispatcher); + + // find the window in _windows who's peasant's Id matches the peasant's Id + // and remove it + _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [&](const auto& w) { + return w->Peasant().GetID() == senderID; + }), + _windows.end()); - _threads.emplace_back(func); + if (_windows.size() == 0) + { + _close(); + } + }); - LOG_IF_FAILED(SetThreadDescription(_threads.back().native_handle(), L"Window Thread")); + window->Start(); } void WindowEmperor::_becomeMonarch() diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index f59fbaaed2f..dc6aa39e6fd 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -20,7 +20,6 @@ class WindowEmperor public: WindowEmperor() noexcept; ~WindowEmperor(); - bool ShouldExit(); void WaitForWindows(); bool HandleCommandlineArgs(); diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 50fcdd4f19f..ae9626789ca 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -9,13 +9,35 @@ WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic, winrt::Microsoft::Terminal::Remoting::WindowManager2 manager, winrt::Microsoft::Terminal::Remoting::Peasant peasant) : _peasant{ peasant }, - _host{ logic, args, manager, peasant } + _appLogic{ logic }, + _args{ args }, + _manager{ manager } { + // DO NOT +} + +void WindowThread::Start() +{ + _thread = std::thread([this]() { + // Start the Apphost HERE, on the actual thread we want XAML to run on + _host = std::make_unique<::AppHost>(_appLogic, + _args, + _manager, + _peasant); + + // Enter the main window loop. + const auto exitCode = WindowProc(); + _host = nullptr; + + _ExitedHandlers(_peasant.GetID()); + return exitCode; + }); + LOG_IF_FAILED(SetThreadDescription(_thread.native_handle(), L"Window Thread")); } winrt::TerminalApp::TerminalWindow WindowThread::Logic() { - return _host.Logic(); + return _host->Logic(); } static bool _messageIsF7Keypress(const MSG& message) @@ -37,7 +59,11 @@ int WindowThread::WindowProc() // Initialize the xaml content. This must be called AFTER the // WindowsXamlManager is initialized. - _host.Initialize(); + _host->Initialize(); + + // Inform the emperor that we're ready to go. We need to do this after + // Initialize, so that the windowLogic is ready to be used + _StartedHandlers(); MSG message; @@ -53,7 +79,7 @@ int WindowThread::WindowProc() // been handled we can discard the message before we even translate it. if (_messageIsF7Keypress(message)) { - if (_host.OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true)) + if (_host->OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true)) { // The application consumed the F7. Don't let Xaml get it. continue; @@ -66,7 +92,7 @@ int WindowThread::WindowProc() if (_messageIsAltKeyup(message)) { // Let's pass to the application - if (_host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false)) + if (_host->OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false)) { // The application consumed the Alt. Don't let Xaml get it. continue; @@ -78,7 +104,7 @@ int WindowThread::WindowProc() // above, we steal the event and hand it off to the host. if (_messageIsAltSpaceKeypress(message)) { - _host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true); + _host->OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true); continue; } @@ -87,3 +113,7 @@ int WindowThread::WindowProc() } return 0; } +winrt::Microsoft::Terminal::Remoting::Peasant WindowThread::Peasant() +{ + return _peasant; +} diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index d97a9dfbc43..581878e6f62 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -14,8 +14,19 @@ class WindowThread int WindowProc(); winrt::TerminalApp::TerminalWindow Logic(); + void Start(); + winrt::Microsoft::Terminal::Remoting::Peasant Peasant(); + + WINRT_CALLBACK(Started, winrt::delegate<>); + WINRT_CALLBACK(Exited, winrt::delegate); private: - ::AppHost _host; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; + + winrt::TerminalApp::AppLogic _appLogic{ nullptr }; + winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs _args{ nullptr }; + winrt::Microsoft::Terminal::Remoting::WindowManager2 _manager{ nullptr }; + + std::thread _thread; + std::unique_ptr<::AppHost> _host{ nullptr }; }; diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index 43934998b9f..40bb70a238c 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -115,15 +115,8 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) winrt::init_apartment(winrt::apartment_type::single_threaded); ::WindowEmperor emperor{}; - if (emperor.ShouldExit()) - { - ExitProcess(0); - } if (emperor.HandleCommandlineArgs()) { emperor.WaitForWindows(); - - - } } From 45487294a0da4012cd9f6d6607fd89fa63d3892d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Feb 2023 11:16:37 -0600 Subject: [PATCH 051/122] Some additional cleanup of the WindowManager code 3 TODOs left --- src/cascadia/Remoting/WindowManager2.cpp | 219 +++++++----------- src/cascadia/Remoting/WindowManager2.h | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 46 +--- src/cascadia/WindowsTerminal/AppHost.h | 1 - .../WindowsTerminal/WindowEmperor.cpp | 91 +++++--- src/cascadia/WindowsTerminal/WindowEmperor.h | 11 +- 6 files changed, 164 insertions(+), 206 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index ab3d3d660b4..402f6ca2c49 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -94,7 +94,34 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool shouldCreateWindow = false; _createMonarch(); - if (_monarch == nullptr) + if (_monarch) + { + // We connectecd to a monarch instance, not us though. + + shouldCreateWindow = false; + std::optional givenID; + winrt::hstring givenName{}; + + // Send the commandline over to the monarch process + if (_proposeToMonarch(args, givenID, givenName)) + { + // If that succeeded, then we don't need to make a new window. + // Our job is done. Either the monarch is going to run the + // commandline in an existing window, or a new one, but either way, + // this process doesn't need to make a new window. + + return *winrt::make_self(shouldCreateWindow); + } + // Otherwise, we'll try to handle this ourselves. + } + + // Theoretically, this condition is always true here: + // + // if (_monarch == nullptr) + // + // If we do still have a _monarch at this point, then we must have + // successfully proposed to it in _proposeToMonarch, so we can't get + // here with a monarch. { // No pre-existing instance. @@ -107,11 +134,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // This is handled by some handler in-proc _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); - // { - // const auto targetWindow = _appLogic.FindTargetWindow(findWindowArgs->Args().Commandline()); - // findWindowArgs->ResultTargetWindow(targetWindow.WindowId()); - // findWindowArgs->ResultTargetWindowName(targetWindow.WindowName()); - // } // After the event was handled, ResultTargetWindow() will be filled with // the parsed result. @@ -125,7 +147,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation shouldCreateWindow = false; return *winrt::make_self(shouldCreateWindow); - // return; } else { @@ -136,52 +157,39 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // getting any windows. _registerAsMonarch(); _createMonarch(); - _createCallbacks(); if (!_monarch) { - // TODO! something catastrophically bad happened here. + // Something catastrophically bad happened here. But we + // don't want to just exit immediately. No, instead, we'll + // just instantiate a local Monarch instance. We're firmly + // in the realm of undefined behavior, but better to have + // some window than not. + _monarch = winrt::make(); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_FaileToCoCreate", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } + _createCallbacks(); - // I don't _really_ think we need to propose this again. WE - // basically did that work above. We've already gotten the - // result - - // * we don't care about the name or ID, we want a new window. - - //Remoting::ProposeCommandlineResult result = _monarch.ProposeCommandline(args); - - // TODO! So, we wanted a new peasant. Cool! + // So, we wanted a new peasant. Cool! // // We need to fill in args.ResultTargetWindow, // args.ResultTargetWindowName so that we can create the new // window with those values. Otherwise, the very first window // won't obey the given name / ID. - shouldCreateWindow = true; - auto result = winrt::make_self(shouldCreateWindow); - result->Id(); - result->WindowName(); - return *result; + // + // So let's just ask the monarch (ourselves) to get those values. + return _monarch.ProposeCommandline(args); } } - else - { - // We connectecd to a monarch instance, not us though. - - shouldCreateWindow = false; - std::optional givenID; - winrt::hstring givenName{}; - - // Send the commandline over to the monarch process - _proposeToMonarch(args, givenID, givenName); - - // Our job is done. Either the monarch is going to run the - // commandline in an existing window, or a new one, but either way, - // this process doesn't need to make a new window. - - return *winrt::make_self(shouldCreateWindow); - } } - void WindowManager2::_proposeToMonarch(const Remoting::CommandlineArgs& args, + // Method Description: + // - Helper attempting to call to the monarch multiple times. If the monarch + // fails to respond, or we encounter any sort of error, we'll try again + // until we find one, or decisively determine there isn't one. + bool WindowManager2::_proposeToMonarch(const Remoting::CommandlineArgs& args, std::optional& /*givenID*/, winrt::hstring& /*givenName*/) { @@ -217,10 +225,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // dies between now and the inspection of // `result.ShouldCreateWindow` below, we don't want to explode // (since _proposeToMonarch is not try/caught). - auto outOfProcResult = _monarch.ProposeCommandline(args); - result = winrt::make(outOfProcResult); - proposedCommandline = true; + _monarch.ProposeCommandline(args); + return true; } catch (...) { @@ -246,93 +253,45 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (attempts >= 10) { - // TODO! - break; + // We've tried 10 times to find the monarch, failing each + // time. Since we have no idea why, we're guessing that in + // this case, there's just a Monarch registered that's + // misbehaving. In this case, just fall back to + // "IsolatedMonarchMode" - we can't trust the currently + // registered one. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_TooManyAttempts_NullMonarchIsolateMode", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // Set the monarch to null, so that we'll create a new one + // (or just generally check if we need to even make a window + // for this commandline.) + _monarch = nullptr; + return false; + } + else + { + // We failed to ask the monarch. It must have died. Try and + // find another monarch. + _createMonarch(); + if (!_monarch) + { + // We failed to create a monarch. That means there + // aren't any other windows, and we can become the monarch. + return false; + } + // Go back around the loop. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_proposeToMonarch_tryAgain", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } - // if (attempts >= 10) - // { - // // We've tried 10 times to find the monarch, failing each - // // time. Since we have no idea why, we're guessing that in - // // this case, there's just a Monarch registered that's - // // misbehaving. In this case, just fall back to - // // "IsolatedMonarchMode" - we can't trust the currently - // // registered one. - // TraceLoggingWrite(g_hRemotingProvider, - // "WindowManager_TooManyAttempts_NullMonarchIsolateMode", - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // _monarch = winrt::make(); - // _createCallbacks(); - // } - // else - // { - // // We failed to ask the monarch. It must have died. Try and - // // find the real monarch. Don't perform an election, that - // // assumes we have a peasant, which we don't yet. - // _createMonarchAndCallbacks(); - // // _createMonarchAndCallbacks will initialize _isKing - // } - // if (_isKing) - // { - // // We became the king. We don't need to ProposeCommandline to ourself, we're just - // // going to do it. - // // - // // Return early, because there's nothing else for us to do here. - // TraceLoggingWrite(g_hRemotingProvider, - // "WindowManager_proposeToMonarch_becameKing", - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // // In WindowManager::ProposeCommandline, had we been the - // // king originally, we would have started by setting - // // this to true. We became the monarch here, so set it - // // here as well. - // _shouldCreateWindow = true; - // return; - // } - - // // Here, we created the new monarch, it wasn't us, so we're - // // gonna go through the while loop again and ask the new - // // king. - // TraceLoggingWrite(g_hRemotingProvider, - // "WindowManager_proposeToMonarch_tryAgain", - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } } - // // Here, the monarch (not us) has replied to the message. Get the - // // valuables out of the response: - // _shouldCreateWindow = result.ShouldCreateWindow(); - // if (result.Id()) - // { - // givenID = result.Id().Value(); - // } - // givenName = result.WindowName(); - - // // TraceLogging doesn't have a good solution for logging an - // // optional. So we have to repeat the calls here: - // if (givenID) - // { - // TraceLoggingWrite(g_hRemotingProvider, - // "WindowManager_ProposeCommandline", - // TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - // TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), - // TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // } - // else - // { - // TraceLoggingWrite(g_hRemotingProvider, - // "WindowManager_ProposeCommandline", - // TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - // TraceLoggingPointer(nullptr, "Id", "No ID provided"), - // TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - // TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // } + // I don't think we can ever get here, but the compiler doesn't know + return false; } Remoting::Peasant WindowManager2::CreateAPeasant(Remoting::WindowRequestedArgs args) @@ -358,14 +317,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // If the peasant asks us to quit we should not try to act in future elections. - p->QuitRequested([weakThis{ get_weak() }](auto&&, auto&&) { - // if (auto wm = weakThis.get()) - // { - // wm->_monarchWaitInterrupt.SetEvent(); - // } - }); - return *p; } diff --git a/src/cascadia/Remoting/WindowManager2.h b/src/cascadia/Remoting/WindowManager2.h index 3de1b41883e..7bb8f755b05 100644 --- a/src/cascadia/Remoting/WindowManager2.h +++ b/src/cascadia/Remoting/WindowManager2.h @@ -45,7 +45,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _createMonarch(); void _registerAsMonarch(); - void _proposeToMonarch(const Remoting::CommandlineArgs& args, + bool _proposeToMonarch(const Remoting::CommandlineArgs& args, std::optional& givenID, winrt::hstring& givenName); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index eded913c4c8..8916ab85255 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -221,7 +221,11 @@ void AppHost::_HandleCommandlineArgs() const auto numPeasants = _windowManager2.GetNumberOfPeasants(); if (numPeasants == 1) { - // TODO! this is vaugely off by one. Not sure, but if you restore 2 windows, you seem to get two copies of the second. Yikes. + // TODO! this is vaugely off by one. Not sure, but if you restore 2 + // windows, you seem to get two copies of the second. Yikes. And + // this wasn't just because I was setting the debug commandline to + // `nt ; nt`. Calling wtd with two persisted windows just creates + // two of the second persisted window, ew. const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); if (_appLogic.ShouldUsePersistedLayout() && layouts && @@ -853,46 +857,6 @@ winrt::Windows::Foundation::IAsyncOperation AppHost::_GetWindowL co_return layoutJson; } -// Method Description: -// - Called when the monarch failed to summon a window for a given set of -// SummonWindowSelectionArgs. In this case, we should create the specified -// window ourselves. -// - This is to support the scenario like `globalSummon(Name="_quake")` being -// used to summon the window if it already exists, or create it if it doesn't. -// Arguments: -// - args: Contains information on how we should name the window -// Return Value: -// - -winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::GlobalSummonArgs args) -{ - // Hop to the BG thread - co_await winrt::resume_background(); - - // This will get us the correct exe for dev/preview/release. If you - // don't stick this in a local, it'll get mangled by ShellExecute. I - // have no idea why. - const auto exePath{ GetWtExePath() }; - - // If we weren't given a name, then just use new to force the window to be - // unnamed. - winrt::hstring cmdline{ - fmt::format(L"-w {}", - args.Name().empty() ? L"new" : - args.Name()) - }; - - SHELLEXECUTEINFOW seInfo{ 0 }; - seInfo.cbSize = sizeof(seInfo); - seInfo.fMask = SEE_MASK_NOASYNC; - seInfo.lpVerb = L"open"; - seInfo.lpFile = exePath.c_str(); - seInfo.lpParameters = cmdline.c_str(); - seInfo.nShow = SW_SHOWNORMAL; - LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo)); - - co_return; -} - // Method Description: // - Helper to initialize our instance of IVirtualDesktopManager. If we already // got one, then this will just return true. Otherwise, we'll try and init a diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 9de8144d752..7d576fc36e3 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -85,7 +85,6 @@ class AppHost bool _LazyLoadDesktopManager(); - winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& args); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index e42185201cb..91cf84d3a2a 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -4,14 +4,12 @@ #include "pch.h" #include "WindowEmperor.h" -// #include "MonarchFactory.h" -// #include "CommandlineArgs.h" #include "../inc/WindowingBehavior.h" -// #include "FindTargetWindowArgs.h" -// #include "ProposeCommandlineResult.h" #include "../../types/inc/utils.hpp" +#include "../WinRTUtils/inc/WtExeUtils.h" + #include "resource.h" #include "NotificationIcon.h" @@ -57,6 +55,11 @@ WindowEmperor::~WindowEmperor() // //debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL); // debug->DisableLeakTrackingForThread(); // } + // + // TODO! We're still somehow leaking on exit, even though the islands, + // hosts, xaml sources, everything, seem to get closed and dtor'd before + // this point. Theoretically, we shouldn't event have leak reporting enabled + // for this thread. It's a real thinker. _app.Close(); _app = nullptr; @@ -126,11 +129,11 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; - window->Started([this, sender=window]() -> winrt::fire_and_forget { + window->Started([this, sender = window]() -> winrt::fire_and_forget { // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); @@ -161,42 +164,46 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co window->Start(); } +// Method Description: +// - Set up all sorts of handlers now that we've determined that we're a process +// that will end up hosting the windows. These include: +// - Setting up a message window to handle hotkeys and notification icon +// invokes. +// - Setting up the global hotkeys. +// - Setting up the notification icon. +// - Setting up callbacks for when the settings change. +// - Setting up callbacks for when the number of windows changes. +// - Setting up the throttled func for layout persistence. Arguments: +// - void WindowEmperor::_becomeMonarch() { _createMessageWindow(); - //////////////////////////////////////////////////////////////////////////// _setupGlobalHotkeys(); + // When the settings change, we'll want to update our global hotkeys and our + // notification icon based on the new settings. _app.Logic().SettingsChanged([this](auto&&, const TerminalApp::SettingsLoadEventArgs& args) { if (SUCCEEDED(args.Result())) { _setupGlobalHotkeys(); - _checkWindowsForNotificationIcon(); } }); - //////////////////////////////////////////////////////////////////////////// - + // On startup, immediately check if we need to show the notification icon. _checkWindowsForNotificationIcon(); - //////////////////////////////////////////////////////////////////////////// - // Set the number of open windows (so we know if we are the last window) // and subscribe for updates if there are any changes to that number. _WindowCreatedToken = _manager.WindowCreated({ this, &WindowEmperor::_numberOfWindowsChanged }); _WindowClosedToken = _manager.WindowClosed({ 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(); })); @@ -221,14 +228,6 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II // If we closed out the quake window, and don't otherwise need the tray // icon, let's get rid of it. _checkWindowsForNotificationIcon(); - - // TODO! apparently, we crash when whe actually post a quit, handle it, and - // then leak all our threads. That's not good, but also, this should only - // happen once our threads all exited. So figure that out. - if (numWindows == 0) - { - // _close(); - } } // Raised from our windowManager (on behalf of the monarch). We respond by @@ -431,6 +430,47 @@ winrt::fire_and_forget WindowEmperor::_close() #pragma endregion #pragma region GlobalHotkeys + +// Method Description: +// - Called when the monarch failed to summon a window for a given set of +// SummonWindowSelectionArgs. In this case, we should create the specified +// window ourselves. +// - This is to support the scenario like `globalSummon(Name="_quake")` being +// used to summon the window if it already exists, or create it if it doesn't. +// Arguments: +// - args: Contains information on how we should name the window +// Return Value: +// - +static winrt::fire_and_forget _createNewTerminalWindow(Settings::Model::GlobalSummonArgs args) +{ + // Hop to the BG thread + co_await winrt::resume_background(); + + // This will get us the correct exe for dev/preview/release. If you + // don't stick this in a local, it'll get mangled by ShellExecute. I + // have no idea why. + const auto exePath{ GetWtExePath() }; + + // If we weren't given a name, then just use new to force the window to be + // unnamed. + winrt::hstring cmdline{ + fmt::format(L"-w {}", + args.Name().empty() ? L"new" : + args.Name()) + }; + + SHELLEXECUTEINFOW seInfo{ 0 }; + seInfo.cbSize = sizeof(seInfo); + seInfo.fMask = SEE_MASK_NOASYNC; + seInfo.lpVerb = L"open"; + seInfo.lpFile = exePath.c_str(); + seInfo.lpParameters = cmdline.c_str(); + seInfo.nShow = SW_SHOWNORMAL; + LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo)); + + co_return; +} + void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) { if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) @@ -470,8 +510,7 @@ void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) else { // We should make the window ourselves. - // TODO! - // _createNewTerminalWindow(summonArgs); + _createNewTerminalWindow(summonArgs); } } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index dc6aa39e6fd..fee566c785e 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -1,12 +1,17 @@ /*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. +Copyright (c) Microsoft Corporation Licensed under the MIT license. Class Name: - WindowEmperor.h Abstract: -- TODO! +- The WindowEmperor is our new class for managing the single Terminal process + with all our windows. It will be responsible for handling the commandline + arguments. It will initially try to find another terminal process to + communicate with. If it does, it'll hand off to the existing process. +- If it determines that it should create a window, it will set up a new thread + for that window, and a message loop on the main thread for handling global + state, such as hotkeys and the notification icon. --*/ From 76609371396ba67b485ac62b70a6526c5942cf7f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Feb 2023 13:04:59 -0600 Subject: [PATCH 052/122] and with that, we're ready to cleanup for review. 2 TODOs left --- src/cascadia/Remoting/Monarch.cpp | 8 ++++++ src/cascadia/TerminalApp/AppLogic.cpp | 11 ++++++-- src/cascadia/TerminalApp/AppLogic.h | 2 ++ src/cascadia/TerminalApp/AppLogic.idl | 11 +++++++- src/cascadia/WindowsTerminal/AppHost.cpp | 25 +++++++++++-------- src/cascadia/WindowsTerminal/AppHost.h | 2 ++ .../WindowsTerminal/WindowEmperor.cpp | 13 ++++++++-- 7 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f828fd05dbd..726882a8989 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -768,6 +768,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *result; } } + else if (targetWindow == WindowingBehaviorUseNone) + { + // In this case, the targetWindow was UseNone, which means that we + // want to make a message box, but otherwise not make a Terminal + // window. + auto result = winrt::make_self(false); + return *result; + } // If we get here, we couldn't find an existing window. Make a new one. TraceLoggingWrite(g_hRemotingProvider, diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 2105af24279..2717cec205d 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -565,7 +565,7 @@ namespace winrt::TerminalApp::implementation { if (!appArgs.GetExitMessage().empty()) { - return winrt::make(WindowingBehaviorUseNew); + return winrt::make(WindowingBehaviorUseNone); } const std::string parsedTarget{ appArgs.GetTargetWindow() }; @@ -644,7 +644,7 @@ namespace winrt::TerminalApp::implementation // create a new window. Then, in that new window, we'll try to set the // StartupActions, which will again fail, returning the correct error // message. - return winrt::make(WindowingBehaviorUseNew); + return winrt::make(WindowingBehaviorUseNone); } Windows::Foundation::Collections::IMapView AppLogic::GlobalHotkeys() @@ -699,4 +699,11 @@ namespace winrt::TerminalApp::implementation ApplicationState::SharedInstance().PersistedWindowLayouts(winrt::single_threaded_vector(std::move(converted))); } + + TerminalApp::ParseCommandlineResult AppLogic::GetParseCommandlineMessage(array_view args) + { + ::TerminalApp::AppCommandlineArgs _appArgs; + const auto r = _appArgs.ParseArgs(args); + return TerminalApp::ParseCommandlineResult{ winrt::to_hstring(_appArgs.GetExitMessage()), r}; + } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 759cfb09500..fe2b2435c4a 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -67,6 +67,8 @@ namespace winrt::TerminalApp::implementation TerminalApp::TerminalWindow CreateNewWindow(); + TerminalApp::ParseCommandlineResult GetParseCommandlineMessage(array_view args); + TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); private: diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index a5db001de67..8499d53a52c 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -10,6 +10,13 @@ namespace TerminalApp String WindowName { get; }; }; + struct ParseCommandlineResult + { + String Message; + Int32 ExitCode; + }; + + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. [default_interface] runtimeclass AppLogic @@ -40,7 +47,9 @@ namespace TerminalApp TerminalWindow CreateNewWindow(); - Windows.Foundation.Collections.IMapView GlobalHotkeys(); + ParseCommandlineResult GetParseCommandlineMessage(String[] args); + + IMapView GlobalHotkeys(); event Windows.Foundation.TypedEventHandler SettingsChanged; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 8916ab85255..6ba6b8c3bfe 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -139,6 +139,19 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& } } +void AppHost::s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& result) +{ + const auto displayHelp = result.ExitCode == 0; + const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE; + const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR; + // TODO:GH#4134: polish this dialog more, to make the text more + // like msiexec /? + MessageBoxW(nullptr, + result.Message.data(), + GetStringResource(messageTitle).data(), + MB_OK | messageIcon); +} + // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to // the WindowManager, to ask if we should become a window process. @@ -171,19 +184,11 @@ void AppHost::_HandleCommandlineArgs() const auto message = _windowLogic.ParseCommandlineMessage(); if (!message.empty()) { - const auto displayHelp = result == 0; - const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE; - const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR; - // TODO:GH#4134: polish this dialog more, to make the text more - // like msiexec /? - MessageBoxW(nullptr, - message.data(), - GetStringResource(messageTitle).data(), - MB_OK | messageIcon); + AppHost::s_DisplayMessageBox({ message, result}); if (_windowLogic.ShouldExitEarly()) { - ExitProcess(result); + ExitThread(result); } } } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 7d576fc36e3..dc307261cd9 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -24,6 +24,8 @@ class AppHost bool HasWindow(); + static void s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& message); + winrt::TerminalApp::TerminalWindow Logic(); private: diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 91cf84d3a2a..fcb9ecae594 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -60,6 +60,8 @@ WindowEmperor::~WindowEmperor() // hosts, xaml sources, everything, seem to get closed and dtor'd before // this point. Theoretically, we shouldn't event have leak reporting enabled // for this thread. It's a real thinker. + // + // I need someone to help take a look at this with me. _app.Close(); _app = nullptr; @@ -97,8 +99,6 @@ bool WindowEmperor::HandleCommandlineArgs() const auto result = _manager.ProposeCommandline2(eventArgs); - // TODO! createWindow is false in cases like wt --help. Figure that out. - if (result.ShouldCreateWindow()) { CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }, true); @@ -109,6 +109,15 @@ bool WindowEmperor::HandleCommandlineArgs() _becomeMonarch(); } + else + { + const auto res = _app.Logic().GetParseCommandlineMessage(eventArgs.Commandline()); + if (!res.Message.empty()) + { + AppHost::s_DisplayMessageBox(res); + ExitThread(res.ExitCode); + } + } return result.ShouldCreateWindow(); } From 0ad5b596bd8bbd717d3abb158080f32274442e24 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Feb 2023 13:24:15 -0600 Subject: [PATCH 053/122] I think it's ready for review --- .../Microsoft.Terminal.RemotingLib.vcxproj | 7 - src/cascadia/Remoting/WindowManager.cpp | 762 +++++------------- src/cascadia/Remoting/WindowManager.h | 76 +- src/cascadia/Remoting/WindowManager.idl | 30 +- src/cascadia/Remoting/WindowManager2.cpp | 428 ---------- src/cascadia/Remoting/WindowManager2.h | 63 -- src/cascadia/Remoting/WindowManager2.idl | 44 - src/cascadia/WindowsTerminal/AppHost.cpp | 22 +- src/cascadia/WindowsTerminal/AppHost.h | 8 +- src/cascadia/WindowsTerminal/WindowEmperor.h | 4 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 2 +- src/cascadia/WindowsTerminal/WindowThread.h | 4 +- 12 files changed, 251 insertions(+), 1199 deletions(-) delete mode 100644 src/cascadia/Remoting/WindowManager2.cpp delete mode 100644 src/cascadia/Remoting/WindowManager2.h delete mode 100644 src/cascadia/Remoting/WindowManager2.idl diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index ed9d78dd450..ce78fc9c4b2 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -51,9 +51,6 @@ WindowManager.idl - - WindowManager2.idl - Peasant.idl @@ -96,9 +93,6 @@ WindowManager.idl - - WindowManager2.idl - Peasant.idl @@ -110,7 +104,6 @@ - diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 8c0b8d5a900..12e28fc2d80 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -2,10 +2,13 @@ // Licensed under the MIT license. #include "pch.h" + #include "WindowManager.h" + +#include "../inc/WindowingBehavior.h" #include "MonarchFactory.h" + #include "CommandlineArgs.h" -#include "../inc/WindowingBehavior.h" #include "FindTargetWindowArgs.h" #include "ProposeCommandlineResult.h" @@ -21,32 +24,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowManager::WindowManager() { - _monarchWaitInterrupt.create(); - - // Register with COM as a server for the Monarch class - _registerAsMonarch(); - // Instantiate an instance of the Monarch. This may or may not be in-proc! - auto foundMonarch = false; - while (!foundMonarch) - { - try - { - _createMonarchAndCallbacks(); - // _createMonarchAndCallbacks will initialize _isKing - foundMonarch = true; - } - catch (...) - { - // If we fail to find the monarch, - // stay in this jail until we do. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ExceptionInCtor", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } } - WindowManager::~WindowManager() { // IMPORTANT! Tear down the registration as soon as we exit. If we're not a @@ -55,32 +33,165 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // monarch! CoRevokeClassObject(_registrationHostClass); _registrationHostClass = 0; - SignalClose(); - _monarchWaitInterrupt.SetEvent(); + } - // A thread is joinable once it's been started. Basically this just - // makes sure that the thread isn't just default-constructed. - if (_electionThread.joinable()) - { - _electionThread.join(); - } + void WindowManager::_createMonarch() + { + // Heads up! This only works because we're using + // "metadata-based-marshalling" for our WinRT types. That means the OS is + // using the .winmd file we generate to figure out the proxy/stub + // definitions for our types automatically. This only works in the following + // cases: + // + // * If we're running unpackaged: the .winmd must be a sibling of the .exe + // * If we're running packaged: the .winmd must be in the package root + _monarch = try_create_instance(Monarch_clsid, + CLSCTX_LOCAL_SERVER); + } + + // Check if we became the king, and if we are, wire up callbacks. + void WindowManager::_createCallbacks() + { + assert(_monarch); + // Here, we're the king! + // + // This is where you should do any additional setup that might need to be + // done when we become the king. This will be called both for the first + // window, and when the current monarch dies. + + _monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers }); + _monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers }); + _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); + _monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); }); + _monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); }); + _monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers }); + + _monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow }); + } + + void WindowManager::_registerAsMonarch() + { + winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, + winrt::make<::MonarchFactory>().get(), + CLSCTX_LOCAL_SERVER, + REGCLS_MULTIPLEUSE, + &_registrationHostClass)); + } + + void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + { + _FindTargetWindowRequestedHandlers(sender, args); + } + void WindowManager::_raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args) + { + _RequestNewWindowHandlers(sender, args); } - void WindowManager::SignalClose() + Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline2(const Remoting::CommandlineArgs& args) { + bool shouldCreateWindow = false; + + _createMonarch(); if (_monarch) { - try + // We connectecd to a monarch instance, not us though. + + shouldCreateWindow = false; + std::optional givenID; + winrt::hstring givenName{}; + + // Send the commandline over to the monarch process + if (_proposeToMonarch(args, givenID, givenName)) { - _monarch.SignalClose(_peasant.GetID()); + // If that succeeded, then we don't need to make a new window. + // Our job is done. Either the monarch is going to run the + // commandline in an existing window, or a new one, but either way, + // this process doesn't need to make a new window. + + return *winrt::make_self(shouldCreateWindow); + } + // Otherwise, we'll try to handle this ourselves. + } + + // Theoretically, this condition is always true here: + // + // if (_monarch == nullptr) + // + // If we do still have a _monarch at this point, then we must have + // successfully proposed to it in _proposeToMonarch, so we can't get + // here with a monarch. + { + // No pre-existing instance. + + // Raise an event, to ask how to handle this commandline. We can't ask + // the app ourselves - we exist isolated from that knowledge (and + // dependency hell). The WindowManager will raise this up to the app + // host, which will then ask the AppLogic, who will then parse the + // commandline and determine the provided ID of the window. + auto findWindowArgs{ winrt::make_self(args) }; + + // This is handled by some handler in-proc + _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); + + // After the event was handled, ResultTargetWindow() will be filled with + // the parsed result. + const auto targetWindow = findWindowArgs->ResultTargetWindow(); + const auto targetWindowName = findWindowArgs->ResultTargetWindowName(); + + if (targetWindow == WindowingBehaviorUseNone) + { + // This commandline doesn't deserve a window. Don't make a monarch + // either. + shouldCreateWindow = false; + + return *winrt::make_self(shouldCreateWindow); + } + else + { + // This commandline _does_ want a window, which means we do want + // to create a window, and a monarch. + // + // Congrats! This is now THE PROCESS. It's the only one that's + // getting any windows. + _registerAsMonarch(); + _createMonarch(); + if (!_monarch) + { + // Something catastrophically bad happened here. But we + // don't want to just exit immediately. No, instead, we'll + // just instantiate a local Monarch instance. We're firmly + // in the realm of undefined behavior, but better to have + // some window than not. + _monarch = winrt::make(); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_FaileToCoCreate", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } + _createCallbacks(); + + // So, we wanted a new peasant. Cool! + // + // We need to fill in args.ResultTargetWindow, + // args.ResultTargetWindowName so that we can create the new + // window with those values. Otherwise, the very first window + // won't obey the given name / ID. + // + // So let's just ask the monarch (ourselves) to get those values. + return _monarch.ProposeCommandline(args); } - CATCH_LOG() } } - void WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args, - std::optional& givenID, - winrt::hstring& givenName) + // Method Description: + // - Helper attempting to call to the monarch multiple times. If the monarch + // fails to respond, or we encounter any sort of error, we'll try again + // until we find one, or decisively determine there isn't one. + bool WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args, + std::optional& /*givenID*/, + winrt::hstring& /*givenName*/) { // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below. static constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); @@ -114,10 +225,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // dies between now and the inspection of // `result.ShouldCreateWindow` below, we don't want to explode // (since _proposeToMonarch is not try/caught). - auto outOfProcResult = _monarch.ProposeCommandline(args); - result = winrt::make(outOfProcResult); - proposedCommandline = true; + _monarch.ProposeCommandline(args); + return true; } catch (...) { @@ -154,560 +264,74 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _monarch = winrt::make(); - _createCallbacks(); + // Set the monarch to null, so that we'll create a new one + // (or just generally check if we need to even make a window + // for this commandline.) + _monarch = nullptr; + return false; } else { // We failed to ask the monarch. It must have died. Try and - // find the real monarch. Don't perform an election, that - // assumes we have a peasant, which we don't yet. - _createMonarchAndCallbacks(); - // _createMonarchAndCallbacks will initialize _isKing - } - if (_isKing) - { - // We became the king. We don't need to ProposeCommandline to ourself, we're just - // going to do it. - // - // Return early, because there's nothing else for us to do here. + // find another monarch. + _createMonarch(); + if (!_monarch) + { + // We failed to create a monarch. That means there + // aren't any other windows, and we can become the monarch. + return false; + } + // Go back around the loop. TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_proposeToMonarch_becameKing", + "WindowManager_proposeToMonarch_tryAgain", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // In WindowManager::ProposeCommandline, had we been the - // king originally, we would have started by setting - // this to true. We became the monarch here, so set it - // here as well. - _shouldCreateWindow = true; - return; } - - // Here, we created the new monarch, it wasn't us, so we're - // gonna go through the while loop again and ask the new - // king. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_proposeToMonarch_tryAgain", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } } - // Here, the monarch (not us) has replied to the message. Get the - // valuables out of the response: - _shouldCreateWindow = result.ShouldCreateWindow(); - if (result.Id()) - { - givenID = result.Id().Value(); - } - givenName = result.WindowName(); - - // TraceLogging doesn't have a good solution for logging an - // optional. So we have to repeat the calls here: - if (givenID) - { - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ProposeCommandline", - TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - else - { - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ProposeCommandline", - TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingPointer(nullptr, "Id", "No ID provided"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } + // I don't think we can ever get here, but the compiler doesn't know + return false; } - void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) - { - // If we're the king, we _definitely_ want to process the arguments, we were - // launched with them! - // - // Otherwise, the King will tell us if we should make a new window - _shouldCreateWindow = _isKing; - std::optional givenID; - winrt::hstring givenName{}; - if (!_isKing) - { - _proposeToMonarch(args, givenID, givenName); - } - // During _proposeToMonarch, it's possible that we found that the king was dead, and we're the new king. Cool! Do this now. - if (_isKing) - { - // We're the monarch, we don't need to propose anything. We're just - // going to do it. - // - // However, we _do_ need to ask what our name should be. It's - // possible someone started the _first_ wt with something like `wt - // -w king` as the commandline - we want to make sure we set our - // name to "king". - // - // The FindTargetWindow event is the WindowManager's way of saying - // "I do not know how to figure out how to turn this list of args - // into a window ID/name. Whoever's listening to this event does, so - // I'll ask them". It's a convoluted way of hooking the - // WindowManager up to AppLogic without actually telling it anything - // about TerminalApp (or even WindowsTerminal) - auto findWindowArgs{ winrt::make_self(args) }; - _raiseFindTargetWindowRequested(nullptr, *findWindowArgs); - - const auto responseId = findWindowArgs->ResultTargetWindow(); - if (responseId > 0) - { - givenID = ::base::saturated_cast(responseId); - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ProposeCommandline_AsMonarch", - TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - else if (responseId == WindowingBehaviorUseName) - { - givenName = findWindowArgs->ResultTargetWindowName(); - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ProposeCommandline_AsMonarch", - TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - else - { - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ProposeCommandline_AsMonarch", - TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingUInt64(0, "Id", "The ID we should assign our peasant"), - TraceLoggingWideString(L"", "Name", "The name we should assign this window"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - - if (_shouldCreateWindow) - { - // If we should create a new window, then instantiate our Peasant - // instance, and tell that peasant to handle that commandline. - _createOurPeasant({ givenID }, givenName); - - // Spawn a thread to wait on the monarch, and handle the election - if (!_isKing) - { - _createPeasantThread(); - } - - // This right here will just tell us to stash the args away for the - // future. The AppHost hasn't yet set up the callbacks, and the rest - // of the app hasn't started at all. We'll note them and come back - // later. - _peasant.ExecuteCommandline(args); - } - // Otherwise, we'll do _nothing_. - } - - bool WindowManager::ShouldCreateWindow() - { - return _shouldCreateWindow; - } - - void WindowManager::_registerAsMonarch() - { - winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, - winrt::make<::MonarchFactory>().get(), - CLSCTX_LOCAL_SERVER, - REGCLS_MULTIPLEUSE, - &_registrationHostClass)); - } - - void WindowManager::_createMonarch() - { - // Heads up! This only works because we're using - // "metadata-based-marshalling" for our WinRT types. That means the OS is - // using the .winmd file we generate to figure out the proxy/stub - // definitions for our types automatically. This only works in the following - // cases: - // - // * If we're running unpackaged: the .winmd must be a sibling of the .exe - // * If we're running packaged: the .winmd must be in the package root - _monarch = create_instance(Monarch_clsid, - CLSCTX_LOCAL_SERVER); - } - - // Tries to instantiate a monarch, tries again, and eventually either throws - // (so that the caller will try again) or falls back to the isolated - // monarch. - void WindowManager::_redundantCreateMonarch() - { - _createMonarch(); - - if (_monarch == nullptr) - { - // See MSFT:38540483, GH#12774 for details. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_NullMonarchTryAgain", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // Here we're gonna just give it a quick second try.Probably not - // definitive, but might help. - _createMonarch(); - } - - if (_monarch == nullptr) - { - // See MSFT:38540483, GH#12774 for details. - if constexpr (Feature_IsolatedMonarchMode::IsEnabled()) - { - // Fall back to having a in proc monarch. Were now isolated from - // other windows. This is a pretty torn state, but at least we - // didn't just explode. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_NullMonarchIsolateMode", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - _monarch = winrt::make(); - } - else - { - // The monarch is null. We're hoping that we can find another, - // hopefully us. We're gonna go back around the loop again and - // see what happens. If this is really an infinite loop (where - // the OS won't even give us back US as the monarch), then I - // suppose we'll find out soon enough. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_NullMonarchTryAgain", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - winrt::hresult_error(E_UNEXPECTED, L"Did not expect the Monarch to ever be null"); - } - } - } - - // NOTE: This can throw! Callers include: - // - the constructor, who performs this in a loop until it successfully - // find a a monarch - // - the performElection method, which is called in the waitOnMonarch - // thread. All the calls in that thread are wrapped in try/catch's - // already. - // - _createOurPeasant, who might do this in a loop to establish us with the - // monarch. - void WindowManager::_createMonarchAndCallbacks() - { - _redundantCreateMonarch(); - // We're pretty confident that we have a Monarch here. - _createCallbacks(); - } - - // Check if we became the king, and if we are, wire up callbacks. - void WindowManager::_createCallbacks() - { - // Save the result of checking if we're the king. We want to avoid - // unnecessary calls back and forth if we can. - _isKing = _areWeTheKing(); - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ConnectedToMonarch", - TraceLoggingUInt64(_monarch.GetPID(), "monarchPID", "The PID of the new Monarch"), - TraceLoggingBoolean(_isKing, "isKing", "true if we are the new monarch"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - if (_peasant) - { - if (const auto& lastActivated{ _peasant.GetLastActivatedArgs() }) - { - // Inform the monarch of the time we were last activated - _monarch.HandleActivatePeasant(lastActivated); - } - } - - if (!_isKing) - { - return; - } - // Here, we're the king! - // - // This is where you should do any additional setup that might need to be - // done when we become the king. This will be called both for the first - // window, and when the current monarch dies. - - _monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers }); - _monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers }); - _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); - _monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); }); - _monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); }); - _monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers }); - - _BecameMonarchHandlers(*this, nullptr); - } - - bool WindowManager::_areWeTheKing() - { - const auto ourPID{ GetCurrentProcessId() }; - const auto kingPID{ _monarch.GetPID() }; - return (ourPID == kingPID); - } - - Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID, - const winrt::hstring& givenName) + Remoting::Peasant WindowManager::CreateAPeasant(Remoting::WindowRequestedArgs args) { auto p = winrt::make_self(); - if (givenID) + if (args.Id()) { - p->AssignID(givenID.value()); + p->AssignID(args.Id().Value()); } // If the name wasn't specified, this will be an empty string. - p->WindowName(givenName); - _peasant = *p; + p->WindowName(args.WindowName()); - // Try to add us to the monarch. If that fails, try to find a monarch - // again, until we find one (we will eventually find us) - while (true) - { - try - { - _monarch.AddPeasant(_peasant); - break; - } - catch (...) - { - try - { - // Wrap this in its own try/catch, because this can throw. - _createMonarchAndCallbacks(); - } - catch (...) - { - } - } - } + p->ExecuteCommandline(*winrt::make_self(args.Commandline(), args.CurrentDirectory())); + + _monarch.AddPeasant(*p); - _peasant.GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers }); + p->GetWindowLayoutRequested({ get_weak(), &WindowManager::_GetWindowLayoutRequestedHandlers }); TraceLoggingWrite(g_hRemotingProvider, "WindowManager_CreateOurPeasant", - TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"), + TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // If the peasant asks us to quit we should not try to act in future elections. - _peasant.QuitRequested([weakThis{ get_weak() }](auto&&, auto&&) { - if (auto wm = weakThis.get()) - { - wm->_monarchWaitInterrupt.SetEvent(); - } - }); - - return _peasant; - } - - // Method Description: - // - Attempt to connect to the monarch process. This might be us! - // - For the new monarch, add us to their list of peasants. - // Arguments: - // - - // Return Value: - // - true iff we're the new monarch process. - // NOTE: This can throw! - bool WindowManager::_performElection() - { - _createMonarchAndCallbacks(); - - // Tell the new monarch who we are. We might be that monarch! - _monarch.AddPeasant(_peasant); - - // This method is only called when a _new_ monarch is elected. So - // don't do anything here that needs to be done for all monarch - // windows. This should only be for work that's done when a window - // _becomes_ a monarch, after the death of the previous monarch. - return _isKing; + return *p; } - void WindowManager::_createPeasantThread() + void WindowManager::SignalClose(Remoting::Peasant peasant) { - // If we catch an exception trying to get at the monarch ever, we can - // set the _monarchWaitInterrupt, and use that to trigger a new - // election. Though, we wouldn't be able to retry the function that - // caused the exception in the first place... - - _electionThread = std::thread([this] { - _waitOnMonarchThread(); - }); - } - - void WindowManager::_waitOnMonarchThread() - { - // This is the array of HANDLEs that we're going to wait on in - // WaitForMultipleObjects below. - // * waits[0] will be the handle to the monarch process. It gets - // signalled when the process exits / dies. - // * waits[1] is the handle to our _monarchWaitInterrupt event. Another - // thread can use that to manually break this loop. We'll do that when - // we're getting torn down. - HANDLE waits[2]; - waits[1] = _monarchWaitInterrupt.get(); - const auto peasantID = _peasant.GetID(); // safe: _peasant is in-proc. - - auto exitThreadRequested = false; - while (!exitThreadRequested) + if (_monarch) { - // At any point in all this, the current monarch might die. If it - // does, we'll go straight to a new election, in the "jail" - // try/catch below. Worst case, eventually, we'll become the new - // monarch. try { - // This might fail to even ask the monarch for its PID. - wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, - FALSE, - static_cast(_monarch.GetPID())) }; - - // If we fail to open the monarch, then they don't exist - // anymore! Go straight to an election. - if (hMonarch.get() == nullptr) - { - const auto gle = GetLastError(); - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_FailedToOpenMonarch", - TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), - TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - exitThreadRequested = _performElection(); - continue; - } - - waits[0] = hMonarch.get(); - auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); - - switch (waitResult) - { - case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the monarch process - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_MonarchDied", - TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // Connect to the new monarch, which might be us! - // If we become the monarch, then we'll return true and exit this thread. - exitThreadRequested = _performElection(); - break; - - case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_MonarchWaitInterrupted", - TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - exitThreadRequested = true; - break; - - case WAIT_TIMEOUT: - // This should be impossible. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_MonarchWaitTimeout", - TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - exitThreadRequested = true; - break; - - default: - { - // Returning any other value is invalid. Just die. - const auto gle = GetLastError(); - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_WaitFailed", - TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), - TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - ExitProcess(0); - } - } - } - catch (...) - { - // Theoretically, if window[1] dies when we're trying to get - // its PID we'll get here. If we just try to do the election - // once here, it's possible we might elect window[2], but have - // it die before we add ourselves as a peasant. That - // _performElection call will throw, and we wouldn't catch it - // here, and we'd die. - - // Instead, we're going to have a resilient election process. - // We're going to keep trying an election, until one _doesn't_ - // throw an exception. That might mean burning through all the - // other dying monarchs until we find us as the monarch. But if - // this process is alive, then there's _someone_ in the line of - // succession. - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ExceptionInWaitThread", - TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - auto foundNewMonarch = false; - while (!foundNewMonarch) - { - try - { - exitThreadRequested = _performElection(); - // It doesn't matter if we're the monarch, or someone - // else is, but if we complete the election, then we've - // registered with a new one. We can escape this jail - // and re-enter society. - foundNewMonarch = true; - } - catch (...) - { - // If we fail to acknowledge the results of the election, - // stay in this jail until we do. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ExceptionInNestedWaitThread", - TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } + _monarch.SignalClose(peasant.GetID()); } + CATCH_LOG() } } - Remoting::Peasant WindowManager::CurrentWindow() - { - return _peasant; - } - - void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) - { - _FindTargetWindowRequestedHandlers(sender, args); - } - - bool WindowManager::IsMonarch() - { - return _isKing; - } - void WindowManager::SummonWindow(const Remoting::SummonWindowSelectionArgs& args) { // We should only ever get called when we are the monarch, because only @@ -747,10 +371,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - // Return Value: // - - winrt::fire_and_forget WindowManager::RequestShowNotificationIcon() + winrt::fire_and_forget WindowManager::RequestShowNotificationIcon(Remoting::Peasant peasant) { co_await winrt::resume_background(); - _peasant.RequestShowNotificationIcon(); + peasant.RequestShowNotificationIcon(); } // Method Description: @@ -759,11 +383,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - // Return Value: // - - winrt::fire_and_forget WindowManager::RequestHideNotificationIcon() + winrt::fire_and_forget WindowManager::RequestHideNotificationIcon(Remoting::Peasant peasant) { auto strongThis{ get_strong() }; co_await winrt::resume_background(); - _peasant.RequestHideNotificationIcon(); + peasant.RequestHideNotificationIcon(); } // Method Description: @@ -772,11 +396,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - // Return Value: // - - winrt::fire_and_forget WindowManager::RequestQuitAll() + winrt::fire_and_forget WindowManager::RequestQuitAll(Remoting::Peasant peasant) { auto strongThis{ get_strong() }; co_await winrt::resume_background(); - _peasant.RequestQuitAll(); + peasant.RequestQuitAll(); } bool WindowManager::DoesQuakeWindowExist() @@ -784,9 +408,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _monarch.DoesQuakeWindowExist(); } - void WindowManager::UpdateActiveTabTitle(winrt::hstring title) + void WindowManager::UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant) { - winrt::get_self(_peasant)->ActiveTabTitle(title); + winrt::get_self(peasant)->ActiveTabTitle(title); } Windows::Foundation::Collections::IVector WindowManager::GetAllWindowLayouts() diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 96c60710d79..531b7a6f18a 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -1,23 +1,3 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- WindowManager.h - -Abstract: -- The Window Manager takes care of coordinating the monarch and peasant for this - process. -- It's responsible for registering as a potential future monarch. It's also - responsible for creating the Peasant for this process when it's determined - this process should become a window process. -- If we aren't the monarch, it's responsible for watching the current monarch - process, and finding the new one if the current monarch dies. -- When the monarch needs to ask the TerminalApp about how to parse a - commandline, it'll ask by raising an event that we'll bubble up to the - AppHost. - ---*/ #pragma once @@ -29,30 +9,26 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { struct WindowManager : public WindowManagerT { + public: WindowManager(); ~WindowManager(); + winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline2(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + Remoting::Peasant CreateAPeasant(Remoting::WindowRequestedArgs args); - void ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - bool ShouldCreateWindow(); - - winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); - bool IsMonarch(); + void SignalClose(Remoting::Peasant peasant); void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); - void SignalClose(); - void SummonAllWindows(); - uint64_t GetNumberOfPeasants(); Windows::Foundation::Collections::IVectorView GetPeasantInfos(); - - winrt::fire_and_forget RequestShowNotificationIcon(); - winrt::fire_and_forget RequestHideNotificationIcon(); - winrt::fire_and_forget RequestQuitAll(); - bool DoesQuakeWindowExist(); - void UpdateActiveTabTitle(winrt::hstring title); + uint64_t GetNumberOfPeasants(); + winrt::fire_and_forget RequestShowNotificationIcon(Remoting::Peasant peasant); + winrt::fire_and_forget RequestHideNotificationIcon(Remoting::Peasant peasant); + winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); + void UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); + bool DoesQuakeWindowExist(); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); - TYPED_EVENT(BecameMonarch, 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(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); @@ -60,34 +36,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation 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: - bool _shouldCreateWindow{ false }; - bool _isKing{ false }; DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr }; - winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; - wil::unique_event _monarchWaitInterrupt; - std::thread _electionThread; - - void _registerAsMonarch(); void _createMonarch(); - void _redundantCreateMonarch(); - void _createMonarchAndCallbacks(); - void _createCallbacks(); - bool _areWeTheKing(); - winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional givenID, - const winrt::hstring& givenName); - - bool _performElection(); - void _createPeasantThread(); - void _waitOnMonarchThread(); - void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + void _registerAsMonarch(); - void _proposeToMonarch(const Remoting::CommandlineArgs& args, + bool _proposeToMonarch(const Remoting::CommandlineArgs& args, std::optional& givenID, winrt::hstring& givenName); + + void _createCallbacks(); + void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + void _raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); }; } diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 917347f2e9b..45f7468c166 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -7,24 +7,29 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass WindowManager { WindowManager(); - void ProposeCommandline(CommandlineArgs args); - void SignalClose(); - Boolean ShouldCreateWindow { get; }; - IPeasant CurrentWindow(); - Boolean IsMonarch { get; }; + + ProposeCommandlineResult ProposeCommandline2(CommandlineArgs args); + Peasant CreateAPeasant(WindowRequestedArgs args); + + void SignalClose(Peasant p); + void RequestShowNotificationIcon(Peasant p); + void RequestHideNotificationIcon(Peasant p); + void UpdateActiveTabTitle(String title, Peasant p); + void RequestQuitAll(Peasant p); + void SummonWindow(SummonWindowSelectionArgs args); void SummonAllWindows(); - void RequestShowNotificationIcon(); - void RequestHideNotificationIcon(); + Windows.Foundation.Collections.IVector GetAllWindowLayouts(); + Windows.Foundation.Collections.IVectorView GetPeasantInfos(); UInt64 GetNumberOfPeasants(); - void RequestQuitAll(); - void UpdateActiveTabTitle(String title); + + Boolean DoesQuakeWindowExist(); - Windows.Foundation.Collections.IVectorView GetPeasantInfos(); + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; - event Windows.Foundation.TypedEventHandler BecameMonarch; + event Windows.Foundation.TypedEventHandler WindowCreated; event Windows.Foundation.TypedEventHandler WindowClosed; event Windows.Foundation.TypedEventHandler QuitAllRequested; @@ -32,5 +37,8 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; + + event Windows.Foundation.TypedEventHandler RequestNewWindow; + }; } diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp deleted file mode 100644 index 402f6ca2c49..00000000000 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" - -#include "WindowManager2.h" - -#include "../inc/WindowingBehavior.h" -#include "MonarchFactory.h" - -#include "CommandlineArgs.h" -#include "FindTargetWindowArgs.h" -#include "ProposeCommandlineResult.h" - -#include "WindowManager2.g.cpp" -#include "../../types/inc/utils.hpp" - -using namespace winrt; -using namespace winrt::Microsoft::Terminal; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - WindowManager2::WindowManager2() - { - } - WindowManager2::~WindowManager2() - { - // IMPORTANT! Tear down the registration as soon as we exit. If we're not a - // real peasant window (the monarch passed our commandline to someone else), - // then the monarch dies, we don't want our registration becoming the active - // monarch! - CoRevokeClassObject(_registrationHostClass); - _registrationHostClass = 0; - } - - void WindowManager2::_createMonarch() - { - // Heads up! This only works because we're using - // "metadata-based-marshalling" for our WinRT types. That means the OS is - // using the .winmd file we generate to figure out the proxy/stub - // definitions for our types automatically. This only works in the following - // cases: - // - // * If we're running unpackaged: the .winmd must be a sibling of the .exe - // * If we're running packaged: the .winmd must be in the package root - _monarch = try_create_instance(Monarch_clsid, - CLSCTX_LOCAL_SERVER); - } - - // Check if we became the king, and if we are, wire up callbacks. - void WindowManager2::_createCallbacks() - { - assert(_monarch); - // Here, we're the king! - // - // This is where you should do any additional setup that might need to be - // done when we become the king. This will be called both for the first - // window, and when the current monarch dies. - - _monarch.WindowCreated({ get_weak(), &WindowManager2::_WindowCreatedHandlers }); - _monarch.WindowClosed({ get_weak(), &WindowManager2::_WindowClosedHandlers }); - _monarch.FindTargetWindowRequested({ this, &WindowManager2::_raiseFindTargetWindowRequested }); - _monarch.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); }); - _monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); }); - _monarch.QuitAllRequested({ get_weak(), &WindowManager2::_QuitAllRequestedHandlers }); - - _monarch.RequestNewWindow({ get_weak(), &WindowManager2::_raiseRequestNewWindow }); - } - - void WindowManager2::_registerAsMonarch() - { - winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, - winrt::make<::MonarchFactory>().get(), - CLSCTX_LOCAL_SERVER, - REGCLS_MULTIPLEUSE, - &_registrationHostClass)); - } - - void WindowManager2::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) - { - _FindTargetWindowRequestedHandlers(sender, args); - } - void WindowManager2::_raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args) - { - _RequestNewWindowHandlers(sender, args); - } - - Remoting::ProposeCommandlineResult WindowManager2::ProposeCommandline2(const Remoting::CommandlineArgs& args) - { - bool shouldCreateWindow = false; - - _createMonarch(); - if (_monarch) - { - // We connectecd to a monarch instance, not us though. - - shouldCreateWindow = false; - std::optional givenID; - winrt::hstring givenName{}; - - // Send the commandline over to the monarch process - if (_proposeToMonarch(args, givenID, givenName)) - { - // If that succeeded, then we don't need to make a new window. - // Our job is done. Either the monarch is going to run the - // commandline in an existing window, or a new one, but either way, - // this process doesn't need to make a new window. - - return *winrt::make_self(shouldCreateWindow); - } - // Otherwise, we'll try to handle this ourselves. - } - - // Theoretically, this condition is always true here: - // - // if (_monarch == nullptr) - // - // If we do still have a _monarch at this point, then we must have - // successfully proposed to it in _proposeToMonarch, so we can't get - // here with a monarch. - { - // No pre-existing instance. - - // Raise an event, to ask how to handle this commandline. We can't ask - // the app ourselves - we exist isolated from that knowledge (and - // dependency hell). The WindowManager will raise this up to the app - // host, which will then ask the AppLogic, who will then parse the - // commandline and determine the provided ID of the window. - auto findWindowArgs{ winrt::make_self(args) }; - - // This is handled by some handler in-proc - _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); - - // After the event was handled, ResultTargetWindow() will be filled with - // the parsed result. - const auto targetWindow = findWindowArgs->ResultTargetWindow(); - const auto targetWindowName = findWindowArgs->ResultTargetWindowName(); - - if (targetWindow == WindowingBehaviorUseNone) - { - // This commandline doesn't deserve a window. Don't make a monarch - // either. - shouldCreateWindow = false; - - return *winrt::make_self(shouldCreateWindow); - } - else - { - // This commandline _does_ want a window, which means we do want - // to create a window, and a monarch. - // - // Congrats! This is now THE PROCESS. It's the only one that's - // getting any windows. - _registerAsMonarch(); - _createMonarch(); - if (!_monarch) - { - // Something catastrophically bad happened here. But we - // don't want to just exit immediately. No, instead, we'll - // just instantiate a local Monarch instance. We're firmly - // in the realm of undefined behavior, but better to have - // some window than not. - _monarch = winrt::make(); - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_FaileToCoCreate", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - _createCallbacks(); - - // So, we wanted a new peasant. Cool! - // - // We need to fill in args.ResultTargetWindow, - // args.ResultTargetWindowName so that we can create the new - // window with those values. Otherwise, the very first window - // won't obey the given name / ID. - // - // So let's just ask the monarch (ourselves) to get those values. - return _monarch.ProposeCommandline(args); - } - } - } - - // Method Description: - // - Helper attempting to call to the monarch multiple times. If the monarch - // fails to respond, or we encounter any sort of error, we'll try again - // until we find one, or decisively determine there isn't one. - bool WindowManager2::_proposeToMonarch(const Remoting::CommandlineArgs& args, - std::optional& /*givenID*/, - winrt::hstring& /*givenName*/) - { - // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below. - static constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); - static constexpr auto RPC_CALL_FAILED_HR = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED); - - // The monarch may respond back "you should be a new - // window, with ID,name of (id, name)". Really the responses are: - // * You should not create a new window - // * Create a new window (but without a given ID or name). The - // Monarch will assign your ID/name later - // * Create a new window, and you'll have this ID or name - // - This is the case where the user provides `wt -w 1`, and - // there's no existing window 1 - - // You can emulate the monarch dying by: starting a terminal, sticking a - // breakpoint in - // TerminalApp!winrt::TerminalApp::implementation::AppLogic::_doFindTargetWindow, - // starting a defterm, and when that BP gets hit, kill the original - // monarch, and see what happens here. - - auto proposedCommandline = false; - Remoting::ProposeCommandlineResult result{ nullptr }; - auto attempts = 0; - while (!proposedCommandline) - { - try - { - // MSFT:38542548 _We believe_ that this is the source of the - // crash here. After we get the result, stash its values into a - // local copy, so that we can check them later. If the Monarch - // dies between now and the inspection of - // `result.ShouldCreateWindow` below, we don't want to explode - // (since _proposeToMonarch is not try/caught). - - _monarch.ProposeCommandline(args); - return true; - } - catch (...) - { - // We did not successfully ask the king what to do. This could - // be for many reasons. Most commonly, the monarch died as we - // were talking to it. That could be a RPC_SERVER_UNAVAILABLE_HR - // or RPC_CALL_FAILED_HR (GH#12666). We also saw a - // RPC_S_CALL_FAILED_DNE in GH#11790. Ultimately, if this is - // gonna fail, we want to just try again, regardless of the - // cause. That's why we're no longer checking what the exception - // was, we're just always gonna try again regardless. - // - // They hopefully just died here. That's okay, let's just go - // ask the next in the line of succession. At the very worst, - // we'll find _us_, (likely last in the line). - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_proposeToMonarch_unexpectedExceptionFromKing", - TraceLoggingInt32(attempts, "attempts", "How many times we've tried"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - LOG_CAUGHT_EXCEPTION(); - attempts++; - - if (attempts >= 10) - { - // We've tried 10 times to find the monarch, failing each - // time. Since we have no idea why, we're guessing that in - // this case, there's just a Monarch registered that's - // misbehaving. In this case, just fall back to - // "IsolatedMonarchMode" - we can't trust the currently - // registered one. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_TooManyAttempts_NullMonarchIsolateMode", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // Set the monarch to null, so that we'll create a new one - // (or just generally check if we need to even make a window - // for this commandline.) - _monarch = nullptr; - return false; - } - else - { - // We failed to ask the monarch. It must have died. Try and - // find another monarch. - _createMonarch(); - if (!_monarch) - { - // We failed to create a monarch. That means there - // aren't any other windows, and we can become the monarch. - return false; - } - // Go back around the loop. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_proposeToMonarch_tryAgain", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - } - - // I don't think we can ever get here, but the compiler doesn't know - return false; - } - - Remoting::Peasant WindowManager2::CreateAPeasant(Remoting::WindowRequestedArgs args) - { - auto p = winrt::make_self(); - if (args.Id()) - { - p->AssignID(args.Id().Value()); - } - - // If the name wasn't specified, this will be an empty string. - p->WindowName(args.WindowName()); - - p->ExecuteCommandline(*winrt::make_self(args.Commandline(), args.CurrentDirectory())); - - _monarch.AddPeasant(*p); - - p->GetWindowLayoutRequested({ get_weak(), &WindowManager2::_GetWindowLayoutRequestedHandlers }); - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_CreateOurPeasant", - TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - return *p; - } - - void WindowManager2::SignalClose(Remoting::Peasant peasant) - { - if (_monarch) - { - try - { - _monarch.SignalClose(peasant.GetID()); - } - CATCH_LOG() - } - } - - void WindowManager2::SummonWindow(const Remoting::SummonWindowSelectionArgs& args) - { - // We should only ever get called when we are the monarch, because only - // the monarch ever registers for the global hotkey. So the monarch is - // the only window that will be calling this. - _monarch.SummonWindow(args); - } - - void WindowManager2::SummonAllWindows() - { - _monarch.SummonAllWindows(); - } - - Windows::Foundation::Collections::IVectorView WindowManager2::GetPeasantInfos() - { - // We should only get called when we're the monarch since the monarch - // is the only one that knows about all peasants. - return _monarch.GetPeasantInfos(); - } - - uint64_t WindowManager2::GetNumberOfPeasants() - { - if (_monarch) - { - try - { - return _monarch.GetNumberOfPeasants(); - } - CATCH_LOG() - } - return 0; - } - - // Method Description: - // - Ask the monarch to show a notification icon. - // Arguments: - // - - // Return Value: - // - - winrt::fire_and_forget WindowManager2::RequestShowNotificationIcon(Remoting::Peasant peasant) - { - co_await winrt::resume_background(); - peasant.RequestShowNotificationIcon(); - } - - // Method Description: - // - Ask the monarch to hide its notification icon. - // Arguments: - // - - // Return Value: - // - - winrt::fire_and_forget WindowManager2::RequestHideNotificationIcon(Remoting::Peasant peasant) - { - auto strongThis{ get_strong() }; - co_await winrt::resume_background(); - peasant.RequestHideNotificationIcon(); - } - - // Method Description: - // - Ask the monarch to quit all windows. - // Arguments: - // - - // Return Value: - // - - winrt::fire_and_forget WindowManager2::RequestQuitAll(Remoting::Peasant peasant) - { - auto strongThis{ get_strong() }; - co_await winrt::resume_background(); - peasant.RequestQuitAll(); - } - - bool WindowManager2::DoesQuakeWindowExist() - { - return _monarch.DoesQuakeWindowExist(); - } - - void WindowManager2::UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant) - { - winrt::get_self(peasant)->ActiveTabTitle(title); - } - - Windows::Foundation::Collections::IVector WindowManager2::GetAllWindowLayouts() - { - if (_monarch) - { - try - { - return _monarch.GetAllWindowLayouts(); - } - CATCH_LOG() - } - return nullptr; - } -} diff --git a/src/cascadia/Remoting/WindowManager2.h b/src/cascadia/Remoting/WindowManager2.h deleted file mode 100644 index 7bb8f755b05..00000000000 --- a/src/cascadia/Remoting/WindowManager2.h +++ /dev/null @@ -1,63 +0,0 @@ - -#pragma once - -#include "WindowManager2.g.h" -#include "Peasant.h" -#include "Monarch.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct WindowManager2 : public WindowManager2T - { - public: - WindowManager2(); - ~WindowManager2(); - winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline2(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - Remoting::Peasant CreateAPeasant(Remoting::WindowRequestedArgs args); - - void SignalClose(Remoting::Peasant peasant); - void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); - void SummonAllWindows(); - Windows::Foundation::Collections::IVectorView GetPeasantInfos(); - uint64_t GetNumberOfPeasants(); - winrt::fire_and_forget RequestShowNotificationIcon(Remoting::Peasant peasant); - winrt::fire_and_forget RequestHideNotificationIcon(Remoting::Peasant peasant); - winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); - void UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant); - Windows::Foundation::Collections::IVector GetAllWindowLayouts(); - bool DoesQuakeWindowExist(); - - TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); - - TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(HideNotificationIconRequested, 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: - DWORD _registrationHostClass{ 0 }; - winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr }; - - void _createMonarch(); - void _registerAsMonarch(); - - bool _proposeToMonarch(const Remoting::CommandlineArgs& args, - std::optional& givenID, - winrt::hstring& givenName); - - void _createCallbacks(); - void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); - void _raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(WindowManager2); -} diff --git a/src/cascadia/Remoting/WindowManager2.idl b/src/cascadia/Remoting/WindowManager2.idl deleted file mode 100644 index b111b06294d..00000000000 --- a/src/cascadia/Remoting/WindowManager2.idl +++ /dev/null @@ -1,44 +0,0 @@ -import "Peasant.idl"; -import "Monarch.idl"; - - -namespace Microsoft.Terminal.Remoting -{ - [default_interface] runtimeclass WindowManager2 - { - WindowManager2(); - - ProposeCommandlineResult ProposeCommandline2(CommandlineArgs args); - Peasant CreateAPeasant(WindowRequestedArgs args); - - void SignalClose(Peasant p); - void RequestShowNotificationIcon(Peasant p); - void RequestHideNotificationIcon(Peasant p); - void UpdateActiveTabTitle(String title, Peasant p); - void RequestQuitAll(Peasant p); - - void SummonWindow(SummonWindowSelectionArgs args); - void SummonAllWindows(); - - Windows.Foundation.Collections.IVector GetAllWindowLayouts(); - Windows.Foundation.Collections.IVectorView GetPeasantInfos(); - - UInt64 GetNumberOfPeasants(); - - - Boolean DoesQuakeWindowExist(); - - event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; - - event Windows.Foundation.TypedEventHandler WindowCreated; - event Windows.Foundation.TypedEventHandler WindowClosed; - event Windows.Foundation.TypedEventHandler QuitAllRequested; - event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; - event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; - event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; - - - event Windows.Foundation.TypedEventHandler RequestNewWindow; - - }; -} diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6ba6b8c3bfe..49fb2180b55 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -30,9 +30,9 @@ static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - const Remoting::WindowManager2& manager, + const Remoting::WindowManager& manager, const Remoting::Peasant& peasant) noexcept : - _windowManager2{ manager }, + _windowManager{ manager }, _peasant{ peasant }, _appLogic{ logic }, // don't make one, we're going to take a ref on app's _windowLogic{ nullptr }, @@ -89,8 +89,8 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, _window->MakeWindow(); - _GetWindowLayoutRequestedToken = _windowManager2.GetWindowLayoutRequested([this](auto&&, - const Remoting::GetWindowLayoutArgs& args) { + _GetWindowLayoutRequestedToken = _windowManager.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()); @@ -184,7 +184,7 @@ void AppHost::_HandleCommandlineArgs() const auto message = _windowLogic.ParseCommandlineMessage(); if (!message.empty()) { - AppHost::s_DisplayMessageBox({ message, result}); + AppHost::s_DisplayMessageBox({ message, result }); if (_windowLogic.ShouldExitEarly()) { @@ -223,7 +223,7 @@ void AppHost::_HandleCommandlineArgs() // much, that there was no reasonable way of moving this. Moving it also // seemed to reorder bits of init so much that everything broke. So // we'll leave it here. - const auto numPeasants = _windowManager2.GetNumberOfPeasants(); + const auto numPeasants = _windowManager.GetNumberOfPeasants(); if (numPeasants == 1) { // TODO! this is vaugely off by one. Not sure, but if you restore 2 @@ -358,7 +358,7 @@ void AppHost::Initialize() _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. - _windowManager2.RequestQuitAll(_peasant); + _windowManager.RequestQuitAll(_peasant); }); // Load bearing: make sure the PropertyChanged handler is added before we @@ -437,7 +437,7 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* { _window->UpdateTitle(newTitle); } - _windowManager2.UpdateActiveTabTitle(newTitle, _peasant); + _windowManager.UpdateActiveTabTitle(newTitle, _peasant); } // Method Description: @@ -450,12 +450,12 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* 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. - _windowManager2.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); + _windowManager.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); // 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. - _windowManager2.SignalClose(_peasant); + _windowManager.SignalClose(_peasant); _window->Close(); } @@ -1079,7 +1079,7 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation: void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - _windowManager2.RequestQuitAll(_peasant); + _windowManager.RequestQuitAll(_peasant); } void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&, diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index dc307261cd9..1195e7a0e8b 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -9,10 +9,9 @@ class AppHost { public: - // AppHost(const winrt::TerminalApp::AppLogic& logic) noexcept; AppHost(const winrt::TerminalApp::AppLogic& logic, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - const winrt::Microsoft::Terminal::Remoting::WindowManager2& manager, + const winrt::Microsoft::Terminal::Remoting::WindowManager& manager, const winrt::Microsoft::Terminal::Remoting::Peasant& peasant) noexcept; virtual ~AppHost(); @@ -34,7 +33,7 @@ class AppHost winrt::TerminalApp::AppLogic _appLogic; winrt::TerminalApp::TerminalWindow _windowLogic; - winrt::Microsoft::Terminal::Remoting::WindowManager2 _windowManager2{ nullptr }; + winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; winrt::com_ptr _desktopManager{ nullptr }; @@ -124,8 +123,6 @@ class AppHost void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); winrt::event_token _GetWindowLayoutRequestedToken; - // winrt::event_token _WindowCreatedToken; - // winrt::event_token _WindowClosedToken; // 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 @@ -134,7 +131,6 @@ class AppHost struct Revokers { // Event handlers to revoke in ~AppHost, before calling App.Close - winrt::Microsoft::Terminal::Remoting::WindowManager::BecameMonarch_revoker BecameMonarch; winrt::Microsoft::Terminal::Remoting::Peasant::ExecuteCommandlineRequested_revoker peasantExecuteCommandlineRequested; winrt::Microsoft::Terminal::Remoting::Peasant::SummonRequested_revoker peasantSummonRequested; winrt::Microsoft::Terminal::Remoting::Peasant::DisplayWindowIdRequested_revoker peasantDisplayWindowIdRequested; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index fee566c785e..4ae6bdfe1f9 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -36,7 +36,7 @@ class WindowEmperor private: winrt::TerminalApp::App _app; winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr }; - winrt::Microsoft::Terminal::Remoting::WindowManager2 _manager; + winrt::Microsoft::Terminal::Remoting::WindowManager _manager; std::vector> _windows; std::vector _threads; @@ -77,6 +77,6 @@ class WindowEmperor { winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; - winrt::Microsoft::Terminal::Remoting::WindowManager2::QuitAllRequested_revoker QuitAllRequested; + winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; } _revokers{}; }; diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index ae9626789ca..030b1a05aba 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -6,7 +6,7 @@ WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - winrt::Microsoft::Terminal::Remoting::WindowManager2 manager, + winrt::Microsoft::Terminal::Remoting::WindowManager manager, winrt::Microsoft::Terminal::Remoting::Peasant peasant) : _peasant{ peasant }, _appLogic{ logic }, diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 581878e6f62..6719734fc6f 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -9,7 +9,7 @@ class WindowThread public: WindowThread(const winrt::TerminalApp::AppLogic& logic, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - winrt::Microsoft::Terminal::Remoting::WindowManager2 manager, + winrt::Microsoft::Terminal::Remoting::WindowManager manager, winrt::Microsoft::Terminal::Remoting::Peasant peasant); int WindowProc(); @@ -25,7 +25,7 @@ class WindowThread winrt::TerminalApp::AppLogic _appLogic{ nullptr }; winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs _args{ nullptr }; - winrt::Microsoft::Terminal::Remoting::WindowManager2 _manager{ nullptr }; + winrt::Microsoft::Terminal::Remoting::WindowManager _manager{ nullptr }; std::thread _thread; std::unique_ptr<::AppHost> _host{ nullptr }; From e40575b18ba848c0fa6de6499cde9a2f431bc6e7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Feb 2023 16:50:58 -0600 Subject: [PATCH 054/122] let's do it --- src/cascadia/TerminalApp/App.cpp | 21 +- src/cascadia/TerminalApp/AppLogic.cpp | 118 ++--- src/cascadia/TerminalApp/AppLogic.h | 11 +- src/cascadia/TerminalApp/AppLogic.idl | 10 +- .../TerminalApp/SettingsLoadEventArgs.h | 30 ++ src/cascadia/TerminalApp/TabManagement.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 192 ++------ src/cascadia/TerminalApp/TerminalPage.h | 26 +- src/cascadia/TerminalApp/TerminalPage.idl | 21 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 430 +++++++++++------- src/cascadia/TerminalApp/TerminalWindow.h | 55 ++- src/cascadia/TerminalApp/TerminalWindow.idl | 35 +- .../CascadiaSettingsSerialization.cpp | 6 +- .../GlobalAppSettings.cpp | 5 + .../TerminalSettingsModel/GlobalAppSettings.h | 1 + .../GlobalAppSettings.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 61 +-- src/cascadia/WindowsTerminal/AppHost.h | 6 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 8 +- 19 files changed, 549 insertions(+), 490 deletions(-) create mode 100644 src/cascadia/TerminalApp/SettingsLoadEventArgs.h diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 1a22cfc5b32..b4019d95227 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -77,24 +77,7 @@ namespace winrt::TerminalApp::implementation /// Details about the launch request and process. void App::OnLaunched(const LaunchActivatedEventArgs& /*e*/) { - // TODO! UWP mode is straight up not supported anymore, yea? - // - // if this is a UWP... it means its our problem to hook up the content to the window here. - //if (_isUwp) - //{ - // auto content = Window::Current().Content(); - // if (content == nullptr) - // { - // auto logic = Logic(); - // logic.RunAsUwp(); // Must set UWP status first, settings might change based on it. - // logic.ReloadSettings(); - // logic.Create(); - - // auto page = logic.GetRoot().as(); - - // Window::Current().Content(page); - // Window::Current().Activate(); - // } - //} + // We used to support a pure UWP version of the Terminal. This method + // was only ever used to do UWP-specific setup of our App. } } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index ad77ccdc3cf..adffee5f5c2 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -31,13 +31,20 @@ namespace winrt } //////////////////////////////////////////////////////////////////////////////// -// TODO! This section probably should be in TerminalWindow with the warnings - -// clang-format off -static const std::array settingsLoadErrorsLabels { +// Error message handling. This is in this file rather than with the warnings in +// TerminalWindow, becuase the error text might also just be a serializationgs +// error message. So AppLogic needs to know the actual text of the error. + +// !!! IMPORTANT !!! +// Make sure that these keys are in the same order as the +// SettingsLoadWarnings/Errors enum is! +static const std::array settingsLoadErrorsLabels{ USES_RESOURCE(L"NoProfilesText"), USES_RESOURCE(L"AllProfilesHiddenText") }; + +static_assert(settingsLoadErrorsLabels.size() == static_cast(SettingsLoadErrors::ERRORS_SIZE)); + template winrt::hstring _GetMessageText(uint32_t index, const T& keys) { @@ -187,7 +194,9 @@ namespace winrt::TerminalApp::implementation _settings.GlobalSettings().ShowTabsInTitlebar(false); } - // TODO! These used to be in `_root->Initialized`. Where do they belong now? + // These used to be in `TerminalPage::Initialized`, so that they started + // _after_ the Terminal window was started and displayed. These could + // theoretically move there again too. { // Both LoadSettings and ReloadSettings are supposed to call this function, // but LoadSettings skips it, so that the UI starts up faster. @@ -256,10 +265,10 @@ namespace winrt::TerminalApp::implementation return E_INVALIDARG; } - _warnings.clear(); + _warnings.Clear(); for (uint32_t i = 0; i < newSettings.Warnings().Size(); i++) { - _warnings.push_back(newSettings.Warnings().GetAt(i)); + _warnings.Append(newSettings.Warnings().GetAt(i)); } _hasSettingsStartupActions = false; @@ -279,12 +288,13 @@ namespace winrt::TerminalApp::implementation } else { - _warnings.push_back(SettingsLoadWarnings::FailedToParseStartupActions); + _warnings.Append(SettingsLoadWarnings::FailedToParseStartupActions); } } _settings = std::move(newSettings); - hr = _warnings.empty() ? S_OK : S_FALSE; + + hr = (_warnings.Size()) == 0 ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) { @@ -478,14 +488,13 @@ namespace winrt::TerminalApp::implementation } else { - // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} - // - // _SettingsChangedHandlers(*this, make_self( _settingsLoadedResult, warnings, _settingsLoadExceptionText, settings})) - - // const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); - // const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); - // return; + auto ev = winrt::make_self(true, + static_cast(_settingsLoadedResult), + _settingsLoadExceptionText, + _warnings, + _settings); + _SettingsChangedHandlers(*this, *ev); + return; } } @@ -496,11 +505,6 @@ namespace winrt::TerminalApp::implementation return; } - // if (_settingsLoadedResult == S_FALSE) - // { - // _ShowLoadWarningsDialog(); - // } - // Here, we successfully reloaded the settings, and created a new // TerminalSettings object. @@ -508,7 +512,12 @@ namespace winrt::TerminalApp::implementation _ApplyStartupTaskStateChange(); _ProcessLazySettingsChanges(); - _SettingsChangedHandlers(*this, _settings); + auto ev = winrt::make_self(!initialLoad, + _settingsLoadedResult, + _settingsLoadExceptionText, + _warnings, + _settings); + _SettingsChangedHandlers(*this, *ev); } // Method Description: @@ -518,38 +527,6 @@ namespace winrt::TerminalApp::implementation return _settings; } - // Method Description: - // - Triggers the setup of the listener for incoming console connections - // from the operating system. - // Arguments: - // - - // Return Value: - // - - void AppLogic::SetInboundListener() - { - // TODO! - // _root->SetInboundListener(false); - } - - bool AppLogic::ShouldImmediatelyHandoffToElevated() - { - // TODO! Merge that code in here. - // * Probably need to pass in the startupActions that the first window is being started with - // * Or like, a reference to the first TerminalWindow object, or something - - // return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; - return false; - } - void AppLogic::HandoffToElevated() - { - // TODO! Merge that code in here. - - // if (_root) - // { - // _root->HandoffToElevated(_settings); - // } - } - // Method Description: // - Parse the given commandline args in an attempt to find the specified // window. The rest of the args are ignored for now (they'll be handled @@ -687,7 +664,15 @@ namespace winrt::TerminalApp::implementation { ReloadSettings(); } - auto window = winrt::make_self(_settings); + + auto ev = winrt::make_self(false, + _settingsLoadedResult, + _settingsLoadExceptionText, + _warnings, + _settings); + + auto window = winrt::make_self(*ev); + this->SettingsChanged({ window->get_weak(), &implementation::TerminalWindow::UpdateSettingsHandler }); if (_hasSettingsStartupActions) { @@ -695,4 +680,25 @@ namespace winrt::TerminalApp::implementation } return *window; } + + bool AppLogic::ShouldUsePersistedLayout() const + { + 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))); + } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index ffe744b8978..759cfb09500 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -54,13 +54,12 @@ namespace winrt::TerminalApp::implementation bool HasSettingsStartupActions() const noexcept; + bool ShouldUsePersistedLayout() const; + void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); + [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; TerminalApp::FindTargetWindowResult FindTargetWindow(array_view actions); - bool ShouldImmediatelyHandoffToElevated(); - void HandoffToElevated(); - - void SetInboundListener(); Windows::Foundation::Collections::IMapView GlobalHotkeys(); @@ -68,7 +67,7 @@ namespace winrt::TerminalApp::implementation TerminalApp::TerminalWindow CreateNewWindow(); - TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); private: bool _isUwp{ false }; @@ -86,7 +85,7 @@ namespace winrt::TerminalApp::implementation std::shared_ptr> _reloadSettings; til::throttled_func_trailing<> _reloadState; - std::vector _warnings; + winrt::Windows::Foundation::Collections::IVector _warnings{ winrt::multi_threaded_vector() }; // These fields invoke _reloadSettings and must be destroyed before _reloadSettings. // (C++ destroys members in reverse-declaration-order.) diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 7946403aeed..a5db001de67 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -29,9 +29,10 @@ namespace TerminalApp Boolean HasSettingsStartupActions(); - void ReloadSettings(); + Boolean ShouldUsePersistedLayout(); + void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); - void SetInboundListener(); + void ReloadSettings(); Microsoft.Terminal.Settings.Model.Theme Theme { get; }; @@ -39,12 +40,9 @@ namespace TerminalApp TerminalWindow CreateNewWindow(); - Boolean ShouldImmediatelyHandoffToElevated(); - void HandoffToElevated(); - Windows.Foundation.Collections.IMapView GlobalHotkeys(); - event Windows.Foundation.TypedEventHandler SettingsChanged; + event Windows.Foundation.TypedEventHandler SettingsChanged; } } diff --git a/src/cascadia/TerminalApp/SettingsLoadEventArgs.h b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h new file mode 100644 index 00000000000..e31dd5e2a91 --- /dev/null +++ b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "SettingsLoadEventArgs.g.h" +#include +namespace winrt::TerminalApp::implementation +{ + struct SettingsLoadEventArgs : SettingsLoadEventArgsT + { + WINRT_PROPERTY(bool, Reload, false); + WINRT_PROPERTY(uint64_t, Result, S_OK); + WINRT_PROPERTY(winrt::hstring, ExceptionText, L""); + WINRT_PROPERTY(winrt::Windows::Foundation::Collections::IVector, Warnings, nullptr); + WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::CascadiaSettings, NewSettings, nullptr); + + public: + SettingsLoadEventArgs(bool reload, + uint64_t result, + const winrt::hstring& exceptionText, + const winrt::Windows::Foundation::Collections::IVector& warnings, + const Microsoft::Terminal::Settings::Model::CascadiaSettings& newSettings) : + _Reload{ reload }, + _Result{ result }, + _ExceptionText{ exceptionText }, + _Warnings{ warnings }, + _NewSettings{ newSettings } {}; + }; +} diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index c417af3c4e2..f1134efbd36 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -523,7 +523,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. - if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + if (!_maintainStateOnTabClose && _settings.GlobalSettings().ShouldUsePersistedLayout() && _numOpenWindows == 1) { auto state = ApplicationState::SharedInstance(); state.PersistedWindowLayouts(nullptr); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 64ae2fbc858..932ad147dbc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -321,18 +321,6 @@ namespace winrt::TerminalApp::implementation ShowSetAsDefaultInfoBar(); } - // Method Description; - // - Checks if the current terminal window should load or save its layout information. - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - true if the ApplicationState should be used. - bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const - { - return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; - } - // Method Description: // - This is a bit of trickiness: If we're running unelevated, and the user // passed in only --elevate actions, the we don't _actually_ want to @@ -347,7 +335,7 @@ namespace winrt::TerminalApp::implementation // GH#12267: Don't forget about defterm handoff here. If we're being // created for embedding, then _yea_, we don't need to handoff to an // elevated window. - if (!_startupActions || IsElevated() || _shouldStartInboundListener) + if (!_startupActions || IsElevated() || _shouldStartInboundListener || _startupActions.Size() == 0) { // there aren't startup actions, or we're elevated. In that case, go for it. return false; @@ -444,32 +432,6 @@ namespace winrt::TerminalApp::implementation } } - // Method Description; - // - Checks if the current window is configured to load a particular layout - // Arguments: - // - settings: The settings to use as this may be called before the page is - // fully initialized. - // Return Value: - // - non-null if there is a particular saved layout to use - std::optional TerminalPage::LoadPersistedLayoutIdx(CascadiaSettings& settings) const - { - return ShouldUsePersistedLayout(settings) ? _loadFromPersistedLayoutIdx : std::nullopt; - } - - WindowLayout TerminalPage::LoadPersistedLayout(CascadiaSettings& settings) const - { - if (const auto idx = LoadPersistedLayoutIdx(settings)) - { - const auto i = idx.value(); - const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (layouts && layouts.Size() > i) - { - return layouts.GetAt(i); - } - } - return nullptr; - } - winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e) { Windows::Foundation::Collections::IVectorView items; @@ -553,16 +515,6 @@ namespace winrt::TerminalApp::implementation { _startupState = StartupState::InStartup; - // If we are provided with an index, the cases where we have - // commandline args and startup actions are already handled. - if (const auto layout = LoadPersistedLayout(_settings)) - { - if (layout.TabLayout().Size() > 0) - { - _startupActions = layout.TabLayout(); - } - } - ProcessStartupActions(_startupActions, true); // If we were told that the COM server needs to be started to listen for incoming @@ -1854,11 +1806,11 @@ namespace winrt::TerminalApp::implementation } // If the user set a custom name, save it - if (_WindowName != L"") + if (_WindowProperties.WindowName() != L"") { ActionAndArgs action; action.Action(ShortcutAction::RenameWindow); - RenameWindowArgs args{ _WindowName }; + RenameWindowArgs args{ _WindowProperties.WindowName() }; action.Args(args); actions.emplace_back(std::move(action)); @@ -1908,7 +1860,7 @@ namespace winrt::TerminalApp::implementation } } - if (ShouldUsePersistedLayout(_settings)) + 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 @@ -3067,7 +3019,7 @@ namespace winrt::TerminalApp::implementation auto listCopy = actions; _startupActions = winrt::single_threaded_vector(std::move(listCopy)); const auto afterSize = _startupActions.Size(); - assert(initSize == afterSize);// you donkey + assert(initSize == afterSize); // you donkey } // Routine Description: @@ -3867,105 +3819,14 @@ namespace winrt::TerminalApp::implementation } } - // WindowName is a otherwise generic WINRT_OBSERVABLE_PROPERTY, but it needs - // to raise a PropertyChanged for WindowNameForDisplay, instead of - // WindowName. - winrt::hstring TerminalPage::WindowName() const noexcept - { - return _WindowName; - } - - winrt::fire_and_forget TerminalPage::WindowName(const winrt::hstring& value) - { - const auto oldIsQuakeMode = IsQuakeWindow(); - const auto changed = _WindowName != value; - if (changed) - { - _WindowName = value; - } - auto weakThis{ get_weak() }; - // On the foreground thread, raise property changed notifications, and - // display the success toast. - co_await wil::resume_foreground(Dispatcher()); - if (auto page{ weakThis.get() }) - { - if (changed) - { - page->_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowName" }); - page->_PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); - - // DON'T display the confirmation if this is the name we were - // given on startup! - if (page->_startupState == StartupState::Initialized) - { - page->IdentifyWindow(); - - // If we're entering quake mode, or leaving it - if (IsQuakeWindow() != oldIsQuakeMode) - { - // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode - // If we're entering Quake Mode from Focus Mode, then this will do nothing - // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing - SetFocusMode(true); - _IsQuakeWindowChangedHandlers(*this, nullptr); - } - } - } - } - } - - // WindowId is a otherwise generic WINRT_OBSERVABLE_PROPERTY, but it needs - // to raise a PropertyChanged for WindowIdForDisplay, instead of - // WindowId. - uint64_t TerminalPage::WindowId() const noexcept - { - return _WindowId; - } - void TerminalPage::WindowId(const uint64_t& value) - { - if (_WindowId != value) - { - _WindowId = value; - _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); - } - } - - void TerminalPage::SetPersistedLayoutIdx(const uint32_t idx) - { - _loadFromPersistedLayoutIdx = idx; - } - void TerminalPage::SetNumberOfOpenWindows(const uint64_t num) { + // This is used in TerminalPage::_RemoveTab, when we close a tab. If we + // close the last tab, and there's only one window open, then we will + // call to persist _no_ state. _numOpenWindows = num; } - // Method Description: - // - Returns a label like "Window: 1234" for the ID of this window - // Arguments: - // - - // Return Value: - // - a string for displaying the name of the window. - winrt::hstring TerminalPage::WindowIdForDisplay() const noexcept - { - return winrt::hstring{ fmt::format(L"{}: {}", - std::wstring_view(RS_(L"WindowIdLabel")), - _WindowId) }; - } - - // Method Description: - // - Returns a label like "" when the window has no name, or the name of the window. - // Arguments: - // - - // Return Value: - // - a string for displaying the name of the window. - winrt::hstring TerminalPage::WindowNameForDisplay() const noexcept - { - return _WindowName.empty() ? - winrt::hstring{ fmt::format(L"<{}>", RS_(L"UnnamedWindowName")) } : - _WindowName; - } - // Method Description: // - Called when an attempt to rename the window has failed. This will open // the toast displaying a message to the user that the attempt to rename @@ -4077,17 +3938,12 @@ namespace winrt::TerminalApp::implementation else if (key == Windows::System::VirtualKey::Escape) { // User wants to discard the changes they made - WindowRenamerTextBox().Text(WindowName()); + WindowRenamerTextBox().Text(WindowProperties().WindowName()); WindowRenamer().IsOpen(false); _renamerPressedEnter = false; } } - bool TerminalPage::IsQuakeWindow() const noexcept - { - return WindowName() == QuakeWindowName; - } - // Method Description: // - This function stops people from duplicating the base profile, because // it gets ~ ~ weird ~ ~ when they do. Remove when TODO GH#5047 is done. @@ -4463,4 +4319,34 @@ namespace winrt::TerminalApp::implementation _activated = activated; _updateThemeColors(); } + + TerminalApp::IWindowProperties TerminalPage::WindowProperties() + { + return _WindowProperties; + } + void TerminalPage::WindowProperties(const TerminalApp::IWindowProperties& props) + { + _WindowProperties = props; + } + + winrt::fire_and_forget TerminalPage::WindowNameChanged() + { + auto weakThis{ get_weak() }; + // On the foreground thread, raise property changed notifications, and + // display the success toast. + co_await wil::resume_foreground(Dispatcher()); + if (auto page{ weakThis.get() }) + { + _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowName" }); + _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); + _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); + + // DON'T display the confirmation if this is the name we were + // given on startup! + if (page->_startupState == StartupState::Initialized) + { + page->IdentifyWindow(); + } + } + } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 9c1705706a0..28b1db98c25 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -63,11 +63,8 @@ namespace winrt::TerminalApp::implementation void Create(); - bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; bool ShouldImmediatelyHandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; void HandoffToElevated(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); - std::optional LoadPersistedLayoutIdx(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; - winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const; Microsoft::Terminal::Settings::Model::WindowLayout GetWindowLayout(); winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e); @@ -117,20 +114,14 @@ namespace winrt::TerminalApp::implementation const bool initial, const winrt::hstring cwd = L""); - // Normally, WindowName and WindowId would be - // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise - // WindowNameForDisplay and WindowIdForDisplay instead - winrt::hstring WindowName() const noexcept; - winrt::fire_and_forget WindowName(const winrt::hstring& value); - uint64_t WindowId() const noexcept; - void WindowId(const uint64_t& value); + // For the sake of XAML binding: + winrt::hstring WindowName() const noexcept { return _WindowProperties.WindowName(); }; + uint64_t WindowId() const noexcept { return _WindowProperties.WindowId(); }; + winrt::hstring WindowIdForDisplay() const noexcept { return _WindowProperties.WindowIdForDisplay(); }; + winrt::hstring WindowNameForDisplay() const noexcept { return _WindowProperties.WindowNameForDisplay(); }; void SetNumberOfOpenWindows(const uint64_t value); - void SetPersistedLayoutIdx(const uint32_t value); - winrt::hstring WindowIdForDisplay() const noexcept; - winrt::hstring WindowNameForDisplay() const noexcept; - bool IsQuakeWindow() const noexcept; bool IsElevated() const noexcept; void OpenSettingsUI(); @@ -138,6 +129,10 @@ namespace winrt::TerminalApp::implementation bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); + TerminalApp::IWindowProperties WindowProperties(); + void WindowProperties(const TerminalApp::IWindowProperties& props); + winrt::fire_and_forget WindowNameChanged(); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -153,7 +148,6 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(Initialized, IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs); TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable); TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs); - TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable); TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable); TYPED_EVENT(CloseRequested, IInspectable, IInspectable); TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable); @@ -230,6 +224,8 @@ namespace winrt::TerminalApp::implementation int _renamerLayoutCount{ 0 }; bool _renamerPressedEnter{ false }; + TerminalApp::IWindowProperties _WindowProperties{ nullptr }; + winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); void _ShowAboutDialog(); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 5535c527a21..b363e183355 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -17,6 +17,15 @@ namespace TerminalApp Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); }; + interface IWindowProperties + { + String WindowName { get; }; + UInt64 WindowId { get; }; + String WindowNameForDisplay { get; }; + String WindowIdForDisplay { get; }; + Boolean IsQuakeWindow(); + }; + [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener { TerminalPage(); @@ -29,13 +38,9 @@ namespace TerminalApp Boolean Fullscreen { get; }; Boolean AlwaysOnTop { get; }; + IWindowProperties WindowProperties { get; }; void IdentifyWindow(); - String WindowName; - UInt64 WindowId; - String WindowNameForDisplay { get; }; - String WindowIdForDisplay { get; }; void RenameFailed(); - Boolean IsQuakeWindow(); // We cannot use the default XAML APIs because we want to make sure // that there's only one application-global dialog visible at a time, @@ -49,6 +54,11 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); + String WindowName { get; }; + UInt64 WindowId { get; }; + String WindowNameForDisplay { get; }; + String WindowIdForDisplay { get; }; + event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler LastTabClosed; event Windows.Foundation.TypedEventHandler SetTitleBarContent; @@ -59,7 +69,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; event Windows.Foundation.TypedEventHandler RenameWindowRequested; - event Windows.Foundation.TypedEventHandler IsQuakeWindowChanged; event Windows.Foundation.TypedEventHandler SummonWindowRequested; event Windows.Foundation.TypedEventHandler CloseRequested; event Windows.Foundation.TypedEventHandler OpenSystemMenu; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 44ddc48c6f3..75d0c4c6592 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -5,6 +5,7 @@ #include "TerminalWindow.h" #include "../inc/WindowingBehavior.h" #include "TerminalWindow.g.cpp" +#include "SettingsLoadEventArgs.g.cpp" #include #include @@ -26,14 +27,14 @@ using namespace ::TerminalApp; namespace winrt { namespace MUX = Microsoft::UI::Xaml; + namespace WUX = Windows::UI::Xaml; using IInspectable = Windows::Foundation::IInspectable; } -// clang-format off // !!! IMPORTANT !!! // Make sure that these keys are in the same order as the // SettingsLoadWarnings/Errors enum is! -static const std::array settingsLoadWarningsLabels { +static const std::array settingsLoadWarningsLabels{ USES_RESOURCE(L"MissingDefaultProfileText"), USES_RESOURCE(L"DuplicateProfileText"), USES_RESOURCE(L"UnknownColorSchemeText"), @@ -51,14 +52,9 @@ static const std::array settingsLoadWarningsLabels { USES_RESOURCE(L"UnknownTheme"), USES_RESOURCE(L"DuplicateRemainingProfilesEntry"), }; -static const std::array settingsLoadErrorsLabels { - USES_RESOURCE(L"NoProfilesText"), - USES_RESOURCE(L"AllProfilesHiddenText") -}; -// clang-format on static_assert(settingsLoadWarningsLabels.size() == static_cast(SettingsLoadWarnings::WARNINGS_SIZE)); -static_assert(settingsLoadErrorsLabels.size() == static_cast(SettingsLoadErrors::ERRORS_SIZE)); +// Errors are defined in AppLogic.cpp // Function Description: // - General-purpose helper for looking up a localized string for a @@ -94,19 +90,6 @@ static winrt::hstring _GetWarningText(SettingsLoadWarnings warning) return _GetMessageText(static_cast(warning), settingsLoadWarningsLabels); } -// // Function Description: -// // - Gets the text from our ResourceDictionary for the given -// // SettingsLoadError. If there is no such text, we'll return nullptr. -// // - The warning should have an entry in settingsLoadErrorsLabels. -// // Arguments: -// // - error: the SettingsLoadErrors value to get the localized text for. -// // Return Value: -// // - localized text for the given error -// static winrt::hstring _GetErrorText(SettingsLoadErrors error) -// { -// return _GetMessageText(static_cast(error), settingsLoadErrorsLabels); -// } - // Function Description: // - Creates a Run of text to display an error message. The text is yellow or // red for dark/light theme, respectively. @@ -134,9 +117,14 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD namespace winrt::TerminalApp::implementation { - TerminalWindow::TerminalWindow(const CascadiaSettings& settings) : - _settings{ settings } + TerminalWindow::TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult) : + _settings{ settingsLoadedResult.NewSettings() }, + _initialLoadResult{ settingsLoadedResult } { + _root = winrt::make_self(); + _root->WindowProperties(*this); + _dialog = ContentDialog{}; + // 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, // then it might look like App just failed to activate, which will @@ -147,14 +135,44 @@ namespace winrt::TerminalApp::implementation // make sure that there's a terminal page for callers of // SetTitleBarContent _isElevated = ::Microsoft::Console::Utils::IsElevated(); - _root = winrt::make_self(); } // Method Description: // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT TerminalWindow::Initialize(HWND hwnd) { - _dialog = ContentDialog{}; + // Pass commandline args into the TerminalPage. If we were supposed to + // load from a persisted layout, do that instead. + auto foundLayout = false; + if (const auto& layout = LoadPersistedLayout()) + { + if (layout.TabLayout().Size() > 0) + { + std::vector actions; + for (const auto& a : layout.TabLayout()) + { + actions.emplace_back(a); + } + _root->SetStartupActions(actions); + foundLayout = true; + } + } + if (!foundLayout) + { + _root->SetStartupActions(_appArgs.GetStartupActions()); + } + + // Check if we were started as a COM server for inbound connections of console sessions + // coming out of the operating system default application feature. If so, + // tell TerminalPage to start the listener as we have to make sure it has the chance + // to register a handler to hear about the requests first and is all ready to receive + // them before the COM server registers itself. Otherwise, the request might come + // in and be routed to an event with no handlers or a non-ready Page. + if (_appArgs.IsHandoffListener()) + { + _root->SetInboundListener(true); + } + return _root->Initialize(hwnd); } // Method Description: @@ -248,7 +266,9 @@ namespace winrt::TerminalApp::implementation _RefreshThemeRoutine(); - auto args = winrt::make_self(RS_(L"SettingsMenuItem"), SystemMenuChangeAction::Add, SystemMenuItemHandler(this, &TerminalWindow::_OpenSettingsUI)); + auto args = winrt::make_self(RS_(L"SettingsMenuItem"), + SystemMenuChangeAction::Add, + SystemMenuItemHandler(this, &TerminalWindow::_OpenSettingsUI)); _SystemMenuChangeRequestedHandlers(*this, *args); TraceLoggingWrite( @@ -313,7 +333,7 @@ namespace winrt::TerminalApp::implementation // - dialog: the dialog object that is going to show up // Return value: // - an IAsyncOperation with the dialog result - winrt::Windows::Foundation::IAsyncOperation TerminalWindow::ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog) + winrt::Windows::Foundation::IAsyncOperation TerminalWindow::ShowDialog(winrt::WUX::Controls::ContentDialog dialog) { // DON'T release this lock in a wil::scope_exit. The scope_exit will get // called when we await, which is not what we want. @@ -386,7 +406,8 @@ namespace winrt::TerminalApp::implementation // - contentKey: The key to use to lookup the content text from our resources. void TerminalWindow::_ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, - HRESULT settingsLoadedResult) + HRESULT settingsLoadedResult, + const winrt::hstring& exceptionText) { auto title = GetLibraryResourceString(titleKey); auto buttonText = RS_(L"Ok"); @@ -405,13 +426,12 @@ namespace winrt::TerminalApp::implementation if (FAILED(settingsLoadedResult)) { - // TODO! _settingsLoadExceptionText needs to get into the TerminalWindow somehow - - // if (!_settingsLoadExceptionText.empty()) - // { - // warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); - // warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - // } + if (!exceptionText.empty()) + { + warningsTextBlock.Inlines().Append(_BuildErrorRun(exceptionText, + winrt::WUX::Application::Current().as<::winrt::TerminalApp::App>().Resources())); + warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + } } // Add a note that we're using the default settings in this case. @@ -436,7 +456,7 @@ namespace winrt::TerminalApp::implementation // validating the settings. // - Only one dialog can be visible at a time. If another dialog is visible // when this is called, nothing happens. See ShowDialog for details - void TerminalWindow::_ShowLoadWarningsDialog() + void TerminalWindow::_ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector& warnings) { auto title = RS_(L"SettingsValidateErrorTitle"); auto buttonText = RS_(L"Ok"); @@ -447,18 +467,16 @@ namespace winrt::TerminalApp::implementation // Make sure the lines of text wrap warningsTextBlock.TextWrapping(TextWrapping::Wrap); - // TODO! warnings need to get into here somehow - // - // for (const auto& warning : _warnings) - // { - // // Try looking up the warning message key for each warning. - // const auto warningText = _GetWarningText(warning); - // if (!warningText.empty()) - // { - // warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Resources())); - // warningsTextBlock.Inlines().Append(Documents::LineBreak{}); - // } - // } + for (const auto& warning : warnings) + { + // Try looking up the warning message key for each warning. + const auto warningText = _GetWarningText(warning); + if (!warningText.empty()) + { + warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, winrt::WUX::Application::Current().as<::winrt::TerminalApp::App>().Resources())); + warningsTextBlock.Inlines().Append(Documents::LineBreak{}); + } + } Controls::ContentDialog dialog; dialog.Title(winrt::box_value(title)); @@ -489,18 +507,17 @@ namespace winrt::TerminalApp::implementation } } - // TODO! Yea, more of "how do we get _settingsLoadedResult / warnings / error" into here? - // - // if (FAILED(_settingsLoadedResult)) - // { - // const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle"); - // const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText"); - // _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); - // } - // else if (_settingsLoadedResult == S_FALSE) - // { - // _ShowLoadWarningsDialog(); - // } + const auto& settingsLoadedResult = gsl::narrow_cast(_initialLoadResult.Result()); + if (FAILED(settingsLoadedResult)) + { + const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"InitialJsonParseErrorText"); + _ShowLoadErrorsDialog(titleKey, textKey, settingsLoadedResult, _initialLoadResult.ExceptionText()); + } + else if (settingsLoadedResult == S_FALSE) + { + _ShowLoadWarningsDialog(_initialLoadResult.Warnings()); + } } // Method Description: @@ -566,7 +583,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Size proposedSize{}; const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = LoadPersistedLayout()) { if (layout.InitialSize()) { @@ -596,32 +613,34 @@ namespace winrt::TerminalApp::implementation // the height of the tab bar here. if (_settings.GlobalSettings().ShowTabsInTitlebar()) { - // If we're showing the tabs in the titlebar, we need to use a - // TitlebarControl here to calculate how much space to reserve. + // In the past, we used to actually instantiate a TitlebarControl + // and use Measure() to determine the DesiredSize of the control, to + // reserve exactly what we'd need. // - // We'll create a fake TitlebarControl, and we'll propose an - // available size to it with Measure(). After Measure() is called, - // the TitlebarControl's DesiredSize will contain the _unscaled_ - // size that the titlebar would like to use. We'll use that as part - // of the height calculation here. - auto titlebar = TitlebarControl{ static_cast(0) }; - titlebar.Measure({ SHRT_MAX, SHRT_MAX }); - proposedSize.Height += (titlebar.DesiredSize().Height) * scale; + // We can't do that anymore, because this is now called _before_ + // we've initialized XAML for this thread. We can't start XAML till + + // we have an HWND, and we can't finish creating the window till we + // know how big it should be. + // + // Instead, we'll just hardcode how big the titlebar should be. If + + // the titlebar / tab row ever change size, these numbers will have + // to change accordingly. + + static constexpr auto titlebarHeight = 40; + proposedSize.Height += (titlebarHeight)*scale; } else if (_settings.GlobalSettings().AlwaysShowTabs()) { - // Otherwise, let's use a TabRowControl to calculate how much extra - // space we'll need. + // Same comment as above, but with a TabRowControl. // - // Similarly to above, we'll measure it with an arbitrarily large - // available space, to make sure we get all the space it wants. - auto tabControl = TabRowControl(); - tabControl.Measure({ SHRT_MAX, SHRT_MAX }); - - // For whatever reason, there's about 10px of unaccounted-for space - // in the application. I couldn't tell you where these 10px are - // coming from, but they need to be included in this math. - proposedSize.Height += (tabControl.DesiredSize().Height + 10) * scale; + // A note from before: For whatever reason, there's about 10px of + // unaccounted-for space in the application. I couldn't tell you + // where these 10px are coming from, but they need to be included in + // this math. + static constexpr auto tabRowHeight = 32; + proposedSize.Height += (tabRowHeight + 10) * scale; } return proposedSize; @@ -642,7 +661,7 @@ namespace winrt::TerminalApp::implementation // commandline, then use that to override the value from the settings. const auto valueFromSettings = _settings.GlobalSettings().LaunchMode(); const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode(); - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = LoadPersistedLayout()) { if (layout.LaunchMode()) { @@ -668,7 +687,7 @@ namespace winrt::TerminalApp::implementation { auto initialPosition{ _settings.GlobalSettings().InitialPosition() }; - if (const auto layout = _root->LoadPersistedLayout(_settings)) + if (const auto layout = LoadPersistedLayout()) { if (layout.InitialPosition()) { @@ -713,24 +732,34 @@ namespace winrt::TerminalApp::implementation _RequestedThemeChangedHandlers(*this, Theme()); } - void TerminalWindow::UpdateSettings(const HRESULT settingsLoadedResult, const CascadiaSettings& settings) + winrt::fire_and_forget TerminalWindow::UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args) { - if (FAILED(settingsLoadedResult)) + _settings = args.NewSettings(); + // Update the settings in TerminalPage + _root->SetSettings(_settings, true); + + co_await wil::resume_foreground(_root->Dispatcher()); + + // Bubble the notification up to the AppHost, now that we've updated our _settings. + _SettingsChangedHandlers(*this, args); + + if (FAILED(args.Result())) { const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, textKey, settingsLoadedResult); - return; + _ShowLoadErrorsDialog(titleKey, + textKey, + gsl::narrow_cast(args.Result()), + args.ExceptionText()); + co_return; } - else if (settingsLoadedResult == S_FALSE) + else if (args.Result() == S_FALSE) { - _ShowLoadWarningsDialog(); + _ShowLoadWarningsDialog(args.Warnings()); } - _settings = settings; - // Update the settings in TerminalPage - _root->SetSettings(_settings, true); _RefreshThemeRoutine(); } + void TerminalWindow::_OpenSettingsUI() { _root->OpenSettingsUI(); @@ -846,7 +875,7 @@ namespace winrt::TerminalApp::implementation { // If persisted layout is enabled and we are the last window closing // we should save our state. - if (_root->ShouldUsePersistedLayout(_settings) && _numOpenWindows == 1) + if (_settings.GlobalSettings().ShouldUsePersistedLayout() && _numOpenWindows == 1) { if (const auto layout = _root->GetWindowLayout()) { @@ -879,7 +908,10 @@ namespace winrt::TerminalApp::implementation } void TerminalWindow::WindowActivated(const bool activated) { - _root->WindowActivated(activated); + if (_root) + { + _root->WindowActivated(activated); + } } // Method Description: @@ -920,10 +952,6 @@ namespace winrt::TerminalApp::implementation //////////////////////////////////////////////////////////////////////////// - // bool TerminalWindow::HasSettingsStartupActions() const noexcept - // { - // return _hasSettingsStartupActions; - // } void TerminalWindow::SetSettingsStartupArgs(const std::vector& actions) { for (const auto& action : actions) @@ -954,6 +982,8 @@ namespace winrt::TerminalApp::implementation // or 0. (see TerminalWindow::_ParseArgs) int32_t TerminalWindow::SetStartupCommandline(array_view args) { + // This is called in AppHost::ctor(), before we've created the window + // (or called TerminalWindow::Initialize) const auto result = _appArgs.ParseArgs(args); if (result == 0) { @@ -961,22 +991,15 @@ namespace winrt::TerminalApp::implementation // then it contains only the executable name and no other arguments. _hasCommandLineArguments = args.size() > 1; _appArgs.ValidateStartupCommands(); - if (const auto idx = _appArgs.GetPersistedLayoutIdx()) - { - _root->SetPersistedLayoutIdx(idx.value()); - } - _root->SetStartupActions(_appArgs.GetStartupActions()); - // Check if we were started as a COM server for inbound connections of console sessions - // coming out of the operating system default application feature. If so, - // tell TerminalPage to start the listener as we have to make sure it has the chance - // to register a handler to hear about the requests first and is all ready to receive - // them before the COM server registers itself. Otherwise, the request might come - // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs.IsHandoffListener()) - { - _root->SetInboundListener(true); - } + // DON'T pass the args into the page yet. It doesn't exist yet. + // Instead, we'll handle that in Initialize, when we first instantiate the page. + } + + // If we have a -s param passed to us to load a saved layout, cache that now. + if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + { + SetPersistedLayoutIdx(idx.value()); } return result; @@ -1038,27 +1061,6 @@ namespace winrt::TerminalApp::implementation //////////////////////////////////////////////////////////////////////////// - bool TerminalWindow::ShouldUsePersistedLayout() - { - return _root != nullptr ? _root->ShouldUsePersistedLayout(_settings) : false; - } - - void TerminalWindow::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))); - } - hstring TerminalWindow::GetWindowLayoutJson(LaunchPosition position) { if (_root != nullptr) @@ -1072,65 +1074,135 @@ namespace winrt::TerminalApp::implementation return L""; } - void TerminalWindow::IdentifyWindow() + void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) { - if (_root) - { - _root->IdentifyWindow(); - } + _loadFromPersistedLayoutIdx = idx; } - winrt::hstring TerminalWindow::WindowName() + // Method Description; + // - Checks if the current window is configured to load a particular layout + // Arguments: + // - settings: The settings to use as this may be called before the page is + // fully initialized. + // Return Value: + // - non-null if there is a particular saved layout to use + std::optional TerminalWindow::LoadPersistedLayoutIdx() const { - return _root ? _root->WindowName() : L""; + return _settings.GlobalSettings().ShouldUsePersistedLayout() ? _loadFromPersistedLayoutIdx : std::nullopt; } - void TerminalWindow::WindowName(const winrt::hstring& name) + + WindowLayout TerminalWindow::LoadPersistedLayout() const { - if (_root) + if (const auto idx = LoadPersistedLayoutIdx()) { - _root->WindowName(name); + const auto i = idx.value(); + const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); + if (layouts && layouts.Size() > i) + { + return layouts.GetAt(i); + } } + return nullptr; } - uint64_t TerminalWindow::WindowId() - { - return _root ? _root->WindowId() : 0; - } - void TerminalWindow::WindowId(const uint64_t& id) + + void TerminalWindow::SetNumberOfOpenWindows(const uint64_t num) { + _numOpenWindows = num; if (_root) { - _root->WindowId(id); + _root->SetNumberOfOpenWindows(num); } } + //////////////////////////////////////////////////////////////////////////// - void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) + void TerminalWindow::IdentifyWindow() { if (_root) { - _root->SetPersistedLayoutIdx(idx); + _root->IdentifyWindow(); } } - void TerminalWindow::SetNumberOfOpenWindows(const uint64_t num) + void TerminalWindow::RenameFailed() { - _numOpenWindows = num; if (_root) { - _root->SetNumberOfOpenWindows(num); + _root->RenameFailed(); } } - void TerminalWindow::RenameFailed() + winrt::hstring TerminalWindow::WindowName() const noexcept { - if (_root) + return _WindowName; + } + void TerminalWindow::WindowName(const winrt::hstring& name) + { + const auto oldIsQuakeMode = IsQuakeWindow(); + + const auto changed = _WindowName != name; + if (changed) { - _root->RenameFailed(); + _WindowName = name; + if (_root) + { + _root->WindowNameChanged(); + + // If we're entering quake mode, or leaving it + if (IsQuakeWindow() != oldIsQuakeMode) + { + // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode + // If we're entering Quake Mode from Focus Mode, then this will do nothing + // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing + _root->SetFocusMode(true); + _IsQuakeWindowChangedHandlers(*this, nullptr); + } + } + } + } + uint64_t TerminalWindow::WindowId() const noexcept + { + return _WindowId; + } + void TerminalWindow::WindowId(const uint64_t& id) + { + if (_WindowId != id) + { + _WindowId = id; + if (_root) + { + _root->WindowNameChanged(); + } } } + // Method Description: + // - Returns a label like "Window: 1234" for the ID of this window + // Arguments: + // - + // Return Value: + // - a string for displaying the name of the window. + winrt::hstring TerminalWindow::WindowIdForDisplay() const noexcept + { + return winrt::hstring{ fmt::format(L"{}: {}", + std::wstring_view(RS_(L"WindowIdLabel")), + _WindowId) }; + } + + // Method Description: + // - Returns a label like "" when the window has no name, or the name of the window. + // Arguments: + // - + // Return Value: + // - a string for displaying the name of the window. + winrt::hstring TerminalWindow::WindowNameForDisplay() const noexcept + { + return _WindowName.empty() ? + winrt::hstring{ fmt::format(L"<{}>", RS_(L"UnnamedWindowName")) } : + _WindowName; + } bool TerminalWindow::IsQuakeWindow() const noexcept { - return _root->IsQuakeWindow(); + return _WindowName == QuakeWindowName; } void TerminalWindow::RequestExitFullscreen() @@ -1142,14 +1214,42 @@ namespace winrt::TerminalApp::implementation { return _settings.GlobalSettings().AutoHideWindow(); } - // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} + void TerminalWindow::UpdateSettingsHandler(const winrt::IInspectable& /*sender*/, - const winrt::IInspectable& arg) + const winrt::TerminalApp::SettingsLoadEventArgs& arg) + { + UpdateSettings(arg); + } + //////////////////////////////////////////////////////////////////////////// + + bool TerminalWindow::ShouldImmediatelyHandoffToElevated() + { + return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; + } + + // Method Description: + // - Escape hatch for immediately dispatching requests to elevated windows + // when first launched. At this point in startup, the window doesn't exist + // yet, XAML hasn't been started, but we need to dispatch these actions. + // We can't just go through ProcessStartupActions, because that processes + // the actions async using the XAML dispatcher (which doesn't exist yet) + // - DON'T CALL THIS if you haven't already checked + // ShouldImmediatelyHandoffToElevated. If you're thinking about calling + // this outside of the one place it's used, that's probably the wrong + // solution. + // Arguments: + // - settings: the settings we should use for dispatching these actions. At + // this point in startup, we hadn't otherwise been initialized with these, + // so use them now. + // Return Value: + // - + void TerminalWindow::HandoffToElevated() { - if (const auto& settings{ arg.try_as() }) + if (_root) { - this->UpdateSettings(S_OK, settings); - _root->SetSettings(_settings, true); + _root->HandoffToElevated(_settings); + return; } } + }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 9d5605d00ca..b7e6a22ee9e 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -6,6 +6,7 @@ #include "TerminalWindow.g.h" #include "SystemMenuChangeArgs.g.h" +#include "SettingsLoadEventArgs.h" #include "TerminalPage.h" #include @@ -35,7 +36,7 @@ namespace winrt::TerminalApp::implementation struct TerminalWindow : TerminalWindowT { public: - TerminalWindow(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult); ~TerminalWindow() = default; STDMETHODIMP Initialize(HWND hwnd); @@ -47,7 +48,7 @@ namespace winrt::TerminalApp::implementation void Quit(); - void UpdateSettings(const HRESULT settingsLoadedResult, const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings); + winrt::fire_and_forget UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args); bool HasCommandlineArguments() const noexcept; // bool HasSettingsStartupActions() const noexcept; @@ -57,25 +58,26 @@ namespace winrt::TerminalApp::implementation winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); + bool ShouldImmediatelyHandoffToElevated(); + void HandoffToElevated(); + bool FocusMode() const; bool Fullscreen() const; void Maximized(bool newMaximized); bool AlwaysOnTop() const; bool AutoHideWindow(); - bool ShouldUsePersistedLayout(); - hstring GetWindowLayoutJson(Microsoft::Terminal::Settings::Model::LaunchPosition position); - void SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts); void IdentifyWindow(); void RenameFailed(); - winrt::hstring WindowName(); - void WindowName(const winrt::hstring& name); - uint64_t WindowId(); - void WindowId(const uint64_t& id); + + std::optional LoadPersistedLayoutIdx() const; + winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout() const; + void SetPersistedLayoutIdx(const uint32_t idx); void SetNumberOfOpenWindows(const uint64_t num); - bool IsQuakeWindow() const noexcept; + bool ShouldUsePersistedLayout() const; + void RequestExitFullscreen(); Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); @@ -108,7 +110,18 @@ namespace winrt::TerminalApp::implementation void DismissDialog(); Microsoft::Terminal::Settings::Model::Theme Theme(); - void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); + void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& arg); + + // Normally, WindowName and WindowId would be + // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise + // WindowNameForDisplay and WindowIdForDisplay instead + winrt::hstring WindowName() const noexcept; + void WindowName(const winrt::hstring& value); + uint64_t WindowId() const noexcept; + void WindowId(const uint64_t& value); + winrt::hstring WindowIdForDisplay() const noexcept; + winrt::hstring WindowNameForDisplay() const noexcept; + bool IsQuakeWindow() const noexcept; // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. @@ -139,12 +152,21 @@ namespace winrt::TerminalApp::implementation bool _gotSettingsStartupActions{ false }; std::vector _settingsStartupArgs{}; - uint64_t _numOpenWindows{ 0 }; + winrt::hstring _WindowName{}; + uint64_t _WindowId{ 0 }; + + uint64_t _numOpenWindows{ 1 }; + std::optional _loadFromPersistedLayoutIdx{}; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; + TerminalApp::SettingsLoadEventArgs _initialLoadResult{ nullptr }; + + void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, + const winrt::hstring& contentKey, + HRESULT settingsLoadedResult, + const winrt::hstring& exceptionText); + void _ShowLoadWarningsDialog(const Windows::Foundation::Collections::IVector& warnings); - void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); - void _ShowLoadWarningsDialog(); bool _IsKeyboardServiceEnabled(); void _RefreshThemeRoutine(); @@ -164,15 +186,18 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress); FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested); FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested); - FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged); FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested); FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested); FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu); FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested); FORWARDED_TYPED_EVENT(ShowWindowChanged, Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs, _root, ShowWindowChanged); + TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable); + TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); + TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); + #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; #endif diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index fcd8cd9346f..a35836cc9a4 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -27,12 +27,22 @@ namespace TerminalApp SystemMenuItemHandler Handler { get; }; }; + [default_interface] runtimeclass SettingsLoadEventArgs + { + Boolean Reload { get; }; + UInt64 Result { get; }; + IVector Warnings { get; }; + String ExceptionText { get; }; + + Microsoft.Terminal.Settings.Model.CascadiaSettings NewSettings { get; }; + + }; + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. - [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged + [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, IWindowProperties, Windows.UI.Xaml.Data.INotifyPropertyChanged { - TerminalWindow(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); - + TerminalWindow(SettingsLoadEventArgs result); // 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, @@ -46,7 +56,6 @@ namespace TerminalApp Boolean IsElevated(); Boolean HasCommandlineArguments(); - // Boolean HasSettingsStartupActions(); Int32 SetStartupCommandline(String[] commands); @@ -54,6 +63,9 @@ namespace TerminalApp String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; + Boolean ShouldImmediatelyHandoffToElevated(); + void HandoffToElevated(); + void Quit(); Windows.UI.Xaml.UIElement GetRoot(); @@ -66,13 +78,10 @@ namespace TerminalApp Boolean AutoHideWindow { get; }; void IdentifyWindow(); - String WindowName; - UInt64 WindowId; void SetPersistedLayoutIdx(UInt32 idx); void SetNumberOfOpenWindows(UInt64 num); void RenameFailed(); void RequestExitFullscreen(); - Boolean IsQuakeWindow(); Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); Boolean CenterOnLaunch { get; }; @@ -91,19 +100,19 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); - Boolean ShouldUsePersistedLayout(); - // Boolean ShouldImmediatelyHandoffToElevated(); - // void HandoffToElevated(); String GetWindowLayoutJson(Microsoft.Terminal.Settings.Model.LaunchPosition position); - void SaveWindowLayoutJsons(Windows.Foundation.Collections.IVector layouts); Boolean GetMinimizeToNotificationArea(); Boolean GetAlwaysShowNotificationIcon(); Boolean GetShowTitleInTitlebar(); + // These already have accessort as a part of IWindowProperties, but we + // also want to be able to set them. + String WindowName { set; }; + UInt64 WindowId { set; }; + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. - Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); void DismissDialog(); event Windows.Foundation.TypedEventHandler SetTitleBarContent; @@ -126,5 +135,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; event Windows.Foundation.TypedEventHandler ShowWindowChanged; + event Windows.Foundation.TypedEventHandler SettingsChanged; + } } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index e337ba4e452..aaa3f63a66d 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -954,9 +954,9 @@ void CascadiaSettings::_researchOnLoad() // dark: 2 // a custom theme: 3 const auto themeChoice = themeInUse == L"system" ? 0 : - themeInUse == L"light" ? 1 : - themeInUse == L"dark" ? 2 : - 3; + themeInUse == L"light" ? 1 : + themeInUse == L"dark" ? 2 : + 3; TraceLoggingWrite( g_hSettingsModelProvider, diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 55ec4d634bf..249bc060f7e 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -239,3 +239,8 @@ winrt::Windows::Foundation::Collections::IMapView Themes() noexcept; void AddTheme(const Model::Theme& theme); Model::Theme CurrentTheme() noexcept; + bool ShouldUsePersistedLayout() const; INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, UnparsedDefaultProfile, L""); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 4ee2b0bf9e6..738159b5eba 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -108,5 +108,6 @@ namespace Microsoft.Terminal.Settings.Model void AddTheme(Theme theme); INHERITABLE_SETTING(ThemePair, Theme); Theme CurrentTheme { get; }; + Boolean ShouldUsePersistedLayout(); } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 60ded0d3fe8..255af507370 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -79,11 +79,6 @@ AppHost::AppHost() noexcept : std::placeholders::_3); _window->SetCreateCallback(pfn); - _window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::TerminalWindow::CalcSnappedDimension, - _windowLogic, - std::placeholders::_1, - std::placeholders::_2)); - // Event handlers: // MAKE SURE THEY ARE ALL: // * winrt::auto_revoke @@ -120,19 +115,6 @@ AppHost::AppHost() noexcept : { _BecomeMonarch(nullptr, nullptr); } - - // Create a throttled function for updating the window state, to match the - // one requested by the pty. A 200ms delay was chosen because it's the - // typical animation timeout in Windows. This does result in a delay between - // the PTY requesting a change to the window state and the Terminal - // realizing it, but should mitigate issues where the Terminal and PTY get - // de-sync'd. - _showHideWindowThrottler = std::make_shared>( - winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), - std::chrono::milliseconds(200), - [this](const bool show) { - _window->ShowWindowChanged(show); - }); } AppHost::~AppHost() @@ -269,10 +251,10 @@ void AppHost::_HandleCommandlineArgs() // the window at all here. In that case, we're going through this // special escape hatch to dispatch all the calls to elevate-shim, and // then we're going to exit immediately. - if (_appLogic.ShouldImmediatelyHandoffToElevated()) + if (_windowLogic.ShouldImmediatelyHandoffToElevated()) { _shouldCreateWindow = false; - _appLogic.HandoffToElevated(); + _windowLogic.HandoffToElevated(); return; } @@ -294,7 +276,7 @@ void AppHost::_HandleCommandlineArgs() { const auto numPeasants = _windowManager.GetNumberOfPeasants(); const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); - if (_windowLogic.ShouldUsePersistedLayout() && + if (_appLogic.ShouldUsePersistedLayout() && layouts && layouts.Size() > 0) { @@ -432,7 +414,13 @@ void AppHost::Initialize() _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 }); - _revokers.SettingsChanged = _appLogic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); + + // A note: make sure to listen to our _window_'s settings changed, not the + // applogic's. We want to make sure the event has gone through the window + // logic _before_ we handle it, so we can ask the window about it's newest + // properties. + _revokers.SettingsChanged = _windowLogic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); + _revokers.IsQuakeWindowChanged = _windowLogic.IsQuakeWindowChanged(winrt::auto_revoke, { this, &AppHost::_IsQuakeWindowChanged }); _revokers.SummonWindowRequested = _windowLogic.SummonWindowRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowRequested }); _revokers.OpenSystemMenu = _windowLogic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu }); @@ -448,6 +436,24 @@ void AppHost::Initialize() // while the screen is off. TerminalTrySetAutoCompleteAnimationsWhenOccluded(static_cast<::IUnknown*>(winrt::get_abi(_windowLogic.GetRoot())), true); + _window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::TerminalWindow::CalcSnappedDimension, + _windowLogic, + std::placeholders::_1, + std::placeholders::_2)); + + // Create a throttled function for updating the window state, to match the + // one requested by the pty. A 200ms delay was chosen because it's the + // typical animation timeout in Windows. This does result in a delay between + // the PTY requesting a change to the window state and the Terminal + // realizing it, but should mitigate issues where the Terminal and PTY get + // de-sync'd. + _showHideWindowThrottler = std::make_shared>( + winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), + std::chrono::milliseconds(200), + [this](const bool show) { + _window->ShowWindowChanged(show); + }); + _window->UpdateTitle(_windowLogic.Title()); // Set up the content of the application. If the app has a custom titlebar, @@ -1007,7 +1013,7 @@ winrt::Windows::Foundation::IAsyncAction AppHost::_SaveWindowLayouts() // Make sure we run on a background thread to not block anything. co_await winrt::resume_background(); - if (_windowLogic.ShouldUsePersistedLayout()) + if (_appLogic.ShouldUsePersistedLayout()) { try { @@ -1022,7 +1028,7 @@ winrt::Windows::Foundation::IAsyncAction AppHost::_SaveWindowLayouts() TraceLoggingDescription("Logged when writing window state"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - _windowLogic.SaveWindowLayoutJsons(layoutJsons); + _appLogic.SaveWindowLayoutJsons(layoutJsons); } catch (...) { @@ -1063,11 +1069,6 @@ winrt::fire_and_forget AppHost::_SaveWindowLayoutsRepeat() } } -void AppHost::_listenForInboundConnections() -{ - _appLogic.SetInboundListener(); -} - winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { // The hotkey MUST be registered on the main thread. It will fail otherwise! @@ -1399,7 +1400,7 @@ void AppHost::_updateTheme() } void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) + const winrt::TerminalApp::SettingsLoadEventArgs& /*args*/) { _setupGlobalHotkeys(); diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 5f9b48dc33e..abee5a9e668 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -19,6 +19,7 @@ class AppHost void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); bool HasWindow(); + winrt::TerminalApp::TerminalWindow Logic(); private: std::unique_ptr _window; @@ -26,6 +27,7 @@ class AppHost winrt::TerminalApp::AppLogic _appLogic; winrt::TerminalApp::TerminalWindow _windowLogic; winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; + winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; std::vector _hotkeys; winrt::com_ptr _desktopManager{ nullptr }; @@ -89,7 +91,7 @@ class AppHost winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + const winrt::TerminalApp::SettingsLoadEventArgs& args); void _IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); @@ -171,7 +173,7 @@ class AppHost winrt::TerminalApp::TerminalWindow::QuitRequested_revoker QuitRequested; winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged; winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged; - winrt::TerminalApp::AppLogic::SettingsChanged_revoker SettingsChanged; + winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged; winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index a06dd7818e2..acc039ae447 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -405,7 +405,13 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam // - The total dimension long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize) { - return gsl::narrow_cast(_pfnSnapDimensionCallback(isWidth, gsl::narrow_cast(clientSize)) + nonClientSize); + if (_pfnSnapDimensionCallback) + { + return gsl::narrow_cast(_pfnSnapDimensionCallback(isWidth, gsl::narrow_cast(clientSize)) + nonClientSize); + } + // We might have been called in WM_CREATE, before we've initialized XAML or + // our page. That's okay. + return clientSize + nonClientSize; } [[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept From a76993365eec7481ca6dd858b6bebfca57088ba2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Feb 2023 17:21:55 -0600 Subject: [PATCH 055/122] merges are hard some times --- src/cascadia/TerminalApp/TerminalPage.idl | 7 ------- src/cascadia/TerminalSettingsModel/GlobalAppSettings.h | 2 -- src/cascadia/WindowsTerminal/AppHost.h | 2 -- 3 files changed, 11 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 494b61506bf..62176b0a1b4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -19,13 +19,8 @@ namespace TerminalApp interface IWindowProperties { -<<<<<<< HEAD - String WindowName; - UInt64 WindowId; -======= String WindowName { get; }; UInt64 WindowId { get; }; ->>>>>>> dev/migrie/oop/3/foreword String WindowNameForDisplay { get; }; String WindowIdForDisplay { get; }; Boolean IsQuakeWindow(); @@ -47,8 +42,6 @@ namespace TerminalApp void IdentifyWindow(); void RenameFailed(); - IWindowProperties WindowProperties { get; }; - // We cannot use the default XAML APIs because we want to make sure // that there's only one application-global dialog visible at a time, // and because of GH#5224. diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index e3bb9e0cd19..8ef1a8a2f72 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -64,8 +64,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::Theme CurrentTheme() noexcept; bool ShouldUsePersistedLayout() const; - bool ShouldUsePersistedLayout() const; - void ExpandCommands(const Windows::Foundation::Collections::IVectorView& profiles, const Windows::Foundation::Collections::IMapView& schemes); diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index d54d9644823..89b328fe32e 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -26,8 +26,6 @@ class AppHost static void s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& message); - winrt::TerminalApp::TerminalWindow Logic(); - private: std::unique_ptr _window; From 33685d9e9dc8ba4eae348fcdb8731c04af081586 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 09:26:51 -0600 Subject: [PATCH 056/122] bwahahahahaha --- src/cascadia/TerminalApp/TerminalPage.cpp | 9 --------- .../CascadiaSettingsSerialization.cpp | 6 +++--- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 932ad147dbc..798f869e981 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -605,11 +605,6 @@ namespace winrt::TerminalApp::implementation if (auto page{ weakThis.get() }) { - const auto memberSize = page->_startupActions.Size(); - const auto paramSecondSize = actions.Size(); - memberSize; - paramFistSize; - paramSecondSize; for (const auto& action : actions) { if (auto page{ weakThis.get() }) @@ -3011,15 +3006,11 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::SetStartupActions(std::vector& actions) { - const auto initSize = actions.size(); - initSize; // The fastest way to copy all the actions out of the std::vector and // put them into a winrt::IVector is by making a copy, then moving the // copy into the winrt vector ctor. auto listCopy = actions; _startupActions = winrt::single_threaded_vector(std::move(listCopy)); - const auto afterSize = _startupActions.Size(); - assert(initSize == afterSize); // you donkey } // Routine Description: diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index aaa3f63a66d..e337ba4e452 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -954,9 +954,9 @@ void CascadiaSettings::_researchOnLoad() // dark: 2 // a custom theme: 3 const auto themeChoice = themeInUse == L"system" ? 0 : - themeInUse == L"light" ? 1 : - themeInUse == L"dark" ? 2 : - 3; + themeInUse == L"light" ? 1 : + themeInUse == L"dark" ? 2 : + 3; TraceLoggingWrite( g_hSettingsModelProvider, From a4f19a9dff33497d3e313b320ba3b790a8fbac9f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 09:28:13 -0600 Subject: [PATCH 057/122] spel --- src/cascadia/TerminalApp/AppLogic.cpp | 2 +- src/cascadia/TerminalApp/TerminalWindow.idl | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index adffee5f5c2..9ed6078cfcd 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -32,7 +32,7 @@ namespace winrt //////////////////////////////////////////////////////////////////////////////// // Error message handling. This is in this file rather than with the warnings in -// TerminalWindow, becuase the error text might also just be a serializationgs +// TerminalWindow, because the error text might also just be a serialization // error message. So AppLogic needs to know the actual text of the error. // !!! IMPORTANT !!! diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index a35836cc9a4..5bcb0294982 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -106,7 +106,7 @@ namespace TerminalApp Boolean GetAlwaysShowNotificationIcon(); Boolean GetShowTitleInTitlebar(); - // These already have accessort as a part of IWindowProperties, but we + // These already have accessors as a part of IWindowProperties, but we // also want to be able to set them. String WindowName { set; }; UInt64 WindowId { set; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 255af507370..30af459f4be 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -416,7 +416,7 @@ void AppHost::Initialize() _revokers.RenameWindowRequested = _windowLogic.RenameWindowRequested(winrt::auto_revoke, { this, &AppHost::_RenameWindowRequested }); // A note: make sure to listen to our _window_'s settings changed, not the - // applogic's. We want to make sure the event has gone through the window + // AppLogic's. We want to make sure the event has gone through the window // logic _before_ we handle it, so we can ask the window about it's newest // properties. _revokers.SettingsChanged = _windowLogic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); From 2621519f2098ec2eb6eca1907a25d4a97a985e25 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 10:46:37 -0600 Subject: [PATCH 058/122] cleanup for review --- .github/actions/spelling/allow/apis.txt | 1 + .../#5000 - Process Model 3.0.md | 27 ------------------- src/cascadia/Remoting/WindowManager.cpp | 8 +++--- src/cascadia/Remoting/WindowManager.h | 20 ++++++++++++-- src/cascadia/Remoting/WindowManager.idl | 4 +-- src/cascadia/TerminalApp/TerminalWindow.cpp | 3 +-- src/cascadia/TerminalApp/TerminalWindow.h | 2 -- src/cascadia/WindowsTerminal/IslandWindow.cpp | 2 -- .../WindowsTerminal/WindowEmperor.cpp | 10 +++---- 9 files changed, 28 insertions(+), 49 deletions(-) delete mode 100644 doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index ce36d8d2a2b..4a18b038127 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -160,6 +160,7 @@ rcx REGCLS RETURNCMD rfind +RLO ROOTOWNER roundf RSHIFT diff --git a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md deleted file mode 100644 index ca3914617bf..00000000000 --- a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 3.0.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -author: Mike Griese @zadjii-msft -created on: 2023-01-27 -last updated: 2023-01-27 -issue id: #5000 ---- - -# Windows Terminal Process Model 3.0 - -Everything is one process. - -WIP branch: https://github.com/microsoft/terminal/compare/main...dev/migrie/f/process-model-v3-test-0 - -* [ ] One `App` per process. -* [ ] One `AppHost` per thread. - * Which means one `AppLogic` per thread/window. -* [ ] `ContentManager` for storing GUID -> `ControlInteractivity`'s - * Idea: Let's make it `map`, and then QI to get the `FrameworkElement`? - * Remoting could host it then -* [ ] `WindowManager` tries to get the monarch. If there isn't one, then Terminal isn't running. - * [ ] `Don't register as a `Monarch` host if we found one. - * [ ] Wait to register as the monarch till we determine that we want to actually make a window. - * [ ] `WindowManager::ProposeCommandline`: we only ever need to create a new window if we are the `Monarch`. -* [ ] -* [ ] -* [ ] -* [ ] diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 12e28fc2d80..2af998e9ff1 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -96,7 +96,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _createMonarch(); if (_monarch) { - // We connectecd to a monarch instance, not us though. + // We connected to a monarch instance, not us though. shouldCreateWindow = false; std::optional givenID; @@ -123,7 +123,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // successfully proposed to it in _proposeToMonarch, so we can't get // here with a monarch. { - // No pre-existing instance. + // No preexisting instance. // Raise an event, to ask how to handle this commandline. We can't ask // the app ourselves - we exist isolated from that knowledge (and @@ -166,7 +166,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // some window than not. _monarch = winrt::make(); TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_FaileToCoCreate", + "WindowManager_FailedToCoCreate", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } @@ -294,7 +294,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return false; } - Remoting::Peasant WindowManager::CreateAPeasant(Remoting::WindowRequestedArgs args) + Remoting::Peasant WindowManager::CreatePeasant(Remoting::WindowRequestedArgs args) { auto p = winrt::make_self(); if (args.Id()) diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 531b7a6f18a..cc647fc4daf 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -1,4 +1,20 @@ - +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +Class Name: +- WindowManager.h +Abstract: +- The Window Manager takes care of coordinating the monarch and peasant for this + process. +- It's responsible for registering as a potential future monarch. It's also + responsible for creating the Peasant for this process when it's determined + this process should become a window process. +- If we aren't the monarch, it's responsible for watching the current monarch + process, and finding the new one if the current monarch dies. +- When the monarch needs to ask the TerminalApp about how to parse a + commandline, it'll ask by raising an event that we'll bubble up to the + AppHost. +--*/ #pragma once #include "WindowManager.g.h" @@ -13,7 +29,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager(); ~WindowManager(); winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline2(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - Remoting::Peasant CreateAPeasant(Remoting::WindowRequestedArgs args); + Remoting::Peasant CreatePeasant(Remoting::WindowRequestedArgs args); void SignalClose(Remoting::Peasant peasant); void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 45f7468c166..79b47c75226 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -9,7 +9,7 @@ namespace Microsoft.Terminal.Remoting WindowManager(); ProposeCommandlineResult ProposeCommandline2(CommandlineArgs args); - Peasant CreateAPeasant(WindowRequestedArgs args); + Peasant CreatePeasant(WindowRequestedArgs args); void SignalClose(Peasant p); void RequestShowNotificationIcon(Peasant p); @@ -25,7 +25,6 @@ namespace Microsoft.Terminal.Remoting UInt64 GetNumberOfPeasants(); - Boolean DoesQuakeWindowExist(); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; @@ -37,7 +36,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; - event Windows.Foundation.TypedEventHandler RequestNewWindow; }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index eec45606d17..16f620bebac 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -53,8 +53,7 @@ static const std::array settingsLoadWarningsLabels{ USES_RESOURCE(L"DuplicateRemainingProfilesEntry"), }; static_assert(settingsLoadWarningsLabels.size() == static_cast(SettingsLoadWarnings::WARNINGS_SIZE)); - -// Errors are definted in AppLogic.cpp +// Errors are defined in AppLogic.cpp // Function Description: // - General-purpose helper for looking up a localized string for a diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index c627b1ec83b..5bec441ed99 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -135,8 +135,6 @@ namespace winrt::TerminalApp::implementation winrt::event_token PropertyChanged(Windows::UI::Xaml::Data::PropertyChangedEventHandler const& handler) { return _root->PropertyChanged(handler); } void PropertyChanged(winrt::event_token const& token) { _root->PropertyChanged(token); } - // WINRT_PROPERTY(TerminalApp::WindowProperties, WindowProperties, nullptr); - TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme); private: diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 2e858f998bd..978159b2803 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -27,8 +27,6 @@ using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; #define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS" #define IDM_SYSTEM_MENU_BEGIN 0x1000 -// const UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated"); - IslandWindow::IslandWindow() noexcept : _interopWindowHandle{ nullptr }, _rootGrid{ nullptr }, diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index fcb9ecae594..d9518faa872 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -14,7 +14,7 @@ #include "NotificationIcon.h" // This was an enormous dead end. -// #include +// #include // #include // typedef GUID DXGI_DEBUG_ID; // const DXGI_DEBUG_ID DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 }; @@ -134,7 +134,7 @@ void WindowEmperor::WaitForWindows() void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, const bool /*firstWindow*/) { - Remoting::Peasant peasant{ _manager.CreateAPeasant(args) }; + Remoting::Peasant peasant{ _manager.CreatePeasant(args) }; auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; @@ -240,7 +240,7 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II } // Raised from our windowManager (on behalf of the monarch). We respond by -// giving the monarch an async fuction that the manager should wait on before +// 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) @@ -392,14 +392,12 @@ LRESULT WindowEmperor::MessageHandler(UINT const message, WPARAM const wParam, L case NIN_SELECT: case NIN_KEYSELECT: { - // _NotifyNotificationIconPressedHandlers(); _notificationIcon->NotificationIconPressed(); return 0; } case WM_CONTEXTMENU: { const til::point eventPoint{ GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam) }; - // _NotifyShowNotificationIconContextMenuHandlers(eventPoint); _notificationIcon->ShowContextMenu(eventPoint, _manager.GetPeasantInfos()); return 0; } @@ -408,7 +406,6 @@ LRESULT WindowEmperor::MessageHandler(UINT const message, WPARAM const wParam, L } case WM_MENUCOMMAND: { - // _NotifyNotificationIconMenuItemSelectedHandlers((HMENU)lparam, (UINT)wparam); _notificationIcon->MenuItemSelected((HMENU)lParam, (UINT)wParam); return 0; } @@ -420,7 +417,6 @@ LRESULT WindowEmperor::MessageHandler(UINT const message, WPARAM const wParam, L // message at runtime. if (message == WM_TASKBARCREATED) { - // _NotifyReAddNotificationIconHandlers(); _notificationIcon->ReAddNotificationIcon(); return 0; } From 603a2ce53a88fe839de99c801fa64a13d6777836 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 10:56:31 -0600 Subject: [PATCH 059/122] spel --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 4 ++-- src/cascadia/WindowsTerminal/WindowThread.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e79cac9b88a..b1ab627b8f1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4114,7 +4114,7 @@ namespace winrt::TerminalApp::implementation terminalTab->GetRootPane()->WalkTree([&](auto&& pane) { // TODO, but of middling priority. We probably shouldn't // SetupResources on _every_ pane. We can probably call - // that on the root, and then have that backchannel to + // that on the root, and then have that back channel to // update the whole tree. // Update the brushes that Pane's use... diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 16f620bebac..a6fcd3f8e80 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -627,7 +627,7 @@ namespace winrt::TerminalApp::implementation // we have an HWND, and we can't finish creating the window till we // know how big it should be. // - // Instead, we'll just hardcode how big the titlebar chould be. If + // Instead, we'll just hardcode how big the titlebar should be. If // the titlebar / tab row ever change size, these numbers will have // to change accordingly. diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 818776f1190..63eb0e7693d 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -226,7 +226,7 @@ void AppHost::_HandleCommandlineArgs() const auto numPeasants = _windowManager.GetNumberOfPeasants(); if (numPeasants == 1) { - // TODO! this is vaugely off by one. Not sure, but if you restore 2 + // TODO! this is vaguely off by one. Not sure, but if you restore 2 // windows, you seem to get two copies of the second. Yikes. And // this wasn't just because I was setting the debug commandline to // `nt ; nt`. Calling wtd with two persisted windows just creates @@ -1065,7 +1065,7 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab _window->IsQuakeWindow(_windowLogic.IsQuakeWindow()); } -// Raised from our Peasant. We handle by propogating the call to our terminal window. +// Raised from our Peasant. We handle by propagating the call to our terminal window. winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 030b1a05aba..6cc6093c986 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -13,13 +13,13 @@ WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic, _args{ args }, _manager{ manager } { - // DO NOT + // DO NOT start the AppHost here in the ctor, as that will start XAML on the wrong thread! } void WindowThread::Start() { _thread = std::thread([this]() { - // Start the Apphost HERE, on the actual thread we want XAML to run on + // Start the AppHost HERE, on the actual thread we want XAML to run on _host = std::make_unique<::AppHost>(_appLogic, _args, _manager, From a9ac218c4d7e9c21950040a4c7d1fa3960dba3c3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 10:57:19 -0600 Subject: [PATCH 060/122] runformat --- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index d9518faa872..92cabd3375a 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -142,7 +142,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); From 82224bc87a4b44b414f911c8be6ce8fcc6b8680f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 11:48:32 -0600 Subject: [PATCH 061/122] this was usnused --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 798f869e981..89169200e08 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -573,8 +573,6 @@ namespace winrt::TerminalApp::implementation const bool initial, const winrt::hstring cwd) { - const auto paramFistSize = actions.Size(); - auto weakThis{ get_weak() }; // Handle it on a subsequent pass of the UI thread. From d9d4d2e62dcef2e63e1086df8f231c7931c634f0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 12:08:54 -0600 Subject: [PATCH 062/122] get the tests to build, at least --- src/cascadia/LocalTests_TerminalApp/TabTests.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index 53013f93721..cee11484427 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -1245,7 +1245,9 @@ namespace TerminalAppLocalTests page->RenameWindowRequested([&page](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) { // In the real terminal, this would bounce up to the monarch and // come back down. Instead, immediately call back and set the name. - page->WindowName(args.ProposedName()); + + // TODO! This doesn't work _at all_ anymore. May need to re-evaluate if this test is even possible as-is. + // page->WindowName(args.ProposedName()); }); auto windowNameChanged = false; From 77346000df21498af0c3453e422403f90da5fe4b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 16:08:35 -0600 Subject: [PATCH 063/122] [valaquenta] Lifetime manage controls (cherry picked from commit 4eba01f919951934b9b1f0a49fd4a3863c59959f) --- src/cascadia/TerminalApp/ContentManager.cpp | 12 +++++++++++- src/cascadia/TerminalApp/ContentManager.h | 3 +++ src/cascadia/TerminalControl/ControlCore.idl | 1 - .../TerminalControl/ControlInteractivity.cpp | 9 +++++++++ src/cascadia/TerminalControl/ControlInteractivity.h | 4 ++++ .../TerminalControl/ControlInteractivity.idl | 4 ++++ src/cascadia/TerminalControl/TermControl.cpp | 2 +- 7 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 1b9d5f68e4b..254ac4d6e1d 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -31,7 +31,7 @@ namespace winrt::TerminalApp::implementation TerminalConnection::ITerminalConnection connection) { auto content = ControlInteractivity{ settings, unfocusedAppearance, connection }; - // winrt::guid g{ ::Microsoft::Console::Utils::CreateGuid() }; + content.Closed({ this, &ContentManager::_closedHandler }); _content.Insert(content.Id(), content); return content; } @@ -40,4 +40,14 @@ namespace winrt::TerminalApp::implementation { return _content.TryLookup(id); } + + void ContentManager::_closedHandler(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable e) + { + if (const auto& content{ sender.try_as() }) + { + const auto& contentGuid{ content.Id() }; + _content.TryRemove(contentGuid); + } + } } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index bd2725cac6e..4f3c631a5ac 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -21,5 +21,8 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IMap _content{ winrt::multi_threaded_map() }; + + void _closedHandler(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable e); }; } diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index a8df501469b..3a09dc5f747 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -126,7 +126,6 @@ namespace Microsoft.Terminal.Control String HoveredUriText { get; }; Windows.Foundation.IReference HoveredCell { get; }; - void Close(); void BlinkCursor(); Boolean IsInReadOnlyMode { get; }; Boolean CursorOn; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index dbb47b6b543..82de849b018 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -80,6 +80,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation return *_core; } + void ControlInteractivity::Close() + { + _ClosedHandlers(*this, nullptr); + if (_core) + { + _core->Close(); + } + } + // Method Description: // - Returns the number of clicks that occurred (double and triple click support). // Every call to this function registers a click. diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index f45e91793db..91af62cfca6 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -44,6 +44,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void Initialize(); Control::ControlCore Core(); + void Close(); + Control::InteractivityAutomationPeer OnCreateAutomationPeer(); ::Microsoft::Console::Render::IRenderData* GetRenderData() const; @@ -91,6 +93,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs); + TYPED_EVENT(Closed, IInspectable, IInspectable); + private: // NOTE: _uiaEngine must be ordered before _core. // diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 97b050a736b..56cda3532f3 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -25,6 +25,8 @@ namespace Microsoft.Terminal.Control Guid Id { get; }; + void Close(); + InteractivityAutomationPeer OnCreateAutomationPeer(); Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference formats); @@ -67,6 +69,8 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler ScrollPositionChanged; event Windows.Foundation.TypedEventHandler PasteFromClipboard; + event Windows.Foundation.TypedEventHandler Closed; + }; } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index db74370ecb7..b7ed25040bf 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2007,7 +2007,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TSFInputControl().Close(); _autoScrollTimer.Stop(); - _core.Close(); + _interactivity.Close(); } } From 28735112d207032bc780d0913d479d0b257ee4e0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 11:43:10 -0600 Subject: [PATCH 064/122] Don't add multiple event handlers for the core Basically, because we were projecting the event straight trhough the control, from core->page, basically when we would do a PasteRequested, we'd call the handler registered on the first control, then also the handler added by the second. --- src/cascadia/TerminalControl/TermControl.cpp | 10 ++++++++++ src/cascadia/TerminalControl/TermControl.h | 21 ++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b7ed25040bf..c2b973df8bb 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -93,6 +93,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation _interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); _interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); + _core.CopyToClipboard({ get_weak(), &TermControl::_bubbleCopyToClipboard }); + _core.TitleChanged({ get_weak(), &TermControl::_bubbleTitleChanged }); + _core.TabColorChanged({ get_weak(), &TermControl::_bubbleTabColorChanged }); + _core.TaskbarProgressChanged({ get_weak(), &TermControl::_bubbleSetTaskbarProgress }); + _core.ConnectionStateChanged({ get_weak(), &TermControl::_bubbleConnectionStateChanged }); + _core.ShowWindowChanged({ get_weak(), &TermControl::_bubbleShowWindowChanged }); + _core.CloseTerminalRequested({ get_weak(), &TermControl::_bubbleCloseTerminalRequested }); + + _interactivity.PasteFromClipboard({ get_weak(), &TermControl::_bubblePasteFromClipboard }); + // Initialize the terminal only once the swapchainpanel is loaded - that // way, we'll be able to query the real pixel size it got on layout _layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) { diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 79050015335..835c1aff661 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -140,19 +140,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); +// Same thing, but handler is a projected type, not an implementation +#define BUBBLED_FORWARDED_TYPED_EVENT(name, sender, args) \ + TYPED_EVENT(name, sender, args) \ + void _bubble##name(const sender& s, const args& a) { _##name##Handlers(s, a); } + // -------------------------------- WinRT Events --------------------------------- // clang-format off WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); - PROJECTED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs, _core, CopyToClipboard); - PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged); - PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged); - PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged); - PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged); - PROJECTED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs, _core, ShowWindowChanged); - PROJECTED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable, _core, CloseTerminalRequested); + BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); + BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); - PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard); + BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs); From 855a79dbe086ff6bfa8db5245a6d9b3a1c82f04d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 12:55:44 -0600 Subject: [PATCH 065/122] Add auto_revokers to all TermControl ev handlers, but that wasn't what ended up fixing this bug. All we actually needed was to do a _FontSizeChangedHandlers() to set up the font size and the markers in the TermControl. --- src/cascadia/TerminalControl/TermControl.cpp | 69 +++++++++++--------- src/cascadia/TerminalControl/TermControl.h | 30 +++++++++ 2 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index c2b973df8bb..26d2b9712ed 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -72,36 +72,36 @@ namespace winrt::Microsoft::Terminal::Control::implementation // These events might all be triggered by the connection, but that // should be drained and closed before we complete destruction. So these // are safe. - _core.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); - _core.WarningBell({ this, &TermControl::_coreWarningBell }); - _core.CursorPositionChanged({ this, &TermControl::_CursorPositionChanged }); + _revokers.coreScrollPositionChanged = _core.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged }); + _revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell }); + _revokers.CursorPositionChanged = _core.CursorPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_CursorPositionChanged }); // This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here. - _core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState }); + _revokers.RendererEnteredErrorState = _core.RendererEnteredErrorState(winrt::auto_revoke, { get_weak(), &TermControl::_RendererEnteredErrorState }); // These callbacks can only really be triggered by UI interactions. So // they don't need weak refs - they can't be triggered unless we're // alive. - _core.BackgroundColorChanged({ this, &TermControl::_coreBackgroundColorChanged }); - _core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged }); - _core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged }); - _core.RaiseNotice({ this, &TermControl::_coreRaisedNotice }); - _core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged }); - _core.FoundMatch({ this, &TermControl::_coreFoundMatch }); - _core.UpdateSelectionMarkers({ this, &TermControl::_updateSelectionMarkers }); - _core.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); - _interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler }); - _interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged }); - - _core.CopyToClipboard({ get_weak(), &TermControl::_bubbleCopyToClipboard }); - _core.TitleChanged({ get_weak(), &TermControl::_bubbleTitleChanged }); - _core.TabColorChanged({ get_weak(), &TermControl::_bubbleTabColorChanged }); - _core.TaskbarProgressChanged({ get_weak(), &TermControl::_bubbleSetTaskbarProgress }); - _core.ConnectionStateChanged({ get_weak(), &TermControl::_bubbleConnectionStateChanged }); - _core.ShowWindowChanged({ get_weak(), &TermControl::_bubbleShowWindowChanged }); - _core.CloseTerminalRequested({ get_weak(), &TermControl::_bubbleCloseTerminalRequested }); - - _interactivity.PasteFromClipboard({ get_weak(), &TermControl::_bubblePasteFromClipboard }); + _revokers.BackgroundColorChanged = _core.BackgroundColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreBackgroundColorChanged }); + _revokers.FontSizeChanged = _core.FontSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreFontSizeChanged }); + _revokers.TransparencyChanged = _core.TransparencyChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreTransparencyChanged }); + _revokers.RaiseNotice = _core.RaiseNotice(winrt::auto_revoke, { get_weak(), &TermControl::_coreRaisedNotice }); + _revokers.HoveredHyperlinkChanged = _core.HoveredHyperlinkChanged(winrt::auto_revoke, { get_weak(), &TermControl::_hoveredHyperlinkChanged }); + _revokers.FoundMatch = _core.FoundMatch(winrt::auto_revoke, { get_weak(), &TermControl::_coreFoundMatch }); + _revokers.UpdateSelectionMarkers = _core.UpdateSelectionMarkers(winrt::auto_revoke, { get_weak(), &TermControl::_updateSelectionMarkers }); + _revokers.coreOpenHyperlink = _core.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler }); + _revokers.interactivityOpenHyperlink = _interactivity.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler }); + _revokers.interactivityScrollPositionChanged = _interactivity.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged }); + + _revokers.CopyToClipboard = _core.CopyToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCopyToClipboard }); + _revokers.TitleChanged = _core.TitleChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTitleChanged }); + _revokers.TabColorChanged = _core.TabColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTabColorChanged }); + _revokers.TaskbarProgressChanged = _core.TaskbarProgressChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSetTaskbarProgress }); + _revokers.ConnectionStateChanged = _core.ConnectionStateChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleConnectionStateChanged }); + _revokers.ShowWindowChanged = _core.ShowWindowChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleShowWindowChanged }); + _revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested }); + + _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); // Initialize the terminal only once the swapchainpanel is loaded - that // way, we'll be able to query the real pixel size it got on layout @@ -147,7 +147,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000)); _autoScrollTimer.Interval(AutoScrollUpdateInterval); - _autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll }); + _autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll }); _ApplyUISettings(); } @@ -847,7 +847,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // after Enable, then it'll be possible to paint the frame once // _before_ the warning handler is set up, and then warnings from // the first paint will be ignored! - _core.RendererWarning({ get_weak(), &TermControl::_RendererWarning }); + _revokers.RendererWarning = _core.RendererWarning(winrt::auto_revoke, { get_weak(), &TermControl::_RendererWarning }); const auto coreInitialized = _core.Initialize(panelWidth, panelHeight, @@ -858,7 +858,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } _interactivity.Initialize(); - _core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); + _revokers.SwapChainChanged = _core.SwapChainChanged(winrt::auto_revoke, { get_weak(), &TermControl::RenderEngineSwapChainChanged }); _core.EnablePainting(); auto bufferHeight = _core.BufferHeight(); @@ -2013,6 +2013,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation _RestorePointerCursorHandlers(*this, nullptr); + _revokers = {}; + // Disconnect the TSF input control so it doesn't receive EditContext events. TSFInputControl().Close(); _autoScrollTimer.Stop(); @@ -2954,9 +2956,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Ensure the marker is oriented properly // (i.e. if start is at the beginning of the buffer, it should be flipped) - auto transform{ marker.RenderTransform().as() }; - transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0)); - marker.RenderTransform(transform); + // + // Note - This RenderTransform might not be a + // ScaleTransform, if we haven't had a _coreFontSizeChanged + // handled yet, because that's the first place we set the + // RenderTransform + if (const auto& transform{ marker.RenderTransform().try_as() }) + { + transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0)); + marker.RenderTransform(transform); + } // Compute the location of the top left corner of the cell in DIPS auto terminalPos{ targetEnd ? markerData.EndPos : markerData.StartPos }; diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 835c1aff661..4a4d41cac73 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -322,6 +322,36 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::point _toPosInDips(const Core::Point terminalCellPos); void _throttledUpdateScrollbar(const ScrollBarUpdate& update); + + struct Revokers + { + Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged; + Control::ControlCore::WarningBell_revoker WarningBell; + Control::ControlCore::CursorPositionChanged_revoker CursorPositionChanged; + Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState; + Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged; + Control::ControlCore::FontSizeChanged_revoker FontSizeChanged; + Control::ControlCore::TransparencyChanged_revoker TransparencyChanged; + Control::ControlCore::RaiseNotice_revoker RaiseNotice; + Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged; + Control::ControlCore::FoundMatch_revoker FoundMatch; + Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers; + Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink; + Control::ControlCore::CopyToClipboard_revoker CopyToClipboard; + Control::ControlCore::TitleChanged_revoker TitleChanged; + Control::ControlCore::TabColorChanged_revoker TabColorChanged; + Control::ControlCore::TaskbarProgressChanged_revoker TaskbarProgressChanged; + Control::ControlCore::ConnectionStateChanged_revoker ConnectionStateChanged; + Control::ControlCore::ShowWindowChanged_revoker ShowWindowChanged; + Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested; + // These are set up in _InitializeTerminal + Control::ControlCore::RendererWarning_revoker RendererWarning; + Control::ControlCore::SwapChainChanged_revoker SwapChainChanged; + + Control::ControlInteractivity::OpenHyperlink_revoker interactivityOpenHyperlink; + Control::ControlInteractivity::ScrollPositionChanged_revoker interactivityScrollPositionChanged; + Control::ControlInteractivity::PasteFromClipboard_revoker PasteFromClipboard; + } _revokers{}; }; } From 4b22963f9d3b5da04f560ecb0fd9827b9a12ce2e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Feb 2023 09:18:48 -0600 Subject: [PATCH 066/122] notes --- src/cascadia/TerminalControl/TermControl.cpp | 1 + src/cascadia/TerminalControl/TermControl.h | 8 ++------ src/cascadia/inc/cppwinrt_utils.h | 21 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 26d2b9712ed..c0f31c66a8b 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -93,6 +93,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.interactivityOpenHyperlink = _interactivity.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler }); _revokers.interactivityScrollPositionChanged = _interactivity.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged }); + // "Bubbled" events - ones we want to handle, by raising our own event. _revokers.CopyToClipboard = _core.CopyToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCopyToClipboard }); _revokers.TitleChanged = _core.TitleChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTitleChanged }); _revokers.TabColorChanged = _core.TabColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTabColorChanged }); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 4a4d41cac73..acb4d95c1e4 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -139,16 +139,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation void AdjustOpacity(const double opacity, const bool relative); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - -// Same thing, but handler is a projected type, not an implementation -#define BUBBLED_FORWARDED_TYPED_EVENT(name, sender, args) \ - TYPED_EVENT(name, sender, args) \ - void _bubble##name(const sender& s, const args& a) { _##name##Handlers(s, a); } - // -------------------------------- WinRT Events --------------------------------- // clang-format off WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); + // UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE + BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index 1e9ccb1a0d0..ec6c1003877 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -102,6 +102,27 @@ public: winrt::event_token name(const Windows::Foundation::TypedEventHandler& h) { return handler.handlerName(h); } \ void name(const winrt::event_token& token) noexcept { handler.handlerName(token); } +// This is a bit like *FORWARDED_TYPED_EVENT. When you use a forwarded event, +// the handler gets added to the object that's raising the event. For example, +// the TerminalPage might be the handler for the TermControl's +// BackgroundColorChanged event, which is actually implemented by the +// ControlCore. So when Core raises an event, it immediately calls the handler +// on the Page. +// +// Instead, the BUBBELD eventk introduces an indirection layer. In the above +// example, the Core would raise the event, but now the Control would handle it, +// and raise an event with each of its own handlers. +// +// This allows us to detach the core from the control safely, witout needing to +// re-wire all the event handlers from page->control again. +// +// Implement like: +// +// _core.TitleChanged({ get_weak(), &TermControl::_bubbleTitleChanged }); +#define BUBBLED_FORWARDED_TYPED_EVENT(name, sender, args) \ + TYPED_EVENT(name, sender, args) \ + void _bubble##name(const sender& s, const args& a) { _##name##Handlers(s, a); } + // Use this macro to quick implement both the getter and setter for a property. // This should only be used for simple types where there's no logic in the // getter/setter beyond just accessing/updating the value. From d06ad8b5dc151b51b372065ce36b46da82060c1f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Feb 2023 09:48:19 -0600 Subject: [PATCH 067/122] spel --- src/cascadia/inc/cppwinrt_utils.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index ec6c1003877..fd8aff723bc 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -109,11 +109,11 @@ public: // ControlCore. So when Core raises an event, it immediately calls the handler // on the Page. // -// Instead, the BUBBELD eventk introduces an indirection layer. In the above +// Instead, the BUBBLED event introduces an indirection layer. In the above // example, the Core would raise the event, but now the Control would handle it, // and raise an event with each of its own handlers. // -// This allows us to detach the core from the control safely, witout needing to +// This allows us to detach the core from the control safely, without needing to // re-wire all the event handlers from page->control again. // // Implement like: From 118bffa6cdcbd1144e267f863ab9b29202530ae9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Feb 2023 15:51:18 -0600 Subject: [PATCH 068/122] fix the tests --- .../LocalTests_TerminalApp/TabTests.cpp | 29 +++++++++++++++---- src/cascadia/TerminalApp/TerminalPage.h | 3 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index cee11484427..c1edac3c313 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -42,6 +42,17 @@ namespace TerminalAppLocalTests // an updated TAEF that will let us install framework packages when the test // package is deployed. Until then, these tests won't deploy in CI. + struct WindowProperties : winrt::implements + { + WINRT_PROPERTY(winrt::hstring, WindowName); + WINRT_PROPERTY(uint64_t, WindowId); + WINRT_PROPERTY(winrt::hstring, WindowNameForDisplay); + WINRT_PROPERTY(winrt::hstring, WindowIdForDisplay); + + public: + bool IsQuakeWindow() { return _WindowName == L"_quake"; }; + }; + class TabTests { // For this set of tests, we need to activate some XAML content. For @@ -110,6 +121,7 @@ namespace TerminalAppLocalTests void _initializeTerminalPage(winrt::com_ptr& page, CascadiaSettings initialSettings); winrt::com_ptr _commonSetup(); + winrt::com_ptr _windowProperties; }; template @@ -239,11 +251,15 @@ namespace TerminalAppLocalTests // it's weird. winrt::TerminalApp::TerminalPage projectedPage{ nullptr }; + _windowProperties = winrt::make_self(); + winrt::TerminalApp::IWindowProperties iProps{ *_windowProperties }; + Log::Comment(NoThrowString().Format(L"Construct the TerminalPage")); - auto result = RunOnUIThread([&projectedPage, &page, initialSettings]() { + auto result = RunOnUIThread([&projectedPage, &page, initialSettings, iProps]() { projectedPage = winrt::TerminalApp::TerminalPage(); page.copy_from(winrt::get_self(projectedPage)); page->_settings = initialSettings; + page->WindowProperties(iProps); }); VERIFY_SUCCEEDED(result); @@ -1242,12 +1258,13 @@ namespace TerminalAppLocalTests END_TEST_METHOD_PROPERTIES() auto page = _commonSetup(); - page->RenameWindowRequested([&page](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) { + page->RenameWindowRequested([&page, this](auto&&, const winrt::TerminalApp::RenameWindowRequestedArgs args) { // In the real terminal, this would bounce up to the monarch and // come back down. Instead, immediately call back and set the name. - - // TODO! This doesn't work _at all_ anymore. May need to re-evaluate if this test is even possible as-is. - // page->WindowName(args.ProposedName()); + // + // This replicates how TerminalWindow works + _windowProperties->WindowName(args.ProposedName()); + page->WindowNameChanged(); }); auto windowNameChanged = false; @@ -1262,7 +1279,7 @@ namespace TerminalAppLocalTests page->_RequestWindowRename(winrt::hstring{ L"Foo" }); }); TestOnUIThread([&]() { - VERIFY_ARE_EQUAL(L"Foo", page->_WindowName); + VERIFY_ARE_EQUAL(L"Foo", page->WindowName()); VERIFY_IS_TRUE(windowNameChanged, L"The window name should have changed, and we should have raised a notification that WindowNameForDisplay changed"); }); diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 28b1db98c25..c82a365c4b8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -186,8 +186,7 @@ namespace winrt::TerminalApp::implementation bool _isFullscreen{ false }; bool _isMaximized{ false }; bool _isAlwaysOnTop{ false }; - winrt::hstring _WindowName{}; - uint64_t _WindowId{ 0 }; + std::optional _loadFromPersistedLayoutIdx{}; uint64_t _numOpenWindows{ 0 }; From bc80943fd9e67c86fdb410b50daad99e090ca373 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Feb 2023 17:07:59 -0600 Subject: [PATCH 069/122] the tests all...pass? --- .../LocalTests_TerminalApp/SettingsTests.cpp | 45 +++++++++++-------- .../UnitTests_Remoting/RemotingTests.cpp | 1 + 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp index a3dcbb45f4e..c13945a47ba 100644 --- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -164,8 +164,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); } - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); @@ -287,8 +288,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); } - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); @@ -412,8 +414,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); } - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); @@ -527,8 +530,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); @@ -621,8 +625,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); @@ -744,8 +749,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); @@ -880,8 +886,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); @@ -982,8 +989,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(settings.ActionMap().NameMap(), settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); @@ -1205,8 +1213,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${scheme.name}", realArgs.TerminalArgs().Profile()); } - auto expandedCommands = winrt::TerminalApp::implementation::TerminalPage::_ExpandCommands(nameMap, settings.ActiveProfiles().GetView(), settings.GlobalSettings().ColorSchemes()); - _logCommandNames(expandedCommands.GetView()); + settings.ExpandCommands(); + const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; + _logCommandNames(expandedCommands); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index f3fd19537eb..bc7a7c6664f 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -121,6 +121,7 @@ namespace RemotingUnitTests 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); }; class RemotingTests From 667b6586d2ae91e1903c7b00396e3c398629bd21 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 15 Feb 2023 15:33:36 -0600 Subject: [PATCH 070/122] I'm committing everything just in case, but I'm gonna revert and just do the part that fixed it --- src/cascadia/TerminalApp/TabManagement.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 77 ++++++++++++------- src/cascadia/TerminalApp/TerminalPage.h | 10 ++- src/cascadia/TerminalApp/TerminalWindow.cpp | 56 ++++++++------ src/cascadia/TerminalApp/TerminalWindow.h | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- .../WindowsTerminal/WindowEmperor.cpp | 7 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 8 ++ 8 files changed, 101 insertions(+), 63 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index f1134efbd36..779cb491e2c 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -226,7 +226,7 @@ namespace winrt::TerminalApp::implementation } } - tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick }); + tabViewItem.PointerReleased({ get_weak(), &TerminalPage::_OnTabClick }); // When the tab requests close, try to close it (prompt for approval, if required) newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index b1ab627b8f1..81438e96c23 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -39,7 +39,7 @@ using namespace ::Microsoft::Console; using namespace ::Microsoft::Terminal::Core; using namespace std::chrono_literals; -#define HOOKUP_ACTION(action) _actionDispatch->action({ this, &TerminalPage::_Handle##action }); +#define HOOKUP_ACTION(action) _actionDispatch->action({ get_weak(), &TerminalPage::_Handle##action }); namespace winrt { @@ -60,6 +60,25 @@ namespace winrt::TerminalApp::implementation InitializeComponent(); } + TerminalPage::~TerminalPage() + { + // Dummy line to get a breakpoint somewnere. We can remove this. + _WindowProperties = nullptr; + } + + void TerminalPage::BigRedButton() + { + _windowIdToast = nullptr; + _windowRenameFailedToast = nullptr; + + _actionDispatch = nullptr; + _tabView = nullptr; + _tabRow = nullptr; + _tabContent = nullptr; + _newTabButton = nullptr; + _tabColorPicker = nullptr; + _settings = nullptr; + } // Method Description: // - implements the IInitializeWithWindow interface from shobjidl_core. // - We're going to use this HWND as the owner for the ConPTY windows, via @@ -222,7 +241,7 @@ namespace winrt::TerminalApp::implementation _RegisterActionCallbacks(); // Hook up inbound connection event handler - ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection }); + ConptyConnection::NewConnection({ get_weak(), &TerminalPage::_OnNewConnection }); //Event Bindings (Early) _newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) { @@ -237,9 +256,9 @@ namespace winrt::TerminalApp::implementation page->NewTerminalByDrop(e); } }); - _tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged }); - _tabView.TabCloseRequested({ this, &TerminalPage::_OnTabCloseRequested }); - _tabView.TabItemsChanged({ this, &TerminalPage::_OnTabItemsChanged }); + _tabView.SelectionChanged({ get_weak(), &TerminalPage::_OnTabSelectionChanged }); + _tabView.TabCloseRequested({ get_weak(), &TerminalPage::_OnTabCloseRequested }); + _tabView.TabItemsChanged({ get_weak(), &TerminalPage::_OnTabItemsChanged }); _CreateNewTabFlyout(); @@ -248,16 +267,16 @@ namespace winrt::TerminalApp::implementation // When the visibility of the command palette changes to "collapsed", // the palette has been closed. Toss focus back to the currently active // control. - CommandPalette().RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) { - if (CommandPalette().Visibility() == Visibility::Collapsed) - { - _FocusActiveControl(nullptr, nullptr); - } - }); - CommandPalette().DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested }); - CommandPalette().CommandLineExecutionRequested({ this, &TerminalPage::_OnCommandLineExecutionRequested }); - CommandPalette().SwitchToTabRequested({ this, &TerminalPage::_OnSwitchToTabRequested }); - CommandPalette().PreviewAction({ this, &TerminalPage::_PreviewActionHandler }); + // CommandPalette().RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) { + // if (CommandPalette().Visibility() == Visibility::Collapsed) + // { + // _FocusActiveControl(nullptr, nullptr); + // } + // }); + CommandPalette().DispatchCommandRequested({ get_weak(), &TerminalPage::_OnDispatchCommandRequested }); + CommandPalette().CommandLineExecutionRequested({ get_weak(), &TerminalPage::_OnCommandLineExecutionRequested }); + CommandPalette().SwitchToTabRequested({ get_weak(), &TerminalPage::_OnSwitchToTabRequested }); + CommandPalette().PreviewAction({ get_weak(), &TerminalPage::_PreviewActionHandler }); // Settings AllowDependentAnimations will affect whether animations are // enabled application-wide, so we don't need to check it each time we @@ -269,7 +288,7 @@ namespace winrt::TerminalApp::implementation // window will be, so they can subdivide that space. // // _OnFirstLayout will remove this handler so it doesn't get called more than once. - _layoutUpdatedRevoker = _tabContent.LayoutUpdated(winrt::auto_revoke, { this, &TerminalPage::_OnFirstLayout }); + _layoutUpdatedRevoker = _tabContent.LayoutUpdated(winrt::auto_revoke, { get_weak(), &TerminalPage::_OnFirstLayout }); _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); @@ -788,7 +807,7 @@ namespace winrt::TerminalApp::implementation ico.Symbol(WUX::Controls::Symbol::Setting); settingsItem.Icon(ico); - settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick }); + settingsItem.Click({ get_weak(), &TerminalPage::_SettingsButtonOnClick }); newTabFlyout.Items().Append(settingsItem); auto actionMap = _settings.ActionMap(); @@ -811,7 +830,7 @@ namespace winrt::TerminalApp::implementation commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); commandPaletteFlyout.Icon(commandPaletteIcon); - commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick }); + commandPaletteFlyout.Click({ get_weak(), &TerminalPage::_CommandPaletteButtonOnClick }); newTabFlyout.Items().Append(commandPaletteFlyout); const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) }; @@ -833,7 +852,7 @@ namespace winrt::TerminalApp::implementation aboutIcon.Symbol(WUX::Controls::Symbol::Help); aboutFlyout.Icon(aboutIcon); - aboutFlyout.Click({ this, &TerminalPage::_AboutButtonOnClick }); + aboutFlyout.Click({ get_weak(), &TerminalPage::_AboutButtonOnClick }); newTabFlyout.Items().Append(aboutFlyout); } @@ -845,9 +864,9 @@ namespace winrt::TerminalApp::implementation // Since the previous focus location might be discarded in the background, // e.g., the command palette will be dismissed by the menu, // and then closing the fly-out will move the focus to wrong location. - newTabFlyout.Opening([this](auto&&, auto&&) { - _FocusCurrentTab(true); - }); + // newTabFlyout.Opening([this](auto&&, auto&&) { + // _FocusCurrentTab(true); + // }); _newTabButton.Flyout(newTabFlyout); } @@ -1554,16 +1573,16 @@ namespace winrt::TerminalApp::implementation // - term: The newly created TermControl to connect the events for void TerminalPage::_RegisterTerminalEvents(TermControl term) { - term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler }); + term.RaiseNotice({ get_weak(), &TerminalPage::_ControlNoticeRaisedHandler }); // Add an event handler when the terminal's selection wants to be copied. // When the text buffer data is retrieved, we'll copy the data into the Clipboard - term.CopyToClipboard({ this, &TerminalPage::_CopyToClipboardHandler }); + term.CopyToClipboard({ get_weak(), &TerminalPage::_CopyToClipboardHandler }); // Add an event handler when the terminal wants to paste data from the Clipboard. - term.PasteFromClipboard({ this, &TerminalPage::_PasteFromClipboardHandler }); + term.PasteFromClipboard({ get_weak(), &TerminalPage::_PasteFromClipboardHandler }); - term.OpenHyperlink({ this, &TerminalPage::_OpenHyperlinkHandler }); + term.OpenHyperlink({ get_weak(), &TerminalPage::_OpenHyperlinkHandler }); term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler }); term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler }); @@ -3405,7 +3424,7 @@ namespace winrt::TerminalApp::implementation } // GH#8767 - let unhandled keys in the SUI try to run commands too. - sui.KeyDown({ this, &TerminalPage::_KeyDownHandler }); + sui.KeyDown({ get_weak(), &TerminalPage::_KeyDownHandler }); sui.OpenJson([weakThis{ get_weak() }](auto&& /*s*/, winrt::Microsoft::Terminal::Settings::Model::SettingsTarget e) { if (auto page{ weakThis.get() }) @@ -3436,7 +3455,7 @@ namespace winrt::TerminalApp::implementation // Update the state of the close button to match the current theme _updateTabCloseButton(tabViewItem); - tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick }); + tabViewItem.PointerPressed({ get_weak(), &TerminalPage::_OnTabClick }); // When the tab requests close, try to close it (prompt for approval, if required) newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { @@ -3850,7 +3869,7 @@ namespace winrt::TerminalApp::implementation else if (key == Windows::System::VirtualKey::Escape) { // User wants to discard the changes they made - WindowRenamerTextBox().Text(WindowProperties().WindowName()); + // WindowRenamerTextBox().Text(WindowProperties().WindowName()); WindowRenamer().IsOpen(false); _renamerPressedEnter = false; } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 7f2d0e13b4f..5028647af8a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -54,6 +54,8 @@ namespace winrt::TerminalApp::implementation { public: TerminalPage(); + void BigRedButton(); + ~TerminalPage(); // This implements shobjidl's IInitializeWithWindow, but due to a XAML Compiler bug we cannot // put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331 @@ -116,10 +118,10 @@ namespace winrt::TerminalApp::implementation const winrt::hstring cwd = L""); // For the sake of XAML binding: - winrt::hstring WindowName() const noexcept { return _WindowProperties.WindowName(); }; - uint64_t WindowId() const noexcept { return _WindowProperties.WindowId(); }; - winrt::hstring WindowIdForDisplay() const noexcept { return _WindowProperties.WindowIdForDisplay(); }; - winrt::hstring WindowNameForDisplay() const noexcept { return _WindowProperties.WindowNameForDisplay(); }; + winrt::hstring WindowName() const noexcept { return L""; /*return _WindowProperties.WindowName();*/ }; + uint64_t WindowId() const noexcept { return 1u; /*return _WindowProperties.WindowId();*/ }; + winrt::hstring WindowIdForDisplay() const noexcept { return L""; /*return _WindowProperties.WindowIdForDisplay();*/ }; + winrt::hstring WindowNameForDisplay() const noexcept { return L""; /*return _WindowProperties.WindowNameForDisplay();*/ }; void SetNumberOfOpenWindows(const uint64_t value); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index a6fcd3f8e80..c2fd6c325eb 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -132,12 +132,18 @@ namespace winrt::TerminalApp::implementation _isElevated = ::Microsoft::Console::Utils::IsElevated(); } + TerminalWindow::~TerminalWindow() + { + _root->BigRedButton(); + _root = nullptr; + } + // Method Description: // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT TerminalWindow::Initialize(HWND hwnd) { _root = winrt::make_self(); - _root->WindowProperties(*this); + // _root->WindowProperties(*this); _dialog = ContentDialog{}; // Pass commandline args into the TerminalPage. If we were supposed to @@ -237,30 +243,30 @@ namespace winrt::TerminalApp::implementation } _root->SetSettings(_settings, false); - _root->Loaded({ this, &TerminalWindow::_OnLoaded }); - _root->Initialized([this](auto&&, auto&&) { - // GH#288 - When we finish initialization, if the user wanted us - // launched _fullscreen_, toggle fullscreen mode. This will make sure - // that the window size is _first_ set up as something sensible, so - // leaving fullscreen returns to a reasonable size. - const auto launchMode = this->GetLaunchMode(); - if (IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) - { - _root->SetFocusMode(true); - } - - // The IslandWindow handles (creating) the maximized state - // we just want to record it here on the page as well. - if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode)) - { - _root->Maximized(true); - } - - if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow()) - { - _root->SetFullscreen(true); - } - }); + _root->Loaded({ get_weak(), &TerminalWindow::_OnLoaded }); + // _root->Initialized([this](auto&&, auto&&) { + // // GH#288 - When we finish initialization, if the user wanted us + // // launched _fullscreen_, toggle fullscreen mode. This will make sure + // // that the window size is _first_ set up as something sensible, so + // // leaving fullscreen returns to a reasonable size. + // const auto launchMode = this->GetLaunchMode(); + // if (IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) + // { + // _root->SetFocusMode(true); + // } + + // // The IslandWindow handles (creating) the maximized state + // // we just want to record it here on the page as well. + // if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode)) + // { + // _root->Maximized(true); + // } + + // if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow()) + // { + // _root->SetFullscreen(true); + // } + // }); _root->Create(); _RefreshThemeRoutine(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 5bec441ed99..04bb63b289c 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -38,7 +38,7 @@ namespace winrt::TerminalApp::implementation { public: TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult); - ~TerminalWindow() = default; + ~TerminalWindow(); STDMETHODIMP Initialize(HWND hwnd); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 63eb0e7693d..eeb9df0b755 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -108,8 +108,8 @@ AppHost::~AppHost() _revokers = {}; _showHideWindowThrottler.reset(); - _window = nullptr; + _windowLogic = nullptr; } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 92cabd3375a..6e7338739fb 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -62,9 +62,12 @@ WindowEmperor::~WindowEmperor() // for this thread. It's a real thinker. // // I need someone to help take a look at this with me. - + _dispatcher = nullptr; + _getWindowLayoutThrottler.reset(); + _manager = nullptr; _app.Close(); _app = nullptr; + // Sleep(20000); } void _buildArgsFromCommandline(std::vector& args) @@ -142,7 +145,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 6cc6093c986..c7c8b038dda 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -29,6 +29,14 @@ void WindowThread::Start() const auto exitCode = WindowProc(); _host = nullptr; + { + MSG msg = {}; + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) + { + ::DispatchMessageW(&msg); + } + } + _ExitedHandlers(_peasant.GetID()); return exitCode; }); From 2bc578bc6c7acc3a50910a492608a0a82df6aae7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 15 Feb 2023 15:33:49 -0600 Subject: [PATCH 071/122] Revert "I'm committing everything just in case, but I'm gonna revert and just do the part that fixed it" This reverts commit 667b6586d2ae91e1903c7b00396e3c398629bd21. --- src/cascadia/TerminalApp/TabManagement.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 77 +++++++------------ src/cascadia/TerminalApp/TerminalPage.h | 10 +-- src/cascadia/TerminalApp/TerminalWindow.cpp | 56 ++++++-------- src/cascadia/TerminalApp/TerminalWindow.h | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- .../WindowsTerminal/WindowEmperor.cpp | 7 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 8 -- 8 files changed, 63 insertions(+), 101 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 779cb491e2c..f1134efbd36 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -226,7 +226,7 @@ namespace winrt::TerminalApp::implementation } } - tabViewItem.PointerReleased({ get_weak(), &TerminalPage::_OnTabClick }); + tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick }); // When the tab requests close, try to close it (prompt for approval, if required) newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 81438e96c23..b1ab627b8f1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -39,7 +39,7 @@ using namespace ::Microsoft::Console; using namespace ::Microsoft::Terminal::Core; using namespace std::chrono_literals; -#define HOOKUP_ACTION(action) _actionDispatch->action({ get_weak(), &TerminalPage::_Handle##action }); +#define HOOKUP_ACTION(action) _actionDispatch->action({ this, &TerminalPage::_Handle##action }); namespace winrt { @@ -60,25 +60,6 @@ namespace winrt::TerminalApp::implementation InitializeComponent(); } - TerminalPage::~TerminalPage() - { - // Dummy line to get a breakpoint somewnere. We can remove this. - _WindowProperties = nullptr; - } - - void TerminalPage::BigRedButton() - { - _windowIdToast = nullptr; - _windowRenameFailedToast = nullptr; - - _actionDispatch = nullptr; - _tabView = nullptr; - _tabRow = nullptr; - _tabContent = nullptr; - _newTabButton = nullptr; - _tabColorPicker = nullptr; - _settings = nullptr; - } // Method Description: // - implements the IInitializeWithWindow interface from shobjidl_core. // - We're going to use this HWND as the owner for the ConPTY windows, via @@ -241,7 +222,7 @@ namespace winrt::TerminalApp::implementation _RegisterActionCallbacks(); // Hook up inbound connection event handler - ConptyConnection::NewConnection({ get_weak(), &TerminalPage::_OnNewConnection }); + ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection }); //Event Bindings (Early) _newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) { @@ -256,9 +237,9 @@ namespace winrt::TerminalApp::implementation page->NewTerminalByDrop(e); } }); - _tabView.SelectionChanged({ get_weak(), &TerminalPage::_OnTabSelectionChanged }); - _tabView.TabCloseRequested({ get_weak(), &TerminalPage::_OnTabCloseRequested }); - _tabView.TabItemsChanged({ get_weak(), &TerminalPage::_OnTabItemsChanged }); + _tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged }); + _tabView.TabCloseRequested({ this, &TerminalPage::_OnTabCloseRequested }); + _tabView.TabItemsChanged({ this, &TerminalPage::_OnTabItemsChanged }); _CreateNewTabFlyout(); @@ -267,16 +248,16 @@ namespace winrt::TerminalApp::implementation // When the visibility of the command palette changes to "collapsed", // the palette has been closed. Toss focus back to the currently active // control. - // CommandPalette().RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) { - // if (CommandPalette().Visibility() == Visibility::Collapsed) - // { - // _FocusActiveControl(nullptr, nullptr); - // } - // }); - CommandPalette().DispatchCommandRequested({ get_weak(), &TerminalPage::_OnDispatchCommandRequested }); - CommandPalette().CommandLineExecutionRequested({ get_weak(), &TerminalPage::_OnCommandLineExecutionRequested }); - CommandPalette().SwitchToTabRequested({ get_weak(), &TerminalPage::_OnSwitchToTabRequested }); - CommandPalette().PreviewAction({ get_weak(), &TerminalPage::_PreviewActionHandler }); + CommandPalette().RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) { + if (CommandPalette().Visibility() == Visibility::Collapsed) + { + _FocusActiveControl(nullptr, nullptr); + } + }); + CommandPalette().DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested }); + CommandPalette().CommandLineExecutionRequested({ this, &TerminalPage::_OnCommandLineExecutionRequested }); + CommandPalette().SwitchToTabRequested({ this, &TerminalPage::_OnSwitchToTabRequested }); + CommandPalette().PreviewAction({ this, &TerminalPage::_PreviewActionHandler }); // Settings AllowDependentAnimations will affect whether animations are // enabled application-wide, so we don't need to check it each time we @@ -288,7 +269,7 @@ namespace winrt::TerminalApp::implementation // window will be, so they can subdivide that space. // // _OnFirstLayout will remove this handler so it doesn't get called more than once. - _layoutUpdatedRevoker = _tabContent.LayoutUpdated(winrt::auto_revoke, { get_weak(), &TerminalPage::_OnFirstLayout }); + _layoutUpdatedRevoker = _tabContent.LayoutUpdated(winrt::auto_revoke, { this, &TerminalPage::_OnFirstLayout }); _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); @@ -807,7 +788,7 @@ namespace winrt::TerminalApp::implementation ico.Symbol(WUX::Controls::Symbol::Setting); settingsItem.Icon(ico); - settingsItem.Click({ get_weak(), &TerminalPage::_SettingsButtonOnClick }); + settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick }); newTabFlyout.Items().Append(settingsItem); auto actionMap = _settings.ActionMap(); @@ -830,7 +811,7 @@ namespace winrt::TerminalApp::implementation commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); commandPaletteFlyout.Icon(commandPaletteIcon); - commandPaletteFlyout.Click({ get_weak(), &TerminalPage::_CommandPaletteButtonOnClick }); + commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick }); newTabFlyout.Items().Append(commandPaletteFlyout); const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) }; @@ -852,7 +833,7 @@ namespace winrt::TerminalApp::implementation aboutIcon.Symbol(WUX::Controls::Symbol::Help); aboutFlyout.Icon(aboutIcon); - aboutFlyout.Click({ get_weak(), &TerminalPage::_AboutButtonOnClick }); + aboutFlyout.Click({ this, &TerminalPage::_AboutButtonOnClick }); newTabFlyout.Items().Append(aboutFlyout); } @@ -864,9 +845,9 @@ namespace winrt::TerminalApp::implementation // Since the previous focus location might be discarded in the background, // e.g., the command palette will be dismissed by the menu, // and then closing the fly-out will move the focus to wrong location. - // newTabFlyout.Opening([this](auto&&, auto&&) { - // _FocusCurrentTab(true); - // }); + newTabFlyout.Opening([this](auto&&, auto&&) { + _FocusCurrentTab(true); + }); _newTabButton.Flyout(newTabFlyout); } @@ -1573,16 +1554,16 @@ namespace winrt::TerminalApp::implementation // - term: The newly created TermControl to connect the events for void TerminalPage::_RegisterTerminalEvents(TermControl term) { - term.RaiseNotice({ get_weak(), &TerminalPage::_ControlNoticeRaisedHandler }); + term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler }); // Add an event handler when the terminal's selection wants to be copied. // When the text buffer data is retrieved, we'll copy the data into the Clipboard - term.CopyToClipboard({ get_weak(), &TerminalPage::_CopyToClipboardHandler }); + term.CopyToClipboard({ this, &TerminalPage::_CopyToClipboardHandler }); // Add an event handler when the terminal wants to paste data from the Clipboard. - term.PasteFromClipboard({ get_weak(), &TerminalPage::_PasteFromClipboardHandler }); + term.PasteFromClipboard({ this, &TerminalPage::_PasteFromClipboardHandler }); - term.OpenHyperlink({ get_weak(), &TerminalPage::_OpenHyperlinkHandler }); + term.OpenHyperlink({ this, &TerminalPage::_OpenHyperlinkHandler }); term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler }); term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler }); @@ -3424,7 +3405,7 @@ namespace winrt::TerminalApp::implementation } // GH#8767 - let unhandled keys in the SUI try to run commands too. - sui.KeyDown({ get_weak(), &TerminalPage::_KeyDownHandler }); + sui.KeyDown({ this, &TerminalPage::_KeyDownHandler }); sui.OpenJson([weakThis{ get_weak() }](auto&& /*s*/, winrt::Microsoft::Terminal::Settings::Model::SettingsTarget e) { if (auto page{ weakThis.get() }) @@ -3455,7 +3436,7 @@ namespace winrt::TerminalApp::implementation // Update the state of the close button to match the current theme _updateTabCloseButton(tabViewItem); - tabViewItem.PointerPressed({ get_weak(), &TerminalPage::_OnTabClick }); + tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick }); // When the tab requests close, try to close it (prompt for approval, if required) newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) { @@ -3869,7 +3850,7 @@ namespace winrt::TerminalApp::implementation else if (key == Windows::System::VirtualKey::Escape) { // User wants to discard the changes they made - // WindowRenamerTextBox().Text(WindowProperties().WindowName()); + WindowRenamerTextBox().Text(WindowProperties().WindowName()); WindowRenamer().IsOpen(false); _renamerPressedEnter = false; } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 5028647af8a..7f2d0e13b4f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -54,8 +54,6 @@ namespace winrt::TerminalApp::implementation { public: TerminalPage(); - void BigRedButton(); - ~TerminalPage(); // This implements shobjidl's IInitializeWithWindow, but due to a XAML Compiler bug we cannot // put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331 @@ -118,10 +116,10 @@ namespace winrt::TerminalApp::implementation const winrt::hstring cwd = L""); // For the sake of XAML binding: - winrt::hstring WindowName() const noexcept { return L""; /*return _WindowProperties.WindowName();*/ }; - uint64_t WindowId() const noexcept { return 1u; /*return _WindowProperties.WindowId();*/ }; - winrt::hstring WindowIdForDisplay() const noexcept { return L""; /*return _WindowProperties.WindowIdForDisplay();*/ }; - winrt::hstring WindowNameForDisplay() const noexcept { return L""; /*return _WindowProperties.WindowNameForDisplay();*/ }; + winrt::hstring WindowName() const noexcept { return _WindowProperties.WindowName(); }; + uint64_t WindowId() const noexcept { return _WindowProperties.WindowId(); }; + winrt::hstring WindowIdForDisplay() const noexcept { return _WindowProperties.WindowIdForDisplay(); }; + winrt::hstring WindowNameForDisplay() const noexcept { return _WindowProperties.WindowNameForDisplay(); }; void SetNumberOfOpenWindows(const uint64_t value); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index c2fd6c325eb..a6fcd3f8e80 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -132,18 +132,12 @@ namespace winrt::TerminalApp::implementation _isElevated = ::Microsoft::Console::Utils::IsElevated(); } - TerminalWindow::~TerminalWindow() - { - _root->BigRedButton(); - _root = nullptr; - } - // Method Description: // - Implements the IInitializeWithWindow interface from shobjidl_core. HRESULT TerminalWindow::Initialize(HWND hwnd) { _root = winrt::make_self(); - // _root->WindowProperties(*this); + _root->WindowProperties(*this); _dialog = ContentDialog{}; // Pass commandline args into the TerminalPage. If we were supposed to @@ -243,30 +237,30 @@ namespace winrt::TerminalApp::implementation } _root->SetSettings(_settings, false); - _root->Loaded({ get_weak(), &TerminalWindow::_OnLoaded }); - // _root->Initialized([this](auto&&, auto&&) { - // // GH#288 - When we finish initialization, if the user wanted us - // // launched _fullscreen_, toggle fullscreen mode. This will make sure - // // that the window size is _first_ set up as something sensible, so - // // leaving fullscreen returns to a reasonable size. - // const auto launchMode = this->GetLaunchMode(); - // if (IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) - // { - // _root->SetFocusMode(true); - // } - - // // The IslandWindow handles (creating) the maximized state - // // we just want to record it here on the page as well. - // if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode)) - // { - // _root->Maximized(true); - // } - - // if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow()) - // { - // _root->SetFullscreen(true); - // } - // }); + _root->Loaded({ this, &TerminalWindow::_OnLoaded }); + _root->Initialized([this](auto&&, auto&&) { + // GH#288 - When we finish initialization, if the user wanted us + // launched _fullscreen_, toggle fullscreen mode. This will make sure + // that the window size is _first_ set up as something sensible, so + // leaving fullscreen returns to a reasonable size. + const auto launchMode = this->GetLaunchMode(); + if (IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) + { + _root->SetFocusMode(true); + } + + // The IslandWindow handles (creating) the maximized state + // we just want to record it here on the page as well. + if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode)) + { + _root->Maximized(true); + } + + if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow()) + { + _root->SetFullscreen(true); + } + }); _root->Create(); _RefreshThemeRoutine(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 04bb63b289c..5bec441ed99 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -38,7 +38,7 @@ namespace winrt::TerminalApp::implementation { public: TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult); - ~TerminalWindow(); + ~TerminalWindow() = default; STDMETHODIMP Initialize(HWND hwnd); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index eeb9df0b755..63eb0e7693d 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -108,8 +108,8 @@ AppHost::~AppHost() _revokers = {}; _showHideWindowThrottler.reset(); + _window = nullptr; - _windowLogic = nullptr; } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 6e7338739fb..92cabd3375a 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -62,12 +62,9 @@ WindowEmperor::~WindowEmperor() // for this thread. It's a real thinker. // // I need someone to help take a look at this with me. - _dispatcher = nullptr; - _getWindowLayoutThrottler.reset(); - _manager = nullptr; + _app.Close(); _app = nullptr; - // Sleep(20000); } void _buildArgsFromCommandline(std::vector& args) @@ -145,7 +142,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index c7c8b038dda..6cc6093c986 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -29,14 +29,6 @@ void WindowThread::Start() const auto exitCode = WindowProc(); _host = nullptr; - { - MSG msg = {}; - while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) - { - ::DispatchMessageW(&msg); - } - } - _ExitedHandlers(_peasant.GetID()); return exitCode; }); From 62a4ee058be054cb3435de05324a3f2231c91905 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 15 Feb 2023 15:39:23 -0600 Subject: [PATCH 072/122] https://media.tenor.com/knFWHfkiwoUAAAAC/im-fine-hades.gif --- src/cascadia/WindowsTerminal/WindowThread.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 6cc6093c986..344f0a53980 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -29,6 +29,13 @@ void WindowThread::Start() const auto exitCode = WindowProc(); _host = nullptr; + { + MSG msg = {}; + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) + { + ::DispatchMessageW(&msg); + } + } _ExitedHandlers(_peasant.GetID()); return exitCode; }); From 091f32c575436c9f6ceafe560fee4da0ca056b3c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 15 Feb 2023 15:43:20 -0600 Subject: [PATCH 073/122] a comment to go with the parent commit --- src/cascadia/WindowsTerminal/WindowThread.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 344f0a53980..7da27b91013 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -29,6 +29,14 @@ void WindowThread::Start() const auto exitCode = WindowProc(); _host = nullptr; + // !! LOAD BEARING !! + // + // Make sure to finish pumping all the messages for our thread here. We + // may think we're all done, but we're not quite. XAML needs more time + // to pump the remaining events through, even at the point we're + // exiting. So do that now. If you don't, then the last tab to close + // will never actually destruct the last tab / TermControl / ControlCore + // / renderer. { MSG msg = {}; while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) From ecab57fba16e2f652c56a417199d55a832587efb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 16 Feb 2023 11:10:06 -0600 Subject: [PATCH 074/122] that was quite silly --- src/cascadia/WindowsTerminal/AppHost.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 63eb0e7693d..43bb3b1add4 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -89,8 +89,8 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, _window->MakeWindow(); - _GetWindowLayoutRequestedToken = _windowManager.GetWindowLayoutRequested([this](auto&&, - const Remoting::GetWindowLayoutArgs& args) { + _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()); @@ -450,7 +450,7 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* 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. - _windowManager.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); + _peasant.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); // 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 From 13257dafa07424641ad8292185e970bc47fc36cd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 16 Feb 2023 11:11:09 -0600 Subject: [PATCH 075/122] 0 TODOs remain --- src/cascadia/WindowsTerminal/AppHost.cpp | 5 ----- .../WindowsTerminal/WindowEmperor.cpp | 22 +------------------ 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 43bb3b1add4..528bc15b3c6 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -226,11 +226,6 @@ void AppHost::_HandleCommandlineArgs() const auto numPeasants = _windowManager.GetNumberOfPeasants(); if (numPeasants == 1) { - // TODO! this is vaguely off by one. Not sure, but if you restore 2 - // windows, you seem to get two copies of the second. Yikes. And - // this wasn't just because I was setting the debug commandline to - // `nt ; nt`. Calling wtd with two persisted windows just creates - // two of the second persisted window, ew. const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); if (_appLogic.ShouldUsePersistedLayout() && layouts && diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 92cabd3375a..ce78ffff513 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -13,12 +13,6 @@ #include "resource.h" #include "NotificationIcon.h" -// This was an enormous dead end. -// #include -// #include -// typedef GUID DXGI_DEBUG_ID; -// const DXGI_DEBUG_ID DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8 }; - using namespace winrt; using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings::Model; @@ -49,20 +43,6 @@ WindowEmperor::WindowEmperor() noexcept : WindowEmperor::~WindowEmperor() { - // This was an enormous dead end. - // if (wil::com_ptr debug; SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(debug.addressof())))) - // { - // //debug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL); - // debug->DisableLeakTrackingForThread(); - // } - // - // TODO! We're still somehow leaking on exit, even though the islands, - // hosts, xaml sources, everything, seem to get closed and dtor'd before - // this point. Theoretically, we shouldn't event have leak reporting enabled - // for this thread. It's a real thinker. - // - // I need someone to help take a look at this with me. - _app.Close(); _app = nullptr; } @@ -142,7 +122,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); From 9a4739659bb6f03efa2b7d9e3c03770b59406758 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 16:51:16 -0600 Subject: [PATCH 076/122] [ainulindale] The Terminal shouldn't crash on a WM_SETTINGCHANGE (cherry picked from commit 76497251509d90dd658f3a5b48d5af036c7c5528) --- src/cascadia/WindowsTerminal/AppHost.cpp | 10 +++++++++- src/cascadia/WindowsTerminal/AppHost.h | 4 ++++ src/cascadia/WindowsTerminal/WindowEmperor.cpp | 5 +++++ src/cascadia/WindowsTerminal/WindowThread.cpp | 2 +- src/cascadia/WindowsTerminal/WindowThread.h | 1 + 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 528bc15b3c6..062baede65a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -333,7 +333,7 @@ void AppHost::Initialize() _window->DragRegionClicked([this]() { _windowLogic.TitlebarClicked(); }); _window->WindowVisibilityChanged([this](bool showOrHide) { _windowLogic.WindowVisibilityChanged(showOrHide); }); - _window->UpdateSettingsRequested([this]() { _appLogic.ReloadSettings(); }); + _window->UpdateSettingsRequested({ this, &AppHost::_requestUpdateSettings }); _revokers.RequestedThemeChanged = _windowLogic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme }); _revokers.FullscreenChanged = _windowLogic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged }); @@ -1195,3 +1195,11 @@ winrt::TerminalApp::TerminalWindow AppHost::Logic() { return _windowLogic; } + +// Bubble the update settings request up to the emperor. We're being called on +// the Window thread, but the Emperor needs to update the settings on the _main_ +// thread. +void AppHost::_requestUpdateSettings() +{ + _UpdateSettingsRequestedHandlers(); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 89b328fe32e..05812db7c25 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -26,6 +26,8 @@ class AppHost static void s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& message); + WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate); + private: std::unique_ptr _window; @@ -121,6 +123,8 @@ class AppHost void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); + void _requestUpdateSettings(); + winrt::event_token _GetWindowLayoutRequestedToken; // Helper struct. By putting these all into one struct, we can revoke them diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index ce78ffff513..72c55c127ad 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -126,6 +126,11 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); + sender->UpdateSettingsRequested([this]() -> winrt::fire_and_forget { + // We MUST be on the main thread to update the settings. We will crash when trying to enumerate fragement extensions otherwise. + co_await wil::resume_foreground(this->_dispatcher); + _app.Logic().ReloadSettings(); + }); // These come in on the sender's thread. Move back to our thread. co_await wil::resume_foreground(_dispatcher); diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 7da27b91013..917435a69f3 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -24,7 +24,7 @@ void WindowThread::Start() _args, _manager, _peasant); - + _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); }); // Enter the main window loop. const auto exitCode = WindowProc(); _host = nullptr; diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 6719734fc6f..720c70b89d6 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -19,6 +19,7 @@ class WindowThread WINRT_CALLBACK(Started, winrt::delegate<>); WINRT_CALLBACK(Exited, winrt::delegate); + WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate); private: winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; From c79f27c3dac686c5f30b6448831d856a61fd57ca Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Feb 2023 08:32:18 -0600 Subject: [PATCH 077/122] std::vectors are better --- src/cascadia/TerminalApp/AppLogic.cpp | 14 +++++++------- src/cascadia/TerminalApp/AppLogic.h | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 9ed6078cfcd..55e1e78d8bd 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -265,10 +265,10 @@ namespace winrt::TerminalApp::implementation return E_INVALIDARG; } - _warnings.Clear(); + _warnings.clear(); for (uint32_t i = 0; i < newSettings.Warnings().Size(); i++) { - _warnings.Append(newSettings.Warnings().GetAt(i)); + _warnings.push_back(newSettings.Warnings().GetAt(i)); } _hasSettingsStartupActions = false; @@ -288,13 +288,13 @@ namespace winrt::TerminalApp::implementation } else { - _warnings.Append(SettingsLoadWarnings::FailedToParseStartupActions); + _warnings.push_back(SettingsLoadWarnings::FailedToParseStartupActions); } } _settings = std::move(newSettings); - hr = (_warnings.Size()) == 0 ? S_OK : S_FALSE; + hr = (_warnings.size()) == 0u ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) { @@ -491,7 +491,7 @@ namespace winrt::TerminalApp::implementation auto ev = winrt::make_self(true, static_cast(_settingsLoadedResult), _settingsLoadExceptionText, - _warnings, + winrt::multi_threaded_vector(std::move(_warnings)), _settings); _SettingsChangedHandlers(*this, *ev); return; @@ -515,7 +515,7 @@ namespace winrt::TerminalApp::implementation auto ev = winrt::make_self(!initialLoad, _settingsLoadedResult, _settingsLoadExceptionText, - _warnings, + winrt::multi_threaded_vector(std::move(_warnings)), _settings); _SettingsChangedHandlers(*this, *ev); } @@ -668,7 +668,7 @@ namespace winrt::TerminalApp::implementation auto ev = winrt::make_self(false, _settingsLoadedResult, _settingsLoadExceptionText, - _warnings, + winrt::multi_threaded_vector(std::move(_warnings)), _settings); auto window = winrt::make_self(*ev); diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 759cfb09500..821ec0f5f10 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -85,7 +85,7 @@ namespace winrt::TerminalApp::implementation std::shared_ptr> _reloadSettings; til::throttled_func_trailing<> _reloadState; - winrt::Windows::Foundation::Collections::IVector _warnings{ winrt::multi_threaded_vector() }; + std::vector _warnings{}; // These fields invoke _reloadSettings and must be destroyed before _reloadSettings. // (C++ destroys members in reverse-declaration-order.) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 89169200e08..9361f533460 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1799,11 +1799,11 @@ namespace winrt::TerminalApp::implementation } // If the user set a custom name, save it - if (_WindowProperties.WindowName() != L"") + if (const auto windowName = _WindowProperties.WindowName(); !windowName.empty()) { ActionAndArgs action; action.Action(ShortcutAction::RenameWindow); - RenameWindowArgs args{ _WindowProperties.WindowName() }; + RenameWindowArgs args{ windowName }; action.Args(args); actions.emplace_back(std::move(action)); From cacb822cf662a341903ec4888a21fb3cfab20369 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Feb 2023 10:16:22 -0600 Subject: [PATCH 078/122] Dustin wanted a setting to opt-in to isolated mode, and this seems to work --- .vscode/settings.json | 4 +- src/cascadia/Remoting/WindowManager.cpp | 45 +++++++-- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/Remoting/WindowManager.idl | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 12 ++- src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 2 +- .../GlobalAppSettings.idl | 1 + .../TerminalSettingsModel/MTSMSettings.h | 91 ++++++++++--------- .../WindowsTerminal/WindowEmperor.cpp | 4 +- 10 files changed, 102 insertions(+), 62 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 604f91797d9..bb2f304e54a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,7 +6,7 @@ "C_Cpp.loggingLevel": "None", "files.associations": { "xstring": "cpp", - "*.idl": "cpp", + "*.idl": "midl3", "array": "cpp", "future": "cpp", "istream": "cpp", @@ -106,4 +106,4 @@ "**/packages/**": true, "**/Generated Files/**": true } -} \ No newline at end of file +} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 2af998e9ff1..f406eda3b54 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -89,14 +89,21 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _RequestNewWindowHandlers(sender, args); } - Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline2(const Remoting::CommandlineArgs& args) + Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args, const bool isolatedMode) { bool shouldCreateWindow = false; - _createMonarch(); + if (!isolatedMode) + { + // _createMonarch always attempts to connect an existing monarch. In + // isolated mode, we don't want to do that. + _createMonarch(); + } + if (_monarch) { - // We connected to a monarch instance, not us though. + // We connected to a monarch instance, not us though. This won't hit + // in isolated mode. shouldCreateWindow = false; std::optional givenID; @@ -155,15 +162,33 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // Congrats! This is now THE PROCESS. It's the only one that's // getting any windows. - _registerAsMonarch(); - _createMonarch(); + + // In isolated mode, we don't want to register as the monarch, + // we just want to make a local one. So we'll skip this step. + // The condition below it will handle making the unregistered + // local monarch. + + if (!isolatedMode) + { + _registerAsMonarch(); + _createMonarch(); + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_IntentionallyIsolated", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } + if (!_monarch) { - // Something catastrophically bad happened here. But we - // don't want to just exit immediately. No, instead, we'll - // just instantiate a local Monarch instance. We're firmly - // in the realm of undefined behavior, but better to have - // some window than not. + // Something catastrophically bad happened here OR we were + // intentionally in isolated mode. We don't want to just + // exit immediately. Instead, we'll just instantiate a local + // Monarch instance, without registering it. We're firmly in + // the realm of undefined behavior, but better to have some + // window than not. _monarch = winrt::make(); TraceLoggingWrite(g_hRemotingProvider, "WindowManager_FailedToCoCreate", diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index cc647fc4daf..e4ec245d8b0 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -28,7 +28,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation public: WindowManager(); ~WindowManager(); - winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline2(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args, const bool isolatedMode); Remoting::Peasant CreatePeasant(Remoting::WindowRequestedArgs args); void SignalClose(Remoting::Peasant peasant); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 79b47c75226..ad2986888b4 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -8,7 +8,7 @@ namespace Microsoft.Terminal.Remoting { WindowManager(); - ProposeCommandlineResult ProposeCommandline2(CommandlineArgs args); + ProposeCommandlineResult ProposeCommandline(CommandlineArgs args, Boolean isolatedMode); Peasant CreatePeasant(WindowRequestedArgs args); void SignalClose(Peasant p); diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 462ebb01311..64ae4b2d5f7 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -657,6 +657,16 @@ namespace winrt::TerminalApp::implementation return _settings.GlobalSettings().CurrentTheme(); } + bool AppLogic::IsolatedMode() + { + if (!_loadedInitialSettings) + { + // Load settings if we haven't already + ReloadSettings(); + } + return _settings.GlobalSettings().IsolatedMode(); + } + TerminalApp::TerminalWindow AppLogic::CreateNewWindow() { if (_settings == nullptr) @@ -682,7 +692,7 @@ namespace winrt::TerminalApp::implementation bool AppLogic::ShouldUsePersistedLayout() const { - return _settings.GlobalSettings().ShouldUsePersistedLayout(); + return _settings.GlobalSettings().ShouldUsePersistedLayout() && !_settings.GlobalSettings().IsolatedMode(); } void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 098d0e3b3f5..6ab4722a2aa 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -64,6 +64,7 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IMapView GlobalHotkeys(); Microsoft::Terminal::Settings::Model::Theme Theme(); + bool IsolatedMode(); TerminalApp::TerminalWindow CreateNewWindow(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 8499d53a52c..dea3281142e 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -42,6 +42,7 @@ namespace TerminalApp void ReloadSettings(); Microsoft.Terminal.Settings.Model.Theme Theme { get; }; + Boolean IsolatedMode { get; }; FindTargetWindowResult FindTargetWindow(String[] args); @@ -52,6 +53,5 @@ namespace TerminalApp IMapView GlobalHotkeys(); event Windows.Foundation.TypedEventHandler SettingsChanged; - } } diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index eb5fa11e041..c0ea9ddb302 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -97,6 +97,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, ShowAdminShield); INHERITABLE_SETTING(IVector, NewTabMenu); INHERITABLE_SETTING(Boolean, EnableColorSelection); + INHERITABLE_SETTING(Boolean, IsolatedMode); Windows.Foundation.Collections.IMapView ColorSchemes(); void AddColorScheme(ColorScheme scheme); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 73d7cd80009..0887f64ee50 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -18,51 +18,52 @@ Author(s): // Macro format (defaultArgs are optional): // (type, name, jsonKey, defaultArgs) -#define MTSM_GLOBAL_SETTINGS(X) \ - X(int32_t, InitialRows, "initialRows", 30) \ - X(int32_t, InitialCols, "initialCols", 80) \ - X(hstring, WordDelimiters, "wordDelimiters", DEFAULT_WORD_DELIMITERS) \ - X(bool, CopyOnSelect, "copyOnSelect", false) \ - X(bool, FocusFollowMouse, "focusFollowMouse", false) \ - X(bool, ForceFullRepaintRendering, "experimental.rendering.forceFullRepaint", false) \ - X(bool, SoftwareRendering, "experimental.rendering.software", false) \ - X(bool, UseBackgroundImageForWindow, "experimental.useBackgroundImageForWindow", false) \ - X(bool, ForceVTInput, "experimental.input.forceVT", false) \ - X(bool, TrimBlockSelection, "trimBlockSelection", true) \ - X(bool, DetectURLs, "experimental.detectURLs", true) \ - X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \ - X(Model::NewTabPosition, NewTabPosition, "newTabPosition", Model::NewTabPosition::AfterLastTab) \ - X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \ - X(bool, ConfirmCloseAllTabs, "confirmCloseAllTabs", true) \ - X(Model::ThemePair, Theme, "theme") \ - X(hstring, Language, "language") \ - X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \ - X(bool, UseAcrylicInTabRow, "useAcrylicInTabRow", false) \ - X(bool, ShowTabsInTitlebar, "showTabsInTitlebar", true) \ - X(bool, InputServiceWarning, "inputServiceWarning", true) \ - X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, "copyFormatting", 0) \ - X(bool, WarnAboutLargePaste, "largePasteWarning", true) \ - X(bool, WarnAboutMultiLinePaste, "multiLinePasteWarning", true) \ - X(Model::LaunchPosition, InitialPosition, "initialPosition", nullptr, nullptr) \ - X(bool, CenterOnLaunch, "centerOnLaunch", false) \ - X(Model::FirstWindowPreference, FirstWindowPreference, "firstWindowPreference", FirstWindowPreference::DefaultProfile) \ - X(Model::LaunchMode, LaunchMode, "launchMode", LaunchMode::DefaultMode) \ - X(bool, SnapToGridOnResize, "snapToGridOnResize", true) \ - X(bool, DebugFeaturesEnabled, "debugFeatures", debugFeaturesDefault) \ - X(bool, StartOnUserLogin, "startOnUserLogin", false) \ - X(bool, AlwaysOnTop, "alwaysOnTop", false) \ - X(bool, AutoHideWindow, "autoHideWindow", false) \ - X(Model::TabSwitcherMode, TabSwitcherMode, "tabSwitcherMode", Model::TabSwitcherMode::InOrder) \ - X(bool, DisableAnimations, "disableAnimations", false) \ - X(hstring, StartupActions, "startupActions", L"") \ - X(Model::WindowingMode, WindowingBehavior, "windowingBehavior", Model::WindowingMode::UseNew) \ - X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \ - X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \ - X(winrt::Windows::Foundation::Collections::IVector, DisabledProfileSources, "disabledProfileSources", nullptr) \ - X(bool, ShowAdminShield, "showAdminShield", true) \ - X(bool, TrimPaste, "trimPaste", true) \ - X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \ - X(winrt::Windows::Foundation::Collections::IVector, NewTabMenu, "newTabMenu", winrt::single_threaded_vector({ Model::RemainingProfilesEntry{} })) +#define MTSM_GLOBAL_SETTINGS(X) \ + X(int32_t, InitialRows, "initialRows", 30) \ + X(int32_t, InitialCols, "initialCols", 80) \ + X(hstring, WordDelimiters, "wordDelimiters", DEFAULT_WORD_DELIMITERS) \ + X(bool, CopyOnSelect, "copyOnSelect", false) \ + X(bool, FocusFollowMouse, "focusFollowMouse", false) \ + X(bool, ForceFullRepaintRendering, "experimental.rendering.forceFullRepaint", false) \ + X(bool, SoftwareRendering, "experimental.rendering.software", false) \ + X(bool, UseBackgroundImageForWindow, "experimental.useBackgroundImageForWindow", false) \ + X(bool, ForceVTInput, "experimental.input.forceVT", false) \ + X(bool, TrimBlockSelection, "trimBlockSelection", true) \ + X(bool, DetectURLs, "experimental.detectURLs", true) \ + X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \ + X(Model::NewTabPosition, NewTabPosition, "newTabPosition", Model::NewTabPosition::AfterLastTab) \ + X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \ + X(bool, ConfirmCloseAllTabs, "confirmCloseAllTabs", true) \ + X(Model::ThemePair, Theme, "theme") \ + X(hstring, Language, "language") \ + X(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, "tabWidthMode", winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal) \ + X(bool, UseAcrylicInTabRow, "useAcrylicInTabRow", false) \ + X(bool, ShowTabsInTitlebar, "showTabsInTitlebar", true) \ + X(bool, InputServiceWarning, "inputServiceWarning", true) \ + X(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, "copyFormatting", 0) \ + X(bool, WarnAboutLargePaste, "largePasteWarning", true) \ + X(bool, WarnAboutMultiLinePaste, "multiLinePasteWarning", true) \ + X(Model::LaunchPosition, InitialPosition, "initialPosition", nullptr, nullptr) \ + X(bool, CenterOnLaunch, "centerOnLaunch", false) \ + X(Model::FirstWindowPreference, FirstWindowPreference, "firstWindowPreference", FirstWindowPreference::DefaultProfile) \ + X(Model::LaunchMode, LaunchMode, "launchMode", LaunchMode::DefaultMode) \ + X(bool, SnapToGridOnResize, "snapToGridOnResize", true) \ + X(bool, DebugFeaturesEnabled, "debugFeatures", debugFeaturesDefault) \ + X(bool, StartOnUserLogin, "startOnUserLogin", false) \ + X(bool, AlwaysOnTop, "alwaysOnTop", false) \ + X(bool, AutoHideWindow, "autoHideWindow", false) \ + X(Model::TabSwitcherMode, TabSwitcherMode, "tabSwitcherMode", Model::TabSwitcherMode::InOrder) \ + X(bool, DisableAnimations, "disableAnimations", false) \ + X(hstring, StartupActions, "startupActions", L"") \ + X(Model::WindowingMode, WindowingBehavior, "windowingBehavior", Model::WindowingMode::UseNew) \ + X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \ + X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \ + X(winrt::Windows::Foundation::Collections::IVector, DisabledProfileSources, "disabledProfileSources", nullptr) \ + X(bool, ShowAdminShield, "showAdminShield", true) \ + X(bool, TrimPaste, "trimPaste", true) \ + X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \ + X(winrt::Windows::Foundation::Collections::IVector, NewTabMenu, "newTabMenu", winrt::single_threaded_vector({ Model::RemainingProfilesEntry{} })) \ + X(bool, IsolatedMode, "compatibility.isolatedMode", false) #define MTSM_PROFILE_SETTINGS(X) \ X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \ diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 72c55c127ad..62814fc24f5 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -77,7 +77,9 @@ bool WindowEmperor::HandleCommandlineArgs() Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; - const auto result = _manager.ProposeCommandline2(eventArgs); + const auto isolatedMode{ _app.Logic().IsolatedMode() }; + + const auto result = _manager.ProposeCommandline(eventArgs, isolatedMode); if (result.ShouldCreateWindow()) { From b589d092e9cae72186549ca953e9da5b6bdc206d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 11:22:29 -0600 Subject: [PATCH 079/122] Lots of PR nits --- src/cascadia/TerminalApp/AppLogic.cpp | 18 +++-- .../TerminalApp/TerminalAppLib.vcxproj | 3 + src/cascadia/TerminalApp/TerminalWindow.cpp | 74 ++++++++++--------- src/cascadia/TerminalApp/TerminalWindow.h | 6 +- src/cascadia/TerminalApp/TerminalWindow.idl | 2 - 5 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 55e1e78d8bd..9af911a7813 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -45,6 +45,17 @@ static const std::array settingsLoadErrorsLabels{ static_assert(settingsLoadErrorsLabels.size() == static_cast(SettingsLoadErrors::ERRORS_SIZE)); +// Function Description: +// - General-purpose helper for looking up a localized string for a +// warning/error. First will look for the given key in the provided map of +// keys->strings, where the values in the map are ResourceKeys. If it finds +// one, it will lookup the localized string from that ResourceKey. +// - If it does not find a key, it'll return an empty string +// Arguments: +// - key: the value to use to look for a resource key in the given map +// - map: A map of keys->Resource keys. +// Return Value: +// - the localized string for the given type, if it exists. template winrt::hstring _GetMessageText(uint32_t index, const T& keys) { @@ -67,10 +78,6 @@ static winrt::hstring _GetErrorText(SettingsLoadErrors error) return _GetMessageText(static_cast(error), settingsLoadErrorsLabels); } -// clang-format on - -//////////////////////////////////////////////////////////////////////////////// - static constexpr std::wstring_view StartupTaskName = L"StartTerminalOnLoginTask"; namespace winrt::TerminalApp::implementation @@ -121,9 +128,6 @@ namespace winrt::TerminalApp::implementation // cause you to chase down the rabbit hole of "why is App not // registered?" when it definitely is. - // The TerminalPage has to be constructed during our construction, to - // make sure that there's a terminal page for callers of - // SetTitleBarContent _isElevated = ::Microsoft::Console::Utils::IsElevated(); _reloadSettings = std::make_shared>(winrt::Windows::System::DispatcherQueue::GetForCurrentThread(), std::chrono::milliseconds(100), [weakSelf = get_weak()]() { diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 7fb88842a1c..d72908d5b2a 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -141,6 +141,9 @@ TerminalWindow.idl + + TerminalWindow.idl + diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 75d0c4c6592..b1c0c4142ff 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -121,6 +121,9 @@ namespace winrt::TerminalApp::implementation _settings{ settingsLoadedResult.NewSettings() }, _initialLoadResult{ settingsLoadedResult } { + // The TerminalPage has to be constructed during our construction, to + // make sure that there's a terminal page for callers of + // SetTitleBarContent _root = winrt::make_self(); _root->WindowProperties(*this); _dialog = ContentDialog{}; @@ -131,10 +134,6 @@ namespace winrt::TerminalApp::implementation // cause you to chase down the rabbit hole of "why is App not // registered?" when it definitely is. - // The TerminalPage has to be constructed during our construction, to - // make sure that there's a terminal page for callers of - // SetTitleBarContent - _isElevated = ::Microsoft::Console::Utils::IsElevated(); } // Method Description: @@ -183,7 +182,25 @@ namespace winrt::TerminalApp::implementation // - True if UWP, false otherwise. bool TerminalWindow::IsUwp() const noexcept { - return _isUwp; + // use C++11 magic statics to make sure we only do this once. + // This won't change over the lifetime of the application + + static const auto isUwp = []() { + // *** THIS IS A SINGLETON *** + auto result = false; + + // GH#2455 - Make sure to try/catch calls to Application::Current, + // because that _won't_ be an instance of TerminalApp::App in the + // LocalTests + try + { + result = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp(); + } + CATCH_LOG(); + return result; + }(); + + return isUwp; } // Method Description: @@ -194,19 +211,25 @@ namespace winrt::TerminalApp::implementation // - True if elevated, false otherwise. bool TerminalWindow::IsElevated() const noexcept { - return _isElevated; - } + // use C++11 magic statics to make sure we only do this once. + // This won't change over the lifetime of the application - // Method Description: - // - Called by UWP context invoker to let us know that we may have to change some of our behaviors - // for being a UWP - // Arguments: - // - (sets to UWP = true, one way change) - // Return Value: - // - - void TerminalWindow::RunAsUwp() - { - _isUwp = true; + static const auto isElevated = []() { + // *** THIS IS A SINGLETON *** + auto result = false; + + // GH#2455 - Make sure to try/catch calls to Application::Current, + // because that _won't_ be an instance of TerminalApp::App in the + // LocalTests + try + { + result = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsElevated(); + } + CATCH_LOG(); + return result; + }(); + + return isElevated; } // Method Description: @@ -222,13 +245,6 @@ namespace winrt::TerminalApp::implementation { _root->DialogPresenter(*this); - // In UWP mode, we cannot handle taking over the title bar for tabs, - // so this setting is overridden to false no matter what the preference is. - if (_isUwp) - { - _settings.GlobalSettings().ShowTabsInTitlebar(false); - } - // Pay attention, that even if some command line arguments were parsed (like launch mode), // we will not use the startup actions from settings. // While this simplifies the logic, we might want to reconsider this behavior in the future. @@ -619,12 +635,10 @@ namespace winrt::TerminalApp::implementation // // We can't do that anymore, because this is now called _before_ // we've initialized XAML for this thread. We can't start XAML till - // we have an HWND, and we can't finish creating the window till we // know how big it should be. // // Instead, we'll just hardcode how big the titlebar should be. If - // the titlebar / tab row ever change size, these numbers will have // to change accordingly. @@ -950,9 +964,7 @@ namespace winrt::TerminalApp::implementation return _root ? _root->AlwaysOnTop() : false; } - //////////////////////////////////////////////////////////////////////////// - - void TerminalWindow::SetSettingsStartupArgs(const std::vector& actions) + void TerminalWindow::SetSettingsStartupArgs(const std::vector& actions) { for (const auto& action : actions) { @@ -1059,8 +1071,6 @@ namespace winrt::TerminalApp::implementation return winrt::to_hstring(_appArgs.GetExitMessage()); } - //////////////////////////////////////////////////////////////////////////// - hstring TerminalWindow::GetWindowLayoutJson(LaunchPosition position) { if (_root != nullptr) @@ -1113,7 +1123,6 @@ namespace winrt::TerminalApp::implementation _root->SetNumberOfOpenWindows(num); } } - //////////////////////////////////////////////////////////////////////////// void TerminalWindow::IdentifyWindow() { @@ -1220,7 +1229,6 @@ namespace winrt::TerminalApp::implementation { UpdateSettings(arg); } - //////////////////////////////////////////////////////////////////////////// bool TerminalWindow::ShouldImmediatelyHandoffToElevated() { diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index b7e6a22ee9e..0e5f83e4934 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -42,8 +42,8 @@ namespace winrt::TerminalApp::implementation STDMETHODIMP Initialize(HWND hwnd); void Create(); + bool IsUwp() const noexcept; - void RunAsUwp(); bool IsElevated() const noexcept; void Quit(); @@ -51,7 +51,7 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args); bool HasCommandlineArguments() const noexcept; - // bool HasSettingsStartupActions() const noexcept; + int32_t SetStartupCommandline(array_view actions); int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); void SetSettingsStartupArgs(const std::vector& actions); @@ -136,8 +136,6 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Settings::Model::Theme); private: - bool _isUwp{ false }; - bool _isElevated{ false }; // If you add controls here, but forget to null them either here or in // the ctor, you're going to have a bad time. It'll mysteriously fail to // activate the AppLogic. diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 5bcb0294982..8474fc33908 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -51,8 +51,6 @@ namespace TerminalApp // registered?" when it definitely is. void Create(); - Boolean IsUwp(); - void RunAsUwp(); Boolean IsElevated(); Boolean HasCommandlineArguments(); From 0199aba286534cd871bfe1c15a32dcf2e785627f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 11:29:53 -0600 Subject: [PATCH 080/122] the last nits, I think --- src/cascadia/WindowsTerminal/AppHost.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index c22fc0d115a..a82963b6a67 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -389,7 +389,7 @@ void AppHost::Initialize() _revokers.ChangeMaximizeRequested = _windowLogic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested }); _window->MaximizeChanged([this](bool newMaximize) { - if (_appLogic) + if (_windowLogic) { _windowLogic.Maximized(newMaximize); } @@ -814,14 +814,14 @@ void AppHost::_RaiseVisualBell(const winrt::Windows::Foundation::IInspectable&, // - void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta) { - if (_appLogic) + if (_windowLogic) { // Find all the elements that are underneath the mouse - auto elems = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord.to_winrt_point(), _windowLogic.GetRoot()); + auto elems = Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord.to_winrt_point(), _windowLogic.GetRoot()); for (const auto& e : elems) { // If that element has implemented IMouseWheelListener, call OnMouseWheel on that element. - if (auto control{ e.try_as() }) + if (auto control{ e.try_as() }) { try { From ada3f427a07c8cae676ce4f516243873512490c7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 11:56:37 -0600 Subject: [PATCH 081/122] well that is an underwhelming codeformat commit. Thanks VS --- src/cascadia/TerminalApp/TerminalWindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index b1c0c4142ff..3215a1f546f 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -133,7 +133,6 @@ namespace winrt::TerminalApp::implementation // then it might look like App just failed to activate, which will // cause you to chase down the rabbit hole of "why is App not // registered?" when it definitely is. - } // Method Description: From 5164bffa7860b0d5e6e7b6468b44b6beba8190db Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 12:04:24 -0600 Subject: [PATCH 082/122] hey that's a good point carlos --- src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp | 2 +- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index eb166de9399..dff11656140 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -248,5 +248,5 @@ void GlobalAppSettings::ExpandCommands(const winrt::Windows::Foundation::Collect bool GlobalAppSettings::ShouldUsePersistedLayout() const { - return FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + return FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout && !IsolatedMode(); } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 62814fc24f5..d2c670e1ee1 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -124,7 +124,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); From 7e2eb0d8e5034c4e102588db789f255486dc3bc0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 09:53:51 -0600 Subject: [PATCH 083/122] Apply suggestions from code review Co-authored-by: Carlos Zamora --- src/cascadia/TerminalApp/TerminalPage.h | 2 -- src/cascadia/TerminalApp/TerminalPage.idl | 1 - src/cascadia/WindowsTerminal/WindowEmperor.cpp | 4 ++-- src/cascadia/WindowsTerminal/WindowEmperor.h | 6 +++--- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 7f2d0e13b4f..95bd6b8c00d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -65,7 +65,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(); winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e); @@ -149,7 +148,6 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(Initialized, IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs); TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable); TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs); - TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable); TYPED_EVENT(CloseRequested, IInspectable, IInspectable); TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 62176b0a1b4..b363e183355 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -69,7 +69,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; event Windows.Foundation.TypedEventHandler RenameWindowRequested; - event Windows.Foundation.TypedEventHandler SummonWindowRequested; event Windows.Foundation.TypedEventHandler CloseRequested; event Windows.Foundation.TypedEventHandler OpenSystemMenu; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index d2c670e1ee1..a03fad85b1b 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -129,7 +129,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co this->_checkWindowsForNotificationIcon(); }); sender->UpdateSettingsRequested([this]() -> winrt::fire_and_forget { - // We MUST be on the main thread to update the settings. We will crash when trying to enumerate fragement extensions otherwise. + // We MUST be on the main thread to update the settings. We will crash when trying to enumerate fragment extensions otherwise. co_await wil::resume_foreground(this->_dispatcher); _app.Logic().ReloadSettings(); }); @@ -293,7 +293,7 @@ winrt::fire_and_forget WindowEmperor::_SaveWindowLayoutsRepeat() co_await _SaveWindowLayouts(); // Don't need to save too frequently. - co_await 30s; + 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 diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 4ae6bdfe1f9..d9816192bff 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -5,7 +5,7 @@ Class Name: - WindowEmperor.h Abstract: -- The WindowEmperor is our new class for managing the single Terminal process +- The WindowEmperor is our class for managing the single Terminal process with all our windows. It will be responsible for handling the commandline arguments. It will initially try to find another terminal process to communicate with. If it does, it'll hand off to the existing process. @@ -55,8 +55,8 @@ class WindowEmperor void _quitAllRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs&); - winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts(); - winrt::fire_and_forget _SaveWindowLayoutsRepeat(); + winrt::Windows::Foundation::IAsyncAction _saveWindowLayouts(); + winrt::fire_and_forget _saveWindowLayoutsRepeat(); void _createMessageWindow(); From 5c852c3c4cb1c5bb4ed0e24ace777c855cbc7e65 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 09:54:04 -0600 Subject: [PATCH 084/122] some smaller nits --- doc/cascadia/profiles.schema.json | 7 +++- src/cascadia/TerminalApp/TerminalPage.cpp | 37 ++++++++++------- src/cascadia/TerminalApp/TerminalWindow.cpp | 40 +++++++++++-------- .../TerminalSettingsModel/ActionMap.cpp | 15 +++---- 4 files changed, 60 insertions(+), 39 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index bac66b8066e..21664f3f843 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -1822,7 +1822,7 @@ "name": { "type": "string", "description": "The name of the theme. This will be displayed in the settings UI.", - "not": { + "not": { "enum": [ "light", "dark", "system" ] } }, @@ -2092,6 +2092,11 @@ "description": "When set to true, the terminal will focus the pane on mouse hover.", "type": "boolean" }, + "compatibility.isolatedMode": { + "default": false, + "description": "When set to true, Terminal windows will not be able to interact with each other (including global hotkeys, tab drag/drop, running commandlines in existing windows, etc.). This is a compatibility escape hatch for users who are running into certain windowing issues.", + "type": "boolean" + }, "copyFormatting": { "default": true, "description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.", diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 42b5c9380dd..adeb8adf12e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -97,28 +97,37 @@ namespace winrt::TerminalApp::implementation return S_OK; } + // This may be called on a background thread, or the main thread, but almost + // definitely not on OUR UI thread. winrt::fire_and_forget TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) { _settings = settings; + const auto weakThis{ get_weak() }; co_await wil::resume_foreground(Dispatcher()); + // Back on our UI thread... - // Make sure to _UpdateCommandsForPalette before - // _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make - // sure the KeyChordText of Commands is updated, which needs to - // happen before the Settings UI is reloaded and tries to re-read - // those values. - _UpdateCommandsForPalette(); - CommandPalette().SetActionMap(_settings.ActionMap()); - - if (needRefreshUI) + if (const auto page{ weakThis.get() }) { - _RefreshUIForSettingsReload(); - } + // `this` is safe to use while `page` holds the strong ref - // Upon settings update we reload the system settings for scrolling as well. - // TODO: consider reloading this value periodically. - _systemRowsToScroll = _ReadSystemRowsToScroll(); + // Make sure to _UpdateCommandsForPalette before + // _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make + // sure the KeyChordText of Commands is updated, which needs to + // happen before the Settings UI is reloaded and tries to re-read + // those values. + _UpdateCommandsForPalette(); + CommandPalette().SetActionMap(_settings.ActionMap()); + + if (needRefreshUI) + { + _RefreshUIForSettingsReload(); + } + + // Upon settings update we reload the system settings for scrolling as well. + // TODO: consider reloading this value periodically. + _systemRowsToScroll = _ReadSystemRowsToScroll(); + } } bool TerminalPage::IsElevated() const noexcept diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index ba6cf564532..f95446a729c 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -751,32 +751,38 @@ namespace winrt::TerminalApp::implementation _RequestedThemeChangedHandlers(*this, Theme()); } + // This may be called on a background thread, or the main thread, but almost + // definitely not on OUR UI thread. winrt::fire_and_forget TerminalWindow::UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args) { _settings = args.NewSettings(); // Update the settings in TerminalPage _root->SetSettings(_settings, true); + const auto weakThis{ get_weak() }; co_await wil::resume_foreground(_root->Dispatcher()); - - // Bubble the notification up to the AppHost, now that we've updated our _settings. - _SettingsChangedHandlers(*this, args); - - if (FAILED(args.Result())) + // Back on our UI thread... + if (auto logic{ weakThis.get() }) { - const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); - const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, - textKey, - gsl::narrow_cast(args.Result()), - args.ExceptionText()); - co_return; - } - else if (args.Result() == S_FALSE) - { - _ShowLoadWarningsDialog(args.Warnings()); + // Bubble the notification up to the AppHost, now that we've updated our _settings. + _SettingsChangedHandlers(*this, args); + + if (FAILED(args.Result())) + { + const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); + _ShowLoadErrorsDialog(titleKey, + textKey, + gsl::narrow_cast(args.Result()), + args.ExceptionText()); + co_return; + } + else if (args.Result() == S_FALSE) + { + _ShowLoadWarningsDialog(args.Warnings()); + } + _RefreshThemeRoutine(); } - _RefreshThemeRoutine(); } void TerminalWindow::_OpenSettingsUI() diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index d61d1fa2e2a..cd7b1427254 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -10,6 +10,7 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Control; +using namespace winrt::Windows::Foundation::Collections; namespace winrt::Microsoft::Terminal::Settings::Model::implementation { @@ -118,7 +119,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Method Description: // - Retrieves a map of actions that can be bound to a key - Windows::Foundation::Collections::IMapView ActionMap::AvailableActions() + IMapView ActionMap::AvailableActions() { if (!_AvailableActionsCache) { @@ -172,7 +173,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - Retrieves a map of command names to the commands themselves // - These commands should not be modified directly because they may result in // an invalid state for the `ActionMap` - Windows::Foundation::Collections::IMapView ActionMap::NameMap() + IMapView ActionMap::NameMap() { if (!_NameMapCache) { @@ -283,7 +284,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return cumulativeActions; } - Windows::Foundation::Collections::IMapView ActionMap::GlobalHotkeys() + IMapView ActionMap::GlobalHotkeys() { if (!_GlobalHotkeysCache) { @@ -292,7 +293,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return _GlobalHotkeysCache.GetView(); } - Windows::Foundation::Collections::IMapView ActionMap::KeyBindings() + IMapView ActionMap::KeyBindings() { if (!_KeyBindingMapCache) { @@ -887,8 +888,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return leftName.compare(rightName) < 0; } - void ActionMap::ExpandCommands(const winrt::Windows::Foundation::Collections::IVectorView& profiles, - const winrt::Windows::Foundation::Collections::IMapView& schemes) + void ActionMap::ExpandCommands(const IVectorView& profiles, + const IMapView& schemes) { // TODO in review - It's a little weird to stash the expanded commands // into a separate map. Is it possible to just replace the name map with @@ -925,7 +926,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _ExpandedMapCache = copyOfCommands; } - Windows::Foundation::Collections::IMapView ActionMap::ExpandedCommands() + IMapView ActionMap::ExpandedCommands() { return _ExpandedMapCache.GetView(); } From 8e2c7a7bdd20135ae75786fae4aba809cf525c33 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 10:20:59 -0600 Subject: [PATCH 085/122] some cleanup --- src/cascadia/Remoting/Monarch.cpp | 5 +++++ src/cascadia/Remoting/WindowManager.cpp | 13 ++++--------- src/cascadia/Remoting/WindowManager.h | 4 +--- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 12 ++++++------ 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 726882a8989..025fb30410c 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -688,6 +688,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation case WindowingBehaviorUseName: windowID = _lookupPeasantIdForName(targetWindowName); break; + case WindowingBehaviorUseNone: + // This should be impossible. The if statement above should have + // prevented WindowingBehaviorUseNone from falling in here. + // Explode, because this is a programming error. + THROW_HR(E_UNEXPECTED); default: windowID = ::base::saturated_cast(targetWindow); break; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index f406eda3b54..5fe39db23be 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -106,18 +106,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // in isolated mode. shouldCreateWindow = false; - std::optional givenID; - winrt::hstring givenName{}; - // Send the commandline over to the monarch process - if (_proposeToMonarch(args, givenID, givenName)) + if (_proposeToMonarch(args)) { // If that succeeded, then we don't need to make a new window. // Our job is done. Either the monarch is going to run the // commandline in an existing window, or a new one, but either way, // this process doesn't need to make a new window. - return *winrt::make_self(shouldCreateWindow); + return winrt::make(shouldCreateWindow); } // Otherwise, we'll try to handle this ourselves. } @@ -153,7 +150,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // either. shouldCreateWindow = false; - return *winrt::make_self(shouldCreateWindow); + return winrt::make(shouldCreateWindow); } else { @@ -214,9 +211,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - Helper attempting to call to the monarch multiple times. If the monarch // fails to respond, or we encounter any sort of error, we'll try again // until we find one, or decisively determine there isn't one. - bool WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args, - std::optional& /*givenID*/, - winrt::hstring& /*givenName*/) + bool WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args) { // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below. static constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index e4ec245d8b0..e591be232db 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -61,9 +61,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _createMonarch(); void _registerAsMonarch(); - bool _proposeToMonarch(const Remoting::CommandlineArgs& args, - std::optional& givenID, - winrt::hstring& givenName); + bool _proposeToMonarch(const Remoting::CommandlineArgs& args); void _createCallbacks(); void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index a03fad85b1b..c92cef041f7 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -124,7 +124,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto&&) -> winrt::fire_and_forget { + sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { co_await wil::resume_foreground(this->_dispatcher); this->_checkWindowsForNotificationIcon(); }); @@ -202,7 +202,7 @@ void WindowEmperor::_becomeMonarch() // 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.emplace(std::move(std::chrono::seconds(10)), std::move([this]() { _saveWindowLayoutsRepeat(); })); _getWindowLayoutThrottler.value()(); } @@ -241,12 +241,12 @@ void WindowEmperor::_quitAllRequested(const winrt::Windows::Foundation::IInspect // Tell the monarch to wait for the window layouts to save before // everyone quits. - args.BeforeQuitAllAction(_SaveWindowLayouts()); + args.BeforeQuitAllAction(_saveWindowLayouts()); } #pragma region LayoutPersistence -winrt::Windows::Foundation::IAsyncAction WindowEmperor::_SaveWindowLayouts() +winrt::Windows::Foundation::IAsyncAction WindowEmperor::_saveWindowLayouts() { // Make sure we run on a background thread to not block anything. co_await winrt::resume_background(); @@ -285,12 +285,12 @@ winrt::Windows::Foundation::IAsyncAction WindowEmperor::_SaveWindowLayouts() co_return; } -winrt::fire_and_forget WindowEmperor::_SaveWindowLayoutsRepeat() +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(); + co_await _saveWindowLayouts(); // Don't need to save too frequently. co_await winrt::resume_after(30s); From 08f6a53be5c232ac380d4e02aa7dce15b82bb0fe Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 11:05:16 -0600 Subject: [PATCH 086/122] This is gross but reduces the number of redundant copies of these brushes we need --- src/cascadia/TerminalApp/Pane.cpp | 63 ++++++---------------- src/cascadia/TerminalApp/Pane.h | 12 +++-- src/cascadia/TerminalApp/TerminalPage.cpp | 66 ++++++++++++++++++----- src/cascadia/TerminalApp/TerminalPage.h | 5 +- 4 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 919f48943d8..74f04015c6b 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -80,7 +80,7 @@ Pane::Pane(std::shared_ptr first, // Use the unfocused border color as the pane background, so an actual color // appears behind panes as we animate them sliding in. - _root.Background(_unfocusedBorderBrush); + _root.Background(_themeResources.unfocusedBorderBrush); _root.Children().Append(_borderFirst); _root.Children().Append(_borderSecond); @@ -1393,8 +1393,8 @@ void Pane::UpdateVisuals() { _UpdateBorders(); } - _borderFirst.BorderBrush(_lastActive ? _focusedBorderBrush : _unfocusedBorderBrush); - _borderSecond.BorderBrush(_lastActive ? _focusedBorderBrush : _unfocusedBorderBrush); + _borderFirst.BorderBrush(_lastActive ? _themeResources.focusedBorderBrush : _themeResources.unfocusedBorderBrush); + _borderSecond.BorderBrush(_lastActive ? _themeResources.focusedBorderBrush : _themeResources.unfocusedBorderBrush); } // Method Description: @@ -1846,7 +1846,7 @@ winrt::fire_and_forget Pane::_CloseChildRoutine(const bool closeFirst) Controls::Grid dummyGrid; // GH#603 - we can safely add a BG here, as the control is gone right // away, to fill the space as the rest of the pane expands. - dummyGrid.Background(_unfocusedBorderBrush); + dummyGrid.Background(_themeResources.unfocusedBorderBrush); // It should be the size of the closed pane. dummyGrid.Width(removedOriginalSize.Width); dummyGrid.Height(removedOriginalSize.Height); @@ -2124,7 +2124,7 @@ void Pane::_SetupEntranceAnimation() // * If we give the parent (us) root BG a color, then a transparent pane // will flash opaque during the animation, then back to transparent, which // looks bad. - _secondChild->_root.Background(_unfocusedBorderBrush); + _secondChild->_root.Background(_themeResources.unfocusedBorderBrush); const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast(totalSize)); @@ -3089,51 +3089,20 @@ float Pane::_ClampSplitPosition(const bool widthOrHeight, const float requestedV return std::clamp(requestedValue, minSplitPosition, maxSplitPosition); } -// Function Description: -// - Attempts to load some XAML resources that the Pane will need. This includes: -// * The Color we'll use for active Panes's borders - SystemAccentColor -// * The Brush we'll use for inactive Panes - TabViewBackground (to match the -// color of the titlebar) -// Arguments: -// - requestedTheme: this should be the currently active Theme for the app -// Return Value: -// - -void Pane::SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) +// Method Description: +// - Update our stored brushes for the current theme. This will also recursively +// update all our children. +// - TerminalPage creates these brushes and ultimately owns them. Effectively, +// we're just storing a smart pointer to the page's brushes. +void Pane::UpdateResources(const PaneResources& resources) { - const auto res = Application::Current().Resources(); - const auto accentColorKey = winrt::box_value(L"SystemAccentColor"); - if (res.HasKey(accentColorKey)) - { - const auto colorFromResources = ThemeLookup(res, requestedTheme, accentColorKey); - // If SystemAccentColor is _not_ a Color for some reason, use - // Transparent as the color, so we don't do this process again on - // the next pane (by leaving s_focusedBorderBrush nullptr) - auto actualColor = winrt::unbox_value_or(colorFromResources, Colors::Black()); - _focusedBorderBrush = SolidColorBrush(actualColor); - } - else - { - // DON'T use Transparent here - if it's "Transparent", then it won't - // be able to hittest for clicks, and then clicking on the border - // will eat focus. - _focusedBorderBrush = SolidColorBrush{ Colors::Black() }; - } + _themeResources = resources; + UpdateVisuals(); - const auto unfocusedBorderBrushKey = winrt::box_value(L"UnfocusedBorderBrush"); - if (res.HasKey(unfocusedBorderBrushKey)) - { - // MAKE SURE TO USE ThemeLookup, so that we get the correct resource for - // the requestedTheme, not just the value from the resources (which - // might not respect the settings' requested theme) - auto obj = ThemeLookup(res, requestedTheme, unfocusedBorderBrushKey); - _unfocusedBorderBrush = obj.try_as(); - } - else + if (!_IsLeaf()) { - // DON'T use Transparent here - if it's "Transparent", then it won't - // be able to hittest for clicks, and then clicking on the border - // will eat focus. - _unfocusedBorderBrush = SolidColorBrush{ Colors::Black() }; + _firstChild->UpdateResources(resources); + _secondChild->UpdateResources(resources); } } diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 28a6aafef41..c1a947d4218 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -51,6 +51,12 @@ enum class SplitState : int Vertical = 2 }; +struct PaneResources +{ + winrt::Windows::UI::Xaml::Media::SolidColorBrush focusedBorderBrush{ nullptr }; + winrt::Windows::UI::Xaml::Media::SolidColorBrush unfocusedBorderBrush{ nullptr }; +}; + class Pane : public std::enable_shared_from_this { public: @@ -136,7 +142,7 @@ class Pane : public std::enable_shared_from_this bool ContainsReadOnly() const; - void SetupResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); + void UpdateResources(const PaneResources& resources); // Method Description: // - A helper method for ad-hoc recursion on a pane tree. Walks the pane @@ -217,8 +223,8 @@ class Pane : public std::enable_shared_from_this winrt::Windows::UI::Xaml::Controls::Grid _root{}; winrt::Windows::UI::Xaml::Controls::Border _borderFirst{}; winrt::Windows::UI::Xaml::Controls::Border _borderSecond{}; - winrt::Windows::UI::Xaml::Media::SolidColorBrush _focusedBorderBrush; - winrt::Windows::UI::Xaml::Media::SolidColorBrush _unfocusedBorderBrush; + + PaneResources _themeResources; #pragma region Properties that need to be transferred between child / parent panes upon splitting / closing std::shared_ptr _firstChild{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index adeb8adf12e..d12007d4bea 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -30,10 +30,12 @@ using namespace winrt::Windows::ApplicationModel::DataTransfer; using namespace winrt::Windows::Foundation::Collections; using namespace winrt::Windows::System; using namespace winrt::Windows::System; +using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Core; using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Xaml::Controls; using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Media; using namespace ::TerminalApp; using namespace ::Microsoft::Console; using namespace ::Microsoft::Terminal::Core; @@ -4116,22 +4118,14 @@ namespace winrt::TerminalApp::implementation auto requestedTheme{ theme.RequestedTheme() }; { + _updatePaneResources(requestedTheme); + for (const auto& tab : _tabs) { if (auto terminalTab{ _GetTerminalTabImpl(tab) }) { - terminalTab->GetRootPane()->WalkTree([&](auto&& pane) { - // TODO, but of middling priority. We probably shouldn't - // SetupResources on _every_ pane. We can probably call - // that on the root, and then have that back channel to - // update the whole tree. - - // Update the brushes that Pane's use... - pane->SetupResources(requestedTheme); - // ... then trigger a visual update for all the pane borders to - // apply the new ones. - pane->UpdateVisuals(); - }); + // The root pane will propogate the theme change to all its children. + terminalTab->GetRootPane()->UpdateResources(_paneResources); } } } @@ -4238,6 +4232,54 @@ namespace winrt::TerminalApp::implementation } } + // Function Description: + // - Attempts to load some XAML resources that Panes will need. This includes: + // * The Color they'll use for active Panes's borders - SystemAccentColor + // * The Brush they'll use for inactive Panes - TabViewBackground (to match the + // color of the titlebar) + // Arguments: + // - requestedTheme: this should be the currently active Theme for the app + // Return Value: + // - + void TerminalPage::_updatePaneResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) + { + const auto res = Application::Current().Resources(); + const auto accentColorKey = winrt::box_value(L"SystemAccentColor"); + if (res.HasKey(accentColorKey)) + { + const auto colorFromResources = ThemeLookup(res, requestedTheme, accentColorKey); + // If SystemAccentColor is _not_ a Color for some reason, use + // Transparent as the color, so we don't do this process again on + // the next pane (by leaving s_focusedBorderBrush nullptr) + auto actualColor = winrt::unbox_value_or(colorFromResources, Colors::Black()); + _paneResources.focusedBorderBrush = SolidColorBrush(actualColor); + } + else + { + // DON'T use Transparent here - if it's "Transparent", then it won't + // be able to hittest for clicks, and then clicking on the border + // will eat focus. + _paneResources.focusedBorderBrush = SolidColorBrush{ Colors::Black() }; + } + + const auto unfocusedBorderBrushKey = winrt::box_value(L"UnfocusedBorderBrush"); + if (res.HasKey(unfocusedBorderBrushKey)) + { + // MAKE SURE TO USE ThemeLookup, so that we get the correct resource for + // the requestedTheme, not just the value from the resources (which + // might not respect the settings' requested theme) + auto obj = ThemeLookup(res, requestedTheme, unfocusedBorderBrushKey); + _paneResources.unfocusedBorderBrush = obj.try_as(); + } + else + { + // DON'T use Transparent here - if it's "Transparent", then it won't + // be able to hittest for clicks, and then clicking on the border + // will eat focus. + _paneResources.unfocusedBorderBrush = SolidColorBrush{ Colors::Black() }; + } + } + void TerminalPage::WindowActivated(const bool activated) { // Stash if we're activated. Use that when we reload diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 95bd6b8c00d..899a04c615d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -224,8 +224,10 @@ namespace winrt::TerminalApp::implementation bool _renamerPressedEnter{ false }; TerminalApp::IWindowProperties _WindowProperties{ nullptr }; + PaneResources _paneResources; - winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); + winrt::Windows::Foundation::IAsyncOperation + _ShowDialogHelper(const std::wstring_view& name); void _ShowAboutDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowQuitDialog(); @@ -450,6 +452,7 @@ namespace winrt::TerminalApp::implementation void _updateThemeColors(); void _updateTabCloseButton(const winrt::Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem); + void _updatePaneResources(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args); From 88033246f13c47e756a45035ac2b970a575f1770 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 11:18:02 -0600 Subject: [PATCH 087/122] spelling is hard --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d12007d4bea..013752eb5e3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4124,7 +4124,7 @@ namespace winrt::TerminalApp::implementation { if (auto terminalTab{ _GetTerminalTabImpl(tab) }) { - // The root pane will propogate the theme change to all its children. + // The root pane will propagate the theme change to all its children. terminalTab->GetRootPane()->UpdateResources(_paneResources); } } From 3aa083ba9a4000eb6339d409316bfb3c3aaf1844 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 14:00:09 -0600 Subject: [PATCH 088/122] sure. It doesn't matter' --- .../TerminalSettingsModel/ActionMap.cpp | 5 +---- .../TerminalSettingsModel/Command.cpp | 21 ++++++++----------- src/cascadia/TerminalSettingsModel/Command.h | 6 ++---- .../TerminalSettingsModel/Command.idl | 3 +-- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index cd7b1427254..dae2adf1feb 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -898,8 +898,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // WHILE also making sure that upon re-saving the commands, we don't // actually serialize the results of the expansion. I don't think it is. - auto warnings{ winrt::single_threaded_vector() }; - std::vector sortedSchemes; sortedSchemes.reserve(schemes.Size()); @@ -921,8 +919,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Command::ExpandCommands(copyOfCommands, profiles, - winrt::param::vector_view{ sortedSchemes }, - warnings); + winrt::param::vector_view{ sortedSchemes }); _ExpandedMapCache = copyOfCommands; } diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index f9f52dcedaf..e66901725a9 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -480,8 +480,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - void Command::ExpandCommands(IMap commands, IVectorView profiles, - IVectorView schemes, - IVector warnings) + IVectorView schemes) { std::vector commandsToRemove; std::vector commandsToAdd; @@ -491,7 +490,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { auto cmd{ get_self(nameAndCmd.Value()) }; - auto newCommands = _expandCommand(cmd, profiles, schemes, warnings); + auto newCommands = _expandCommand(cmd, profiles, schemes); if (newCommands.size() > 0) { commandsToRemove.push_back(nameAndCmd.Key()); @@ -529,21 +528,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Arguments: // - expandable: the Command to potentially turn into more commands // - profiles: A list of all the profiles that this command should be expanded on. - // - warnings: If there were any warnings during parsing, they'll be - // appended to this vector. // Return Value: // - and empty vector if the command wasn't expandable, otherwise a list of // the newly-created commands. std::vector Command::_expandCommand(Command* const expandable, IVectorView profiles, - IVectorView schemes, - IVector& warnings) + IVectorView schemes) { std::vector newCommands; if (expandable->HasNestedCommands()) { - ExpandCommands(expandable->_subcommands, profiles, schemes, warnings); + ExpandCommands(expandable->_subcommands, profiles, schemes); } if (expandable->_IterateOn == ExpandCommandType::None) @@ -564,18 +560,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation const auto actualDataEnd = newJsonString.data() + newJsonString.size(); if (!reader->parse(actualDataStart, actualDataEnd, &newJsonValue, &errs)) { - warnings.Append(SettingsLoadWarnings::FailedToParseCommandJson); // If we encounter a re-parsing error, just stop processing the rest of the commands. return false; } // Pass the new json back though FromJson, to get the new expanded value. - std::vector newWarnings; - if (auto newCmd{ Command::FromJson(newJsonValue, newWarnings) }) + // FromJson requires that we pass in a vector to hang on to the + // warnings, but ultimately, we don't care about warnings during + // expansion. + std::vector unused; + if (auto newCmd{ Command::FromJson(newJsonValue, unused) }) { newCommands.push_back(*newCmd); } - std::for_each(newWarnings.begin(), newWarnings.end(), [warnings](auto& warn) { warnings.Append(warn); }); return true; }; diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 4f4dcbe2048..7d246da26b1 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -43,8 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static void ExpandCommands(Windows::Foundation::Collections::IMap commands, Windows::Foundation::Collections::IVectorView profiles, - Windows::Foundation::Collections::IVectorView schemes, - Windows::Foundation::Collections::IVector warnings); + Windows::Foundation::Collections::IVectorView schemes); static std::vector LayerJson(Windows::Foundation::Collections::IMap& commands, const Json::Value& json); @@ -83,8 +82,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static std::vector _expandCommand(Command* const expandable, Windows::Foundation::Collections::IVectorView profiles, - Windows::Foundation::Collections::IVectorView schemes, - Windows::Foundation::Collections::IVector& warnings); + Windows::Foundation::Collections::IVectorView schemes); friend class SettingsModelLocalTests::DeserializationTests; friend class SettingsModelLocalTests::CommandTests; }; diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index b749a79fb0a..6a5a5fb950d 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -45,7 +45,6 @@ namespace Microsoft.Terminal.Settings.Model static void ExpandCommands(Windows.Foundation.Collections.IMap commands, Windows.Foundation.Collections.IVectorView profiles, - Windows.Foundation.Collections.IVectorView schemes, - Windows.Foundation.Collections.IVector warnings); + Windows.Foundation.Collections.IVectorView schemes); } } From 9a9fa4392a918708bafc5192dbc347cf4053feaf Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Mar 2023 13:25:45 -0600 Subject: [PATCH 089/122] [ainulindale] Prevent a race between the Emperor and the window logic when updating settings (cherry picked from commit 35c7474abe61aab08598d10b5e62923d3d978471) --- src/cascadia/TerminalApp/AppLogic.cpp | 12 ++++++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 3 ++ src/cascadia/TerminalApp/TerminalPage.cpp | 40 +++++++------------ src/cascadia/TerminalApp/TerminalPage.h | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 14 +++---- src/cascadia/TerminalApp/TerminalWindow.h | 1 - src/cascadia/TerminalApp/TerminalWindow.idl | 1 - .../WindowsTerminal/WindowEmperor.cpp | 10 +++-- 9 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 1a62d88e1e9..19411fd38eb 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -673,6 +673,18 @@ namespace winrt::TerminalApp::implementation } return _settings.GlobalSettings().IsolatedMode(); } + bool AppLogic::RequestsTrayIcon() + { + if (!_loadedInitialSettings) + { + // Load settings if we haven't already + ReloadSettings(); + } + + return _settings.GlobalSettings().AlwaysShowNotificationIcon() || + _settings.GlobalSettings().MinimizeToNotificationArea(); + ; + } TerminalApp::TerminalWindow AppLogic::CreateNewWindow() { diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 6ab4722a2aa..165f0935cbf 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -65,6 +65,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::Theme Theme(); bool IsolatedMode(); + bool RequestsTrayIcon(); TerminalApp::TerminalWindow CreateNewWindow(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index dea3281142e..e33b1cce13c 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -41,8 +41,10 @@ namespace TerminalApp void ReloadSettings(); + // Selected settings to expose Microsoft.Terminal.Settings.Model.Theme Theme { get; }; Boolean IsolatedMode { get; }; + Boolean RequestsTrayIcon { get; }; FindTargetWindowResult FindTargetWindow(String[] args); @@ -52,6 +54,7 @@ namespace TerminalApp IMapView GlobalHotkeys(); + event Windows.Foundation.TypedEventHandler SettingsChanged; } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 013752eb5e3..5e62b4ed71b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -99,37 +99,27 @@ namespace winrt::TerminalApp::implementation return S_OK; } - // This may be called on a background thread, or the main thread, but almost - // definitely not on OUR UI thread. - winrt::fire_and_forget TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) + // INVARIANT: This needs to be called on OUR UI thread! + void TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) { _settings = settings; - const auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher()); - // Back on our UI thread... + // Make sure to _UpdateCommandsForPalette before + // _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make + // sure the KeyChordText of Commands is updated, which needs to + // happen before the Settings UI is reloaded and tries to re-read + // those values. + _UpdateCommandsForPalette(); + CommandPalette().SetActionMap(_settings.ActionMap()); - if (const auto page{ weakThis.get() }) + if (needRefreshUI) { - // `this` is safe to use while `page` holds the strong ref - - // Make sure to _UpdateCommandsForPalette before - // _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make - // sure the KeyChordText of Commands is updated, which needs to - // happen before the Settings UI is reloaded and tries to re-read - // those values. - _UpdateCommandsForPalette(); - CommandPalette().SetActionMap(_settings.ActionMap()); - - if (needRefreshUI) - { - _RefreshUIForSettingsReload(); - } - - // Upon settings update we reload the system settings for scrolling as well. - // TODO: consider reloading this value periodically. - _systemRowsToScroll = _ReadSystemRowsToScroll(); + _RefreshUIForSettingsReload(); } + + // Upon settings update we reload the system settings for scrolling as well. + // TODO: consider reloading this value periodically. + _systemRowsToScroll = _ReadSystemRowsToScroll(); } bool TerminalPage::IsElevated() const noexcept diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 899a04c615d..1a872aab357 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -59,7 +59,7 @@ namespace winrt::TerminalApp::implementation // put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331 STDMETHODIMP Initialize(HWND hwnd); - winrt::fire_and_forget SetSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings, bool needRefreshUI); + void SetSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings, bool needRefreshUI); void Create(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index f95446a729c..61fbb801776 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -252,7 +252,7 @@ namespace winrt::TerminalApp::implementation _root->SetStartupActions(_settingsStartupArgs); } - _root->SetSettings(_settings, false); + _root->SetSettings(_settings, false); // We're on our UI thread right now, so this is safe _root->Loaded({ this, &TerminalWindow::_OnLoaded }); _root->Initialized([this](auto&&, auto&&) { // GH#288 - When we finish initialization, if the user wanted us @@ -325,12 +325,6 @@ namespace winrt::TerminalApp::implementation { return _settings.GlobalSettings().AlwaysShowNotificationIcon(); } - bool TerminalWindow::RequestsTrayIcon() - { - return _settings.GlobalSettings().AlwaysShowNotificationIcon() || - _settings.GlobalSettings().MinimizeToNotificationArea() || - IsQuakeWindow(); - } bool TerminalWindow::GetShowTitleInTitlebar() { @@ -756,14 +750,16 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalWindow::UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args) { _settings = args.NewSettings(); - // Update the settings in TerminalPage - _root->SetSettings(_settings, true); const auto weakThis{ get_weak() }; co_await wil::resume_foreground(_root->Dispatcher()); // Back on our UI thread... if (auto logic{ weakThis.get() }) { + // Update the settings in TerminalPage + // We're on our UI thread right now, so this is safe + _root->SetSettings(_settings, true); + // Bubble the notification up to the AppHost, now that we've updated our _settings. _SettingsChangedHandlers(*this, args); diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index a88c28acff1..4d0a0d9e6b6 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -106,7 +106,6 @@ namespace winrt::TerminalApp::implementation bool GetMinimizeToNotificationArea(); bool GetAlwaysShowNotificationIcon(); - bool RequestsTrayIcon(); bool GetShowTitleInTitlebar(); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index f31a52fa94d..8474fc33908 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -102,7 +102,6 @@ namespace TerminalApp Boolean GetMinimizeToNotificationArea(); Boolean GetAlwaysShowNotificationIcon(); - Boolean RequestsTrayIcon(); Boolean GetShowTitleInTitlebar(); // These already have accessors as a part of IWindowProperties, but we diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index c92cef041f7..71bcb8bc416 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -645,11 +645,15 @@ void WindowEmperor::_checkWindowsForNotificationIcon() // re-summon any hidden windows, but right now we're not keeping track of // who's hidden, so just summon them all. Tracking the work to do a "summon // all minimized" in GH#10448 - - bool needsIcon = false; + // + // To avoid races between us thinking the settings updated, and the windows + // themselves getting the new settings, only ask the app logic for the + // RequestsTrayIcon setting value, and combine that with the result of each + // window (which won't change during a settings reload). + bool needsIcon = _app.Logic().RequestsTrayIcon(); for (const auto& _windowThread : _windows) { - needsIcon |= _windowThread->Logic().RequestsTrayIcon(); + needsIcon |= _windowThread->Logic().IsQuakeWindow(); } if (needsIcon) From b24cf615ea447dc1fa41cd9d82ee2cfbad49bff1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Mar 2023 16:28:38 -0600 Subject: [PATCH 090/122] some of the more minor cleanup elements --- src/cascadia/Remoting/Monarch.cpp | 15 ++++--- src/cascadia/Remoting/WindowManager.cpp | 39 ++----------------- src/cascadia/Remoting/WindowManager.h | 5 +-- src/cascadia/Remoting/WindowManager.idl | 5 +-- src/cascadia/TerminalApp/TerminalPage.cpp | 2 + src/cascadia/WindowsTerminal/AppHost.cpp | 4 +- .../WindowsTerminal/WindowEmperor.cpp | 29 +++++++++----- src/cascadia/WindowsTerminal/WindowEmperor.h | 4 +- 8 files changed, 41 insertions(+), 62 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 025fb30410c..10cbbf78faf 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -659,6 +659,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + if (targetWindow == WindowingBehaviorUseNone) + { + // In this case, the targetWindow was UseNone, which means that we + // want to make a message box, but otherwise not make a Terminal + // window. + return winrt::make(false); + } // If there's a valid ID returned, then let's try and find the peasant // that goes with it. Alternatively, if we were given a magic windowing // constant, we can use that to look up an appropriate peasant. @@ -773,14 +780,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *result; } } - else if (targetWindow == WindowingBehaviorUseNone) - { - // In this case, the targetWindow was UseNone, which means that we - // want to make a message box, but otherwise not make a Terminal - // window. - auto result = winrt::make_self(false); - return *result; - } // If we get here, we couldn't find an existing window. Make a new one. TraceLoggingWrite(g_hRemotingProvider, diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 5fe39db23be..02e6ab6fcfc 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -91,8 +91,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args, const bool isolatedMode) { - bool shouldCreateWindow = false; - if (!isolatedMode) { // _createMonarch always attempts to connect an existing monarch. In @@ -105,7 +103,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // We connected to a monarch instance, not us though. This won't hit // in isolated mode. - shouldCreateWindow = false; // Send the commandline over to the monarch process if (_proposeToMonarch(args)) { @@ -114,7 +111,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // commandline in an existing window, or a new one, but either way, // this process doesn't need to make a new window. - return winrt::make(shouldCreateWindow); + return winrt::make(false); } // Otherwise, we'll try to handle this ourselves. } @@ -148,9 +145,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // This commandline doesn't deserve a window. Don't make a monarch // either. - shouldCreateWindow = false; - - return winrt::make(shouldCreateWindow); + return winrt::make(false); } else { @@ -317,9 +312,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Remoting::Peasant WindowManager::CreatePeasant(Remoting::WindowRequestedArgs args) { auto p = winrt::make_self(); - if (args.Id()) + if (const auto id = args.Id()) { - p->AssignID(args.Id().Value()); + p->AssignID(id.Value()); } // If the name wasn't specified, this will be an empty string. @@ -385,31 +380,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return 0; } - // Method Description: - // - Ask the monarch to show a notification icon. - // Arguments: - // - - // Return Value: - // - - winrt::fire_and_forget WindowManager::RequestShowNotificationIcon(Remoting::Peasant peasant) - { - co_await winrt::resume_background(); - peasant.RequestShowNotificationIcon(); - } - - // Method Description: - // - Ask the monarch to hide its notification icon. - // Arguments: - // - - // Return Value: - // - - winrt::fire_and_forget WindowManager::RequestHideNotificationIcon(Remoting::Peasant peasant) - { - auto strongThis{ get_strong() }; - co_await winrt::resume_background(); - peasant.RequestHideNotificationIcon(); - } - // Method Description: // - Ask the monarch to quit all windows. // Arguments: @@ -418,7 +388,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - winrt::fire_and_forget WindowManager::RequestQuitAll(Remoting::Peasant peasant) { - auto strongThis{ get_strong() }; co_await winrt::resume_background(); peasant.RequestQuitAll(); } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index e591be232db..c6d5b687d64 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -36,9 +36,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void SummonAllWindows(); Windows::Foundation::Collections::IVectorView GetPeasantInfos(); uint64_t GetNumberOfPeasants(); - winrt::fire_and_forget RequestShowNotificationIcon(Remoting::Peasant peasant); - winrt::fire_and_forget RequestHideNotificationIcon(Remoting::Peasant peasant); - winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); + + static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); void UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); bool DoesQuakeWindowExist(); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index ad2986888b4..2adcd9e80c4 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -12,10 +12,9 @@ namespace Microsoft.Terminal.Remoting Peasant CreatePeasant(WindowRequestedArgs args); void SignalClose(Peasant p); - void RequestShowNotificationIcon(Peasant p); - void RequestHideNotificationIcon(Peasant p); + void UpdateActiveTabTitle(String title, Peasant p); - void RequestQuitAll(Peasant p); + static void RequestQuitAll(Peasant p); void SummonWindow(SummonWindowSelectionArgs args); void SummonAllWindows(); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 5e62b4ed71b..d03a3d70b9a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -102,6 +102,8 @@ namespace winrt::TerminalApp::implementation // INVARIANT: This needs to be called on OUR UI thread! void TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) { + assert(Dispatcher().HasThreadAccess()); + _settings = settings; // Make sure to _UpdateCommandsForPalette before diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6a22800723a..4c112dc2ec1 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -353,7 +353,7 @@ void AppHost::Initialize() _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. - _windowManager.RequestQuitAll(_peasant); + Remoting::WindowManager::RequestQuitAll(_peasant); }); // Load bearing: make sure the PropertyChanged handler is added before we @@ -1074,7 +1074,7 @@ winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation: void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - _windowManager.RequestQuitAll(_peasant); + Remoting::WindowManager::RequestQuitAll(_peasant); } void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&, diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 71bcb8bc416..cfdaca73bff 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -124,15 +124,8 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, co // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged([this](auto&&, auto &&) -> winrt::fire_and_forget { - co_await wil::resume_foreground(this->_dispatcher); - this->_checkWindowsForNotificationIcon(); - }); - sender->UpdateSettingsRequested([this]() -> winrt::fire_and_forget { - // We MUST be on the main thread to update the settings. We will crash when trying to enumerate fragment extensions otherwise. - co_await wil::resume_foreground(this->_dispatcher); - _app.Logic().ReloadSettings(); - }); + sender->Logic().IsQuakeWindowChanged({ this, &WindowEmperor::_windowIsQuakeWindowChanged }); + sender->UpdateSettingsRequested({ this, &WindowEmperor::_windowRequestUpdateSettings }); // These come in on the sender's thread. Move back to our thread. co_await wil::resume_foreground(_dispatcher); @@ -604,7 +597,7 @@ winrt::fire_and_forget WindowEmperor::_setupGlobalHotkeys() } #pragma endregion -//////////////////////////////////////////////////////////////////////////////// + #pragma region NotificationIcon // Method Description: // - Creates a Notification Icon and hooks up its handlers @@ -688,3 +681,19 @@ void WindowEmperor::_hideNotificationIconRequested() } } #pragma endregion + +// A callback to the window's logic to let us know when the window's +// quake mode state changes. We'll use this to check if we need to add +// or remove the notification icon. +winrt::fire_and_forget WindowEmperor::_windowIsQuakeWindowChanged(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable args) +{ + co_await wil::resume_foreground(this->_dispatcher); + _checkWindowsForNotificationIcon(); +} +winrt::fire_and_forget WindowEmperor::_windowRequestUpdateSettings() +{ + // We MUST be on the main thread to update the settings. We will crash when trying to enumerate fragment extensions otherwise. + co_await wil::resume_foreground(this->_dispatcher); + _app.Logic().ReloadSettings(); +} diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index d9816192bff..f05fab4f836 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -39,7 +39,6 @@ class WindowEmperor winrt::Microsoft::Terminal::Remoting::WindowManager _manager; std::vector> _windows; - std::vector _threads; std::optional> _getWindowLayoutThrottler; @@ -55,6 +54,9 @@ class WindowEmperor 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(); From 3fa101731b9266629d4fb25499ebe2f5d60627ae Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Mar 2023 16:44:26 -0600 Subject: [PATCH 091/122] one fewer IReference, thank gods --- src/cascadia/Remoting/Monarch.h | 4 ++-- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/Remoting/WindowManager.cpp | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 6765d0b15ca..762009f1514 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -43,7 +43,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { public: WindowRequestedArgs(const Remoting::ProposeCommandlineResult& result, const Remoting::CommandlineArgs& command) : - _Id{ result.Id() }, + _Id{ result.Id() ? result.Id().Value() : 0 }, // We'll use 0 as a sentinel, since no window will ever get to have that ID _WindowName{ result.WindowName() }, _args{ command.Commandline() }, _CurrentDirectory{ command.CurrentDirectory() } {}; @@ -51,7 +51,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Commandline(const winrt::array_view& value) { _args = { value.begin(), value.end() }; }; winrt::com_array Commandline() { return winrt::com_array{ _args.begin(), _args.end() }; } - WINRT_PROPERTY(Windows::Foundation::IReference, Id); + WINRT_PROPERTY(uint64_t, Id); WINRT_PROPERTY(winrt::hstring, WindowName); WINRT_PROPERTY(winrt::hstring, CurrentDirectory); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 1dc3d53f70e..732dfab25e5 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -21,7 +21,7 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass WindowRequestedArgs { WindowRequestedArgs(ProposeCommandlineResult result, CommandlineArgs command); - Windows.Foundation.IReference Id { get; }; + UInt64 Id { get; }; String WindowName { get; }; String[] Commandline { get; }; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 02e6ab6fcfc..9a850e7a0a7 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -312,9 +312,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Remoting::Peasant WindowManager::CreatePeasant(Remoting::WindowRequestedArgs args) { auto p = winrt::make_self(); + // This will be false if the Id is 0, which is our sentinel for "no specific ID was requested" if (const auto id = args.Id()) { - p->AssignID(id.Value()); + p->AssignID(id); } // If the name wasn't specified, this will be an empty string. From 5fe3fa5ea8c744c38f524fe45ce3bc6e55a36ce8 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Mar 2023 10:59:42 -0600 Subject: [PATCH 092/122] some cleanup around expanding commands --- src/cascadia/TerminalApp/AppLogic.cpp | 7 +++--- src/cascadia/TerminalApp/TerminalPage.cpp | 8 +------ .../TerminalSettingsModel/ActionMap.cpp | 24 +++++++++++-------- .../TerminalSettingsModel/ActionMap.h | 4 ++-- .../TerminalSettingsModel/ActionMap.idl | 2 +- .../TerminalSettingsModel/Command.cpp | 8 +++---- src/cascadia/TerminalSettingsModel/Command.h | 2 +- .../TerminalSettingsModel/Command.idl | 4 ---- 8 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 19411fd38eb..831379ff7b0 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -680,10 +680,9 @@ namespace winrt::TerminalApp::implementation // Load settings if we haven't already ReloadSettings(); } - - return _settings.GlobalSettings().AlwaysShowNotificationIcon() || - _settings.GlobalSettings().MinimizeToNotificationArea(); - ; + const auto& globals{ _settings.GlobalSettings() }; + return globals.AlwaysShowNotificationIcon() || + globals.MinimizeToNotificationArea(); } TerminalApp::TerminalWindow AppLogic::CreateNewWindow() diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d03a3d70b9a..4ee3bb04f36 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2911,13 +2911,7 @@ namespace winrt::TerminalApp::implementation { // Update the command palette when settings reload const auto& expanded{ _settings.GlobalSettings().ActionMap().ExpandedCommands() }; - auto commandsCollection = winrt::single_threaded_vector(); - for (const auto& nameAndCommand : expanded) - { - commandsCollection.Append(nameAndCommand.Value()); - } - - CommandPalette().SetCommands(commandsCollection); + CommandPalette().SetCommands(expanded); } // Method Description: diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index dae2adf1feb..b2c78970d45 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "AllShortcutActions.h" #include "ActionMap.h" +#include "Command.h" #include "AllShortcutActions.h" #include "ActionMap.g.cpp" @@ -858,11 +859,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void ActionMap::_recursiveUpdateCommandKeybindingLabels() { - const auto& commands{ _ExpandedMapCache }; + const auto& commands{ _ExpandedCommandsCache }; - for (const auto& nameAndCmd : commands) + for (const auto& command : commands) { - const auto& command = nameAndCmd.Value(); if (command.HasNestedCommands()) { _recursiveUpdateCommandKeybindingLabels(); @@ -912,19 +912,23 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto copyOfCommands = winrt::single_threaded_map(); const auto& commandsToExpand{ NameMap() }; - for (const auto& nameAndCommand : commandsToExpand) + for (auto nameAndCommand : commandsToExpand) { copyOfCommands.Insert(nameAndCommand.Key(), nameAndCommand.Value()); } - Command::ExpandCommands(copyOfCommands, - profiles, - winrt::param::vector_view{ sortedSchemes }); + implementation::Command::ExpandCommands(copyOfCommands, + profiles, + winrt::param::vector_view{ sortedSchemes }); - _ExpandedMapCache = copyOfCommands; + _ExpandedCommandsCache = winrt::single_threaded_vector(); + for (const auto& [_, command] : copyOfCommands) + { + _ExpandedCommandsCache.Append(command); + } } - IMapView ActionMap::ExpandedCommands() + IVector ActionMap::ExpandedCommands() { - return _ExpandedMapCache.GetView(); + return _ExpandedCommandsCache; } } diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index 7f71e09f19d..95ab9e9ab7f 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -75,7 +75,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation void DeleteKeyBinding(const Control::KeyChord& keys); void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action); - Windows::Foundation::Collections::IMapView ExpandedCommands(); + Windows::Foundation::Collections::IVector ExpandedCommands(); void ExpandCommands(const Windows::Foundation::Collections::IVectorView& profiles, const Windows::Foundation::Collections::IMapView& schemes); @@ -101,7 +101,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Windows::Foundation::Collections::IMap _GlobalHotkeysCache{ nullptr }; Windows::Foundation::Collections::IMap _KeyBindingMapCache{ nullptr }; - Windows::Foundation::Collections::IMap _ExpandedMapCache{ nullptr }; + Windows::Foundation::Collections::IVector _ExpandedCommandsCache{ nullptr }; std::unordered_map _NestedCommands; std::vector _IterableCommands; diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.idl b/src/cascadia/TerminalSettingsModel/ActionMap.idl index 8bbc3b1233c..99df487263b 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.idl +++ b/src/cascadia/TerminalSettingsModel/ActionMap.idl @@ -21,7 +21,7 @@ namespace Microsoft.Terminal.Settings.Model Windows.Foundation.Collections.IMapView KeyBindings { get; }; Windows.Foundation.Collections.IMapView GlobalHotkeys { get; }; - Windows.Foundation.Collections.IMapView ExpandedCommands { get; }; + IVector ExpandedCommands { get; }; }; [default_interface] runtimeclass ActionMap : IActionMapView diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index e66901725a9..beea1edfed8 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -478,7 +478,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // appended to this vector. // Return Value: // - - void Command::ExpandCommands(IMap commands, + void Command::ExpandCommands(IMap& commands, IVectorView profiles, IVectorView schemes) { @@ -486,14 +486,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::vector commandsToAdd; // First, collect up all the commands that need replacing. - for (const auto& nameAndCmd : commands) + for (const auto& [name, command] : commands) { - auto cmd{ get_self(nameAndCmd.Value()) }; + auto cmd{ get_self(command) }; auto newCommands = _expandCommand(cmd, profiles, schemes); if (newCommands.size() > 0) { - commandsToRemove.push_back(nameAndCmd.Key()); + commandsToRemove.push_back(name); commandsToAdd.insert(commandsToAdd.end(), newCommands.begin(), newCommands.end()); } } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 7d246da26b1..4c2b17874c2 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -41,7 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::com_ptr FromJson(const Json::Value& json, std::vector& warnings); - static void ExpandCommands(Windows::Foundation::Collections::IMap commands, + static void ExpandCommands(Windows::Foundation::Collections::IMap& commands, Windows::Foundation::Collections::IVectorView profiles, Windows::Foundation::Collections::IVectorView schemes); diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index 6a5a5fb950d..38af6184bef 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -42,9 +42,5 @@ namespace Microsoft.Terminal.Settings.Model Boolean HasNestedCommands { get; }; Windows.Foundation.Collections.IMapView NestedCommands { get; }; - - static void ExpandCommands(Windows.Foundation.Collections.IMap commands, - Windows.Foundation.Collections.IVectorView profiles, - Windows.Foundation.Collections.IVectorView schemes); } } From b0ca581b4374a0b7344271986f745e86f79d5ec2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Mar 2023 11:10:15 -0600 Subject: [PATCH 093/122] .detach so we don't std::explode() --- src/cascadia/WindowsTerminal/WindowThread.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 917435a69f3..35759e9c7d4 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -44,7 +44,16 @@ void WindowThread::Start() ::DispatchMessageW(&msg); } } + + // Raising our Exited event might cause the app to teardown. In the + // highly unlikely case that the main thread got scheduled before the + // event handler returns, it might release the WindowThread while it's + // still holding an outstanding reference to its own thread. That would + // cause a std::terminate. Let's just avoid that by releasing our + // thread. + this->_thread.detach(); _ExitedHandlers(_peasant.GetID()); + return exitCode; }); LOG_IF_FAILED(SetThreadDescription(_thread.native_handle(), L"Window Thread")); From 74af809b6615699d57ced3976f6854c961daca7b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Mar 2023 11:23:59 -0600 Subject: [PATCH 094/122] mmmm fresh cut code --- src/cascadia/Remoting/WindowManager.cpp | 2 -- src/cascadia/Remoting/WindowManager.h | 2 -- src/cascadia/Remoting/WindowManager.idl | 2 -- src/cascadia/WindowsTerminal/AppHost.h | 2 -- src/cascadia/WindowsTerminal/WindowEmperor.h | 2 -- 5 files changed, 10 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 9a850e7a0a7..4086e222f52 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -62,8 +62,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.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequestedHandlers(*this, nullptr); }); - _monarch.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequestedHandlers(*this, nullptr); }); _monarch.QuitAllRequested({ get_weak(), &WindowManager::_QuitAllRequestedHandlers }); _monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow }); diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index c6d5b687d64..26e4a422272 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -46,8 +46,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(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(HideNotificationIconRequested, 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); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 2adcd9e80c4..963c19b6645 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -32,8 +32,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler WindowClosed; event Windows.Foundation.TypedEventHandler QuitAllRequested; event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; - event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; - event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; event Windows.Foundation.TypedEventHandler RequestNewWindow; diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 05812db7c25..740de0957a5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -158,8 +158,6 @@ class AppHost winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged; winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged; winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged; - winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; - winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; } _revokers{}; }; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index f05fab4f836..211adce69fe 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -77,8 +77,6 @@ class WindowEmperor struct Revokers { - winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; - winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; } _revokers{}; }; From 547b2c9e6a3b0107bf80104d29a39d288339aa0a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Mar 2023 11:25:45 -0600 Subject: [PATCH 095/122] you never get used either --- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 6 +++--- src/cascadia/WindowsTerminal/WindowEmperor.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index cfdaca73bff..ad71e404705 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -83,10 +83,10 @@ bool WindowEmperor::HandleCommandlineArgs() if (result.ShouldCreateWindow()) { - CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }, true); + CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }); _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { - CreateNewWindowThread(args, false); + CreateNewWindowThread(args); }); _becomeMonarch(); @@ -114,7 +114,7 @@ void WindowEmperor::WaitForWindows() } } -void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args, const bool /*firstWindow*/) +void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) { Remoting::Peasant peasant{ _manager.CreatePeasant(args) }; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 211adce69fe..12b24eb415b 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -28,7 +28,7 @@ class WindowEmperor void WaitForWindows(); bool HandleCommandlineArgs(); - void CreateNewWindowThread(winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, const bool firstWindow); + void CreateNewWindowThread(winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args); LRESULT MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept; wil::unique_hwnd _window; From 6f6880c017842037e30c56ac4bfd485ed29873f9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Mar 2023 15:18:27 -0600 Subject: [PATCH 096/122] this is more idomatic locking cause it's actually locking --- .../WindowsTerminal/WindowEmperor.cpp | 32 +++++++++++-------- src/cascadia/WindowsTerminal/WindowEmperor.h | 2 +- src/cascadia/WindowsTerminal/pch.h | 2 ++ 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index ad71e404705..e0796c2ee35 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -120,7 +120,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; - window->Started([this, sender = window]() -> winrt::fire_and_forget { + window->Started([this, sender = window]() { // Add a callback to the window's logic to let us know when the window's // quake mode state changes. We'll use this to check if we need to add // or remove the notification icon. @@ -128,23 +128,25 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) sender->UpdateSettingsRequested({ this, &WindowEmperor::_windowRequestUpdateSettings }); // These come in on the sender's thread. Move back to our thread. - co_await wil::resume_foreground(_dispatcher); - - _windows.push_back(std::move(sender)); + { + auto lockedWindows{ _windows.lock() }; + lockedWindows->push_back(sender); + } }); - window->Exited([this](uint64_t senderID) -> winrt::fire_and_forget { - // These come in on the sender's thread. Move back to our thread. - co_await wil::resume_foreground(_dispatcher); + window->Exited([this](uint64_t senderID) { + // These come in on the sender's thread. + + auto lockedWindows{ _windows.lock() }; // find the window in _windows who's peasant's Id matches the peasant's Id // and remove it - _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [&](const auto& w) { - return w->Peasant().GetID() == senderID; - }), - _windows.end()); + lockedWindows->erase(std::remove_if(lockedWindows->begin(), lockedWindows->end(), [&](const auto& w) { + return w->Peasant().GetID() == senderID; + }), + lockedWindows->end()); - if (_windows.size() == 0) + if (lockedWindows->size() == 0) { _close(); } @@ -209,7 +211,8 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II } const auto& numWindows{ _manager.GetNumberOfPeasants() }; - for (const auto& _windowThread : _windows) + auto windows{ _windows.lock_shared() }; + for (const auto& _windowThread : *windows) { _windowThread->Logic().SetNumberOfOpenWindows(numWindows); } @@ -644,7 +647,8 @@ void WindowEmperor::_checkWindowsForNotificationIcon() // RequestsTrayIcon setting value, and combine that with the result of each // window (which won't change during a settings reload). bool needsIcon = _app.Logic().RequestsTrayIcon(); - for (const auto& _windowThread : _windows) + auto windows{ _windows.lock_shared() }; + for (const auto& _windowThread : *windows) { needsIcon |= _windowThread->Logic().IsQuakeWindow(); } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 12b24eb415b..ce6cdd9662b 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -38,7 +38,7 @@ class WindowEmperor winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr }; winrt::Microsoft::Terminal::Remoting::WindowManager _manager; - std::vector> _windows; + til::shared_mutex>> _windows; std::optional> _getWindowLayoutThrottler; diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 345731f39c8..eafe88dfb6c 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -87,7 +87,9 @@ TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider); #include #include #include + #include "til.h" +#include "til/mutex.h" #include #include // must go after the CoreDispatcher type is defined From 1dc243641cca5afecc1d637c8fe48e55d8c70e7a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 8 Mar 2023 16:13:19 -0600 Subject: [PATCH 097/122] weak refs for fun and profit --- .../WindowsTerminal/WindowEmperor.cpp | 75 +++++++++++++------ src/cascadia/WindowsTerminal/WindowEmperor.h | 5 +- src/cascadia/WindowsTerminal/main.cpp | 6 +- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index e0796c2ee35..62df30d099e 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -120,41 +120,68 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; - window->Started([this, sender = window]() { - // Add a callback to the window's logic to let us know when the window's - // quake mode state changes. We'll use this to check if we need to add - // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged({ this, &WindowEmperor::_windowIsQuakeWindowChanged }); - sender->UpdateSettingsRequested({ this, &WindowEmperor::_windowRequestUpdateSettings }); - - // These come in on the sender's thread. Move back to our thread. + std::weak_ptr weakThis{ weak_from_this() }; + + window->Started([weakThis, sender = window]() { + // These come in on the sender's thread. Make sure we haven't died + // since then + if (auto self{ weakThis.lock() }) { - auto lockedWindows{ _windows.lock() }; - lockedWindows->push_back(sender); + self->_windowStartedHandler(sender); } }); - window->Exited([this](uint64_t senderID) { - // These come in on the sender's thread. - - auto lockedWindows{ _windows.lock() }; - - // find the window in _windows who's peasant's Id matches the peasant's Id - // and remove it - lockedWindows->erase(std::remove_if(lockedWindows->begin(), lockedWindows->end(), [&](const auto& w) { - return w->Peasant().GetID() == senderID; - }), - lockedWindows->end()); - - if (lockedWindows->size() == 0) + window->Exited([weakThis](uint64_t senderID) { + // These come in on the sender's thread. Make sure we haven't died + // since then + if (auto self{ weakThis.lock() }) { - _close(); + self->_windowExitedHandler(senderID); } }); window->Start(); } +// Handler for a WindowThread's Started event, which it raises once the window +// thread starts and XAML is ready to go on that thread. Set up some callbacks +// now that we know this window is set up and ready to go. +// Q: Why isn't adding these callbacks just a part of CreateNewWindowThread? +// A: Until the thread actually starts, the AppHost (and its Logic()) haven't +// been ctor'd or initialized, so trying to add callbacks immediately will A/V +void WindowEmperor::_windowStartedHandler(const std::shared_ptr& sender) +{ + // Add a callback to the window's logic to let us know when the window's + // quake mode state changes. We'll use this to check if we need to add + // or remove the notification icon. + sender->Logic().IsQuakeWindowChanged({ this, &WindowEmperor::_windowIsQuakeWindowChanged }); + sender->UpdateSettingsRequested({ this, &WindowEmperor::_windowRequestUpdateSettings }); + + // Now that the window is ready to go, we can add it to our list of windows, + // because we know it will be well behaved. + // + // Be sure to only modify the list of windows under lock. + { + auto lockedWindows{ _windows.lock() }; + lockedWindows->push_back(sender); + } +} +void WindowEmperor::_windowExitedHandler(uint64_t senderID) +{ + auto lockedWindows{ _windows.lock() }; + + // find the window in _windows who's peasant's Id matches the peasant's Id + // and remove it + lockedWindows->erase(std::remove_if(lockedWindows->begin(), lockedWindows->end(), [&](const auto& w) { + return w->Peasant().GetID() == senderID; + }), + lockedWindows->end()); + + if (lockedWindows->size() == 0) + { + _close(); + } +} // Method Description: // - Set up all sorts of handlers now that we've determined that we're a process // that will end up hosting the windows. These include: diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index ce6cdd9662b..61a1980f32d 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -20,7 +20,7 @@ Class Name: #include "WindowThread.h" -class WindowEmperor +class WindowEmperor : std::enable_shared_from_this { public: WindowEmperor() noexcept; @@ -49,6 +49,9 @@ class WindowEmperor std::unique_ptr _notificationIcon; + void _windowStartedHandler(const std::shared_ptr& sender); + void _windowExitedHandler(uint64_t senderID); + void _becomeMonarch(); void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); void _quitAllRequested(const winrt::Windows::Foundation::IInspectable&, diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index 40bb70a238c..09cac10710c 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -114,9 +114,9 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) // doing that, we can safely init as STA before any WinRT dispatches. winrt::init_apartment(winrt::apartment_type::single_threaded); - ::WindowEmperor emperor{}; - if (emperor.HandleCommandlineArgs()) + const auto emperor = std::make_shared<::WindowEmperor>(); + if (emperor->HandleCommandlineArgs()) { - emperor.WaitForWindows(); + emperor->WaitForWindows(); } } From 6e4b2e1048d573b8a8d954e3fdc68a368bd81c69 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Mar 2023 09:27:48 -0600 Subject: [PATCH 098/122] Guess what's LOAD BEARING --- src/cascadia/WindowsTerminal/WindowEmperor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 61a1980f32d..8fa43e4ba3e 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -20,7 +20,7 @@ Class Name: #include "WindowThread.h" -class WindowEmperor : std::enable_shared_from_this +class WindowEmperor : public std::enable_shared_from_this { public: WindowEmperor() noexcept; From 77218139d83edd68184f26de01cb85e52e9606a7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Mar 2023 10:13:50 -0600 Subject: [PATCH 099/122] pr nits --- src/cascadia/Remoting/Monarch.h | 6 +++--- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 +- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 16 +++++++++------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 762009f1514..d347fc73daa 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -42,9 +42,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation struct WindowRequestedArgs : public WindowRequestedArgsT { public: - WindowRequestedArgs(const Remoting::ProposeCommandlineResult& result, const Remoting::CommandlineArgs& command) : - _Id{ result.Id() ? result.Id().Value() : 0 }, // We'll use 0 as a sentinel, since no window will ever get to have that ID - _WindowName{ result.WindowName() }, + WindowRequestedArgs(const Remoting::ProposeCommandlineResult& windowInfo, const Remoting::CommandlineArgs& command) : + _Id{ windowInfo.Id() ? windowInfo.Id().Value() : 0 }, // We'll use 0 as a sentinel, since no window will ever get to have that ID + _WindowName{ windowInfo.WindowName() }, _args{ command.Commandline() }, _CurrentDirectory{ command.CurrentDirectory() } {}; diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 732dfab25e5..c046a82d3b5 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -19,7 +19,7 @@ namespace Microsoft.Terminal.Remoting } [default_interface] runtimeclass WindowRequestedArgs { - WindowRequestedArgs(ProposeCommandlineResult result, CommandlineArgs command); + WindowRequestedArgs(ProposeCommandlineResult windowInfo, CommandlineArgs command); UInt64 Id { get; }; String WindowName { get; }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 61fbb801776..64797b09d90 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -253,7 +253,7 @@ namespace winrt::TerminalApp::implementation } _root->SetSettings(_settings, false); // We're on our UI thread right now, so this is safe - _root->Loaded({ this, &TerminalWindow::_OnLoaded }); + _root->Loaded({ get_weak(), &TerminalWindow::_OnLoaded }); _root->Initialized([this](auto&&, auto&&) { // GH#288 - When we finish initialization, if the user wanted us // launched _fullscreen_, toggle fullscreen mode. This will make sure diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 62df30d099e..db4debfa213 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -172,10 +172,10 @@ void WindowEmperor::_windowExitedHandler(uint64_t senderID) // find the window in _windows who's peasant's Id matches the peasant's Id // and remove it - lockedWindows->erase(std::remove_if(lockedWindows->begin(), lockedWindows->end(), [&](const auto& w) { - return w->Peasant().GetID() == senderID; - }), - lockedWindows->end()); + std::erase_if(*lockedWindows, + [&](const auto& w) { + return w->Peasant().GetID() == senderID; + }); if (lockedWindows->size() == 0) { @@ -674,10 +674,12 @@ void WindowEmperor::_checkWindowsForNotificationIcon() // RequestsTrayIcon setting value, and combine that with the result of each // window (which won't change during a settings reload). bool needsIcon = _app.Logic().RequestsTrayIcon(); - auto windows{ _windows.lock_shared() }; - for (const auto& _windowThread : *windows) { - needsIcon |= _windowThread->Logic().IsQuakeWindow(); + auto windows{ _windows.lock_shared() }; + for (const auto& _windowThread : *windows) + { + needsIcon |= _windowThread->Logic().IsQuakeWindow(); + } } if (needsIcon) From 6dead9934156f687702afae776819ebcec92a4b9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Mar 2023 12:29:23 -0600 Subject: [PATCH 100/122] PR nits --- src/cascadia/TerminalApp/AppLogic.cpp | 7 ++++--- src/cascadia/TerminalApp/SettingsLoadEventArgs.h | 12 ++++++------ src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 9af911a7813..1fbac932e68 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -200,7 +200,8 @@ namespace winrt::TerminalApp::implementation // These used to be in `TerminalPage::Initialized`, so that they started // _after_ the Terminal window was started and displayed. These could - // theoretically move there again too. + // theoretically move there again too. TODO:GH#14957 - evaluate moving + // this after the Page is initialized { // Both LoadSettings and ReloadSettings are supposed to call this function, // but LoadSettings skips it, so that the UI starts up faster. @@ -232,7 +233,7 @@ namespace winrt::TerminalApp::implementation TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); } - }; + } _ApplyLanguageSettingChange(); _ApplyStartupTaskStateChange(); @@ -298,7 +299,7 @@ namespace winrt::TerminalApp::implementation _settings = std::move(newSettings); - hr = (_warnings.size()) == 0u ? S_OK : S_FALSE; + hr = _warnings.empty() ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) { diff --git a/src/cascadia/TerminalApp/SettingsLoadEventArgs.h b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h index e31dd5e2a91..e094810784b 100644 --- a/src/cascadia/TerminalApp/SettingsLoadEventArgs.h +++ b/src/cascadia/TerminalApp/SettingsLoadEventArgs.h @@ -18,13 +18,13 @@ namespace winrt::TerminalApp::implementation public: SettingsLoadEventArgs(bool reload, uint64_t result, - const winrt::hstring& exceptionText, - const winrt::Windows::Foundation::Collections::IVector& warnings, - const Microsoft::Terminal::Settings::Model::CascadiaSettings& newSettings) : + winrt::hstring exceptionText, + winrt::Windows::Foundation::Collections::IVector warnings, + Microsoft::Terminal::Settings::Model::CascadiaSettings newSettings) : _Reload{ reload }, _Result{ result }, - _ExceptionText{ exceptionText }, - _Warnings{ warnings }, - _NewSettings{ newSettings } {}; + _ExceptionText{ std::move(exceptionText) }, + _Warnings{ std::move(warnings) }, + _NewSettings{ std::move(newSettings) } {}; }; } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 9361f533460..fd299249ab3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1799,7 +1799,7 @@ namespace winrt::TerminalApp::implementation } // If the user set a custom name, save it - if (const auto windowName = _WindowProperties.WindowName(); !windowName.empty()) + if (const auto& windowName{ _WindowProperties.WindowName() }; !windowName.empty()) { ActionAndArgs action; action.Action(ShortcutAction::RenameWindow); @@ -3927,7 +3927,7 @@ namespace winrt::TerminalApp::implementation else if (key == Windows::System::VirtualKey::Escape) { // User wants to discard the changes they made - WindowRenamerTextBox().Text(WindowProperties().WindowName()); + WindowRenamerTextBox().Text(_WindowProperties.WindowName()); WindowRenamer().IsOpen(false); _renamerPressedEnter = false; } From 434abc247403a15e3395c0d7473633663805f9f1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Mar 2023 12:52:28 -0600 Subject: [PATCH 101/122] Remove the need for TerminalPage to know the number of open windows This removes all the weirdness around the way that TerminalPage needs to track the number of open windows. Instead of TerminalPage persisting empty state when the last tab closes, it lets the AppHost know that the last tab was closed due to _closing the tab_ (as opposed to closing the window / quitting). This gives AppHost an opportunity to persist empty state for that case, because _it_ knows how many windows there are. This could basically be its own PR. Probably worth xlinking this commit to #9800 --- src/cascadia/TerminalApp/TabManagement.cpp | 8 +------ src/cascadia/TerminalApp/TerminalPage.cpp | 9 +------- src/cascadia/TerminalApp/TerminalPage.h | 13 ++++++++--- src/cascadia/TerminalApp/TerminalPage.idl | 6 ++++- src/cascadia/TerminalApp/TerminalWindow.cpp | 22 +++++++++--------- src/cascadia/TerminalApp/TerminalWindow.h | 4 ++-- src/cascadia/TerminalApp/TerminalWindow.idl | 5 +++-- src/cascadia/WindowsTerminal/AppHost.cpp | 25 ++++++++++++--------- 8 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index f1134efbd36..511c4925fe9 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -523,13 +523,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. - if (!_maintainStateOnTabClose && _settings.GlobalSettings().ShouldUsePersistedLayout() && _numOpenWindows == 1) - { - auto state = ApplicationState::SharedInstance(); - state.PersistedWindowLayouts(nullptr); - } - - _LastTabClosedHandlers(*this, nullptr); + _LastTabClosedHandlers(*this, winrt::make(!_maintainStateOnTabClose)); } else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast(tabIndex)) { diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index fd299249ab3..87cf3df5e90 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -5,6 +5,7 @@ #include "pch.h" #include "TerminalPage.h" #include "TerminalPage.g.cpp" +#include "LastTabClosedEventArgs.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" #include @@ -3808,14 +3809,6 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::SetNumberOfOpenWindows(const uint64_t num) - { - // This is used in TerminalPage::_RemoveTab, when we close a tab. If we - // close the last tab, and there's only one window open, then we will - // call to persist _no_ state. - _numOpenWindows = num; - } - // Method Description: // - Called when an attempt to rename the window has failed. This will open // the toast displaying a message to the user that the attempt to rename diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index c82a365c4b8..f01034a2a4f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -7,6 +7,7 @@ #include "TerminalTab.h" #include "AppKeyBindings.h" #include "AppCommandlineArgs.h" +#include "LastTabClosedEventArgs.g.h" #include "RenameWindowRequestedArgs.g.h" #include "Toast.h" @@ -41,6 +42,15 @@ 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); @@ -120,8 +130,6 @@ namespace winrt::TerminalApp::implementation winrt::hstring WindowIdForDisplay() const noexcept { return _WindowProperties.WindowIdForDisplay(); }; winrt::hstring WindowNameForDisplay() const noexcept { return _WindowProperties.WindowNameForDisplay(); }; - void SetNumberOfOpenWindows(const uint64_t value); - bool IsElevated() const noexcept; void OpenSettingsUI(); @@ -188,7 +196,6 @@ namespace winrt::TerminalApp::implementation bool _isAlwaysOnTop{ false }; std::optional _loadFromPersistedLayoutIdx{}; - uint64_t _numOpenWindows{ 0 }; bool _maintainStateOnTabClose{ false }; bool _rearranging{ false }; diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index b363e183355..a753da2a2b7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -5,7 +5,11 @@ import "IDirectKeyListener.idl"; namespace TerminalApp { - delegate void LastTabClosedEventArgs(); + + [default_interface] runtimeclass LastTabClosedEventArgs + { + Boolean ClearPersistedState { get; }; + }; [default_interface] runtimeclass RenameWindowRequestedArgs { diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 3215a1f546f..258612dfd00 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -882,13 +882,13 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void TerminalWindow::CloseWindow(LaunchPosition pos) + void TerminalWindow::CloseWindow(LaunchPosition pos, const bool isLastWindow) { if (_root) { // If persisted layout is enabled and we are the last window closing // we should save our state. - if (_settings.GlobalSettings().ShouldUsePersistedLayout() && _numOpenWindows == 1) + if (_settings.GlobalSettings().ShouldUsePersistedLayout() && isLastWindow) { if (const auto layout = _root->GetWindowLayout()) { @@ -902,6 +902,15 @@ namespace winrt::TerminalApp::implementation } } + void TerminalWindow::ClearPersistedWindowState() + { + if (_settings.GlobalSettings().ShouldUsePersistedLayout()) + { + auto state = ApplicationState::SharedInstance(); + state.PersistedWindowLayouts(nullptr); + } + } + winrt::TerminalApp::TaskbarState TerminalWindow::TaskbarState() { if (_root) @@ -1114,15 +1123,6 @@ namespace winrt::TerminalApp::implementation return nullptr; } - void TerminalWindow::SetNumberOfOpenWindows(const uint64_t num) - { - _numOpenWindows = num; - if (_root) - { - _root->SetNumberOfOpenWindows(num); - } - } - void TerminalWindow::IdentifyWindow() { if (_root) diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 0e5f83e4934..753cd0e2e81 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -77,6 +77,7 @@ namespace winrt::TerminalApp::implementation void SetPersistedLayoutIdx(const uint32_t idx); void SetNumberOfOpenWindows(const uint64_t num); bool ShouldUsePersistedLayout() const; + void ClearPersistedWindowState(); void RequestExitFullscreen(); @@ -95,7 +96,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); + void CloseWindow(Microsoft::Terminal::Settings::Model::LaunchPosition position, const bool isLastWindow); void WindowVisibilityChanged(const bool showOrHide); winrt::TerminalApp::TaskbarState TaskbarState(); @@ -153,7 +154,6 @@ namespace winrt::TerminalApp::implementation winrt::hstring _WindowName{}; uint64_t _WindowId{ 0 }; - uint64_t _numOpenWindows{ 1 }; std::optional _loadFromPersistedLayoutIdx{}; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 8474fc33908..a4567734121 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -77,7 +77,8 @@ namespace TerminalApp void IdentifyWindow(); void SetPersistedLayoutIdx(UInt32 idx); - void SetNumberOfOpenWindows(UInt64 num); + void ClearPersistedWindowState(); + void RenameFailed(); void RequestExitFullscreen(); @@ -91,7 +92,7 @@ namespace TerminalApp Boolean GetInitialAlwaysOnTop(); Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension); void TitlebarClicked(); - void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position); + void CloseWindow(Microsoft.Terminal.Settings.Model.LaunchPosition position, Boolean isLastWindow); void WindowVisibilityChanged(Boolean showOrHide); TaskbarState TaskbarState{ get; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a82963b6a67..0d41492ee40 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -316,7 +316,6 @@ void AppHost::_HandleCommandlineArgs() )); } } - _windowLogic.SetNumberOfOpenWindows(numPeasants); } _windowLogic.WindowName(peasant.WindowName()); _windowLogic.WindowId(peasant.GetID()); @@ -505,7 +504,7 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /* // - LastTabClosedEventArgs: unused // Return Value: // - -void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/) +void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& args) { if (_windowManager.IsMonarch() && _notificationIcon) { @@ -524,11 +523,22 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se _windowManager.WindowCreated(_WindowCreatedToken); _windowManager.WindowClosed(_WindowClosedToken); + // 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(); + _window->Close(); } @@ -973,22 +983,16 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s _CreateNotificationIcon(); } - // Set the number of open windows (so we know if we are the last window) - // and subscribe for updates if there are any changes to that number. - _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); - _WindowCreatedToken = _windowManager.WindowCreated([this](auto&&, auto&&) { if (_getWindowLayoutThrottler) { _getWindowLayoutThrottler.value()(); - } - _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); + } }); _WindowClosedToken = _windowManager.WindowClosed([this](auto&&, auto&&) { if (_getWindowLayoutThrottler) { _getWindowLayoutThrottler.value()(); } - _windowLogic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); }); // These events are coming from peasants that become or un-become quake windows. @@ -1651,7 +1655,8 @@ void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /* const winrt::Windows::Foundation::IInspectable& /*args*/) { const auto pos = _GetWindowLaunchPosition(); - _windowLogic.CloseWindow(pos); + const bool isLastWindow = _windowManager.GetNumberOfPeasants() == 1; + _windowLogic.CloseWindow(pos, isLastWindow); } void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, From 1b59eb9238a419724e43b454c289630452965c62 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Mar 2023 13:50:13 -0600 Subject: [PATCH 102/122] save this for later, we should only need ot look it up once --- src/cascadia/TerminalApp/TerminalWindow.cpp | 38 ++++++++++++++------- src/cascadia/TerminalApp/TerminalWindow.h | 3 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 258612dfd00..e62d4c1ec30 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -141,21 +141,19 @@ namespace winrt::TerminalApp::implementation { // Pass commandline args into the TerminalPage. If we were supposed to // load from a persisted layout, do that instead. - auto foundLayout = false; + + // layout will only ever be non-null if there were >0 tabs persisted in + // .TabLayout(). We can re-evaluate that as a part of TODO: GH#12633 if (const auto& layout = LoadPersistedLayout()) { - if (layout.TabLayout().Size() > 0) + std::vector actions; + for (const auto& a : layout.TabLayout()) { - std::vector actions; - for (const auto& a : layout.TabLayout()) - { - actions.emplace_back(a); - } - _root->SetStartupActions(actions); - foundLayout = true; + actions.emplace_back(a); } + _root->SetStartupActions(actions); } - if (!foundLayout) + else { _root->SetStartupActions(_appArgs.GetStartupActions()); } @@ -1095,6 +1093,7 @@ namespace winrt::TerminalApp::implementation void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) { _loadFromPersistedLayoutIdx = idx; + _cachedLayout = std::nullopt; } // Method Description; @@ -1109,18 +1108,31 @@ namespace winrt::TerminalApp::implementation return _settings.GlobalSettings().ShouldUsePersistedLayout() ? _loadFromPersistedLayoutIdx : std::nullopt; } - WindowLayout TerminalWindow::LoadPersistedLayout() const + WindowLayout TerminalWindow::LoadPersistedLayout() { + if (_cachedLayout.has_value()) + { + return *_cachedLayout; + } + if (const auto idx = LoadPersistedLayoutIdx()) { const auto i = idx.value(); const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts(); if (layouts && layouts.Size() > i) { - return layouts.GetAt(i); + auto layout = layouts.GetAt(i); + + // TODO: GH#12633: Right now, we're manually making sure that we + // have at least one tab to restore. If we ever want to come + // back and make it so that you can persist position and size, + // but not the tabs themselves, we can revisit this assumption. + _cachedLayout = (layout.TabLayout().Size() > 0) ? layout : nullptr; + return *_cachedLayout; } } - return nullptr; + _cachedLayout = nullptr; + return *_cachedLayout; } void TerminalWindow::IdentifyWindow() diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 753cd0e2e81..4a83fdf4c11 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -72,7 +72,7 @@ namespace winrt::TerminalApp::implementation void RenameFailed(); std::optional LoadPersistedLayoutIdx() const; - winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout() const; + winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(); void SetPersistedLayoutIdx(const uint32_t idx); void SetNumberOfOpenWindows(const uint64_t num); @@ -155,6 +155,7 @@ namespace winrt::TerminalApp::implementation uint64_t _WindowId{ 0 }; std::optional _loadFromPersistedLayoutIdx{}; + std::optional _cachedLayout{ std::nullopt }; Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; TerminalApp::SettingsLoadEventArgs _initialLoadResult{ nullptr }; From 39a94503fc01d29e6893875e5a875ddf61e525ca Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Mar 2023 13:51:43 -0600 Subject: [PATCH 103/122] to wit, also make sure that there is a tablayout --- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index e62d4c1ec30..f307ca66a1e 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1127,7 +1127,7 @@ namespace winrt::TerminalApp::implementation // have at least one tab to restore. If we ever want to come // back and make it so that you can persist position and size, // but not the tabs themselves, we can revisit this assumption. - _cachedLayout = (layout.TabLayout().Size() > 0) ? layout : nullptr; + _cachedLayout = (layout.TabLayout() && layout.TabLayout().Size() > 0) ? layout : nullptr; return *_cachedLayout; } } From 44b238e777fe2114d170791bdb797227c83bef5a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 9 Mar 2023 16:55:05 -0600 Subject: [PATCH 104/122] Redo how ownership of WindowProperties works. This is cleaner by a good amount --- src/cascadia/TerminalApp/TerminalPage.cpp | 29 ++-- src/cascadia/TerminalApp/TerminalPage.h | 15 +- src/cascadia/TerminalApp/TerminalPage.idl | 12 +- src/cascadia/TerminalApp/TerminalPage.xaml | 8 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 146 ++++++++++---------- src/cascadia/TerminalApp/TerminalWindow.h | 34 +++-- src/cascadia/TerminalApp/TerminalWindow.idl | 8 +- 7 files changed, 130 insertions(+), 122 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 87cf3df5e90..95b3f7834e5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -52,13 +52,16 @@ namespace winrt namespace winrt::TerminalApp::implementation { - TerminalPage::TerminalPage() : + TerminalPage::TerminalPage(TerminalApp::WindowProperties properties) : _tabs{ winrt::single_threaded_observable_vector() }, _mruTabs{ winrt::single_threaded_observable_vector() }, _startupActions{ winrt::single_threaded_vector() }, - _hostingHwnd{} + _hostingHwnd{}, + _WindowProperties{ properties } { InitializeComponent(); + + _WindowProperties.PropertyChanged({ get_weak(), &TerminalPage::_windowPropertyChanged }); } // Method Description: @@ -4302,27 +4305,21 @@ namespace winrt::TerminalApp::implementation _updateThemeColors(); } - TerminalApp::IWindowProperties TerminalPage::WindowProperties() - { - return _WindowProperties; - } - void TerminalPage::WindowProperties(const TerminalApp::IWindowProperties& props) - { - _WindowProperties = props; - } - - winrt::fire_and_forget TerminalPage::WindowNameChanged() + // Handler for our WindowProperties's PropertyChanged event. We'll use this + // to pop the "Identify Window" toast when the user renames our window. + winrt::fire_and_forget TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/, + const WUX::Data::PropertyChangedEventArgs& args) { + if (args.PropertyName() != L"WindowName") + { + co_return; + } auto weakThis{ get_weak() }; // On the foreground thread, raise property changed notifications, and // display the success toast. co_await wil::resume_foreground(Dispatcher()); if (auto page{ weakThis.get() }) { - _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowName" }); - _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); - _PropertyChangedHandlers(*this, WUX::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); - // DON'T display the confirmation if this is the name we were // given on startup! if (page->_startupState == StartupState::Initialized) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index f01034a2a4f..a99854374ca 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -63,7 +63,7 @@ namespace winrt::TerminalApp::implementation struct TerminalPage : TerminalPageT { public: - TerminalPage(); + TerminalPage(TerminalApp::WindowProperties properties); // This implements shobjidl's IInitializeWithWindow, but due to a XAML Compiler bug we cannot // put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331 @@ -124,11 +124,7 @@ namespace winrt::TerminalApp::implementation const bool initial, const winrt::hstring cwd = L""); - // For the sake of XAML binding: - winrt::hstring WindowName() const noexcept { return _WindowProperties.WindowName(); }; - uint64_t WindowId() const noexcept { return _WindowProperties.WindowId(); }; - winrt::hstring WindowIdForDisplay() const noexcept { return _WindowProperties.WindowIdForDisplay(); }; - winrt::hstring WindowNameForDisplay() const noexcept { return _WindowProperties.WindowNameForDisplay(); }; + TerminalApp::WindowProperties WindowProperties() const noexcept { return _WindowProperties; }; bool IsElevated() const noexcept; @@ -137,10 +133,6 @@ namespace winrt::TerminalApp::implementation bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); - TerminalApp::IWindowProperties WindowProperties(); - void WindowProperties(const TerminalApp::IWindowProperties& props); - winrt::fire_and_forget WindowNameChanged(); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -230,7 +222,7 @@ namespace winrt::TerminalApp::implementation int _renamerLayoutCount{ 0 }; bool _renamerPressedEnter{ false }; - TerminalApp::IWindowProperties _WindowProperties{ nullptr }; + TerminalApp::WindowProperties _WindowProperties{ nullptr }; winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); @@ -463,6 +455,7 @@ namespace winrt::TerminalApp::implementation void _updateTabCloseButton(const winrt::Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem); winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args); + winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index a753da2a2b7..2fc333afe12 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -21,18 +21,19 @@ namespace TerminalApp Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); }; - interface IWindowProperties + [default_interface] runtimeclass WindowProperties : Windows.UI.Xaml.Data.INotifyPropertyChanged { String WindowName { get; }; UInt64 WindowId { get; }; String WindowNameForDisplay { get; }; String WindowIdForDisplay { get; }; + Boolean IsQuakeWindow(); }; [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener { - TerminalPage(); + TerminalPage(WindowProperties properties); // XAML bound properties String ApplicationDisplayName { get; }; @@ -42,7 +43,7 @@ namespace TerminalApp Boolean Fullscreen { get; }; Boolean AlwaysOnTop { get; }; - IWindowProperties WindowProperties { get; }; + WindowProperties WindowProperties { get; }; void IdentifyWindow(); void RenameFailed(); @@ -58,11 +59,6 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); - String WindowName { get; }; - UInt64 WindowId { get; }; - String WindowNameForDisplay { get; }; - String WindowIdForDisplay { get; }; - event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler LastTabClosed; event Windows.Foundation.TypedEventHandler SetTitleBarContent; diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 3434cd868ee..38d7c0f69f7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -201,10 +201,10 @@ tracked by MUX#4382 --> + Subtitle="{x:Bind WindowProperties.WindowNameForDisplay, Mode=OneWay}" /> + Text="{x:Bind WindowProperties.WindowName, Mode=OneWay}" /> diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index f307ca66a1e..eb0ef7c78b9 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -6,6 +6,7 @@ #include "../inc/WindowingBehavior.h" #include "TerminalWindow.g.cpp" #include "SettingsLoadEventArgs.g.cpp" +#include "WindowProperties.g.cpp" #include #include @@ -119,13 +120,13 @@ namespace winrt::TerminalApp::implementation { TerminalWindow::TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult) : _settings{ settingsLoadedResult.NewSettings() }, - _initialLoadResult{ settingsLoadedResult } + _initialLoadResult{ settingsLoadedResult }, + _WindowProperties{ winrt::make_self() } { // The TerminalPage has to be constructed during our construction, to // make sure that there's a terminal page for callers of // SetTitleBarContent - _root = winrt::make_self(); - _root->WindowProperties(*this); + _root = winrt::make_self(*_WindowProperties); _dialog = ContentDialog{}; // For your own sanity, it's better to do setup outside the ctor. @@ -258,7 +259,7 @@ namespace winrt::TerminalApp::implementation // that the window size is _first_ set up as something sensible, so // leaving fullscreen returns to a reasonable size. const auto launchMode = this->GetLaunchMode(); - if (IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) + if (_WindowProperties->IsQuakeWindow() || WI_IsFlagSet(launchMode, LaunchMode::FocusMode)) { _root->SetFocusMode(true); } @@ -270,7 +271,7 @@ namespace winrt::TerminalApp::implementation _root->Maximized(true); } - if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow()) + if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !_WindowProperties->IsQuakeWindow()) { _root->SetFullscreen(true); } @@ -1151,78 +1152,23 @@ namespace winrt::TerminalApp::implementation } } - winrt::hstring TerminalWindow::WindowName() const noexcept - { - return _WindowName; - } void TerminalWindow::WindowName(const winrt::hstring& name) { - const auto oldIsQuakeMode = IsQuakeWindow(); - - const auto changed = _WindowName != name; - if (changed) + const auto oldIsQuakeMode = _WindowProperties->IsQuakeWindow(); + _WindowProperties->WindowName(name); + const auto newIsQuakeMode = _WindowProperties->IsQuakeWindow(); + if (newIsQuakeMode != oldIsQuakeMode) { - _WindowName = name; - if (_root) - { - _root->WindowNameChanged(); - - // If we're entering quake mode, or leaving it - if (IsQuakeWindow() != oldIsQuakeMode) - { - // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode - // If we're entering Quake Mode from Focus Mode, then this will do nothing - // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing - _root->SetFocusMode(true); - _IsQuakeWindowChangedHandlers(*this, nullptr); - } - } + // If we're entering Quake Mode from ~Focus Mode, then this will enter Focus Mode + // If we're entering Quake Mode from Focus Mode, then this will do nothing + // If we're leaving Quake Mode (we're already in Focus Mode), then this will do nothing + _root->SetFocusMode(true); + _IsQuakeWindowChangedHandlers(*this, nullptr); } } - uint64_t TerminalWindow::WindowId() const noexcept - { - return _WindowId; - } void TerminalWindow::WindowId(const uint64_t& id) { - if (_WindowId != id) - { - _WindowId = id; - if (_root) - { - _root->WindowNameChanged(); - } - } - } - - // Method Description: - // - Returns a label like "Window: 1234" for the ID of this window - // Arguments: - // - - // Return Value: - // - a string for displaying the name of the window. - winrt::hstring TerminalWindow::WindowIdForDisplay() const noexcept - { - return winrt::hstring{ fmt::format(L"{}: {}", - std::wstring_view(RS_(L"WindowIdLabel")), - _WindowId) }; - } - - // Method Description: - // - Returns a label like "" when the window has no name, or the name of the window. - // Arguments: - // - - // Return Value: - // - a string for displaying the name of the window. - winrt::hstring TerminalWindow::WindowNameForDisplay() const noexcept - { - return _WindowName.empty() ? - winrt::hstring{ fmt::format(L"<{}>", RS_(L"UnnamedWindowName")) } : - _WindowName; - } - bool TerminalWindow::IsQuakeWindow() const noexcept - { - return _WindowName == QuakeWindowName; + _WindowProperties->WindowId(id); } void TerminalWindow::RequestExitFullscreen() @@ -1271,4 +1217,64 @@ namespace winrt::TerminalApp::implementation } } + winrt::hstring WindowProperties::WindowName() const noexcept + { + return _WindowName; + } + + void WindowProperties::WindowName(const winrt::hstring& value) + { + if (_WindowName != value) + { + _WindowName = value; + _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowName" }); + _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); + } + } + uint64_t WindowProperties::WindowId() const noexcept + { + return _WindowId; + } + + void WindowProperties::WindowId(const uint64_t& value) + { + if (_WindowId != value) + { + _WindowId = value; + _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowId" }); + _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); + } + } + + // Method Description: + // - Returns a label like "Window: 1234" for the ID of this window + // Arguments: + // - + // Return Value: + // - a string for displaying the name of the window. + winrt::hstring WindowProperties::WindowIdForDisplay() const noexcept + { + return winrt::hstring{ fmt::format(L"{}: {}", + std::wstring_view(RS_(L"WindowIdLabel")), + _WindowId) }; + } + + // Method Description: + // - Returns a label like "" when the window has no name, or the name of the window. + // Arguments: + // - + // Return Value: + // - a string for displaying the name of the window. + winrt::hstring WindowProperties::WindowNameForDisplay() const noexcept + { + return _WindowName.empty() ? + winrt::hstring{ fmt::format(L"<{}>", RS_(L"UnnamedWindowName")) } : + _WindowName; + } + + bool WindowProperties::IsQuakeWindow() const noexcept + { + return _WindowName == QuakeWindowName; + } + }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 4a83fdf4c11..1a446010761 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -5,6 +5,7 @@ #include "TerminalWindow.g.h" #include "SystemMenuChangeArgs.g.h" +#include "WindowProperties.g.h" #include "SettingsLoadEventArgs.h" #include "TerminalPage.h" @@ -33,6 +34,26 @@ namespace winrt::TerminalApp::implementation _Name{ name }, _Action{ action }, _Handler{ handler } {}; }; + struct WindowProperties : WindowPropertiesT + { + // Normally, WindowName and WindowId would be + // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise + // WindowNameForDisplay and WindowIdForDisplay instead + winrt::hstring WindowName() const noexcept; + void WindowName(const winrt::hstring& value); + uint64_t WindowId() const noexcept; + void WindowId(const uint64_t& value); + winrt::hstring WindowIdForDisplay() const noexcept; + winrt::hstring WindowNameForDisplay() const noexcept; + bool IsQuakeWindow() const noexcept; + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + + private: + winrt::hstring _WindowName{}; + uint64_t _WindowId{ 0 }; + }; + struct TerminalWindow : TerminalWindowT { public: @@ -113,16 +134,10 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::Theme Theme(); void UpdateSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& arg); - // Normally, WindowName and WindowId would be - // WINRT_OBSERVABLE_PROPERTY's, but we want them to raise - // WindowNameForDisplay and WindowIdForDisplay instead - winrt::hstring WindowName() const noexcept; void WindowName(const winrt::hstring& value); - uint64_t WindowId() const noexcept; void WindowId(const uint64_t& value); - winrt::hstring WindowIdForDisplay() const noexcept; - winrt::hstring WindowNameForDisplay() const noexcept; - bool IsQuakeWindow() const noexcept; + bool IsQuakeWindow() const noexcept { return _WindowProperties->IsQuakeWindow(); } + TerminalApp::WindowProperties WindowProperties() { return *_WindowProperties; } // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. @@ -151,8 +166,7 @@ namespace winrt::TerminalApp::implementation bool _gotSettingsStartupActions{ false }; std::vector _settingsStartupArgs{}; - winrt::hstring _WindowName{}; - uint64_t _WindowId{ 0 }; + winrt::com_ptr _WindowProperties{ nullptr }; std::optional _loadFromPersistedLayoutIdx{}; std::optional _cachedLayout{ std::nullopt }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index a4567734121..a945f88e007 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -40,7 +40,7 @@ namespace TerminalApp // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. - [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, IWindowProperties, Windows.UI.Xaml.Data.INotifyPropertyChanged + [default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged { TerminalWindow(SettingsLoadEventArgs result); @@ -107,8 +107,10 @@ namespace TerminalApp // These already have accessors as a part of IWindowProperties, but we // also want to be able to set them. - String WindowName { set; }; - UInt64 WindowId { set; }; + WindowProperties WindowProperties { get; }; + void WindowName(String name); + void WindowId(UInt64 id); + Boolean IsQuakeWindow(); // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. From 073350e66fb9d8319d59c72ba76e8861ef96fb32 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Mar 2023 09:02:46 -0600 Subject: [PATCH 105/122] fix the tests too --- .../LocalTests_TerminalApp/TabTests.cpp | 36 +++++++------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index c1edac3c313..b5fa445ab77 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "../TerminalApp/TerminalPage.h" +#include "../TerminalApp/TerminalWindow.h" #include "../TerminalApp/MinMaxCloseControl.h" #include "../TerminalApp/TabRowControl.h" #include "../TerminalApp/ShortcutActionDispatch.h" @@ -42,17 +43,6 @@ namespace TerminalAppLocalTests // an updated TAEF that will let us install framework packages when the test // package is deployed. Until then, these tests won't deploy in CI. - struct WindowProperties : winrt::implements - { - WINRT_PROPERTY(winrt::hstring, WindowName); - WINRT_PROPERTY(uint64_t, WindowId); - WINRT_PROPERTY(winrt::hstring, WindowNameForDisplay); - WINRT_PROPERTY(winrt::hstring, WindowIdForDisplay); - - public: - bool IsQuakeWindow() { return _WindowName == L"_quake"; }; - }; - class TabTests { // For this set of tests, we need to activate some XAML content. For @@ -121,7 +111,7 @@ namespace TerminalAppLocalTests void _initializeTerminalPage(winrt::com_ptr& page, CascadiaSettings initialSettings); winrt::com_ptr _commonSetup(); - winrt::com_ptr _windowProperties; + winrt::com_ptr _windowProperties; }; template @@ -206,8 +196,11 @@ namespace TerminalAppLocalTests { winrt::com_ptr page{ nullptr }; - auto result = RunOnUIThread([&page]() { - page = winrt::make_self(); + _windowProperties = winrt::make_self(); + winrt::TerminalApp::WindowProperties props = *_windowProperties; + + auto result = RunOnUIThread([&page, props]() { + page = winrt::make_self(props); VERIFY_IS_NOT_NULL(page); }); VERIFY_SUCCEEDED(result); @@ -251,15 +244,13 @@ namespace TerminalAppLocalTests // it's weird. winrt::TerminalApp::TerminalPage projectedPage{ nullptr }; - _windowProperties = winrt::make_self(); - winrt::TerminalApp::IWindowProperties iProps{ *_windowProperties }; - + _windowProperties = winrt::make_self(); + winrt::TerminalApp::WindowProperties props = *_windowProperties; Log::Comment(NoThrowString().Format(L"Construct the TerminalPage")); - auto result = RunOnUIThread([&projectedPage, &page, initialSettings, iProps]() { - projectedPage = winrt::TerminalApp::TerminalPage(); + auto result = RunOnUIThread([&projectedPage, &page, initialSettings, props]() { + projectedPage = winrt::TerminalApp::TerminalPage(props); page.copy_from(winrt::get_self(projectedPage)); page->_settings = initialSettings; - page->WindowProperties(iProps); }); VERIFY_SUCCEEDED(result); @@ -1264,11 +1255,10 @@ namespace TerminalAppLocalTests // // This replicates how TerminalWindow works _windowProperties->WindowName(args.ProposedName()); - page->WindowNameChanged(); }); auto windowNameChanged = false; - page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable { + _windowProperties->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable { if (args.PropertyName() == L"WindowNameForDisplay") { windowNameChanged = true; @@ -1279,7 +1269,7 @@ namespace TerminalAppLocalTests page->_RequestWindowRename(winrt::hstring{ L"Foo" }); }); TestOnUIThread([&]() { - VERIFY_ARE_EQUAL(L"Foo", page->WindowName()); + VERIFY_ARE_EQUAL(L"Foo", page->WindowProperties().WindowName()); VERIFY_IS_TRUE(windowNameChanged, L"The window name should have changed, and we should have raised a notification that WindowNameForDisplay changed"); }); From ca511c9cc9f4d2f365cafdab08a51a88602fdd80 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Mar 2023 09:04:05 -0600 Subject: [PATCH 106/122] this is why I don't use VS --- src/cascadia/WindowsTerminal/AppHost.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0d41492ee40..bd77664f685 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -538,7 +538,6 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se // event handler finishes. _windowManager.SignalClose(); - _window->Close(); } From f70775aca0ee0fee931b4350769b6bebbd6a1d66 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Mar 2023 10:04:54 -0600 Subject: [PATCH 107/122] I don't trust you phyllis --- src/cascadia/TerminalApp/AppLogic.cpp | 29 ++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 1fbac932e68..c8677eb7f82 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -32,8 +32,12 @@ namespace winrt //////////////////////////////////////////////////////////////////////////////// // Error message handling. This is in this file rather than with the warnings in -// TerminalWindow, because the error text might also just be a serialization -// error message. So AppLogic needs to know the actual text of the error. +// TerminalWindow, because the error text might be: +// * A error we defined here +// * An error from deserializing the json +// * Any other fatal error loading the settings +// So all we pass on is the actual text of the error, rather than the +// combination of things that might have caused an error. // !!! IMPORTANT !!! // Make sure that these keys are in the same order as the @@ -493,10 +497,15 @@ namespace winrt::TerminalApp::implementation } else { + auto warnings{ winrt::multi_threaded_vector() }; + for (auto&& warn : _warnings) + { + warnings.Append(warn); + } auto ev = winrt::make_self(true, static_cast(_settingsLoadedResult), _settingsLoadExceptionText, - winrt::multi_threaded_vector(std::move(_warnings)), + warnings, _settings); _SettingsChangedHandlers(*this, *ev); return; @@ -517,10 +526,15 @@ namespace winrt::TerminalApp::implementation _ApplyStartupTaskStateChange(); _ProcessLazySettingsChanges(); + auto warnings{ winrt::multi_threaded_vector() }; + for (auto&& warn : _warnings) + { + warnings.Append(warn); + } auto ev = winrt::make_self(!initialLoad, _settingsLoadedResult, _settingsLoadExceptionText, - winrt::multi_threaded_vector(std::move(_warnings)), + warnings, _settings); _SettingsChangedHandlers(*this, *ev); } @@ -670,10 +684,15 @@ namespace winrt::TerminalApp::implementation ReloadSettings(); } + auto warnings{ winrt::multi_threaded_vector() }; + for (auto&& warn : _warnings) + { + warnings.Append(warn); + } auto ev = winrt::make_self(false, _settingsLoadedResult, _settingsLoadExceptionText, - winrt::multi_threaded_vector(std::move(_warnings)), + warnings, _settings); auto window = winrt::make_self(*ev); From 339972eceab3fc161a9190ab4ca8019ee74381b7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Mar 2023 14:34:46 -0600 Subject: [PATCH 108/122] tests build and pass again --- .../LocalTests_TerminalApp/SettingsTests.cpp | 110 +++++++++++++----- src/cascadia/Remoting/WindowManager.cpp | 6 +- src/cascadia/Remoting/WindowManager.h | 6 +- .../WindowsTerminal/WindowEmperor.cpp | 28 ++--- src/cascadia/WindowsTerminal/WindowEmperor.h | 8 +- src/cascadia/WindowsTerminal/WindowThread.cpp | 37 +++--- src/cascadia/WindowsTerminal/WindowThread.h | 6 +- 7 files changed, 130 insertions(+), 71 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp index c13945a47ba..49b215133ea 100644 --- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -98,6 +98,26 @@ namespace TerminalAppLocalTests } } } + void _logCommands(winrt::Windows::Foundation::Collections::IVector commands, const int indentation = 1) + { + if (indentation == 1) + { + Log::Comment((commands.Size() == 0) ? L"Commands:\n " : L"Commands:"); + } + for (const auto& cmd : commands) + { + Log::Comment(fmt::format(L"{0:>{1}}* {2}", + L"", + indentation, + cmd.Name()) + .c_str()); + + if (cmd.HasNestedCommands()) + { + _logCommandNames(cmd.NestedCommands(), indentation + 2); + } + } + } }; void SettingsTests::TestIterateCommands() @@ -166,13 +186,14 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); { - auto command = expandedCommands.Lookup(L"iterable command profile0"); + auto command = expandedCommands.GetAt(0); + VERIFY_ARE_EQUAL(L"iterable command profile0", command.Name()); VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -190,7 +211,8 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"iterable command profile1"); + auto command = expandedCommands.GetAt(1); + VERIFY_ARE_EQUAL(L"iterable command profile1", command.Name()); VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -208,7 +230,8 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"iterable command profile2"); + auto command = expandedCommands.GetAt(2); + VERIFY_ARE_EQUAL(L"iterable command profile2", command.Name()); VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -290,13 +313,15 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); { - auto command = expandedCommands.Lookup(L"Split pane, profile: profile0"); + auto command = expandedCommands.GetAt(0); + VERIFY_ARE_EQUAL(L"Split pane, profile: profile0", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -314,7 +339,9 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"Split pane, profile: profile1"); + auto command = expandedCommands.GetAt(1); + VERIFY_ARE_EQUAL(L"Split pane, profile: profile1", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -332,7 +359,9 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"Split pane, profile: profile2"); + auto command = expandedCommands.GetAt(2); + VERIFY_ARE_EQUAL(L"Split pane, profile: profile2", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -416,13 +445,15 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); { - auto command = expandedCommands.Lookup(L"iterable command profile0"); + auto command = expandedCommands.GetAt(0); + VERIFY_ARE_EQUAL(L"iterable command profile0", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -440,7 +471,9 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"iterable command profile1\""); + auto command = expandedCommands.GetAt(1); + VERIFY_ARE_EQUAL(L"iterable command profile1\"", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -458,7 +491,9 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"iterable command profile2"); + auto command = expandedCommands.GetAt(2); + VERIFY_ARE_EQUAL(L"iterable command profile2", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -532,13 +567,14 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); - auto rootCommand = expandedCommands.Lookup(L"Connect to ssh..."); + auto rootCommand = expandedCommands.GetAt(0); VERIFY_IS_NOT_NULL(rootCommand); + VERIFY_ARE_EQUAL(L"Connect to ssh...", rootCommand.Name()); auto rootActionAndArgs = rootCommand.ActionAndArgs(); VERIFY_IS_NOT_NULL(rootActionAndArgs); VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action()); @@ -627,13 +663,15 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); - auto grandparentCommand = expandedCommands.Lookup(L"grandparent"); + auto grandparentCommand = expandedCommands.GetAt(0); VERIFY_IS_NOT_NULL(grandparentCommand); + VERIFY_ARE_EQUAL(L"grandparent", grandparentCommand.Name()); + auto grandparentActionAndArgs = grandparentCommand.ActionAndArgs(); VERIFY_IS_NOT_NULL(grandparentActionAndArgs); VERIFY_ARE_EQUAL(ShortcutAction::Invalid, grandparentActionAndArgs.Action()); @@ -751,16 +789,21 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); - for (auto name : std::vector({ L"profile0", L"profile1", L"profile2" })) + const std::vector profileNames{ L"profile0", L"profile1", L"profile2" }; + for (auto i = 0u; i < profileNames.size(); i++) { - winrt::hstring commandName{ name + L"..." }; - auto command = expandedCommands.Lookup(commandName); + const auto& name{ profileNames[i] }; + winrt::hstring commandName{ profileNames[i] + L"..." }; + + auto command = expandedCommands.GetAt(i); + VERIFY_ARE_EQUAL(commandName, command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -888,13 +931,15 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); - auto rootCommand = expandedCommands.Lookup(L"New Tab With Profile..."); + auto rootCommand = expandedCommands.GetAt(0); VERIFY_IS_NOT_NULL(rootCommand); + VERIFY_ARE_EQUAL(L"New Tab With Profile...", rootCommand.Name()); + auto rootActionAndArgs = rootCommand.ActionAndArgs(); VERIFY_IS_NOT_NULL(rootActionAndArgs); VERIFY_ARE_EQUAL(ShortcutAction::Invalid, rootActionAndArgs.Action()); @@ -991,12 +1036,15 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(1u, expandedCommands.Size()); - auto rootCommand = expandedCommands.Lookup(L"New Pane..."); + auto rootCommand = expandedCommands.GetAt(0); + VERIFY_IS_NOT_NULL(rootCommand); + VERIFY_ARE_EQUAL(L"New Pane...", rootCommand.Name()); + VERIFY_IS_NOT_NULL(rootCommand); auto rootActionAndArgs = rootCommand.ActionAndArgs(); VERIFY_IS_NOT_NULL(rootActionAndArgs); @@ -1215,7 +1263,7 @@ namespace TerminalAppLocalTests settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; - _logCommandNames(expandedCommands); + _logCommands(expandedCommands); VERIFY_ARE_EQUAL(3u, expandedCommands.Size()); @@ -1224,7 +1272,9 @@ namespace TerminalAppLocalTests // just easy tests to write. { - auto command = expandedCommands.Lookup(L"iterable command Campbell"); + auto command = expandedCommands.GetAt(0); + VERIFY_ARE_EQUAL(L"iterable command Campbell", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -1242,7 +1292,9 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"iterable command Campbell PowerShell"); + auto command = expandedCommands.GetAt(1); + VERIFY_ARE_EQUAL(L"iterable command Campbell PowerShell", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); @@ -1260,7 +1312,9 @@ namespace TerminalAppLocalTests } { - auto command = expandedCommands.Lookup(L"iterable command Vintage"); + auto command = expandedCommands.GetAt(2); + VERIFY_ARE_EQUAL(L"iterable command Vintage", command.Name()); + VERIFY_IS_NOT_NULL(command); auto actionAndArgs = command.ActionAndArgs(); VERIFY_IS_NOT_NULL(actionAndArgs); diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 4086e222f52..d448c1cd26e 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -307,7 +307,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return false; } - Remoting::Peasant WindowManager::CreatePeasant(Remoting::WindowRequestedArgs args) + Remoting::Peasant WindowManager::CreatePeasant(const Remoting::WindowRequestedArgs& args) { auto p = winrt::make_self(); // This will be false if the Id is 0, which is our sentinel for "no specific ID was requested" @@ -334,7 +334,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *p; } - void WindowManager::SignalClose(Remoting::Peasant peasant) + void WindowManager::SignalClose(const Remoting::Peasant& peasant) { if (_monarch) { @@ -396,7 +396,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _monarch.DoesQuakeWindowExist(); } - void WindowManager::UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant) + void WindowManager::UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant) { winrt::get_self(peasant)->ActiveTabTitle(title); } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 26e4a422272..bed5fc3ca1b 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -29,16 +29,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager(); ~WindowManager(); winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args, const bool isolatedMode); - Remoting::Peasant CreatePeasant(Remoting::WindowRequestedArgs args); + Remoting::Peasant CreatePeasant(const Remoting::WindowRequestedArgs& args); - void SignalClose(Remoting::Peasant peasant); + void SignalClose(const Remoting::Peasant& peasant); void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); void SummonAllWindows(); Windows::Foundation::Collections::IVectorView GetPeasantInfos(); uint64_t GetNumberOfPeasants(); static winrt::fire_and_forget RequestQuitAll(Remoting::Peasant peasant); - void UpdateActiveTabTitle(winrt::hstring title, Remoting::Peasant peasant); + void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); bool DoesQuakeWindowExist(); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 1e31d5b06d8..fe08d73d559 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -83,11 +83,7 @@ bool WindowEmperor::HandleCommandlineArgs() if (result.ShouldCreateWindow()) { - CreateNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }); - - _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { - CreateNewWindowThread(args); - }); + _createNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }); _becomeMonarch(); } @@ -106,15 +102,15 @@ bool WindowEmperor::HandleCommandlineArgs() void WindowEmperor::WaitForWindows() { - MSG message; - while (GetMessage(&message, nullptr, 0, 0)) + MSG message{}; + while (GetMessageW(&message, nullptr, 0, 0)) { TranslateMessage(&message); DispatchMessage(&message); } } -void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) +void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& args) { Remoting::Peasant peasant{ _manager.CreatePeasant(args) }; @@ -146,7 +142,7 @@ void WindowEmperor::CreateNewWindowThread(Remoting::WindowRequestedArgs args) // Handler for a WindowThread's Started event, which it raises once the window // thread starts and XAML is ready to go on that thread. Set up some callbacks // now that we know this window is set up and ready to go. -// Q: Why isn't adding these callbacks just a part of CreateNewWindowThread? +// Q: Why isn't adding these callbacks just a part of _createNewWindowThread? // A: Until the thread actually starts, the AppHost (and its Logic()) haven't // been ctor'd or initialized, so trying to add callbacks immediately will A/V void WindowEmperor::_windowStartedHandler(const std::shared_ptr& sender) @@ -195,6 +191,12 @@ void WindowEmperor::_windowExitedHandler(uint64_t senderID) // - void WindowEmperor::_becomeMonarch() { + // Add a callback to the window manager so that when the Monarch wants a new + // window made, they come to us + _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { + _createNewWindowThread(args); + }); + _createMessageWindow(); _setupGlobalHotkeys(); @@ -334,7 +336,7 @@ static WindowEmperor* GetThisFromHandle(HWND const window) noexcept const auto data = GetWindowLongPtr(window, GWLP_USERDATA); return reinterpret_cast(data); } -[[nodiscard]] static LRESULT __stdcall MessageWndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept +[[nodiscard]] LRESULT __stdcall WindowEmperor::_wndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { WINRT_ASSERT(window); @@ -349,7 +351,7 @@ static WindowEmperor* GetThisFromHandle(HWND const window) noexcept } else if (WindowEmperor* that = GetThisFromHandle(window)) { - return that->MessageHandler(message, wparam, lparam); + return that->_messageHandler(message, wparam, lparam); } return DefWindowProc(window, message, wparam, lparam); @@ -361,7 +363,7 @@ void WindowEmperor::_createMessageWindow() wc.hInstance = reinterpret_cast(&__ImageBase); wc.lpszClassName = TERMINAL_MESSAGE_CLASS_NAME; wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = MessageWndProc; + wc.lpfnWndProc = WindowEmperor::_wndProc; wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON)); RegisterClass(&wc); WINRT_ASSERT(!_window); @@ -379,7 +381,7 @@ void WindowEmperor::_createMessageWindow() this)); } -LRESULT WindowEmperor::MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept +LRESULT WindowEmperor::_messageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept { switch (message) { diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 8fa43e4ba3e..fc1514d7f2f 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -28,12 +28,14 @@ class WindowEmperor : public std::enable_shared_from_this void WaitForWindows(); bool HandleCommandlineArgs(); - void CreateNewWindowThread(winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args); - LRESULT MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept; +private: + void _createNewWindowThread(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); + + [[nodiscard]] static LRESULT __stdcall _wndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; + LRESULT _messageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept; wil::unique_hwnd _window; -private: winrt::TerminalApp::App _app; winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr }; winrt::Microsoft::Terminal::Remoting::WindowManager _manager; diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index 35759e9c7d4..f95c0c2475f 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -4,21 +4,21 @@ #include "pch.h" #include "WindowThread.h" -WindowThread::WindowThread(const winrt::TerminalApp::AppLogic& logic, +WindowThread::WindowThread(winrt::TerminalApp::AppLogic logic, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, winrt::Microsoft::Terminal::Remoting::WindowManager manager, winrt::Microsoft::Terminal::Remoting::Peasant peasant) : - _peasant{ peasant }, - _appLogic{ logic }, - _args{ args }, - _manager{ manager } + _peasant{ std::move(peasant) }, + _appLogic{ std::move(logic) }, + _args{ std::move(args) }, + _manager{ std::move(manager) } { // DO NOT start the AppHost here in the ctor, as that will start XAML on the wrong thread! } void WindowThread::Start() { - _thread = std::thread([this]() { + auto thread = std::thread([this]() { // Start the AppHost HERE, on the actual thread we want XAML to run on _host = std::make_unique<::AppHost>(_appLogic, _args, @@ -26,7 +26,7 @@ void WindowThread::Start() _peasant); _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); }); // Enter the main window loop. - const auto exitCode = WindowProc(); + const auto exitCode = _messagePump(); _host = nullptr; // !! LOAD BEARING !! @@ -45,18 +45,19 @@ void WindowThread::Start() } } - // Raising our Exited event might cause the app to teardown. In the - // highly unlikely case that the main thread got scheduled before the - // event handler returns, it might release the WindowThread while it's - // still holding an outstanding reference to its own thread. That would - // cause a std::terminate. Let's just avoid that by releasing our - // thread. - this->_thread.detach(); _ExitedHandlers(_peasant.GetID()); return exitCode; }); - LOG_IF_FAILED(SetThreadDescription(_thread.native_handle(), L"Window Thread")); + LOG_IF_FAILED(SetThreadDescription(thread.native_handle(), L"Window Thread")); + + // Raising our Exited event might cause the app to teardown. In the + // highly unlikely case that the main thread got scheduled before the + // event handler returns, it might release the WindowThread while it's + // still holding an outstanding reference to its own thread. That would + // cause a std::terminate. Let's just avoid that by releasing our + // thread. + thread.detach(); } winrt::TerminalApp::TerminalWindow WindowThread::Logic() @@ -77,7 +78,7 @@ static bool _messageIsAltSpaceKeypress(const MSG& message) return message.message == WM_SYSKEYDOWN && message.wParam == VK_SPACE; } -int WindowThread::WindowProc() +int WindowThread::_messagePump() { winrt::init_apartment(winrt::apartment_type::single_threaded); @@ -89,9 +90,9 @@ int WindowThread::WindowProc() // Initialize, so that the windowLogic is ready to be used _StartedHandlers(); - MSG message; + MSG message{}; - while (GetMessage(&message, nullptr, 0, 0)) + while (GetMessageW(&message, nullptr, 0, 0)) { // GH#638 (Pressing F7 brings up both the history AND a caret browsing message) // The Xaml input stack doesn't allow an application to suppress the "caret browsing" diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 720c70b89d6..d45c92b0488 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -7,11 +7,10 @@ class WindowThread { public: - WindowThread(const winrt::TerminalApp::AppLogic& logic, + WindowThread(winrt::TerminalApp::AppLogic logic, winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, winrt::Microsoft::Terminal::Remoting::WindowManager manager, winrt::Microsoft::Terminal::Remoting::Peasant peasant); - int WindowProc(); winrt::TerminalApp::TerminalWindow Logic(); void Start(); @@ -28,6 +27,7 @@ class WindowThread winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs _args{ nullptr }; winrt::Microsoft::Terminal::Remoting::WindowManager _manager{ nullptr }; - std::thread _thread; std::unique_ptr<::AppHost> _host{ nullptr }; + + int _messagePump(); }; From 6d043532fc731695587796f76cd957ddb30fd118 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Mar 2023 14:58:10 -0600 Subject: [PATCH 109/122] yea we can fold this in --- src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp | 9 --------- src/cascadia/TerminalApp/AppLogic.cpp | 2 -- .../CascadiaSettingsSerialization.cpp | 2 ++ 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp index 49b215133ea..a940cc915f3 100644 --- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -184,7 +184,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); } - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -311,7 +310,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); } - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -443,7 +441,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${profile.name}", realArgs.TerminalArgs().Profile()); } - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -565,7 +562,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -661,7 +657,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -787,7 +782,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -929,7 +923,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -1034,7 +1027,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(0u, settings.Warnings().Size()); VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size()); - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); @@ -1261,7 +1253,6 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"${scheme.name}", realArgs.TerminalArgs().Profile()); } - settings.ExpandCommands(); const auto& expandedCommands{ settings.GlobalSettings().ActionMap().ExpandedCommands() }; _logCommands(expandedCommands); diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index c0f0b5c1724..28a39f48dcf 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -303,8 +303,6 @@ namespace winrt::TerminalApp::implementation _settings = std::move(newSettings); - _settings.ExpandCommands(); - hr = _warnings.empty() ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 0c4cb2f16e0..5984053e9e7 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -1110,6 +1110,8 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) : _resolveDefaultProfile(); _resolveNewTabMenuProfiles(); _validateSettings(); + + ExpandCommands(); } // Method Description: From 1138416dc90c16e0bed7b29bf23bfd978d7ff058 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Mar 2023 15:23:28 -0600 Subject: [PATCH 110/122] hey let's make sure it still works --- src/cascadia/TerminalApp/AppLogic.cpp | 14 +++++--------- src/cascadia/TerminalApp/TerminalWindow.cpp | 7 +------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 28a39f48dcf..cc223cac837 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -653,17 +653,13 @@ namespace winrt::TerminalApp::implementation } } - // Any unsuccessful parse will be a new window. That new window will try - // to handle the commandline itself, and find that the commandline - // failed to parse. When that happens, the new window will display the - // message box. + // Any unsuccessful parse will result in _no_ window. We will indicate + // to the caller that they shouldn't make a window. They can still find + // the commandline failed to parse and choose to display the message + // box. // // This will also work for the case where the user specifies an invalid - // commandline in conjunction with `-w 0`. This function will determine - // that the commandline has a parse error, and indicate that we should - // create a new window. Then, in that new window, we'll try to set the - // StartupActions, which will again fail, returning the correct error - // message. + // commandline in conjunction with `-w 0`. return winrt::make(WindowingBehaviorUseNone); } diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index c1f79aff1ab..103431e453e 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1245,12 +1245,7 @@ namespace winrt::TerminalApp::implementation void WindowProperties::WindowId(const uint64_t& value) { - if (_WindowId != value) - { - _WindowId = value; - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowId" }); - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowIdForDisplay" }); - } + _WindowId = value; } // Method Description: From 9957e5ce52260272c620e0cb6ea5ee998c8177db Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 10 Mar 2023 15:53:45 -0600 Subject: [PATCH 111/122] I think that's the last of the nits --- src/cascadia/TerminalApp/AppLogic.cpp | 2 +- .../WindowsTerminal/WindowEmperor.cpp | 28 ++++++++++++++++--- src/cascadia/WindowsTerminal/WindowEmperor.h | 2 ++ src/cascadia/WindowsTerminal/WindowThread.h | 2 +- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index cc223cac837..c69a32177f8 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -724,7 +724,7 @@ namespace winrt::TerminalApp::implementation bool AppLogic::ShouldUsePersistedLayout() const { - return _settings.GlobalSettings().ShouldUsePersistedLayout() && !_settings.GlobalSettings().IsolatedMode(); + return _settings.GlobalSettings().ShouldUsePersistedLayout(); } void AppLogic::SaveWindowLayoutJsons(const Windows::Foundation::Collections::IVector& layouts) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index fe08d73d559..bf2f3fc7420 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -24,8 +24,6 @@ using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; #define TERMINAL_MESSAGE_CLASS_NAME L"TERMINAL_MESSAGE_CLASS" extern "C" IMAGE_DOS_HEADER __ImageBase; -const UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated"); - WindowEmperor::WindowEmperor() noexcept : _app{} { @@ -217,8 +215,8 @@ void WindowEmperor::_becomeMonarch() // Set the number of open windows (so we know if we are the last window) // and subscribe for updates if there are any changes to that number. - _WindowCreatedToken = _manager.WindowCreated({ this, &WindowEmperor::_numberOfWindowsChanged }); - _WindowClosedToken = _manager.WindowClosed({ this, &WindowEmperor::_numberOfWindowsChanged }); + _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. @@ -228,6 +226,24 @@ void WindowEmperor::_becomeMonarch() // 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()(); + + // BODGY + // + // We've got a weird crash that happens terribly inconsistently, but pretty + // readily on migrie's laptop, only in Debug mode. Apparently, there's some + // weird ref-counting magic that goes on during teardown, and our + // Application doesn't get closed quite right, which can cause us to crash + // into the debugger. This of course, only happens on exit, and happens + // somewhere in the XamlHost.dll code. + // + // Crazily, if we _manually leak the Application_ here, then the crash + // doesn't happen. This doesn't matter, because we really want the + // Application to live for _the entire lifetime of the process_, so the only + // time when this object would actually need to get cleaned up is _during + // exit_. So we can safely leak this Application object, and have it just + // get cleaned up normally when our process exits. + auto a{ _app }; + ::winrt::detach_abi(a); } // sender and args are always nullptr @@ -383,6 +399,10 @@ void WindowEmperor::_createMessageWindow() LRESULT WindowEmperor::_messageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept { + // use C++11 magic statics to make sure we only do this once. + // This won't change over the lifetime of the application + static const UINT WM_TASKBARCREATED = []() { return RegisterWindowMessageW(L"TaskbarCreated"); }(); + switch (message) { case WM_HOTKEY: diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index fc1514d7f2f..9ed30a41cea 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -82,6 +82,8 @@ class WindowEmperor : public std::enable_shared_from_this struct Revokers { + 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{}; }; diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index d45c92b0488..595e846e424 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -4,7 +4,7 @@ #include "pch.h" #include "AppHost.h" -class WindowThread +class WindowThread : public std::enable_shared_from_this { public: WindowThread(winrt::TerminalApp::AppLogic logic, From 6aec80b8e7dd20cde2e0495fd05ce1d13d127b04 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Mar 2023 11:25:28 -0500 Subject: [PATCH 112/122] A hotfix for the selfhost build --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 95b3f7834e5..4353776914a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -660,7 +660,7 @@ namespace winrt::TerminalApp::implementation // have a tab yet, but will once we're initialized. if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener)) { - _LastTabClosedHandlers(*this, nullptr); + _LastTabClosedHandlers(*this, winrt::make(false)); } else { From 9316f5b05590ec0f48b42595d08d7b56f505178e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Mar 2023 12:10:49 -0500 Subject: [PATCH 113/122] These are pretty minor for what is a pretty minor PR --- src/cascadia/TerminalApp/ContentManager.h | 22 ++++++++++++++++++++-- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.h | 1 - src/cascadia/TerminalControl/TermControl.h | 3 ++- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 4f3c631a5ac..0681fee4f5f 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -1,6 +1,24 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +Class Name: +- ContentManager.h + +Abstract: +- This is a helper class for tracking all of the terminal "content" instances of + the Terminal. These are all the ControlInteractivity & ControlCore's of each + of our TermControls. These are each assigned a GUID on creation, and stored in + a map for later lookup. +- This is used to enable moving panes between windows. TermControl's are not + thread-agile, so they cannot be reused on other threads. However, the content + is. This helper, which exists as a singleton across all the threads in the + Terminal app, allows each thread to create content, assign it to a + TermControl, detach it from that control, and reattach to new controls on + other threads. +- When you want to create a new TermControl, call CreateCore to instantiate a + new content with a GUID for later reparenting. +--*/ #pragma once #include "ContentManager.g.h" diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 4e50a0637f3..7db63087184 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2578,7 +2578,7 @@ namespace winrt::TerminalApp::implementation // create here. // TermControl will copy the settings out of the settings passed to it. - auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection); + const auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection); TermControl term{ content }; diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index b07317c9b8b..dc63fdd0841 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -223,7 +223,6 @@ namespace winrt::TerminalApp::implementation bool _renamerPressedEnter{ false }; TerminalApp::WindowProperties _WindowProperties{ nullptr }; - PaneResources _paneResources; TerminalApp::ContentManager _manager{ nullptr }; diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index acb4d95c1e4..56b72cd2c11 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -144,7 +144,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); // UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE - + // Those attach the handler to the core directly, and will explode if + // the core ever gets detached & reattached to another window. BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); From 5b3dc083e03c000b7ed24406e25f7624290ad62c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Mar 2023 08:42:41 -0500 Subject: [PATCH 114/122] last nits, let's do this --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 4353776914a..6cfbc4bdf0b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -57,7 +57,7 @@ namespace winrt::TerminalApp::implementation _mruTabs{ winrt::single_threaded_observable_vector() }, _startupActions{ winrt::single_threaded_vector() }, _hostingHwnd{}, - _WindowProperties{ properties } + _WindowProperties{ std::move(properties) } { InitializeComponent(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index bd77664f685..93e68dd0b65 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -983,9 +983,11 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s } _WindowCreatedToken = _windowManager.WindowCreated([this](auto&&, auto&&) { - if (_getWindowLayoutThrottler) { + if (_getWindowLayoutThrottler) + { _getWindowLayoutThrottler.value()(); - } }); + } + }); _WindowClosedToken = _windowManager.WindowClosed([this](auto&&, auto&&) { if (_getWindowLayoutThrottler) From 7142ae88c9a3558a04e765b4ec71a6dc1ebcbccb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 16 Mar 2023 09:59:56 -0500 Subject: [PATCH 115/122] [ainulindale] fix defterm Each page was registering as a handoff target, so basically we'd start the server then yeet the connection back to the first window and presto, you'd have a dead window and a connection on the wrong thread and everything was awful. Instead, only register as the handoff listener when we've actually said we want to be a handoff listener. --- src/cascadia/TerminalApp/TerminalPage.cpp | 8 +++++--- src/cascadia/TerminalApp/TerminalPage.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 5661f8f4a5d..1b4083844b3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -228,9 +228,6 @@ namespace winrt::TerminalApp::implementation // Hookup our event handlers to the ShortcutActionDispatch _RegisterActionCallbacks(); - // Hook up inbound connection event handler - ConptyConnection::NewConnection({ this, &TerminalPage::_OnNewConnection }); - //Event Bindings (Early) _newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) { if (auto page{ weakThis.get() }) @@ -519,6 +516,9 @@ namespace winrt::TerminalApp::implementation { _shouldStartInboundListener = false; + // Hook up inbound connection event handler + _newConnectionRevoker = ConptyConnection::NewConnection(winrt::auto_revoke, { this, &TerminalPage::_OnNewConnection }); + try { winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::StartInboundListener(); @@ -3326,6 +3326,8 @@ namespace winrt::TerminalApp::implementation HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection) { + _newConnectionRevoker.revoke(); + // We need to be on the UI thread in order for _OpenNewTab to run successfully. // HasThreadAccess will return true if we're currently on a UI thread and false otherwise. // When we're on a COM thread, we'll need to dispatch the calls to the UI thread diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index f19a1df1a55..0b9c1f6515e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -226,6 +226,8 @@ namespace winrt::TerminalApp::implementation PaneResources _paneResources; + winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::NewConnection_revoker _newConnectionRevoker; + winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); From d55bb43f47eaa9d987470ca20a239b26bf7e660e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 16 Mar 2023 10:33:40 -0500 Subject: [PATCH 116/122] [ainulindale] windows should get activated when they're created --- src/cascadia/WindowsTerminal/WindowEmperor.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index bf2f3fc7420..170c2c9f725 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -151,6 +151,19 @@ void WindowEmperor::_windowStartedHandler(const std::shared_ptr& s sender->Logic().IsQuakeWindowChanged({ this, &WindowEmperor::_windowIsQuakeWindowChanged }); sender->UpdateSettingsRequested({ this, &WindowEmperor::_windowRequestUpdateSettings }); + // Summon the window to the foreground, since we might not _currently_ be in + // the foreground, but we should act like the new window is. + // + // TODO: GH#14957 - use AllowSetForeground from the original wt.exe instead + Remoting::SummonWindowSelectionArgs args{}; + args.OnCurrentDesktop(false); + args.WindowID(sender->Peasant().GetID()); + args.SummonBehavior().MoveToCurrentDesktop(false); + args.SummonBehavior().ToggleVisibility(false); + args.SummonBehavior().DropdownDuration(0); + args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); + _manager.SummonWindow(args); + // Now that the window is ready to go, we can add it to our list of windows, // because we know it will be well behaved. // From 11f995793cafb7fa535893848f3faacaea915a91 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Mar 2023 11:50:06 -0500 Subject: [PATCH 117/122] [ainulindale] Friendship ended with callbacks --- .../WindowsTerminal/WindowEmperor.cpp | 21 ++--- src/cascadia/WindowsTerminal/WindowThread.cpp | 82 ++++++++----------- src/cascadia/WindowsTerminal/WindowThread.h | 6 +- 3 files changed, 47 insertions(+), 62 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 170c2c9f725..75fe386d6c6 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -111,30 +111,27 @@ void WindowEmperor::WaitForWindows() void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& args) { Remoting::Peasant peasant{ _manager.CreatePeasant(args) }; - auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; - std::weak_ptr weakThis{ weak_from_this() }; - window->Started([weakThis, sender = window]() { - // These come in on the sender's thread. Make sure we haven't died - // since then + std::thread t([weakThis, window]() { + window->CreateHost(); + if (auto self{ weakThis.lock() }) { - self->_windowStartedHandler(sender); + self->_windowStartedHandler(window); } - }); - window->Exited([weakThis](uint64_t senderID) { - // These come in on the sender's thread. Make sure we haven't died - // since then + window->RunMessagePump(); + if (auto self{ weakThis.lock() }) { - self->_windowExitedHandler(senderID); + self->_windowExitedHandler(window->Peasant().GetID()); } }); + LOG_IF_FAILED(SetThreadDescription(t.native_handle(), L"Window Thread")); - window->Start(); + t.detach(); } // Handler for a WindowThread's Started event, which it raises once the window diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp index f95c0c2475f..398a36e16dd 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ b/src/cascadia/WindowsTerminal/WindowThread.cpp @@ -16,48 +16,46 @@ WindowThread::WindowThread(winrt::TerminalApp::AppLogic logic, // DO NOT start the AppHost here in the ctor, as that will start XAML on the wrong thread! } -void WindowThread::Start() +void WindowThread::CreateHost() { - auto thread = std::thread([this]() { - // Start the AppHost HERE, on the actual thread we want XAML to run on - _host = std::make_unique<::AppHost>(_appLogic, - _args, - _manager, - _peasant); - _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); }); - // Enter the main window loop. - const auto exitCode = _messagePump(); - _host = nullptr; + // Start the AppHost HERE, on the actual thread we want XAML to run on + _host = std::make_unique<::AppHost>(_appLogic, + _args, + _manager, + _peasant); + _host->UpdateSettingsRequested([this]() { _UpdateSettingsRequestedHandlers(); }); - // !! LOAD BEARING !! - // - // Make sure to finish pumping all the messages for our thread here. We - // may think we're all done, but we're not quite. XAML needs more time - // to pump the remaining events through, even at the point we're - // exiting. So do that now. If you don't, then the last tab to close - // will never actually destruct the last tab / TermControl / ControlCore - // / renderer. - { - MSG msg = {}; - while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) - { - ::DispatchMessageW(&msg); - } - } + winrt::init_apartment(winrt::apartment_type::single_threaded); - _ExitedHandlers(_peasant.GetID()); + // Initialize the xaml content. This must be called AFTER the + // WindowsXamlManager is initialized. + _host->Initialize(); +} - return exitCode; - }); - LOG_IF_FAILED(SetThreadDescription(thread.native_handle(), L"Window Thread")); +int WindowThread::RunMessagePump() +{ + // Enter the main window loop. + const auto exitCode = _messagePump(); + // Here, the main window loop has exited. + + _host = nullptr; + // !! LOAD BEARING !! + // + // Make sure to finish pumping all the messages for our thread here. We + // may think we're all done, but we're not quite. XAML needs more time + // to pump the remaining events through, even at the point we're + // exiting. So do that now. If you don't, then the last tab to close + // will never actually destruct the last tab / TermControl / ControlCore + // / renderer. + { + MSG msg = {}; + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) + { + ::DispatchMessageW(&msg); + } + } - // Raising our Exited event might cause the app to teardown. In the - // highly unlikely case that the main thread got scheduled before the - // event handler returns, it might release the WindowThread while it's - // still holding an outstanding reference to its own thread. That would - // cause a std::terminate. Let's just avoid that by releasing our - // thread. - thread.detach(); + return exitCode; } winrt::TerminalApp::TerminalWindow WindowThread::Logic() @@ -80,16 +78,6 @@ static bool _messageIsAltSpaceKeypress(const MSG& message) int WindowThread::_messagePump() { - winrt::init_apartment(winrt::apartment_type::single_threaded); - - // Initialize the xaml content. This must be called AFTER the - // WindowsXamlManager is initialized. - _host->Initialize(); - - // Inform the emperor that we're ready to go. We need to do this after - // Initialize, so that the windowLogic is ready to be used - _StartedHandlers(); - MSG message{}; while (GetMessageW(&message, nullptr, 0, 0)) diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h index 595e846e424..fecee558876 100644 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ b/src/cascadia/WindowsTerminal/WindowThread.h @@ -13,11 +13,11 @@ class WindowThread : public std::enable_shared_from_this winrt::Microsoft::Terminal::Remoting::Peasant peasant); winrt::TerminalApp::TerminalWindow Logic(); - void Start(); + void CreateHost(); + int RunMessagePump(); + winrt::Microsoft::Terminal::Remoting::Peasant Peasant(); - WINRT_CALLBACK(Started, winrt::delegate<>); - WINRT_CALLBACK(Exited, winrt::delegate); WINRT_CALLBACK(UpdateSettingsRequested, winrt::delegate); private: From b02b93537925f1127f24af9a41c9af4c2ca4b75f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Mar 2023 06:54:53 -0500 Subject: [PATCH 118/122] easier nits --- src/cascadia/TerminalApp/ContentManager.cpp | 8 ++++---- src/cascadia/TerminalApp/ContentManager.h | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 254ac4d6e1d..589138ced28 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -31,14 +31,14 @@ namespace winrt::TerminalApp::implementation TerminalConnection::ITerminalConnection connection) { auto content = ControlInteractivity{ settings, unfocusedAppearance, connection }; - content.Closed({ this, &ContentManager::_closedHandler }); - _content.Insert(content.Id(), content); + content.Closed({ get_weak(), &ContentManager::_closedHandler }); + _content.insert(std::make_pair(content.Id(), content)); return content; } ControlInteractivity ContentManager::LookupCore(winrt::guid id) { - return _content.TryLookup(id); + return _content.at(id); } void ContentManager::_closedHandler(winrt::Windows::Foundation::IInspectable sender, @@ -47,7 +47,7 @@ namespace winrt::TerminalApp::implementation if (const auto& content{ sender.try_as() }) { const auto& contentGuid{ content.Id() }; - _content.TryRemove(contentGuid); + _content.erase(contentGuid); } } } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 0681fee4f5f..c7ec0fda712 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -36,9 +36,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Control::ControlInteractivity LookupCore(winrt::guid id); private: - Windows::Foundation::Collections::IMap _content{ - winrt::multi_threaded_map() - }; + std::unordered_map _content; void _closedHandler(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable e); From f73ec9a0b4d7eb2a7c6c34d74644e24d99f0a074 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Mar 2023 09:55:30 -0500 Subject: [PATCH 119/122] fix the tests --- src/cascadia/LocalTests_TerminalApp/TabTests.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index b5fa445ab77..44f1a9c2db8 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -10,6 +10,7 @@ #include "../TerminalApp/ShortcutActionDispatch.h" #include "../TerminalApp/TerminalTab.h" #include "../TerminalApp/CommandPalette.h" +#include "../TerminalApp/ContentManager.h" #include "CppWinrtTailored.h" using namespace Microsoft::Console; @@ -112,6 +113,7 @@ namespace TerminalAppLocalTests CascadiaSettings initialSettings); winrt::com_ptr _commonSetup(); winrt::com_ptr _windowProperties; + winrt::com_ptr _contentManager; }; template @@ -199,8 +201,11 @@ namespace TerminalAppLocalTests _windowProperties = winrt::make_self(); winrt::TerminalApp::WindowProperties props = *_windowProperties; - auto result = RunOnUIThread([&page, props]() { - page = winrt::make_self(props); + _contentManager = winrt::make_self(); + winrt::TerminalApp::ContentManager contentManager = *_contentManager; + + auto result = RunOnUIThread([&page, props, contentManager]() { + page = winrt::make_self(props, contentManager); VERIFY_IS_NOT_NULL(page); }); VERIFY_SUCCEEDED(result); @@ -246,9 +251,11 @@ namespace TerminalAppLocalTests _windowProperties = winrt::make_self(); winrt::TerminalApp::WindowProperties props = *_windowProperties; + _contentManager = winrt::make_self(); + winrt::TerminalApp::ContentManager contentManager = *_contentManager; Log::Comment(NoThrowString().Format(L"Construct the TerminalPage")); - auto result = RunOnUIThread([&projectedPage, &page, initialSettings, props]() { - projectedPage = winrt::TerminalApp::TerminalPage(props); + auto result = RunOnUIThread([&projectedPage, &page, initialSettings, props, contentManager]() { + projectedPage = winrt::TerminalApp::TerminalPage(props, contentManager); page.copy_from(winrt::get_self(projectedPage)); page->_settings = initialSettings; }); From 36597444deee3e8c851464450130d3f30b42f2e9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Mar 2023 10:15:55 -0500 Subject: [PATCH 120/122] why not just use uints? --- src/cascadia/TerminalApp/ContentManager.cpp | 6 +++--- src/cascadia/TerminalApp/ContentManager.h | 4 ++-- src/cascadia/TerminalApp/TerminalPage.idl | 2 +- src/cascadia/TerminalControl/ControlInteractivity.cpp | 8 +++++--- src/cascadia/TerminalControl/ControlInteractivity.h | 5 +++-- src/cascadia/TerminalControl/ControlInteractivity.idl | 2 +- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 589138ced28..6e4687b7419 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -36,7 +36,7 @@ namespace winrt::TerminalApp::implementation return content; } - ControlInteractivity ContentManager::LookupCore(winrt::guid id) + ControlInteractivity ContentManager::LookupCore(uint64_t id) { return _content.at(id); } @@ -46,8 +46,8 @@ namespace winrt::TerminalApp::implementation { if (const auto& content{ sender.try_as() }) { - const auto& contentGuid{ content.Id() }; - _content.erase(contentGuid); + const auto& contentId{ content.Id() }; + _content.erase(contentId); } } } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index c7ec0fda712..5a092b26aca 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -33,10 +33,10 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Control::ControlInteractivity CreateCore(Microsoft::Terminal::Control::IControlSettings settings, Microsoft::Terminal::Control::IControlAppearance unfocusedAppearance, Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); - Microsoft::Terminal::Control::ControlInteractivity LookupCore(winrt::guid id); + Microsoft::Terminal::Control::ControlInteractivity LookupCore(uint64_t id); private: - std::unordered_map _content; + std::unordered_map _content; void _closedHandler(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable e); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 96f93ce9ffc..3f27359b3db 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -13,7 +13,7 @@ namespace TerminalApp Microsoft.Terminal.Control.ControlInteractivity CreateCore(Microsoft.Terminal.Control.IControlSettings settings, Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); - Microsoft.Terminal.Control.ControlInteractivity LookupCore(Guid id); + Microsoft.Terminal.Control.ControlInteractivity LookupCore(UInt64 id); } [default_interface] runtimeclass LastTabClosedEventArgs diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 88472c598dd..11400a6bf97 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -27,6 +27,8 @@ static constexpr unsigned int MAX_CLICK_COUNT = 3; namespace winrt::Microsoft::Terminal::Control::implementation { + std::atomic ControlInteractivity::_nextId{ 1 }; + static constexpr TerminalInput::MouseButtonState toInternalMouseState(const Control::MouseButtonState& state) { return TerminalInput::MouseButtonState{ @@ -44,14 +46,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation _lastMouseClickPos{}, _selectionNeedsToBeCopied{ false } { - _guid = ::Microsoft::Console::Utils::CreateGuid(); + _id = ControlInteractivity::_nextId++; _core = winrt::make_self(settings, unfocusedAppearance, connection); } - winrt::guid ControlInteractivity::Id() + uint64_t ControlInteractivity::Id() { - return _guid; + return _id; } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 4d1c0256ec7..b22c02bcc94 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -87,7 +87,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SetEndSelectionPoint(const Core::Point pixelPosition); bool ManglePathsForWsl(); - winrt::guid Id(); + uint64_t Id(); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); @@ -136,7 +136,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::optional::interval> _lastHoveredInterval{ std::nullopt }; - winrt::guid _guid; + uint64_t _id; + static std::atomic _nextId; unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime); void _updateSystemParameterSettings() noexcept; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 1ea5907b4bf..81d5e082a0a 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -23,7 +23,7 @@ namespace Microsoft.Terminal.Control void GotFocus(); void LostFocus(); - Guid Id { get; }; + UInt64 Id { get; }; void Close(); From 3af4d94a4723f186e4279ef1a55ec5d4d3cdf96c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Mar 2023 12:46:42 -0500 Subject: [PATCH 121/122] I don't know how to stl --- src/cascadia/TerminalApp/ContentManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 6e4687b7419..7acadcefb49 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -32,13 +32,16 @@ namespace winrt::TerminalApp::implementation { auto content = ControlInteractivity{ settings, unfocusedAppearance, connection }; content.Closed({ get_weak(), &ContentManager::_closedHandler }); - _content.insert(std::make_pair(content.Id(), content)); + + _content.emplace(content.Id(), content); + return content; } ControlInteractivity ContentManager::LookupCore(uint64_t id) { - return _content.at(id); + const auto it = _content.find(id); + return it != _content.end() ? it->second : ControlInteractivity{ nullptr }; } void ContentManager::_closedHandler(winrt::Windows::Foundation::IInspectable sender, From 697b420d9b607b5057ec2d2e6196c43cf959c22e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 Mar 2023 11:35:33 -0500 Subject: [PATCH 122/122] notes from PR --- src/cascadia/TerminalApp/AppLogic.h | 2 +- src/cascadia/TerminalApp/ContentManager.cpp | 15 +++++---------- src/cascadia/TerminalApp/ContentManager.h | 8 ++++---- src/cascadia/TerminalApp/TerminalPage.idl | 5 +---- .../TerminalControl/ControlInteractivity.cpp | 2 +- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 679e3d40a69..f5a206b1826 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -101,7 +101,7 @@ namespace winrt::TerminalApp::implementation winrt::com_ptr _languageProfileNotifier; wil::unique_folder_change_reader_nothrow _reader; - TerminalApp::ContentManager _contentManager{ *winrt::make_self() }; + TerminalApp::ContentManager _contentManager{ winrt::make() }; static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view args, const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior); diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 7acadcefb49..3fb5bdb21ee 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -19,18 +19,13 @@ using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Microsoft::Terminal::Settings::Model; -namespace winrt -{ - namespace MUX = Microsoft::UI::Xaml; - using IInspectable = Windows::Foundation::IInspectable; -} namespace winrt::TerminalApp::implementation { - ControlInteractivity ContentManager::CreateCore(Microsoft::Terminal::Control::IControlSettings settings, - IControlAppearance unfocusedAppearance, - TerminalConnection::ITerminalConnection connection) + ControlInteractivity ContentManager::CreateCore(const Microsoft::Terminal::Control::IControlSettings& settings, + const IControlAppearance& unfocusedAppearance, + const TerminalConnection::ITerminalConnection& connection) { - auto content = ControlInteractivity{ settings, unfocusedAppearance, connection }; + ControlInteractivity content{ settings, unfocusedAppearance, connection }; content.Closed({ get_weak(), &ContentManager::_closedHandler }); _content.emplace(content.Id(), content); @@ -38,7 +33,7 @@ namespace winrt::TerminalApp::implementation return content; } - ControlInteractivity ContentManager::LookupCore(uint64_t id) + ControlInteractivity ContentManager::TryLookupCore(uint64_t id) { const auto it = _content.find(id); return it != _content.end() ? it->second : ControlInteractivity{ nullptr }; diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 5a092b26aca..ce7ba807045 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -30,10 +30,10 @@ namespace winrt::TerminalApp::implementation { public: ContentManager() = default; - Microsoft::Terminal::Control::ControlInteractivity CreateCore(Microsoft::Terminal::Control::IControlSettings settings, - Microsoft::Terminal::Control::IControlAppearance unfocusedAppearance, - Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); - Microsoft::Terminal::Control::ControlInteractivity LookupCore(uint64_t id); + Microsoft::Terminal::Control::ControlInteractivity CreateCore(const Microsoft::Terminal::Control::IControlSettings& settings, + const Microsoft::Terminal::Control::IControlAppearance& unfocusedAppearance, + const Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); + Microsoft::Terminal::Control::ControlInteractivity TryLookupCore(uint64_t id); private: std::unordered_map _content; diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 3f27359b3db..1e5fc3415d0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -5,15 +5,12 @@ import "IDirectKeyListener.idl"; namespace TerminalApp { - - // See IDialogPresenter and TerminalPage's DialogPresenter for more - // information. [default_interface] runtimeclass ContentManager { Microsoft.Terminal.Control.ControlInteractivity CreateCore(Microsoft.Terminal.Control.IControlSettings settings, Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); - Microsoft.Terminal.Control.ControlInteractivity LookupCore(UInt64 id); + Microsoft.Terminal.Control.ControlInteractivity TryLookupCore(UInt64 id); } [default_interface] runtimeclass LastTabClosedEventArgs diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 11400a6bf97..3d38c624036 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -46,7 +46,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _lastMouseClickPos{}, _selectionNeedsToBeCopied{ false } { - _id = ControlInteractivity::_nextId++; + _id = _nextId.fetch_add(1, std::memory_order_relaxed); _core = winrt::make_self(settings, unfocusedAppearance, connection); }