From 581acd40d9a5f7bcefdbaf29516bf418207be727 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 30 Jan 2023 12:37:12 -0600 Subject: [PATCH 001/234] 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/234] 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/234] 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/234] 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/234] 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/234] 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/234] 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/234] 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/234] [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/234] 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/234] 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/234] 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/234] 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/234] 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/234] 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/234] 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/234] 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 37a8fde29cb9ba03d44fdb4fc46a01f0f4fca091 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 25 Aug 2021 15:35:30 -0500 Subject: [PATCH 018/234] Holy shit all the plumbing worked on the first try cherry-picked from 9870eb1d39f126b833555551c08ddcbf2ddb43c6 --- src/cascadia/Remoting/Monarch.cpp | 20 +++++++++++ src/cascadia/Remoting/Monarch.h | 2 ++ src/cascadia/Remoting/Monarch.idl | 2 ++ src/cascadia/Remoting/Peasant.cpp | 18 ++++++++++ src/cascadia/Remoting/Peasant.h | 17 +++++++++ src/cascadia/Remoting/Peasant.idl | 13 +++++++ src/cascadia/Remoting/WindowManager.cpp | 9 +++++ src/cascadia/Remoting/WindowManager.h | 3 ++ src/cascadia/Remoting/WindowManager.idl | 4 +++ src/cascadia/Remoting/WindowManager2.cpp | 9 +++++ src/cascadia/Remoting/WindowManager2.h | 2 ++ src/cascadia/Remoting/WindowManager2.idl | 4 +-- .../TerminalApp/AppActionHandlers.cpp | 2 +- .../TerminalApp/AppCommandlineArgs.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 28 +++++++++++++-- src/cascadia/TerminalApp/TerminalPage.h | 35 ++++++++++--------- src/cascadia/TerminalApp/TerminalPage.idl | 10 ++++++ src/cascadia/TerminalApp/TerminalWindow.cpp | 8 +++++ src/cascadia/TerminalApp/TerminalWindow.h | 5 +++ src/cascadia/TerminalApp/TerminalWindow.idl | 3 ++ src/cascadia/TerminalControl/TermControl.cpp | 5 +++ src/cascadia/TerminalControl/TermControl.h | 2 ++ src/cascadia/TerminalControl/TermControl.idl | 2 ++ .../TerminalSettingsModel/ActionArgs.h | 6 ++-- .../TerminalSettingsModel/ActionArgs.idl | 5 ++- src/cascadia/WindowsTerminal/AppHost.cpp | 12 ++++++- 26 files changed, 202 insertions(+), 26 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f828fd05dbd..2d126dd9c0a 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1043,4 +1043,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return winrt::single_threaded_vector(std::move(vec)); } + + void Monarch::RequestMovePane(winrt::hstring window, + winrt::guid contentGuid, + uint32_t tabIndex) + { + auto windowId = _lookupPeasantIdForName(window); + if (windowId == 0) + { /* TODO! try the name as an integer ID */ + return; + } + + if (auto targetPeasant{ _getPeasant(windowId) }) + { + auto request = winrt::make_self(contentGuid, tabIndex); + targetPeasant.AttachPaneToWindow(*request); + } + else + { /*TODO! log */ + } + } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 6765d0b15ca..c8cea7baa20 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -81,6 +81,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVectorView GetPeasantInfos(); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); + void RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 1dc3d53f70e..0b8b09a1002 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -70,6 +70,8 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.Collections.IVectorView GetPeasantInfos { get; }; Windows.Foundation.Collections.IVector GetAllWindowLayouts(); + void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex); + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 3606d1ff93a..cf4909b6354 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -8,6 +8,7 @@ #include "GetWindowLayoutArgs.h" #include "Peasant.g.cpp" #include "../../types/inc/utils.hpp" +#include "AttachRequest.g.cpp" using namespace winrt; using namespace winrt::Microsoft::Terminal; @@ -275,6 +276,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } + void Peasant::AttachPaneToWindow(Remoting::AttachRequest request) + { + try + { + _AttachRequestedHandlers(*this, request); + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + } + TraceLoggingWrite(g_hRemotingProvider, + "Peasant_AttachPaneToWindow", + + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } + void Peasant::Quit() { try diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 2f9a84384df..aa9f37ef87c 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -5,6 +5,7 @@ #include "Peasant.g.h" #include "RenameRequestArgs.h" +#include "AttachRequest.g.h" namespace RemotingUnitTests { @@ -12,6 +13,17 @@ namespace RemotingUnitTests }; namespace winrt::Microsoft::Terminal::Remoting::implementation { + struct AttachRequest : public AttachRequestT + { + WINRT_PROPERTY(winrt::guid, ContentGuid); + WINRT_PROPERTY(uint32_t, TabIndex); + + public: + AttachRequest(winrt::guid contentGuid, + uint32_t tabIndex) : + _ContentGuid{ contentGuid }, _TabIndex{ tabIndex } {}; + }; + struct Peasant : public PeasantT { Peasant(); @@ -32,6 +44,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void RequestQuitAll(); void Quit(); + void AttachPaneToWindow(Remoting::AttachRequest request); + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); @@ -47,12 +61,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs); TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior); + 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::Windows::Foundation::IInspectable); TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs); + TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); + private: Peasant(const uint64_t testPID); uint64_t _ourPID; diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 4a94f1e2255..4250fe3a0ef 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -51,6 +51,11 @@ namespace Microsoft.Terminal.Remoting MonitorBehavior ToMonitor; } + [default_interface] runtimeclass AttachRequest { + Guid ContentGuid { get; }; + UInt32 TabIndex { get; }; + } + interface IPeasant { CommandlineArgs InitialArgs { get; }; @@ -69,23 +74,31 @@ namespace Microsoft.Terminal.Remoting void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested void Summon(SummonWindowBehavior behavior); + void RequestShowNotificationIcon(); void RequestHideNotificationIcon(); void RequestQuitAll(); void Quit(); String GetWindowLayout(); + void AttachPaneToWindow(AttachRequest request); + + event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; event Windows.Foundation.TypedEventHandler DisplayWindowIdRequested; event Windows.Foundation.TypedEventHandler RenameRequested; event Windows.Foundation.TypedEventHandler SummonRequested; + event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; event Windows.Foundation.TypedEventHandler GetWindowLayoutRequested; event Windows.Foundation.TypedEventHandler QuitAllRequested; event Windows.Foundation.TypedEventHandler QuitRequested; + + event Windows.Foundation.TypedEventHandler AttachRequested; + }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 8c0b8d5a900..c51fcafdf4f 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -801,4 +801,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } return nullptr; } + + winrt::fire_and_forget WindowManager::RequestMovePane(winrt::hstring window, + winrt::guid contentGuid, + uint32_t tabIndex) + { + co_await winrt::resume_background(); + _monarch.RequestMovePane(window, contentGuid, tabIndex); + } + } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 96c60710d79..d3ef28331b6 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -48,9 +48,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::fire_and_forget RequestHideNotificationIcon(); winrt::fire_and_forget RequestQuitAll(); bool DoesQuakeWindowExist(); + void UpdateActiveTabTitle(winrt::hstring title); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); + winrt::fire_and_forget RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex); + 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); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 917347f2e9b..cc26398438e 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -22,7 +22,11 @@ namespace Microsoft.Terminal.Remoting void RequestQuitAll(); void UpdateActiveTabTitle(String title); Boolean DoesQuakeWindowExist(); + Windows.Foundation.Collections.IVectorView GetPeasantInfos(); + + void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex); + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler BecameMonarch; event Windows.Foundation.TypedEventHandler WindowCreated; diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index 84cb71148ef..62cab396945 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -476,4 +476,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } return nullptr; } + + winrt::fire_and_forget WindowManager2::RequestMovePane(winrt::hstring window, + winrt::guid contentGuid, + uint32_t tabIndex) + { + co_await winrt::resume_background(); + _monarch.RequestMovePane(window, contentGuid, tabIndex); + } + } diff --git a/src/cascadia/Remoting/WindowManager2.h b/src/cascadia/Remoting/WindowManager2.h index 3de1b41883e..5abc7507b94 100644 --- a/src/cascadia/Remoting/WindowManager2.h +++ b/src/cascadia/Remoting/WindowManager2.h @@ -27,6 +27,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVector GetAllWindowLayouts(); bool DoesQuakeWindowExist(); + winrt::fire_and_forget RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/WindowManager2.idl b/src/cascadia/Remoting/WindowManager2.idl index b111b06294d..0c49c786c1b 100644 --- a/src/cascadia/Remoting/WindowManager2.idl +++ b/src/cascadia/Remoting/WindowManager2.idl @@ -25,9 +25,10 @@ namespace Microsoft.Terminal.Remoting UInt64 GetNumberOfPeasants(); - Boolean DoesQuakeWindowExist(); + void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex); + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler WindowCreated; @@ -37,7 +38,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/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 8b2cd7b3637..be630f27074 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -209,7 +209,7 @@ namespace winrt::TerminalApp::implementation } else if (const auto& realArgs = args.ActionArgs().try_as()) { - auto moved = _MovePane(realArgs.TabIndex()); + auto moved = _MovePane(realArgs); args.Handled(moved); } } diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index cba81ef9b8f..e9b6368ff0b 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -340,7 +340,7 @@ void AppCommandlineArgs::_buildMovePaneParser() if (_movePaneTabIndex >= 0) { movePaneAction.Action(ShortcutAction::MovePane); - MovePaneArgs args{ static_cast(_movePaneTabIndex) }; + MovePaneArgs args{ static_cast(_movePaneTabIndex), L"" }; movePaneAction.Args(args); _startupActions.push_back(movePaneAction); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f17602c9b63..131075da03c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -6,6 +6,7 @@ #include "TerminalPage.h" #include "TerminalPage.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" +#include "RequestMovePaneArgs.g.cpp" #include @@ -1949,11 +1950,14 @@ namespace winrt::TerminalApp::implementation // - No move will occur if the tabIdx is the same as the current tab, or if // the specified tab is not a host of terminals (such as the settings tab). // Arguments: - // - tabIdx: The target tab index. + // - TODO! // Return Value: // - true if the pane was successfully moved to the new tab. - bool TerminalPage::_MovePane(const uint32_t tabIdx) + bool TerminalPage::_MovePane(MovePaneArgs args) { + const auto tabIdx{ args.TabIndex() }; + const auto windowId{ args.Window() }; + auto focusedTab{ _GetFocusedTabImpl() }; if (!focusedTab) @@ -1961,6 +1965,17 @@ namespace winrt::TerminalApp::implementation return false; } + if (!windowId.empty()) + { + if (const auto& control{ _GetActiveControl() }) + { + const auto currentContentGuid{ control.ContentGuid() }; + auto request = winrt::make_self(currentContentGuid, args); + _RequestMovePaneHandlers(*this, *request); + return true; + } + } + // If we are trying to move from the current tab to the current tab do nothing. if (_GetFocusedTabIndex() == tabIdx) { @@ -1991,6 +2006,15 @@ namespace winrt::TerminalApp::implementation return true; } + winrt::fire_and_forget TerminalPage::AttachPane(winrt::guid contentGuid, uint32_t tabIndex) + { + contentGuid; + tabIndex; + co_await winrt::resume_background(); + // const auto contentProc = _AttachToContentProcess(contentGuid); + // contentProc; + co_await winrt::resume_foreground(Dispatcher()); + } // Method Description: // - Split the focused pane either horizontally or vertically, and place the // given pane accordingly in the tree diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 2f23dff354d..4d805daf465 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -8,6 +8,7 @@ #include "AppKeyBindings.h" #include "AppCommandlineArgs.h" #include "RenameWindowRequestedArgs.g.h" +#include "RequestMovePaneArgs.g.h" #include "Toast.h" #define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); @@ -50,21 +51,17 @@ 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 RequestMovePaneArgs : RequestMovePaneArgsT + { + WINRT_PROPERTY(winrt::guid, ContentGuid); + WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::MovePaneArgs, Args, nullptr); + + public: + RequestMovePaneArgs(const winrt::guid& g, + Microsoft::Terminal::Settings::Model::MovePaneArgs args) : + _ContentGuid{ g }, + _Args{ args } {}; + }; struct TerminalPage : TerminalPageT { @@ -147,6 +144,7 @@ namespace winrt::TerminalApp::implementation // winrt::hstring WindowIdForDisplay() const noexcept; // winrt::hstring WindowNameForDisplay() const noexcept; // bool IsQuakeWindow() const noexcept; + bool IsElevated() const noexcept; void OpenSettingsUI(); @@ -160,6 +158,8 @@ namespace winrt::TerminalApp::implementation // WINRT_PROPERTY(TerminalApp::IWindowProperties, WindowProperties, nullptr); + winrt::fire_and_forget AttachPane(winrt::guid contentGuid, uint32_t tabIndex); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -177,11 +177,14 @@ namespace winrt::TerminalApp::implementation 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); TYPED_EVENT(QuitRequested, IInspectable, IInspectable); TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs) + TYPED_EVENT(RequestMovePane, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMovePaneArgs); + WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr); private: @@ -326,7 +329,7 @@ namespace winrt::TerminalApp::implementation bool _SelectTab(uint32_t tabIndex); bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction); - bool _MovePane(const uint32_t tabIdx); + bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args); template bool _ApplyToActiveControls(F f) diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 9ee1fd8b9f9..b5c62bbc085 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -22,6 +22,11 @@ namespace TerminalApp { String ProposedName { get; }; }; + [default_interface] runtimeclass RequestMovePaneArgs + { + Microsoft.Terminal.Settings.Model.MovePaneArgs Args { get; }; + Guid ContentGuid { get; }; + }; interface IDialogPresenter { @@ -61,6 +66,7 @@ namespace TerminalApp String KeyboardServiceDisabledText { get; }; TaskbarState TaskbarState{ get; }; + void AttachPane(Guid contentGuid, UInt32 tabIndex); Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); @@ -77,8 +83,12 @@ namespace TerminalApp 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 ShowWindowChanged; + + event Windows.Foundation.TypedEventHandler RequestMovePane; + } } diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index a182d58518c..56a322156b1 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1240,4 +1240,12 @@ namespace winrt::TerminalApp::implementation return WindowName() == QuakeWindowName; } //////////////////////////////////////////////////////////////////////////// + + void TerminalWindow::AttachPane(winrt::guid contentGuid, uint32_t tabIndex) + { + if (_root) + { + _root->AttachPane(contentGuid, tabIndex); + } + } }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 9935e8a11e9..5982e68bd90 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -120,6 +120,9 @@ namespace winrt::TerminalApp::implementation winrt::hstring WindowIdForDisplay() const noexcept; winrt::hstring WindowNameForDisplay() const noexcept; bool IsQuakeWindow() const noexcept; + + void AttachPane(winrt::guid contentGuid, uint32_t tabIndex); + // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. // Usually we'd just do @@ -190,6 +193,8 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); + FORWARDED_TYPED_EVENT(RequestMovePane, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMovePaneArgs, _root, RequestMovePane); + #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; #endif diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 2a63b4dfb20..b7efdf81feb 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -125,5 +125,8 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; event Windows.Foundation.TypedEventHandler ShowWindowChanged; + event Windows.Foundation.TypedEventHandler RequestMovePane; + void AttachPane(Guid contentGuid, UInt32 tabIndex); + } } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index db74370ecb7..8afeef08364 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -142,6 +142,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation _ApplyUISettings(); } + winrt::guid TermControl::ContentGuid() const + { + return _interactivity.Id(); + } + void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update) { // Assumptions: diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 79050015335..6959f1d777d 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -35,6 +35,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance); IControlSettings Settings() const; + winrt::guid ContentGuid() const; + hstring GetProfileName() const; bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference& formats); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 9fd43e95f08..827a629adce 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -32,6 +32,8 @@ namespace Microsoft.Terminal.Control void UpdateControlSettings(IControlSettings settings); void UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance); + Guid ContentGuid{ get; }; + Microsoft.Terminal.Control.IControlSettings Settings { get; }; event FontSizeChangedEventArgs FontSizeChanged; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 62fd9f9a085..8d10dca9f32 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -96,8 +96,9 @@ private: X(Windows::Foundation::IReference, CopyFormatting, "copyFormatting", false, nullptr) //////////////////////////////////////////////////////////////////////////////// -#define MOVE_PANE_ARGS(X) \ - X(uint32_t, TabIndex, "index", false, 0) +#define MOVE_PANE_ARGS(X) \ + X(uint32_t, TabIndex, "index", false, 0) \ + X(winrt::hstring, Window, "window", false, L"") //////////////////////////////////////////////////////////////////////////////// #define SWITCH_TO_TAB_ARGS(X) \ @@ -276,6 +277,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ACTION_ARG(Windows::Foundation::IReference, SuppressApplicationTitle, nullptr); ACTION_ARG(winrt::hstring, ColorScheme); ACTION_ARG(Windows::Foundation::IReference, Elevate, nullptr); + ACTION_ARG(winrt::guid, ContentGuid); static constexpr std::string_view CommandlineKey{ "commandline" }; static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index d24322072b6..2889e2c7a76 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -132,6 +132,8 @@ namespace Microsoft.Terminal.Settings.Model // not modify whatever the profile's value is (either true or false) Windows.Foundation.IReference Elevate; + Guid ContentGuid{ get; }; + Boolean Equals(NewTerminalArgs other); String GenerateName(); String ToCommandline(); @@ -158,8 +160,9 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass MovePaneArgs : IActionArgs { - MovePaneArgs(UInt32 tabIndex); + MovePaneArgs(UInt32 tabIndex, String Window); UInt32 TabIndex; + String Window; }; [default_interface] runtimeclass SwitchToTabArgs : IActionArgs diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0979c27c2a5..7e2d040dedc 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -352,6 +352,11 @@ void AppHost::_HandleCommandlineArgs(/*const winrt::Microsoft::Terminal::Remotin // } _windowLogic.WindowName(_peasant.WindowName()); _windowLogic.WindowId(_peasant.GetID()); + + // TODO! add revoker + _peasant.AttachRequested([this](auto&&, Remoting::AttachRequest args) { + _windowLogic.AttachPane(args.ContentGuid(), args.TabIndex()); + }); } } @@ -455,6 +460,11 @@ void AppHost::Initialize() _revokers.QuitRequested = _windowLogic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll }); _revokers.ShowWindowChanged = _windowLogic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged }); + // TODO! revoker + _windowLogic.RequestMovePane([this](auto&&, winrt::TerminalApp::RequestMovePaneArgs args) { + _windowManager2.RequestMovePane(args.Args().Window(), args.ContentGuid(), args.Args().TabIndex()); + }); + // BODGY // On certain builds of Windows, when Terminal is set as the default // it will accumulate an unbounded amount of queued animations while @@ -1500,7 +1510,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()) From 4dcc67b4d706d23d3c058c7e982c014bcebf875e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 14 Jul 2022 13:01:00 -0500 Subject: [PATCH 019/234] Serialize the pane to a Content string, instead of just the guid. There's a lot of renaming, signature changes here. But StartupActions is a good mechanism for passing panes around, more or less. cherry-picked from cfe879da9 --- src/cascadia/Remoting/Monarch.cpp | 16 ++++---- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/Remoting/Peasant.cpp | 5 +-- src/cascadia/Remoting/Peasant.h | 9 +++-- src/cascadia/Remoting/Peasant.idl | 4 +- src/cascadia/Remoting/WindowManager.cpp | 8 ++-- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/Remoting/WindowManager.idl | 2 +- src/cascadia/Remoting/WindowManager2.cpp | 8 ++-- src/cascadia/Remoting/WindowManager2.h | 2 +- src/cascadia/Remoting/WindowManager2.idl | 2 +- src/cascadia/TerminalApp/Pane.cpp | 19 ++++++++-- src/cascadia/TerminalApp/Pane.h | 4 +- src/cascadia/TerminalApp/TerminalPage.cpp | 38 +++++++++++++++---- src/cascadia/TerminalApp/TerminalPage.h | 23 ++++++----- src/cascadia/TerminalApp/TerminalPage.idl | 11 +++--- src/cascadia/TerminalApp/TerminalWindow.cpp | 4 +- src/cascadia/TerminalApp/TerminalWindow.h | 4 +- src/cascadia/TerminalApp/TerminalWindow.idl | 4 +- .../TerminalSettingsModel/ActionAndArgs.cpp | 10 +++++ .../TerminalSettingsModel/ActionAndArgs.h | 2 + .../TerminalSettingsModel/ActionArgs.h | 8 +++- .../TerminalSettingsModel/ActionArgs.idl | 2 +- .../TerminalSettingsModel/Command.idl | 2 + src/cascadia/WindowsTerminal/AppHost.cpp | 7 ++-- 26 files changed, 128 insertions(+), 72 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 2d126dd9c0a..dc498f75731 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1044,23 +1044,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return winrt::single_threaded_vector(std::move(vec)); } - void Monarch::RequestMovePane(winrt::hstring window, - winrt::guid contentGuid, - uint32_t tabIndex) + void Monarch::RequestMoveContent(winrt::hstring window, + winrt::hstring content, + uint32_t tabIndex) { auto windowId = _lookupPeasantIdForName(window); if (windowId == 0) - { /* TODO! try the name as an integer ID */ + { + /* TODO! try the name as an integer ID */ return; } if (auto targetPeasant{ _getPeasant(windowId) }) { - auto request = winrt::make_self(contentGuid, tabIndex); - targetPeasant.AttachPaneToWindow(*request); + auto request = winrt::make_self(content, tabIndex); + targetPeasant.AttachContentToWindow(*request); } else - { /*TODO! log */ + { + /*TODO! log */ } } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index c8cea7baa20..d19c8e71a27 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -81,7 +81,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVectorView GetPeasantInfos(); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); - void RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex); + void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 0b8b09a1002..f67114fdc0f 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -70,7 +70,7 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.Collections.IVectorView GetPeasantInfos { get; }; Windows.Foundation.Collections.IVector GetAllWindowLayouts(); - void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex); + void RequestMoveContent(String window, String content, UInt32 tabIndex); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index cf4909b6354..659de9b72fc 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -276,7 +276,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } - void Peasant::AttachPaneToWindow(Remoting::AttachRequest request) + void Peasant::AttachContentToWindow(Remoting::AttachRequest request) { try { @@ -287,8 +287,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation LOG_CAUGHT_EXCEPTION(); } TraceLoggingWrite(g_hRemotingProvider, - "Peasant_AttachPaneToWindow", - + "Peasant_AttachContentToWindow", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index aa9f37ef87c..a0fb55dce73 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -15,13 +15,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { struct AttachRequest : public AttachRequestT { - WINRT_PROPERTY(winrt::guid, ContentGuid); + WINRT_PROPERTY(winrt::hstring, Content); WINRT_PROPERTY(uint32_t, TabIndex); public: - AttachRequest(winrt::guid contentGuid, + AttachRequest(winrt::hstring content, uint32_t tabIndex) : - _ContentGuid{ contentGuid }, _TabIndex{ tabIndex } {}; + _Content{ content }, + _TabIndex{ tabIndex } {}; }; struct Peasant : public PeasantT @@ -44,7 +45,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void RequestQuitAll(); void Quit(); - void AttachPaneToWindow(Remoting::AttachRequest request); + void AttachContentToWindow(Remoting::AttachRequest request); winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 4250fe3a0ef..1257bda4b8c 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -52,7 +52,7 @@ namespace Microsoft.Terminal.Remoting } [default_interface] runtimeclass AttachRequest { - Guid ContentGuid { get; }; + String Content { get; }; UInt32 TabIndex { get; }; } @@ -81,7 +81,7 @@ namespace Microsoft.Terminal.Remoting void Quit(); String GetWindowLayout(); - void AttachPaneToWindow(AttachRequest request); + void AttachContentToWindow(AttachRequest request); event Windows.Foundation.TypedEventHandler WindowActivated; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index c51fcafdf4f..df739d1c302 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -802,12 +802,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return nullptr; } - winrt::fire_and_forget WindowManager::RequestMovePane(winrt::hstring window, - winrt::guid contentGuid, - uint32_t tabIndex) + winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window, + winrt::hstring content, + uint32_t tabIndex) { co_await winrt::resume_background(); - _monarch.RequestMovePane(window, contentGuid, tabIndex); + _monarch.RequestMoveContent(window, content, tabIndex); } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index d3ef28331b6..293ffec52b9 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -52,7 +52,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void UpdateActiveTabTitle(winrt::hstring title); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); - winrt::fire_and_forget RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex); + winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index cc26398438e..4917d004600 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -25,7 +25,7 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.Collections.IVectorView GetPeasantInfos(); - void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex); + void RequestMoveContent(String window, String content, UInt32 tabIndex); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler BecameMonarch; diff --git a/src/cascadia/Remoting/WindowManager2.cpp b/src/cascadia/Remoting/WindowManager2.cpp index 62cab396945..9b97cda9a40 100644 --- a/src/cascadia/Remoting/WindowManager2.cpp +++ b/src/cascadia/Remoting/WindowManager2.cpp @@ -477,12 +477,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return nullptr; } - winrt::fire_and_forget WindowManager2::RequestMovePane(winrt::hstring window, - winrt::guid contentGuid, - uint32_t tabIndex) + winrt::fire_and_forget WindowManager2::RequestMoveContent(winrt::hstring window, + winrt::hstring content, + uint32_t tabIndex) { co_await winrt::resume_background(); - _monarch.RequestMovePane(window, contentGuid, tabIndex); + _monarch.RequestMoveContent(window, content, tabIndex); } } diff --git a/src/cascadia/Remoting/WindowManager2.h b/src/cascadia/Remoting/WindowManager2.h index 5abc7507b94..b9c8862bae8 100644 --- a/src/cascadia/Remoting/WindowManager2.h +++ b/src/cascadia/Remoting/WindowManager2.h @@ -27,7 +27,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVector GetAllWindowLayouts(); bool DoesQuakeWindowExist(); - winrt::fire_and_forget RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex); + winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/WindowManager2.idl b/src/cascadia/Remoting/WindowManager2.idl index 0c49c786c1b..627e4b4d1cb 100644 --- a/src/cascadia/Remoting/WindowManager2.idl +++ b/src/cascadia/Remoting/WindowManager2.idl @@ -27,7 +27,7 @@ namespace Microsoft.Terminal.Remoting Boolean DoesQuakeWindowExist(); - void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex); + void RequestMoveContent(String window, String content, UInt32 tabIndex); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 47dc1a36271..c3ee4183234 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -114,7 +114,7 @@ Pane::Pane(std::shared_ptr first, // - // Return Value: // - Arguments appropriate for a SplitPane or NewTab action -NewTerminalArgs Pane::GetTerminalArgsForPane() const +NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const { // Leaves are the only things that have controls assert(_IsLeaf()); @@ -159,6 +159,11 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const // object. That would work for schemes set by the Terminal, but not ones set // by VT, but that seems good enough. + if (asContent) + { + args.ContentGuid(_control.ContentGuid()); + } + return args; } @@ -170,14 +175,15 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const // Arguments: // - currentId: the id to use for the current/first pane // - nextId: the id to use for a new pane if we split +// - asContent: TODO! // Return Value: // - The state from building the startup actions, includes a vector of commands, // the original root pane, the id of the focused pane, and the number of panes // created. -Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId) +Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent) { // if we are a leaf then all there is to do is defer to the parent. - if (_IsLeaf()) + if (!asContent && _IsLeaf()) { if (_lastActive) { @@ -190,7 +196,7 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n auto buildSplitPane = [&](auto newPane) { ActionAndArgs actionAndArgs; actionAndArgs.Action(ShortcutAction::SplitPane); - const auto terminalArgs{ newPane->GetTerminalArgsForPane() }; + const auto terminalArgs{ newPane->GetTerminalArgsForPane(asContent) }; // When creating a pane the split size is the size of the new pane // and not position. const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right; @@ -200,6 +206,11 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n return actionAndArgs; }; + if (asContent && _IsLeaf()) + { + return { { buildSplitPane(shared_from_this()) }, shared_from_this(), currentId, 1 }; + } + auto buildMoveFocus = [](auto direction) { MoveFocusArgs args{ direction }; diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 4719d1c4b47..2caa0443d7c 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -91,8 +91,8 @@ class Pane : public std::enable_shared_from_this std::optional focusedPaneId; uint32_t panesCreated; }; - BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId); - winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane() const; + BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false); + winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::Settings::Model::Profile& profile); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 131075da03c..36dd08ce9fc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -6,7 +6,7 @@ #include "TerminalPage.h" #include "TerminalPage.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" -#include "RequestMovePaneArgs.g.cpp" +#include "RequestMoveContentArgs.g.cpp" #include @@ -1967,13 +1967,34 @@ namespace winrt::TerminalApp::implementation if (!windowId.empty()) { - if (const auto& control{ _GetActiveControl() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) + { - const auto currentContentGuid{ control.ContentGuid() }; - auto request = winrt::make_self(currentContentGuid, args); - _RequestMovePaneHandlers(*this, *request); - return true; + if (const auto pane{ terminalTab->GetActivePane() }) + + { + auto startupActions = pane->BuildStartupActions(0, 1, true); + auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions.args)) }; + // Json::Value json{ Json::objectValue }; + // SetValueForKey(json, "content", winRtActions); + // Json::StreamWriterBuilder wbuilder; + // auto str = Json::writeString(wbuilder, json); + auto str = ActionAndArgs::Serialize(winRtActions); + auto request = winrt::make_self(args.Window(), + str, + args.TabIndex()); + _RequestMoveContentHandlers(*this, *request); + return true; + } } + + //if (const auto& control{ _GetActiveControl() }) + //{ + // const auto currentContentGuid{ control.ContentGuid() }; + // auto request = winrt::make_self(currentContentGuid, args); + // _RequestMovePaneHandlers(*this, *request); + // return true; + //} } // If we are trying to move from the current tab to the current tab do nothing. @@ -2006,15 +2027,16 @@ namespace winrt::TerminalApp::implementation return true; } - winrt::fire_and_forget TerminalPage::AttachPane(winrt::guid contentGuid, uint32_t tabIndex) + winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content, uint32_t tabIndex) { - contentGuid; + content; tabIndex; co_await winrt::resume_background(); // const auto contentProc = _AttachToContentProcess(contentGuid); // contentProc; co_await winrt::resume_foreground(Dispatcher()); } + // Method Description: // - Split the focused pane either horizontally or vertically, and place the // given pane accordingly in the tree diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 4d805daf465..6785f040cef 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -8,7 +8,7 @@ #include "AppKeyBindings.h" #include "AppCommandlineArgs.h" #include "RenameWindowRequestedArgs.g.h" -#include "RequestMovePaneArgs.g.h" +#include "RequestMoveContentArgs.g.h" #include "Toast.h" #define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); @@ -51,16 +51,17 @@ namespace winrt::TerminalApp::implementation _ProposedName{ name } {}; }; - struct RequestMovePaneArgs : RequestMovePaneArgsT + struct RequestMoveContentArgs : RequestMoveContentArgsT { - WINRT_PROPERTY(winrt::guid, ContentGuid); - WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::MovePaneArgs, Args, nullptr); + WINRT_PROPERTY(winrt::hstring, Window); + WINRT_PROPERTY(winrt::hstring, Content); + WINRT_PROPERTY(uint32_t, TabIndex); public: - RequestMovePaneArgs(const winrt::guid& g, - Microsoft::Terminal::Settings::Model::MovePaneArgs args) : - _ContentGuid{ g }, - _Args{ args } {}; + RequestMoveContentArgs(const winrt::hstring window, const winrt::hstring content, uint32_t tabIndex) : + _Window{ window }, + _Content{ content }, + _TabIndex{ tabIndex } {}; }; struct TerminalPage : TerminalPageT @@ -156,9 +157,7 @@ namespace winrt::TerminalApp::implementation void WindowProperties(const TerminalApp::IWindowProperties& props); winrt::fire_and_forget WindowNameChanged(); - // WINRT_PROPERTY(TerminalApp::IWindowProperties, WindowProperties, nullptr); - - winrt::fire_and_forget AttachPane(winrt::guid contentGuid, uint32_t tabIndex); + winrt::fire_and_forget AttachContent(winrt::hstring content, uint32_t tabIndex); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); @@ -183,7 +182,7 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(QuitRequested, IInspectable, IInspectable); TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs) - TYPED_EVENT(RequestMovePane, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMovePaneArgs); + TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs); WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index b5c62bbc085..efbcf344924 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -22,10 +22,11 @@ namespace TerminalApp { String ProposedName { get; }; }; - [default_interface] runtimeclass RequestMovePaneArgs + [default_interface] runtimeclass RequestMoveContentArgs { - Microsoft.Terminal.Settings.Model.MovePaneArgs Args { get; }; - Guid ContentGuid { get; }; + String Window { get; }; + String Content { get; }; + UInt32 TabIndex { get; }; }; interface IDialogPresenter @@ -66,7 +67,7 @@ namespace TerminalApp String KeyboardServiceDisabledText { get; }; TaskbarState TaskbarState{ get; }; - void AttachPane(Guid contentGuid, UInt32 tabIndex); + void AttachContent(String content, UInt32 tabIndex); Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); @@ -88,7 +89,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler OpenSystemMenu; event Windows.Foundation.TypedEventHandler ShowWindowChanged; - event Windows.Foundation.TypedEventHandler RequestMovePane; + event Windows.Foundation.TypedEventHandler RequestMoveContent; } } diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 56a322156b1..c6e7f03063d 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1241,11 +1241,11 @@ namespace winrt::TerminalApp::implementation } //////////////////////////////////////////////////////////////////////////// - void TerminalWindow::AttachPane(winrt::guid contentGuid, uint32_t tabIndex) + void TerminalWindow::AttachContent(winrt::hstring content, uint32_t tabIndex) { if (_root) { - _root->AttachPane(contentGuid, tabIndex); + _root->AttachContent(content, tabIndex); } } }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 5982e68bd90..a60dadd5ad9 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -121,7 +121,7 @@ namespace winrt::TerminalApp::implementation winrt::hstring WindowNameForDisplay() const noexcept; bool IsQuakeWindow() const noexcept; - void AttachPane(winrt::guid contentGuid, uint32_t tabIndex); + void AttachContent(winrt::hstring content, uint32_t tabIndex); // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. @@ -193,7 +193,7 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(SystemMenuChangeRequested, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs); - FORWARDED_TYPED_EVENT(RequestMovePane, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMovePaneArgs, _root, RequestMovePane); + FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent); #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index b7efdf81feb..74b813eacb7 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -125,8 +125,8 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; event Windows.Foundation.TypedEventHandler ShowWindowChanged; - event Windows.Foundation.TypedEventHandler RequestMovePane; - void AttachPane(Guid contentGuid, UInt32 tabIndex); + event Windows.Foundation.TypedEventHandler RequestMoveContent; + void AttachContent(String content, UInt32 tabIndex); } } diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 12ca43475d2..dd70dc4a864 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -423,4 +423,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation const auto found = GeneratedActionNames.find(_Action); return found != GeneratedActionNames.end() ? found->second : L""; } + + winrt::hstring ActionAndArgs::Serialize(winrt::Windows::Foundation::Collections::IVector args) + { + Json::Value json{ Json::objectValue }; + JsonUtils::SetValueForKey(json, "actions", args); + Json::StreamWriterBuilder wbuilder; + auto str = Json::writeString(wbuilder, json); + return winrt::to_hstring(str); + } + } diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h index 055bd1abcd0..b13f60ebc81 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h @@ -16,6 +16,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::vector& warnings); static Json::Value ToJson(const Model::ActionAndArgs& val); + static winrt::hstring Serialize(winrt::Windows::Foundation::Collections::IVector args); + ActionAndArgs() = default; ActionAndArgs(ShortcutAction action); ActionAndArgs(ShortcutAction action, IActionArgs args) : diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 8d10dca9f32..fb133babe31 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -288,6 +288,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" }; static constexpr std::string_view ColorSchemeKey{ "colorScheme" }; static constexpr std::string_view ElevateKey{ "elevate" }; + static constexpr std::string_view ContentKey{ "__content" }; public: hstring GenerateName() const; @@ -306,7 +307,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation otherAsUs->_Profile == _Profile && otherAsUs->_SuppressApplicationTitle == _SuppressApplicationTitle && otherAsUs->_ColorScheme == _ColorScheme && - otherAsUs->_Elevate == _Elevate; + otherAsUs->_Elevate == _Elevate && + otherAsUs->_ContentGuid == _ContentGuid; } return false; }; @@ -323,6 +325,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle); JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme); JsonUtils::GetValueForKey(json, ElevateKey, args->_Elevate); + JsonUtils::GetValueForKey(json, ContentKey, args->_ContentGuid); return *args; } static Json::Value ToJson(const Model::NewTerminalArgs& val) @@ -342,6 +345,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle); JsonUtils::SetValueForKey(json, ColorSchemeKey, args->_ColorScheme); JsonUtils::SetValueForKey(json, ElevateKey, args->_Elevate); + JsonUtils::SetValueForKey(json, ContentKey, args->_ContentGuid); return json; } Model::NewTerminalArgs Copy() const @@ -356,6 +360,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_SuppressApplicationTitle = _SuppressApplicationTitle; copy->_ColorScheme = _ColorScheme; copy->_Elevate = _Elevate; + copy->_ContentGuid = _ContentGuid; return *copy; } size_t Hash() const @@ -375,6 +380,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation h.write(SuppressApplicationTitle()); h.write(ColorScheme()); h.write(Elevate()); + h.write(ContentGuid()); } }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 2889e2c7a76..3162bf3cf39 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -132,7 +132,7 @@ namespace Microsoft.Terminal.Settings.Model // not modify whatever the profile's value is (either true or false) Windows.Foundation.IReference Elevate; - Guid ContentGuid{ get; }; + Guid ContentGuid{ get; set; }; Boolean Equals(NewTerminalArgs other); String GenerateName(); diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index b749a79fb0a..a223945f2cc 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -24,6 +24,8 @@ namespace Microsoft.Terminal.Settings.Model ActionAndArgs(); ActionAndArgs(ShortcutAction action, IActionArgs args); + static String Serialize(IVector args); + IActionArgs Args; ShortcutAction Action; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 7e2d040dedc..fd1f65cb243 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -355,7 +355,7 @@ void AppHost::_HandleCommandlineArgs(/*const winrt::Microsoft::Terminal::Remotin // TODO! add revoker _peasant.AttachRequested([this](auto&&, Remoting::AttachRequest args) { - _windowLogic.AttachPane(args.ContentGuid(), args.TabIndex()); + _windowLogic.AttachContent(args.Content(), args.TabIndex()); }); } } @@ -461,8 +461,9 @@ void AppHost::Initialize() _revokers.ShowWindowChanged = _windowLogic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged }); // TODO! revoker - _windowLogic.RequestMovePane([this](auto&&, winrt::TerminalApp::RequestMovePaneArgs args) { - _windowManager2.RequestMovePane(args.Args().Window(), args.ContentGuid(), args.Args().TabIndex()); + // TODO! move to member method + _windowLogic.RequestMoveContent([this](auto&&, winrt::TerminalApp::RequestMoveContentArgs args) { + _windowManager2.RequestMoveContent(args.Window(), args.Content(), args.TabIndex()); }); // BODGY From 6a2239d48703a30fe486b9116563392b0aa2fc36 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 14 Jul 2022 15:38:37 -0500 Subject: [PATCH 020/234] Pane officially opened via the serialized actions, via across the process boundary cherry-picked from 1f2bb760e --- src/cascadia/TerminalApp/Pane.cpp | 3 +- src/cascadia/TerminalApp/TerminalPage.cpp | 219 +++++++++++++++++- src/cascadia/TerminalControl/ControlCore.cpp | 6 + .../TerminalSettingsModel/ActionAndArgs.cpp | 16 ++ .../TerminalSettingsModel/ActionAndArgs.h | 1 + .../TerminalSettingsModel/Command.idl | 1 + 6 files changed, 241 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index c3ee4183234..40d24da80c1 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -200,7 +200,8 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n // When creating a pane the split size is the size of the new pane // and not position. const auto splitDirection = _splitState == SplitState::Horizontal ? SplitDirection::Down : SplitDirection::Right; - SplitPaneArgs args{ SplitType::Manual, splitDirection, 1. - _desiredSplitPosition, terminalArgs }; + const auto splitSize = (asContent && _IsLeaf() ? .5 : 1. - _desiredSplitPosition); + SplitPaneArgs args{ SplitType::Manual, splitDirection, splitSize, terminalArgs }; actionAndArgs.Args(args); return actionAndArgs; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 36dd08ce9fc..e318fa4f959 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2031,10 +2031,16 @@ namespace winrt::TerminalApp::implementation { content; tabIndex; - co_await winrt::resume_background(); - // const auto contentProc = _AttachToContentProcess(contentGuid); - // contentProc; - co_await winrt::resume_foreground(Dispatcher()); + + auto args = ActionAndArgs::Deserialize(content); + // TODO! if the first action is a split pane and tabIndex > tabs.size, + // then remove it and insert an equivalent newTab + + for (const auto& action : args) + { + _actionDispatch->DoAction(action); + } + co_await wil::resume_foreground(Dispatcher()); } // Method Description: @@ -2131,6 +2137,88 @@ namespace winrt::TerminalApp::implementation } } + // <<<<<<< HEAD + // ======= + // winrt::fire_and_forget TerminalPage::_asyncSplitPaneActiveTab(const SplitDirection splitDirection, + // const float splitSize, + // PreparedContent preppedContent) + // { + // // _GetFocusedTabImpl requires us to be on the UI thread + // co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); + // auto focusedTab{ _GetFocusedTabImpl() }; + + // // Clever hack for a crash in startup, with multiple sub-commands. Say + // // you have the following commandline: + // // + // // wtd nt -p "elevated cmd" ; sp -p "elevated cmd" ; sp -p "Command Prompt" + // // + // // Where "elevated cmd" is an elevated profile. + // // + // // In that scenario, we won't dump off the commandline immediately to an + // // elevated window, because it's got the final unelevated split in it. + // // However, when we get to that command, there won't be a tab yet. So + // // we'd crash right about here. + // // + // // Instead, let's just promote this first split to be a tab instead. + // // Crash avoided, and we don't need to worry about inserting a new-tab + // // command in at the start. + // if (!focusedTab) + // { + // if (_tabs.Size() == 0) + // { + // _createNewTabFromContent(preppedContent); + // } + // else + // { + // // The focused tab isn't a terminal tab + // co_return; + // } + // } + // else + // { + // _asyncSplitPaneOnTab(focusedTab, splitDirection, splitSize, preppedContent); + // } + // } + // winrt::fire_and_forget TerminalPage::_asyncSplitPaneOnTab(winrt::com_ptr tab, + // const SplitDirection splitDirection, + // const float splitSize, + // PreparedContent preppedContent) + // { + // // calculate split type + // const auto contentWidth = ::base::saturated_cast(_tabContent.ActualWidth()); + // const auto contentHeight = ::base::saturated_cast(_tabContent.ActualHeight()); + // const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight }; + + // const auto realSplitType = tab->PreCalculateCanSplit(splitDirection, splitSize, availableSpace); + // if (!realSplitType) + // { + // co_return; + // } + + // // unzoom + // _UnZoomIfNeeded(); + + // co_await winrt::resume_background(); + + // auto content = co_await preppedContent.initContentProc; + + // co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); + + // auto pane = _makePaneFromContent(content, preppedContent.controlSettings, preppedContent.profile); + + // tab->SplitPane(*realSplitType, splitSize, pane); + + // // Manually focus the new pane, if we've already initialized + // if (_startupState == StartupState::Initialized) + // { + // if (const auto control = _GetActiveControl()) + // { + // control.Focus(FocusState::Programmatic); + // } + // } + // } + + // >>>>>>> 1f2bb760e (Pane officially opened via the serialized actions, via across the process boundary) // Method Description: // - Switches the split orientation of the currently focused pane. // Arguments: @@ -2687,6 +2775,129 @@ namespace winrt::TerminalApp::implementation } } + // <<<<<<< HEAD + // ======= + // static wil::unique_process_information _createHostClassProcess(const winrt::guid& g) + // { + // auto guidStr{ ::Microsoft::Console::Utils::GuidToString(g) }; + + // // Create an event that the content process will use to signal it is + // // ready to go. We won't need the event after this function, so the + // // unique_event will clean up our handle when we leave this scope. The + // // ContentProcess is responsible for cleaning up its own handle. + // wil::unique_event ev{ CreateEvent(nullptr, true, false, nullptr /*L"contentProcessStarted"*/) }; + // // Make sure to mark this handle as inheritable! Even with + // // bInheritHandles=true, this is only inherited when it's explicitly + // // allowed to be. + // SetHandleInformation(ev.get(), HANDLE_FLAG_INHERIT, 1); + + // // god bless, fmt::format will format a HANDLE like `0xa80` + // std::wstring commandline{ + // fmt::format(L"WindowsTerminal.exe --content {} --signal {}", guidStr, ev.get()) + // }; + + // STARTUPINFO siOne{ 0 }; + // siOne.cb = sizeof(STARTUPINFOW); + // wil::unique_process_information piOne; + // auto succeeded = CreateProcessW( + // nullptr, + // commandline.data(), + // nullptr, // lpProcessAttributes + // nullptr, // lpThreadAttributes + // true, // bInheritHandles + // CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags + // nullptr, // lpEnvironment + // nullptr, // startingDirectory + // &siOne, // lpStartupInfo + // &piOne // lpProcessInformation + // ); + // THROW_IF_WIN32_BOOL_FALSE(succeeded); + + // // Wait for the child process to signal that they're ready. + // WaitForSingleObject(ev.get(), INFINITE); + + // return std::move(piOne); + // } + + // PreparedContent TerminalPage::_prepareContentProc(const NewTerminalArgs& newTerminalArgs, + // const bool duplicate) + // { + // PreparedContent preppedContent; + // _evaluateSettings(newTerminalArgs, duplicate, preppedContent.controlSettings, preppedContent.profile); + // preppedContent.initContentProc = (newTerminalArgs && newTerminalArgs.ContentGuid() != winrt::guid{}) ? + // _AttachToContentProcess(newTerminalArgs.ContentGuid()) : + // _CreateNewContentProcess(preppedContent.profile, preppedContent.controlSettings); + // return preppedContent; + // } + + // Windows::Foundation::IAsyncOperation TerminalPage::_CreateNewContentProcess(Profile profile, + // TerminalSettingsCreateResult settings) + // { + // co_await winrt::resume_background(); + // winrt::guid contentGuid{ ::Microsoft::Console::Utils::CreateGuid() }; + // // Spawn a wt.exe, with the guid on the commandline + // auto piContentProcess{ _createHostClassProcess(contentGuid) }; + + // // DebugBreak(); + + // // THIS MUST TAKE PLACE AFTER _createHostClassProcess. + // // * If we're creating a new OOP control, _createHostClassProcess will + // // spawn the process that will actually host the ContentProcess + // // object. + // // * If we're attaching, then that process already exists. + // ContentProcess content{ nullptr }; + // try + // { + // content = create_instance(contentGuid, CLSCTX_LOCAL_SERVER); + // } + // catch (winrt::hresult_error hr) + // { + // co_return nullptr; + // } + + // if (content == nullptr) + // { + // co_return nullptr; + // } + + // TerminalConnection::ConnectionInformation connectInfo{ _CreateConnectionInfoFromSettings(profile, settings.DefaultSettings()) }; + + // // Init the content proc with the focused/unfocused pair + // if (!content.Initialize(settings.DefaultSettings(), settings.UnfocusedSettings(), connectInfo)) + // { + // co_return nullptr; + // } + + // co_return content; + // } + + // Windows::Foundation::IAsyncOperation TerminalPage::_AttachToContentProcess(const winrt::guid contentGuid) + // { + // ContentProcess content{ nullptr }; + // try + // { + // content = create_instance(contentGuid, CLSCTX_LOCAL_SERVER); + // } + // catch (winrt::hresult_error hr) + // { + // } + // co_return content; + // } + + // // INVARIANT: Must be called on UI thread! + // std::shared_ptr TerminalPage::_makePaneFromContent(ContentProcess content, + // TerminalSettingsCreateResult controlSettings, + // Profile profile) + // { + // // Create the XAML control that will be attached to the content process. + // // We're not passing in a connection, because the contentGuid will be used instead + // const auto control = _InitControl(controlSettings, content.Guid()); + // _RegisterTerminalEvents(control); + + // return std::make_shared(profile, control); + // } + + // >>>>>>> 1f2bb760e (Pane officially opened via the serialized actions, via across the process boundary) TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const ITerminalConnection& connection) { // Do any initialization that needs to apply to _every_ TermControl we diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 5bf00b07982..d55bbda2be8 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1402,9 +1402,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - duration - How long the note should be sustained (in microseconds). void ControlCore::_terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) { + // TODO! In the OOP2 branches, I left a comment about intentionally conflicting here. // The UI thread might try to acquire the console lock from time to time. // --> Unlock it, so the UI doesn't hang while we're busy. const auto suspension = _terminal->SuspendLock(); + // TODO! GH#1256 This is intentionally here to conflict with https://github.com/microsoft/terminal/pull/13471#pullrequestreview-1039353718 + // When we do tearout, we'll need to also recreate the midi thing + + // TODO! I don't know if taht ^ comment is still relevant, but I'll leave it here in case it is. // This call will block for the duration, unless shutdown early. _midiAudio.PlayNote(reinterpret_cast(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast(duration)); @@ -1941,6 +1946,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (owner != _owningHwnd && _connection) { + // TODO GH#1256 change the midi HWND too if (auto conpty{ _connection.try_as() }) { conpty.ReparentWindow(owner); diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index dd70dc4a864..5b1d41226a4 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -432,5 +432,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto str = Json::writeString(wbuilder, json); return winrt::to_hstring(str); } + winrt::Windows::Foundation::Collections::IVector ActionAndArgs::Deserialize(winrt::hstring content) + { + auto data = winrt::to_string(content); + + std::string errs; + std::unique_ptr reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() }; + Json::Value root; + if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs)) + { + throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs)); + } + + winrt::Windows::Foundation::Collections::IVector result{ nullptr }; + JsonUtils::GetValueForKey(root, "actions", result); + return result; + } } diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h index b13f60ebc81..5fa40aea67f 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h @@ -17,6 +17,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static Json::Value ToJson(const Model::ActionAndArgs& val); static winrt::hstring Serialize(winrt::Windows::Foundation::Collections::IVector args); + static winrt::Windows::Foundation::Collections::IVector Deserialize(winrt::hstring content); ActionAndArgs() = default; ActionAndArgs(ShortcutAction action); diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index a223945f2cc..374a8ffded6 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -25,6 +25,7 @@ namespace Microsoft.Terminal.Settings.Model ActionAndArgs(ShortcutAction action, IActionArgs args); static String Serialize(IVector args); + static IVector Deserialize(String content); IActionArgs Args; ShortcutAction Action; From 3bd6957a6d378d32a71e578451a05e440da4ec7a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Feb 2023 11:22:36 -0600 Subject: [PATCH 021/234] This works to open a new pane in the new window... but it doesn't render, and the old pane keeps on choochin. And it only works with a window name, not and ID And the `Dispatcher` definitely needs to get re-wired for the new thread, cause dragging it across a DPI boundary (aka, resize) crashes the window --- src/cascadia/TerminalApp/TerminalPage.cpp | 24 ++++++++++++++++++++++- src/cascadia/TerminalApp/TerminalPage.h | 2 ++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e318fa4f959..fad588d9c5a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2036,11 +2036,11 @@ namespace winrt::TerminalApp::implementation // TODO! if the first action is a split pane and tabIndex > tabs.size, // then remove it and insert an equivalent newTab + co_await wil::resume_foreground(Dispatcher()); for (const auto& action : args) { _actionDispatch->DoAction(action); } - co_await wil::resume_foreground(Dispatcher()); } // Method Description: @@ -2905,7 +2905,20 @@ namespace winrt::TerminalApp::implementation // TermControl will copy the settings out of the settings passed to it. auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection); + return _InitControl(content); + } + + TermControl TerminalPage::_InitControlFromContent(const winrt::guid& contentGuid) + { + if (const auto& content{ _manager.LookupCore(contentGuid) }) + { + return _InitControl(content); + } + return nullptr; + } + TermControl TerminalPage::_InitControl(const ControlInteractivity& content) + { TermControl term{ content }; // GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now. @@ -2965,6 +2978,15 @@ namespace winrt::TerminalApp::implementation profile = _settings.GetProfileForArgs(newTerminalArgs); controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings); } + // TODO! This should really be even above the block above, I'm just kludging it RN so I get a profile + // We'll probably want to not store just Interactivity's, but a {Profile, Interactivity, etc...} blob in ContentManager + if (newTerminalArgs.ContentGuid() != winrt::guid{}) + { + const auto control = _InitControlFromContent(newTerminalArgs.ContentGuid()); + _RegisterTerminalEvents(control); + auto resultPane = std::make_shared(profile, control); + return resultPane; + } // Try to handle auto-elevation if (_maybeElevate(newTerminalArgs, controlSettings, profile)) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 6785f040cef..5dc28a7194c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -419,6 +419,8 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); + winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Control::ControlInteractivity& content); + winrt::Microsoft::Terminal::Control::TermControl _InitControlFromContent(const winrt::guid& contentGuid); std::shared_ptr _MakePane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr, const winrt::TerminalApp::TabBase& sourceTab = nullptr, From d143f312d3a7ebfe8917e430a3df557aef164510 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Feb 2023 11:30:01 -0600 Subject: [PATCH 022/234] Add comments This is basically the comment from aaefdf4408e9951d20661bdece5b0e8f33e1f77a. The other comment wasn't relevant anymore --- src/cascadia/TerminalSettingsModel/ActionArgs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index fb133babe31..494d564cb6a 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -325,6 +325,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle); JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme); JsonUtils::GetValueForKey(json, ElevateKey, args->_Elevate); + + // TODO! We should probably have the ContentGuid be optional, or at + // least serialized smarter, so we definitely don't write it to the + // file, ever. JsonUtils::GetValueForKey(json, ContentKey, args->_ContentGuid); return *args; } From ebe5fd816377eb242a06c7a61ea92a6784879b66 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 14 Jul 2022 16:40:39 -0500 Subject: [PATCH 023/234] can I get a whoop whoop (Adds suuport for moving tabs to another window) cherry-picked from 4acbffb0e --- .../TerminalApp/AppActionHandlers.cpp | 13 +----- src/cascadia/TerminalApp/Pane.cpp | 10 +++-- src/cascadia/TerminalApp/SettingsTab.cpp | 2 +- src/cascadia/TerminalApp/SettingsTab.h | 2 +- src/cascadia/TerminalApp/TabBase.h | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 40 ++++++++++++++++++- src/cascadia/TerminalApp/TerminalPage.h | 1 + src/cascadia/TerminalApp/TerminalTab.cpp | 6 ++- src/cascadia/TerminalApp/TerminalTab.h | 2 +- .../TerminalSettingsModel/ActionArgs.cpp | 7 +++- .../TerminalSettingsModel/ActionArgs.h | 8 +++- .../TerminalSettingsModel/ActionArgs.idl | 3 +- 12 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index be630f27074..b2b00b7efa3 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -789,17 +789,8 @@ namespace winrt::TerminalApp::implementation { if (const auto& realArgs = actionArgs.ActionArgs().try_as()) { - auto direction = realArgs.Direction(); - if (direction != MoveTabDirection::None) - { - if (auto focusedTabIndex = _GetFocusedTabIndex()) - { - auto currentTabIndex = focusedTabIndex.value(); - auto delta = direction == MoveTabDirection::Forward ? 1 : -1; - _TryMoveTab(currentTabIndex, currentTabIndex + delta); - } - } - actionArgs.Handled(true); + auto moved = _MoveTab(realArgs); + actionArgs.Handled(moved); } } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 40d24da80c1..8c6f0c1b4a3 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1407,9 +1407,13 @@ void Pane::UpdateVisuals() if (!_IsLeaf()) { _UpdateBorders(); - } - _borderFirst.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); - _borderSecond.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); + }; + // TODO! + // Hey remember when we made a static brush reference so we didn't have to look these up each time? + // well, that's on the wrong thread always now. Great work. + // + // _borderFirst.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); + // _borderSecond.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); } // Method Description: diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp index 6ac9bb5e79e..16b4ab454fd 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -50,7 +50,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - The list of actions. - std::vector SettingsTab::BuildStartupActions() const + std::vector SettingsTab::BuildStartupActions(const bool /*asContent*/) const { ActionAndArgs action; action.Action(ShortcutAction::OpenSettings); diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h index e9cfc9c8376..1a92cf488a9 100644 --- a/src/cascadia/TerminalApp/SettingsTab.h +++ b/src/cascadia/TerminalApp/SettingsTab.h @@ -30,7 +30,7 @@ namespace winrt::TerminalApp::implementation void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings); void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override; - std::vector BuildStartupActions() const override; + std::vector BuildStartupActions(const bool asContent = false) const override; private: winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h index fb32fe0e377..ca2d0bb7b12 100644 --- a/src/cascadia/TerminalApp/TabBase.h +++ b/src/cascadia/TerminalApp/TabBase.h @@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs); void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap); - virtual std::vector BuildStartupActions() const = 0; + virtual std::vector BuildStartupActions(const bool asContent = false) const = 0; virtual std::optional GetTabColor(); void ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused, diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index fad588d9c5a..eb78c5b8525 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2027,6 +2027,42 @@ namespace winrt::TerminalApp::implementation return true; } + bool TerminalPage::_MoveTab(MoveTabArgs args) + { + const auto windowId{ args.Window() }; + if (!windowId.empty()) + { + if (const auto terminalTab{ _GetFocusedTabImpl() }) + { + auto startupActions = terminalTab->BuildStartupActions(true); + auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; + // Json::Value json{ Json::objectValue }; + // SetValueForKey(json, "content", winRtActions); + // Json::StreamWriterBuilder wbuilder; + // auto str = Json::writeString(wbuilder, json); + auto str = ActionAndArgs::Serialize(winRtActions); + auto request = winrt::make_self(args.Window(), + str, + 0); + _RequestMoveContentHandlers(*this, *request); + return true; + } + } + + auto direction = args.Direction(); + if (direction != MoveTabDirection::None) + { + if (auto focusedTabIndex = _GetFocusedTabIndex()) + { + auto currentTabIndex = focusedTabIndex.value(); + auto delta = direction == MoveTabDirection::Forward ? 1 : -1; + _TryMoveTab(currentTabIndex, currentTabIndex + delta); + } + } + + return true; + } + winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content, uint32_t tabIndex) { content; @@ -2036,7 +2072,7 @@ namespace winrt::TerminalApp::implementation // TODO! if the first action is a split pane and tabIndex > tabs.size, // then remove it and insert an equivalent newTab - co_await wil::resume_foreground(Dispatcher()); + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); // may need to go to the top of _createNewTabFromContent for (const auto& action : args) { _actionDispatch->DoAction(action); @@ -2980,7 +3016,7 @@ namespace winrt::TerminalApp::implementation } // TODO! This should really be even above the block above, I'm just kludging it RN so I get a profile // We'll probably want to not store just Interactivity's, but a {Profile, Interactivity, etc...} blob in ContentManager - if (newTerminalArgs.ContentGuid() != winrt::guid{}) + if (newTerminalArgs && newTerminalArgs.ContentGuid() != winrt::guid{}) { const auto control = _InitControlFromContent(newTerminalArgs.ContentGuid()); _RegisterTerminalEvents(control); diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 5dc28a7194c..4b0defd093b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -329,6 +329,7 @@ namespace winrt::TerminalApp::implementation bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction); bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args); + bool _MoveTab(const Microsoft::Terminal::Settings::Model::MoveTabArgs args); template bool _ApplyToActiveControls(F f) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 8cd4ae86acd..6ca9981c898 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -438,7 +438,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - A vector of commands - std::vector TerminalTab::BuildStartupActions() const + std::vector TerminalTab::BuildStartupActions(const bool asContent) const { // Give initial ids (0 for the child created with this tab, // 1 for the child after the first split. @@ -447,7 +447,7 @@ namespace winrt::TerminalApp::implementation { ActionAndArgs newTabAction{}; newTabAction.Action(ShortcutAction::NewTab); - NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() }; + NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) }; newTabAction.Args(newTabArgs); state.args.emplace(state.args.begin(), std::move(newTabAction)); @@ -783,6 +783,8 @@ namespace winrt::TerminalApp::implementation bool TerminalTab::FocusPane(const uint32_t id) { + if (_rootPane == nullptr) + return false; _changingActivePane = true; const auto res = _rootPane->FocusPane(id); _changingActivePane = false; diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index 53492095db1..304dff1575e 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -82,7 +82,7 @@ namespace winrt::TerminalApp::implementation void EnterZoom(); void ExitZoom(); - std::vector BuildStartupActions() const override; + std::vector BuildStartupActions(const bool asContent = false) const override; int GetLeafPaneCount() const noexcept; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 64c4976af19..d88077011fa 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -13,6 +13,7 @@ #include "ResizePaneArgs.g.cpp" #include "MoveFocusArgs.g.cpp" #include "MovePaneArgs.g.cpp" +#include "MoveTabArgs.g.cpp" #include "SwapPaneArgs.g.cpp" #include "AdjustFontSizeArgs.g.cpp" #include "SendInputArgs.g.cpp" @@ -28,7 +29,6 @@ #include "CloseOtherTabsArgs.g.cpp" #include "CloseTabsAfterArgs.g.cpp" #include "CloseTabArgs.g.cpp" -#include "MoveTabArgs.g.cpp" #include "ScrollToMarkArgs.g.cpp" #include "AddMarkArgs.g.cpp" #include "FindMatchArgs.g.cpp" @@ -245,6 +245,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring MovePaneArgs::GenerateName() const { + // TODO! return winrt::hstring{ fmt::format(L"{}, tab index:{}", RS_(L"MovePaneCommandKey"), TabIndex()) }; @@ -662,6 +663,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation fmt::format(std::wstring_view(RS_(L"MoveTabCommandKey")), directionString) }; + + // TODO! + // return winrt::hstring{ + // fmt::format(L"{}, window:{}", RS_(L"MovePaneCommandKey"), Window()) } winrt::hstring ToggleCommandPaletteArgs::GenerateName() const diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 494d564cb6a..6011dfc4496 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -173,8 +173,12 @@ private: X(Windows::Foundation::IReference, Index, "index", false, nullptr) //////////////////////////////////////////////////////////////////////////////// -#define MOVE_TAB_ARGS(X) \ - X(MoveTabDirection, Direction, "direction", args->Direction() == MoveTabDirection::None, MoveTabDirection::None) +#define MOVE_TAB_ARGS(X) \ + X(MoveTabDirection, Direction, "direction", args->Direction() == MoveTabDirection::None, MoveTabDirection::None) \ + X(winrt::hstring, Window, "window", false, L"") + +// Other ideas: +// X(uint32_t, TabIndex, "index", false, 0) \ // target? source? //////////////////////////////////////////////////////////////////////////////// #define SCROLL_UP_ARGS(X) \ diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 3162bf3cf39..21cf07d4af9 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -279,8 +279,9 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass MoveTabArgs : IActionArgs { - MoveTabArgs(MoveTabDirection direction); + MoveTabArgs(MoveTabDirection direction, String window); MoveTabDirection Direction { get; }; + String Window { get; }; }; [default_interface] runtimeclass ScrollUpArgs : IActionArgs From 0a6c21fed01228afe7540ac2d8564cb03d7fb4c5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 14 Jul 2022 16:47:29 -0500 Subject: [PATCH 024/234] this is important for moving, yknow, the panes --- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 6ca9981c898..e05e5521eac 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -442,7 +442,7 @@ namespace winrt::TerminalApp::implementation { // Give initial ids (0 for the child created with this tab, // 1 for the child after the first split. - auto state = _rootPane->BuildStartupActions(0, 1); + auto state = _rootPane->BuildStartupActions(0, 1, asContent); { ActionAndArgs newTabAction{}; From 1f56807fa02e7ee6fb619b0accc0507381501588 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Feb 2023 12:28:46 -0600 Subject: [PATCH 025/234] move to int IDs is okay too --- src/cascadia/Remoting/Monarch.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index dc498f75731..ea807ea6e1c 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1048,11 +1048,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::hstring content, uint32_t tabIndex) { - auto windowId = _lookupPeasantIdForName(window); + uint64_t windowId = _lookupPeasantIdForName(window); if (windowId == 0) { - /* TODO! try the name as an integer ID */ - return; + // Try the name as an integer ID + uint32_t temp; + if (!Utils::StringToUint(window.c_str(), temp)) + { + return; + } + else + { + windowId = temp; + } } if (auto targetPeasant{ _getPeasant(windowId) }) From 0f3391342389ab9b22da5e4dd29b431d1df873c4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 09:10:04 -0600 Subject: [PATCH 026/234] actually start drawing to the new control when we move it --- src/cascadia/TerminalControl/ControlCore.cpp | 11 +++++++++++ src/cascadia/TerminalControl/ControlCore.h | 2 ++ src/cascadia/TerminalControl/ControlCore.idl | 2 ++ src/cascadia/TerminalControl/TermControl.cpp | 4 ++++ src/renderer/atlas/AtlasEngine.api.cpp | 5 +++++ src/renderer/atlas/AtlasEngine.h | 1 + src/renderer/dx/DxRenderer.cpp | 4 ++++ src/renderer/dx/DxRenderer.hpp | 1 + src/renderer/inc/IRenderEngine.hpp | 1 + 9 files changed, 31 insertions(+) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index d55bbda2be8..3d739d698bc 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1693,6 +1693,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + uint64_t ControlCore::SwapChainHandle() const + { + // This is called by: + // * TermControl::RenderEngineSwapChainChanged, who is only registered + // after Core::Initialize() is called. + // * TermControl::_InitializeTerminal, after the call to Initialize, for + // _AttachDxgiSwapChainToXaml. + // In both cases, we'll have a _renderEngine by then. + return _renderEngine? reinterpret_cast(_renderEngine->GetSwapChainHandle()) : 0u; + } + // Method Description: // - Clear the contents of the buffer. The region cleared is given by // clearType: diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 890e9ec3d51..33fec5c6102 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -73,6 +73,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme); + uint64_t SwapChainHandle() const; + void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index a8df501469b..f20ddea3604 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -76,6 +76,8 @@ namespace Microsoft.Terminal.Control IControlAppearance UnfocusedAppearance { get; }; Boolean HasUnfocusedAppearance(); + UInt64 SwapChainHandle { get; }; + Windows.Foundation.Size FontSize { get; }; String FontFaceName { get; }; UInt16 FontWeight { get; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 8afeef08364..caeba8917df 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -68,6 +68,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation InitializeComponent(); _core = _interactivity.Core(); + if (const auto h{ reinterpret_cast(_core.SwapChainHandle()) }) + { + _AttachDxgiSwapChainToXaml(h); + } // These events might all be triggered by the connection, but that // should be drained and closed before we complete destruction. So these diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index d9871debc6c..453c78a3f4d 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -341,6 +341,11 @@ void AtlasEngine::SetCallback(std::function pfn) noexcept _api.swapChainChangedCallback = std::move(pfn); } +HANDLE AtlasEngine::GetSwapChainHandle() noexcept +{ + return _api.swapChainHandle.get(); +} + void AtlasEngine::EnableTransparentBackground(const bool isTransparent) noexcept { if (_api.enableTransparentBackground != isTransparent) diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index f463a000575..1b0d3df5c19 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -68,6 +68,7 @@ namespace Microsoft::Console::Render // DxRenderer - setter void SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override; void SetCallback(std::function pfn) noexcept override; + HANDLE GetSwapChainHandle() noexcept override; void EnableTransparentBackground(const bool isTransparent) noexcept override; void SetForceFullRepaintRendering(bool enable) noexcept override; [[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index ed5234fc5bd..7a74398085f 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -987,6 +987,10 @@ void DxEngine::SetCallback(std::function pfn) noexcept { _pfn = std::move(pfn); } +HANDLE DxEngine::GetSwapChainHandle() noexcept +{ + return _swapChainHandle.get(); +} void DxEngine::SetWarningCallback(std::function pfn) noexcept { diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index d3242d2a98f..ff17b6f6366 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -58,6 +58,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT SetWindowSize(const til::size pixels) noexcept override; void SetCallback(std::function pfn) noexcept override; + HANDLE GetSwapChainHandle() noexcept override; void SetWarningCallback(std::function pfn) noexcept override; bool GetRetroTerminalEffect() const noexcept override; diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 06a756c627c..60a10d2486a 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -100,6 +100,7 @@ namespace Microsoft::Console::Render // DxRenderer - setter virtual void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept {} virtual void SetCallback(std::function pfn) noexcept {} + virtual HANDLE GetSwapChainHandle() noexcept { return nullptr; } virtual void EnableTransparentBackground(const bool isTransparent) noexcept {} virtual void SetForceFullRepaintRendering(bool enable) noexcept {} virtual [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; } From 713ea71725d0a0425a1c77329b4d42ad29acc3a3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 09:10:17 -0600 Subject: [PATCH 027/234] Revert "this is important for moving, yknow, the panes" This reverts commit 0a6c21fed01228afe7540ac2d8564cb03d7fb4c5. --- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index e05e5521eac..6ca9981c898 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -442,7 +442,7 @@ namespace winrt::TerminalApp::implementation { // Give initial ids (0 for the child created with this tab, // 1 for the child after the first split. - auto state = _rootPane->BuildStartupActions(0, 1, asContent); + auto state = _rootPane->BuildStartupActions(0, 1); { ActionAndArgs newTabAction{}; From 57d1dd435828aebfa2b81e93680da00a89132296 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 09:15:53 -0600 Subject: [PATCH 028/234] [ainulindale] Clean tear down the `App` when the process exits --- 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 631f690e425db3e0d97b2724cee143d16b76e52f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 09:41:16 -0600 Subject: [PATCH 029/234] remove the old control when the content gets moved --- src/cascadia/TerminalControl/ControlCore.cpp | 7 ++++++- src/cascadia/TerminalControl/ControlCore.h | 1 + .../TerminalControl/ControlInteractivity.cpp | 5 +++++ .../TerminalControl/ControlInteractivity.h | 1 + .../TerminalControl/ControlInteractivity.idl | 1 + src/cascadia/TerminalControl/TermControl.cpp | 15 +++++++++++++++ 6 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 3d739d698bc..53cbb7b5b39 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -225,6 +225,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + void ControlCore::Reparent() + { + _CloseTerminalRequestedHandlers(*this, nullptr); + } + bool ControlCore::Initialize(const double actualWidth, const double actualHeight, const double compositionScale) @@ -1701,7 +1706,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // * TermControl::_InitializeTerminal, after the call to Initialize, for // _AttachDxgiSwapChainToXaml. // In both cases, we'll have a _renderEngine by then. - return _renderEngine? reinterpret_cast(_renderEngine->GetSwapChainHandle()) : 0u; + return _renderEngine ? reinterpret_cast(_renderEngine->GetSwapChainHandle()) : 0u; } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 33fec5c6102..9e170d868f6 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -74,6 +74,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme); uint64_t SwapChainHandle() const; + void Reparent(); void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index dbb47b6b543..e562850923b 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -54,6 +54,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _guid; } + void ControlInteractivity::Reparent() + { + _core->Reparent(); + } + // 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 f45e91793db..2ebeb131289 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -86,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ManglePathsForWsl(); winrt::guid Id(); + void Reparent(); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 97b050a736b..4048a2bd4ae 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -24,6 +24,7 @@ namespace Microsoft.Terminal.Control void LostFocus(); Guid Id { get; }; + void Reparent(); InteractivityAutomationPeer OnCreateAutomationPeer(); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index caeba8917df..8bb51dc6ead 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -71,6 +71,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (const auto h{ reinterpret_cast(_core.SwapChainHandle()) }) { _AttachDxgiSwapChainToXaml(h); + _interactivity.Reparent(); + // Reparent will fire off a _core.CloseTerminalRequested, which the + // old window will listen to and know to remove its old control, + // leaving us as the owner. } // These events might all be triggered by the connection, but that @@ -105,6 +109,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // at which to initialize our renderer (and our terminal). // Any earlier than the last layout update and we may not know the terminal's starting size. + // TODO! This certainly needs to be adapted for reparenting. At + // least we don't need to quick return if the core was already + // initialized - we still have state to set up, and resize the core + // to the new swapchain size, etc. if (_InitializeTerminal()) { // Only let this succeed once. @@ -143,6 +151,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation _autoScrollTimer.Interval(AutoScrollUpdateInterval); _autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll }); + // _interactivity.Attached([weakThis = get_weak()]{ + // if (auto control{ weakThis.get() }; !control->_IsClosing()) + // { + // CloseTerminalRequested + // } + // }); + _ApplyUISettings(); } From 17057d147d0b49484222103c29732769f5b1f4bc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 10:45:20 -0600 Subject: [PATCH 030/234] (maybe valaquenta) well, the keybindings stuff is... better? --- src/cascadia/TerminalApp/Pane.cpp | 6 ++++- src/cascadia/TerminalApp/TerminalPage.cpp | 13 ++++++----- src/cascadia/TerminalApp/TerminalPage.h | 2 +- src/cascadia/TerminalControl/ControlCore.cpp | 12 +++++++--- src/cascadia/TerminalControl/ControlCore.h | 4 +++- .../TerminalControl/ControlInteractivity.cpp | 4 ++-- .../TerminalControl/ControlInteractivity.h | 2 +- .../TerminalControl/ControlInteractivity.idl | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 22 ++++++++++++------- src/cascadia/TerminalControl/TermControl.h | 6 ++--- src/cascadia/TerminalControl/TermControl.idl | 2 ++ 11 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 8c6f0c1b4a3..9b4cf7b76be 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1229,7 +1229,11 @@ void Pane::Shutdown() if (_IsLeaf()) { - _control.Close(); + // TOODO! if we call Close here, on a control that was moved to another thread, then it's Dispatcher is no longer this thread, and we'll crash. + // Acn we get away with _not_ calling Close? Seems like shutdown is only called for RemoveTab(TerminalTab), so theoretically, removing the old control tree from the UI tree will release the core, calling it's dtor, which will call Close itself. + // Alternatively, we could try and see if there's only one strong ref to a ControlCore and just noop if there's more than one. + // + // _control.Close(); } else { diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index eb78c5b8525..2b19d484bdb 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2941,22 +2941,25 @@ namespace winrt::TerminalApp::implementation // TermControl will copy the settings out of the settings passed to it. auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection); - return _InitControl(content); + return _InitControl(TermControl{ content }); } TermControl TerminalPage::_InitControlFromContent(const winrt::guid& contentGuid) { if (const auto& content{ _manager.LookupCore(contentGuid) }) { - return _InitControl(content); + // We have to pass in our current keybindings, because that's an + // object that belongs to this TerminalPage, on this thread. If we + // don't, then when we move the content to another thread, and it + // tries to handle a key, it'll callback on the original page's + // stack, inevitably resulting in a wrong_thread + return _InitControl(TermControl::AttachContent(content, *_bindings)); } return nullptr; } - TermControl TerminalPage::_InitControl(const ControlInteractivity& content) + TermControl TerminalPage::_InitControl(const TermControl& term) { - 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 4b0defd093b..6094a4661b7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -420,7 +420,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); - winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Control::ControlInteractivity& content); + winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Control::TermControl& term); winrt::Microsoft::Terminal::Control::TermControl _InitControlFromContent(const winrt::guid& contentGuid); std::shared_ptr _MakePane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr, diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 53cbb7b5b39..7a8918f7cfb 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -154,7 +154,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get())); } + _setupDispatcherAndCallbacks(); + UpdateSettings(settings, unfocusedAppearance); + } + + void ControlCore::_setupDispatcherAndCallbacks() + { // Get our dispatcher. If we're hosted in-proc with XAML, this will get // us the same dispatcher as TermControl::Dispatcher(). If we're out of // proc, this'll return null. We'll need to instead make a new @@ -211,8 +217,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation core->_ScrollPositionChangedHandlers(*core, update); } }); - - UpdateSettings(settings, unfocusedAppearance); } ControlCore::~ControlCore() @@ -225,9 +229,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::Reparent() + void ControlCore::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { _CloseTerminalRequestedHandlers(*this, nullptr); + _settings->KeyBindings(keyBindings); + _setupDispatcherAndCallbacks(); } bool ControlCore::Initialize(const double actualWidth, diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 9e170d868f6..cfffefa88a6 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -74,7 +74,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme); uint64_t SwapChainHandle() const; - void Reparent(); + void Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); @@ -286,6 +286,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::unique_ptr> _updatePatternLocations; std::shared_ptr> _updateScrollBar; + void _setupDispatcherAndCallbacks(); + bool _setFontSizeUnderLock(float fontSize); void _updateFont(const bool initialUpdate = false); void _refreshSizeUnderLock(); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index e562850923b..72d45136b01 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -54,9 +54,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _guid; } - void ControlInteractivity::Reparent() + void ControlInteractivity::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { - _core->Reparent(); + _core->Reparent(keyBindings); } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 2ebeb131289..6cb6d4e1077 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -86,7 +86,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ManglePathsForWsl(); winrt::guid Id(); - void Reparent(); + void Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 4048a2bd4ae..7af80c7e521 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -24,7 +24,7 @@ namespace Microsoft.Terminal.Control void LostFocus(); Guid Id { get; }; - void Reparent(); + void Reparent(Microsoft.Terminal.Control.IKeyBindings keyBindings); InteractivityAutomationPeer OnCreateAutomationPeer(); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 8bb51dc6ead..31172437978 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -68,14 +68,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation InitializeComponent(); _core = _interactivity.Core(); - if (const auto h{ reinterpret_cast(_core.SwapChainHandle()) }) - { - _AttachDxgiSwapChainToXaml(h); - _interactivity.Reparent(); - // Reparent will fire off a _core.CloseTerminalRequested, which the - // old window will listen to and know to remove its old control, - // leaving us as the owner. - } // These events might all be triggered by the connection, but that // should be drained and closed before we complete destruction. So these @@ -161,6 +153,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation _ApplyUISettings(); } + Control::TermControl TermControl::AttachContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings) + { + const auto term{ winrt::make_self(content) }; + term->_AttachDxgiSwapChainToXaml(reinterpret_cast(term->_core.SwapChainHandle())); + content.Reparent(keyBindings); + // if (const auto h{ reinterpret_cast(_core.SwapChainHandle()) }) + // { + // // Reparent will fire off a _core.CloseTerminalRequested, which the + // // old window will listen to and know to remove its old control, + // // leaving us as the owner. + // } + return *term; + } + winrt::guid TermControl::ContentGuid() const { return _interactivity.Id(); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 6959f1d777d..e489025df52 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -27,9 +27,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation { TermControl(Control::ControlInteractivity content); - TermControl(IControlSettings settings, - Control::IControlAppearance unfocusedAppearance, - TerminalConnection::ITerminalConnection connection); + TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection); + + static Control::TermControl AttachContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings); winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings); winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 827a629adce..1a77cffc9b5 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -24,6 +24,8 @@ namespace Microsoft.Terminal.Control IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); + static TermControl AttachContent(ControlInteractivity content, Microsoft.Terminal.Control.IKeyBindings keyBindings); + static Windows.Foundation.Size GetProposedDimensions(IControlSettings settings, UInt32 dpi, Int32 commandlineCols, From 427a4a51c5f57a01d010bab23803a0dba1b7c62c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 13:28:30 -0600 Subject: [PATCH 031/234] [ainulindale] "fix" hot reload Doesn't work with multiple windows open, but doesn't do _nothing_ --- 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 | 12 ++-- src/cascadia/TerminalApp/TerminalWindow.idl | 11 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 4 +- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 9 files changed, 110 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 a277ad7b7fa..65098b5582b 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 17c1782c2d3..cd4c62655bd 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -70,7 +70,7 @@ namespace winrt::TerminalApp::implementation TerminalApp::TerminalWindow CreateNewWindow(); winrt::TerminalApp::ContentManager ContentManager(); - 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 }; @@ -88,7 +88,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 c1fa2d5ca48..cc0382ecfc1 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -46,7 +46,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 c6e7f03063d..d52fa12bceb 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 @@ -410,7 +411,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"); @@ -429,13 +431,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. @@ -460,7 +461,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"); @@ -471,18 +472,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)); @@ -739,20 +738,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(); @@ -1160,13 +1164,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 a60dadd5ad9..b6b7eea94ca 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 @@ -163,8 +164,11 @@ namespace winrt::TerminalApp::implementation TerminalApp::ContentManager _manager{ 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 74b813eacb7..dfd64bf412a 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 fd1f65cb243..21440902001 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1436,8 +1436,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 40046c38dcf93a75622e8932ab5ad48738ffad0a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 15 Jul 2022 06:20:19 -0500 Subject: [PATCH 032/234] one shot one opportunity Cherry-picked from 4b4e057a7 --- src/cascadia/TerminalApp/TerminalPage.cpp | 50 ++++++++++++++++++++++- src/cascadia/TerminalApp/TerminalPage.h | 4 ++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2b19d484bdb..f963ecdf340 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -271,6 +271,10 @@ namespace winrt::TerminalApp::implementation _tabView.TabCloseRequested({ this, &TerminalPage::_OnTabCloseRequested }); _tabView.TabItemsChanged({ this, &TerminalPage::_OnTabItemsChanged }); + _tabView.TabDragStarting({ this, &TerminalPage::_onTabDragStarting }); + _tabView.TabStripDragOver({ this, &TerminalPage::_onTabStripDragOver }); + _tabView.TabStripDrop({ this, &TerminalPage::_onTabStripDrop }); + _CreateNewTabFlyout(); _UpdateTabWidthMode(); @@ -2072,7 +2076,7 @@ namespace winrt::TerminalApp::implementation // TODO! if the first action is a split pane and tabIndex > tabs.size, // then remove it and insert an equivalent newTab - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); // may need to go to the top of _createNewTabFromContent + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); // may need to go to the top of _createNewTabFromContent for (const auto& action : args) { _actionDispatch->DoAction(action); @@ -4742,4 +4746,48 @@ namespace winrt::TerminalApp::implementation } } + void TerminalPage::_onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, + winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) + { + if (const auto terminalTab{ _GetFocusedTabImpl() }) + { + auto startupActions = terminalTab->BuildStartupActions(true); + auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; + auto str = ActionAndArgs::Serialize(winRtActions); + + e.Data().Properties().Insert(L"content", winrt::box_value(str)); + e.Data().RequestedOperation(DataPackageOperation::Move); + } + } + void TerminalPage::_onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::UI::Xaml::DragEventArgs e) + { + if (e.DataView().Properties().HasKey(L"content")) + { + e.AcceptedOperation(DataPackageOperation::Move); + } + } + winrt::fire_and_forget TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::UI::Xaml::DragEventArgs e) + { + auto contentObj{ e.DataView().Properties().TryLookup(L"content") }; + if (contentObj == nullptr) + { + co_return; + } + auto contentString{ winrt::unbox_value(contentObj) }; + if (!contentString.empty()) + { + auto args = ActionAndArgs::Deserialize(contentString); + // TODO! if the first action is a split pane and tabIndex > tabs.size, + // then remove it and insert an equivalent newTab + + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); // may need to go to the top of _createNewTabFromContent + for (const auto& action : args) + { + _actionDispatch->DoAction(action); + } + } + } + } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 6094a4661b7..23f1ac7895d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -492,6 +492,10 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args); + void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e); + void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); + winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); + #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp #define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action); From a20201fff26589e6faf393c07b508e510dc0fc5c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 13:42:54 -0600 Subject: [PATCH 033/234] [silmarillion] Update the validation for the moveTab action --- src/cascadia/TerminalSettingsModel/ActionArgs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 6011dfc4496..80ef0c4f659 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -173,8 +173,8 @@ private: X(Windows::Foundation::IReference, Index, "index", false, nullptr) //////////////////////////////////////////////////////////////////////////////// -#define MOVE_TAB_ARGS(X) \ - X(MoveTabDirection, Direction, "direction", args->Direction() == MoveTabDirection::None, MoveTabDirection::None) \ +#define MOVE_TAB_ARGS(X) \ + X(MoveTabDirection, Direction, "direction", (args->Direction() == MoveTabDirection::None) && (args->Window().empty()), MoveTabDirection::None) \ X(winrt::hstring, Window, "window", false, L"") // Other ideas: From c282797cac2b7f35da5f0c3e44422e71ae2a6cb2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 13:48:52 -0600 Subject: [PATCH 034/234] [silmarillion] Detach on moveTab, but not on drag It remains to be seen if this worked for movePane, but one step at a time --- src/cascadia/TerminalApp/ContentManager.cpp | 22 +++++++++++++++++++ src/cascadia/TerminalApp/ContentManager.h | 8 +++++++ src/cascadia/TerminalApp/Pane.cpp | 7 +++--- src/cascadia/TerminalApp/TerminalPage.cpp | 16 ++++++++++++++ src/cascadia/TerminalApp/TerminalPage.idl | 1 + src/cascadia/TerminalControl/ControlCore.cpp | 3 ++- src/cascadia/TerminalControl/ControlCore.h | 2 ++ src/cascadia/TerminalControl/ControlCore.idl | 2 ++ .../TerminalControl/ControlInteractivity.cpp | 7 ++++++ .../TerminalControl/ControlInteractivity.h | 2 ++ .../TerminalControl/ControlInteractivity.idl | 1 + 11 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 1b9d5f68e4b..e32595927b0 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -26,6 +26,9 @@ namespace winrt } namespace winrt::TerminalApp::implementation { + // TODO! These all probably need to be weak_refs, don't they, so that the + // contentmanager doesn't just keep every control around indefinitely. + ControlInteractivity ContentManager::CreateCore(Microsoft::Terminal::Control::IControlSettings settings, IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) @@ -40,4 +43,23 @@ namespace winrt::TerminalApp::implementation { return _content.TryLookup(id); } + + void ContentManager::Detach(const winrt::guid& contentGuid) + { + if (const auto& content{ LookupCore(contentGuid) }) + { + content.Attached({ get_weak(), &ContentManager::_finalizeDetach }); + _recentlyDetachedContent.Insert(contentGuid, content); + } + } + + void ContentManager::_finalizeDetach(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable e) + { + if (const auto& content{ sender.try_as() }) + { + _recentlyDetachedContent.Remove(content.Id()); + } + } + } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index bd2725cac6e..5770dce8a91 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -17,9 +17,17 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); Microsoft::Terminal::Control::ControlInteractivity LookupCore(winrt::guid id); + void Detach(const winrt::guid& contentGuid); + private: Windows::Foundation::Collections::IMap _content{ winrt::multi_threaded_map() }; + Windows::Foundation::Collections::IMap _recentlyDetachedContent{ + winrt::multi_threaded_map() + }; + + void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable e); }; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 9b4cf7b76be..cdba83967e7 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1232,8 +1232,9 @@ void Pane::Shutdown() // TOODO! if we call Close here, on a control that was moved to another thread, then it's Dispatcher is no longer this thread, and we'll crash. // Acn we get away with _not_ calling Close? Seems like shutdown is only called for RemoveTab(TerminalTab), so theoretically, removing the old control tree from the UI tree will release the core, calling it's dtor, which will call Close itself. // Alternatively, we could try and see if there's only one strong ref to a ControlCore and just noop if there's more than one. - // - // _control.Close(); + // + // I'm bringing this back for a second. + _control.Close(); } else { @@ -1415,7 +1416,7 @@ void Pane::UpdateVisuals() // TODO! // Hey remember when we made a static brush reference so we didn't have to look these up each time? // well, that's on the wrong thread always now. Great work. - // + // // _borderFirst.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); // _borderSecond.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f963ecdf340..a42446709f9 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2039,6 +2039,18 @@ namespace winrt::TerminalApp::implementation if (const auto terminalTab{ _GetFocusedTabImpl() }) { auto startupActions = terminalTab->BuildStartupActions(true); + + // Collect all the content we're about to detach. + if (const auto rootPane = terminalTab->GetRootPane()) + { + rootPane->WalkTree([&](auto p) { + if (const auto& control{ p->GetTerminalControl() }) + { + _manager.Detach(control.ContentGuid()); + } + }); + } + auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; // Json::Value json{ Json::objectValue }; // SetValueForKey(json, "content", winRtActions); @@ -2048,6 +2060,8 @@ namespace winrt::TerminalApp::implementation auto request = winrt::make_self(args.Window(), str, 0); + + _RemoveTab(*terminalTab); _RequestMoveContentHandlers(*this, *request); return true; } @@ -2083,6 +2097,8 @@ namespace winrt::TerminalApp::implementation } } + // TODO! look at 87c840b381870e45bcc9e625fb88a8bdf5106420. That's what I was starting here + // Method Description: // - Split the focused pane either horizontally or vertically, and place the // given pane accordingly in the tree diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index efbcf344924..090b86cff42 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -14,6 +14,7 @@ namespace TerminalApp Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); Microsoft.Terminal.Control.ControlInteractivity LookupCore(Guid id); + void Detach(Guid id); } delegate void LastTabClosedEventArgs(); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 7a8918f7cfb..4c47efdd04c 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -231,9 +231,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { - _CloseTerminalRequestedHandlers(*this, nullptr); _settings->KeyBindings(keyBindings); _setupDispatcherAndCallbacks(); + + _AttachedHandlers(*this, nullptr); } bool ControlCore::Initialize(const double actualWidth, diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index cfffefa88a6..21e6343a071 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -236,6 +236,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); + + TYPED_EVENT(Attached, IInspectable, IInspectable); // clang-format on private: diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index f20ddea3604..8ecab5510d0 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -164,5 +164,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler UpdateSelectionMarkers; event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler CloseTerminalRequested; + + event Windows.Foundation.TypedEventHandler Attached; }; } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 72d45136b01..abb51661a49 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -47,6 +47,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation _guid = ::Microsoft::Console::Utils::CreateGuid(); _core = winrt::make_self(settings, unfocusedAppearance, connection); + + _core->Attached([weakThis = get_weak()](auto&&, auto&&) { + if (auto self{ weakThis.get() }) + { + self->_AttachedHandlers(*self, nullptr); + } + }); } winrt::guid ControlInteractivity::Id() diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 6cb6d4e1077..0afdc6eda3b 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -92,6 +92,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs); + TYPED_EVENT(Attached, 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 7af80c7e521..c0165645695 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -68,6 +68,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler ScrollPositionChanged; event Windows.Foundation.TypedEventHandler PasteFromClipboard; + event Windows.Foundation.TypedEventHandler Attached; }; } From ba7b5aec79dcf1959e47568e3da144d7eeec9265 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 13:59:47 -0600 Subject: [PATCH 035/234] [silmarillion] Pull DetachTab into a method --- src/cascadia/TerminalApp/TerminalPage.cpp | 26 +++++++++++++---------- src/cascadia/TerminalApp/TerminalPage.h | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index a42446709f9..56002a18a45 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2031,6 +2031,20 @@ namespace winrt::TerminalApp::implementation return true; } + void TerminalPage::_DetachTab(const winrt::com_ptr& terminalTab) + { + // Collect all the content we're about to detach. + if (const auto rootPane = terminalTab->GetRootPane()) + { + rootPane->WalkTree([&](auto p) { + if (const auto& control{ p->GetTerminalControl() }) + { + _manager.Detach(control.ContentGuid()); + } + }); + } + } + bool TerminalPage::_MoveTab(MoveTabArgs args) { const auto windowId{ args.Window() }; @@ -2039,17 +2053,7 @@ namespace winrt::TerminalApp::implementation if (const auto terminalTab{ _GetFocusedTabImpl() }) { auto startupActions = terminalTab->BuildStartupActions(true); - - // Collect all the content we're about to detach. - if (const auto rootPane = terminalTab->GetRootPane()) - { - rootPane->WalkTree([&](auto p) { - if (const auto& control{ p->GetTerminalControl() }) - { - _manager.Detach(control.ContentGuid()); - } - }); - } + _DetachTab(terminalTab); auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; // Json::Value json{ Json::objectValue }; diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 23f1ac7895d..fb9d08fc540 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -495,6 +495,7 @@ namespace winrt::TerminalApp::implementation void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e); void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); + void _DetachTab(const winrt::com_ptr& terminalTab); #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp From 828695ee8a962b54bb95f8302fb259ace556d25b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 14:00:00 -0600 Subject: [PATCH 036/234] Detaching during tearout too --- src/cascadia/TerminalApp/TerminalPage.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 56002a18a45..73fec1023e8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4777,6 +4777,27 @@ namespace winrt::TerminalApp::implementation e.Data().Properties().Insert(L"content", winrt::box_value(str)); e.Data().RequestedOperation(DataPackageOperation::Move); + + e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak()](auto&&, auto &&) -> winrt::fire_and_forget { + auto tab = weakTab.get(); + auto page = weakThis.get(); + if (tab && page) + { + // TODO! this loop might be able to go outside the + // OperationCompleted. If we put if before the + // OperationCompleted, then we make sure we've got an extra + // reference to the content, if the operation _is_ completed + // before we hook up the Attached handlers. However,idk what + // happens then if the operation never happens. + // + // Collect all the content we're about to detach. + page->_DetachTab(tab); + + co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); + + page->_RemoveTab(*tab); + } + }); } } void TerminalPage::_onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, From 380f0295cadcffdef5314a73beed654cd13101dc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 13:42:54 -0600 Subject: [PATCH 037/234] [silmarillion] Update the validation for the moveTab action (cherry picked from commit a20201fff26589e6faf393c07b508e510dc0fc5c) --- src/cascadia/TerminalSettingsModel/ActionArgs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 6011dfc4496..80ef0c4f659 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -173,8 +173,8 @@ private: X(Windows::Foundation::IReference, Index, "index", false, nullptr) //////////////////////////////////////////////////////////////////////////////// -#define MOVE_TAB_ARGS(X) \ - X(MoveTabDirection, Direction, "direction", args->Direction() == MoveTabDirection::None, MoveTabDirection::None) \ +#define MOVE_TAB_ARGS(X) \ + X(MoveTabDirection, Direction, "direction", (args->Direction() == MoveTabDirection::None) && (args->Window().empty()), MoveTabDirection::None) \ X(winrt::hstring, Window, "window", false, L"") // Other ideas: From 11f6561153d05de5def2460c1ef862fbce150571 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 13:48:52 -0600 Subject: [PATCH 038/234] [silmarillion] Detach on moveTab, but not on drag It remains to be seen if this worked for movePane, but one step at a time (cherry picked from commit c282797cac2b7f35da5f0c3e44422e71ae2a6cb2) --- src/cascadia/TerminalApp/ContentManager.cpp | 22 +++++++++++++++++++ src/cascadia/TerminalApp/ContentManager.h | 8 +++++++ src/cascadia/TerminalApp/Pane.cpp | 7 +++--- src/cascadia/TerminalApp/TerminalPage.cpp | 16 ++++++++++++++ src/cascadia/TerminalApp/TerminalPage.idl | 1 + src/cascadia/TerminalControl/ControlCore.cpp | 3 ++- src/cascadia/TerminalControl/ControlCore.h | 2 ++ src/cascadia/TerminalControl/ControlCore.idl | 2 ++ .../TerminalControl/ControlInteractivity.cpp | 7 ++++++ .../TerminalControl/ControlInteractivity.h | 2 ++ .../TerminalControl/ControlInteractivity.idl | 1 + 11 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 1b9d5f68e4b..e32595927b0 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -26,6 +26,9 @@ namespace winrt } namespace winrt::TerminalApp::implementation { + // TODO! These all probably need to be weak_refs, don't they, so that the + // contentmanager doesn't just keep every control around indefinitely. + ControlInteractivity ContentManager::CreateCore(Microsoft::Terminal::Control::IControlSettings settings, IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) @@ -40,4 +43,23 @@ namespace winrt::TerminalApp::implementation { return _content.TryLookup(id); } + + void ContentManager::Detach(const winrt::guid& contentGuid) + { + if (const auto& content{ LookupCore(contentGuid) }) + { + content.Attached({ get_weak(), &ContentManager::_finalizeDetach }); + _recentlyDetachedContent.Insert(contentGuid, content); + } + } + + void ContentManager::_finalizeDetach(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable e) + { + if (const auto& content{ sender.try_as() }) + { + _recentlyDetachedContent.Remove(content.Id()); + } + } + } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index bd2725cac6e..5770dce8a91 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -17,9 +17,17 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); Microsoft::Terminal::Control::ControlInteractivity LookupCore(winrt::guid id); + void Detach(const winrt::guid& contentGuid); + private: Windows::Foundation::Collections::IMap _content{ winrt::multi_threaded_map() }; + Windows::Foundation::Collections::IMap _recentlyDetachedContent{ + winrt::multi_threaded_map() + }; + + void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable e); }; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 9b4cf7b76be..cdba83967e7 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1232,8 +1232,9 @@ void Pane::Shutdown() // TOODO! if we call Close here, on a control that was moved to another thread, then it's Dispatcher is no longer this thread, and we'll crash. // Acn we get away with _not_ calling Close? Seems like shutdown is only called for RemoveTab(TerminalTab), so theoretically, removing the old control tree from the UI tree will release the core, calling it's dtor, which will call Close itself. // Alternatively, we could try and see if there's only one strong ref to a ControlCore and just noop if there's more than one. - // - // _control.Close(); + // + // I'm bringing this back for a second. + _control.Close(); } else { @@ -1415,7 +1416,7 @@ void Pane::UpdateVisuals() // TODO! // Hey remember when we made a static brush reference so we didn't have to look these up each time? // well, that's on the wrong thread always now. Great work. - // + // // _borderFirst.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); // _borderSecond.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2b19d484bdb..03a1de0d989 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2035,6 +2035,18 @@ namespace winrt::TerminalApp::implementation if (const auto terminalTab{ _GetFocusedTabImpl() }) { auto startupActions = terminalTab->BuildStartupActions(true); + + // Collect all the content we're about to detach. + if (const auto rootPane = terminalTab->GetRootPane()) + { + rootPane->WalkTree([&](auto p) { + if (const auto& control{ p->GetTerminalControl() }) + { + _manager.Detach(control.ContentGuid()); + } + }); + } + auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; // Json::Value json{ Json::objectValue }; // SetValueForKey(json, "content", winRtActions); @@ -2044,6 +2056,8 @@ namespace winrt::TerminalApp::implementation auto request = winrt::make_self(args.Window(), str, 0); + + _RemoveTab(*terminalTab); _RequestMoveContentHandlers(*this, *request); return true; } @@ -2079,6 +2093,8 @@ namespace winrt::TerminalApp::implementation } } + // TODO! look at 87c840b381870e45bcc9e625fb88a8bdf5106420. That's what I was starting here + // Method Description: // - Split the focused pane either horizontally or vertically, and place the // given pane accordingly in the tree diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index efbcf344924..090b86cff42 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -14,6 +14,7 @@ namespace TerminalApp Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); Microsoft.Terminal.Control.ControlInteractivity LookupCore(Guid id); + void Detach(Guid id); } delegate void LastTabClosedEventArgs(); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 7a8918f7cfb..4c47efdd04c 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -231,9 +231,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { - _CloseTerminalRequestedHandlers(*this, nullptr); _settings->KeyBindings(keyBindings); _setupDispatcherAndCallbacks(); + + _AttachedHandlers(*this, nullptr); } bool ControlCore::Initialize(const double actualWidth, diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index cfffefa88a6..21e6343a071 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -236,6 +236,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); + + TYPED_EVENT(Attached, IInspectable, IInspectable); // clang-format on private: diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index f20ddea3604..8ecab5510d0 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -164,5 +164,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler UpdateSelectionMarkers; event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler CloseTerminalRequested; + + event Windows.Foundation.TypedEventHandler Attached; }; } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 72d45136b01..abb51661a49 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -47,6 +47,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation _guid = ::Microsoft::Console::Utils::CreateGuid(); _core = winrt::make_self(settings, unfocusedAppearance, connection); + + _core->Attached([weakThis = get_weak()](auto&&, auto&&) { + if (auto self{ weakThis.get() }) + { + self->_AttachedHandlers(*self, nullptr); + } + }); } winrt::guid ControlInteractivity::Id() diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 6cb6d4e1077..0afdc6eda3b 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -92,6 +92,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs); + TYPED_EVENT(Attached, 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 7af80c7e521..c0165645695 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -68,6 +68,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler ScrollPositionChanged; event Windows.Foundation.TypedEventHandler PasteFromClipboard; + event Windows.Foundation.TypedEventHandler Attached; }; } From c82051ffdd3260fb4862436b1b5da3a56b16d943 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 13:59:47 -0600 Subject: [PATCH 039/234] [silmarillion] Pull DetachTab into a method (cherry picked from commit ba7b5aec79dcf1959e47568e3da144d7eeec9265) --- src/cascadia/TerminalApp/TerminalPage.cpp | 26 +++++++++++++---------- src/cascadia/TerminalApp/TerminalPage.h | 2 ++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 03a1de0d989..d515007c1dc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2027,6 +2027,20 @@ namespace winrt::TerminalApp::implementation return true; } + void TerminalPage::_DetachTab(const winrt::com_ptr& terminalTab) + { + // Collect all the content we're about to detach. + if (const auto rootPane = terminalTab->GetRootPane()) + { + rootPane->WalkTree([&](auto p) { + if (const auto& control{ p->GetTerminalControl() }) + { + _manager.Detach(control.ContentGuid()); + } + }); + } + } + bool TerminalPage::_MoveTab(MoveTabArgs args) { const auto windowId{ args.Window() }; @@ -2035,17 +2049,7 @@ namespace winrt::TerminalApp::implementation if (const auto terminalTab{ _GetFocusedTabImpl() }) { auto startupActions = terminalTab->BuildStartupActions(true); - - // Collect all the content we're about to detach. - if (const auto rootPane = terminalTab->GetRootPane()) - { - rootPane->WalkTree([&](auto p) { - if (const auto& control{ p->GetTerminalControl() }) - { - _manager.Detach(control.ContentGuid()); - } - }); - } + _DetachTab(terminalTab); auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; // Json::Value json{ Json::objectValue }; diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 6094a4661b7..93fbca35615 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -492,6 +492,8 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args); + void _DetachTab(const winrt::com_ptr& terminalTab); + #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp #define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action); From ee63aff320a7c94c69f2c5431e1269909ee56e1e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Feb 2023 14:19:49 -0600 Subject: [PATCH 040/234] this fixes the selection crash. This makes sense - the original TermControl was kept alive by listeners to events on the Core, who's now on another thread --- src/cascadia/TerminalControl/TermControl.cpp | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 31172437978..19054c6cf1b 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -72,9 +72,9 @@ 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 }); + _core.ScrollPositionChanged({ get_weak(), &TermControl::_ScrollPositionChanged }); + _core.WarningBell({ get_weak(), &TermControl::_coreWarningBell }); + _core.CursorPositionChanged({ 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 }); @@ -82,16 +82,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation // 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.BackgroundColorChanged({ get_weak(), &TermControl::_coreBackgroundColorChanged }); + _core.FontSizeChanged({ get_weak(), &TermControl::_coreFontSizeChanged }); + _core.TransparencyChanged({ get_weak(), &TermControl::_coreTransparencyChanged }); + _core.RaiseNotice({ get_weak(), &TermControl::_coreRaisedNotice }); + _core.HoveredHyperlinkChanged({ get_weak(), &TermControl::_hoveredHyperlinkChanged }); + _core.FoundMatch({ get_weak(), &TermControl::_coreFoundMatch }); + _core.UpdateSelectionMarkers({ get_weak(), &TermControl::_updateSelectionMarkers }); + _core.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler }); + _interactivity.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler }); + _interactivity.ScrollPositionChanged({ get_weak(), &TermControl::_ScrollPositionChanged }); // 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 @@ -141,7 +141,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 }); // _interactivity.Attached([weakThis = get_weak()]{ // if (auto control{ weakThis.get() }; !control->_IsClosing()) From e2882055ee28c30c1f385bfb5b136f7dd438f1b0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 11:19:55 -0600 Subject: [PATCH 041/234] Interestingly, the order matters --- src/cascadia/TerminalSettingsModel/ActionArgs.h | 9 ++++++--- src/cascadia/TerminalSettingsModel/ActionArgs.idl | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 80ef0c4f659..e668f34e1cd 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -173,9 +173,12 @@ private: X(Windows::Foundation::IReference, Index, "index", false, nullptr) //////////////////////////////////////////////////////////////////////////////// -#define MOVE_TAB_ARGS(X) \ - X(MoveTabDirection, Direction, "direction", (args->Direction() == MoveTabDirection::None) && (args->Window().empty()), MoveTabDirection::None) \ - X(winrt::hstring, Window, "window", false, L"") +// Interestingly, the order MATTERS here. Window has to be BEFORE Direction, +// because otherwise we won't have parsed the Window yet when we validate the +// Direction. +#define MOVE_TAB_ARGS(X) \ + X(winrt::hstring, Window, "window", false, L"") \ + X(MoveTabDirection, Direction, "direction", (args->Direction() == MoveTabDirection::None) && (args->Window().empty()), MoveTabDirection::None) // Other ideas: // X(uint32_t, TabIndex, "index", false, 0) \ // target? source? diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 21cf07d4af9..4909784f1a5 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -279,7 +279,7 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass MoveTabArgs : IActionArgs { - MoveTabArgs(MoveTabDirection direction, String window); + MoveTabArgs(String window, MoveTabDirection direction); MoveTabDirection Direction { get; }; String Window { get; }; }; From c9ed8dc0420229f193d74356eabea43d76d5c795 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 11:20:43 -0600 Subject: [PATCH 042/234] When we detach a tab, Close()ing the control shouldn't close the content. --- src/cascadia/TerminalApp/ContentManager.cpp | 4 +- src/cascadia/TerminalApp/ContentManager.h | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.idl | 2 +- src/cascadia/TerminalControl/ControlCore.cpp | 31 +++++------ src/cascadia/TerminalControl/ControlCore.h | 1 + src/cascadia/TerminalControl/ControlCore.idl | 1 + src/cascadia/TerminalControl/TermControl.cpp | 54 ++++++++++++++------ src/cascadia/TerminalControl/TermControl.h | 5 +- src/cascadia/TerminalControl/TermControl.idl | 2 + 10 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index e32595927b0..8dd3d0b234f 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -44,10 +44,12 @@ namespace winrt::TerminalApp::implementation return _content.TryLookup(id); } - void ContentManager::Detach(const winrt::guid& contentGuid) + void ContentManager::Detach(const Microsoft::Terminal::Control::TermControl& control) { + const auto contentGuid{ control.ContentGuid() }; if (const auto& content{ LookupCore(contentGuid) }) { + control.Detach(); content.Attached({ get_weak(), &ContentManager::_finalizeDetach }); _recentlyDetachedContent.Insert(contentGuid, content); } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 5770dce8a91..19cbff05e32 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -17,7 +17,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); Microsoft::Terminal::Control::ControlInteractivity LookupCore(winrt::guid id); - void Detach(const winrt::guid& contentGuid); + void Detach(const Microsoft::Terminal::Control::TermControl& control); private: Windows::Foundation::Collections::IMap _content{ diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d515007c1dc..f433d3fc136 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2035,7 +2035,7 @@ namespace winrt::TerminalApp::implementation rootPane->WalkTree([&](auto p) { if (const auto& control{ p->GetTerminalControl() }) { - _manager.Detach(control.ContentGuid()); + _manager.Detach(control); } }); } diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 090b86cff42..3394ae94831 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -14,7 +14,7 @@ namespace TerminalApp Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); Microsoft.Terminal.Control.ControlInteractivity LookupCore(Guid id); - void Detach(Guid id); + void Detach(Microsoft.Terminal.Control.TermControl control); } delegate void LastTabClosedEventArgs(); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 4c47efdd04c..bdef78d9a48 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -979,18 +979,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::SizeChanged(const double width, const double height) { - // _refreshSizeUnderLock redraws the entire terminal. - // Don't call it if we don't have to. - if (_panelWidth == width && _panelHeight == height) - { - return; - } - - _panelWidth = width; - _panelHeight = height; - - auto lock = _terminal->LockForWriting(); - _refreshSizeUnderLock(); + SizeOrScaleChanged(width, height, _compositionScale); } void ControlCore::ScaleChanged(const double scale) @@ -999,19 +988,31 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return; } + SizeOrScaleChanged(_panelWidth, _panelHeight, scale); + } + void ControlCore::SizeOrScaleChanged(const double width, + const double height, + const double scale) + { // _refreshSizeUnderLock redraws the entire terminal. // Don't call it if we don't have to. - if (_compositionScale == scale) + if (_panelWidth == width && _panelHeight == height && _compositionScale == scale) { return; } + const auto oldScale = _compositionScale; + _panelWidth = width; + _panelHeight = height; _compositionScale = scale; auto lock = _terminal->LockForWriting(); - // _updateFont relies on the new _compositionScale set above - _updateFont(); + if (oldScale != scale) + { + // _updateFont relies on the new _compositionScale set above + _updateFont(); + } _refreshSizeUnderLock(); } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 21e6343a071..6cc6f3e6057 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -78,6 +78,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); + void SizeOrScaleChanged(const double width, const double height, const double scale); void AdjustFontSize(float fontSizeDelta); void ResetFontSize(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 8ecab5510d0..50db7124feb 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -110,6 +110,7 @@ namespace Microsoft.Terminal.Control void AdjustFontSize(Single fontSizeDelta); void SizeChanged(Double width, Double height); void ScaleChanged(Double scale); + void SizeOrScaleChanged(Double width, Double height, Double scale); void ToggleShaderEffects(); void ToggleReadOnlyMode(); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 19054c6cf1b..f9a6d1161f2 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -105,7 +105,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // least we don't need to quick return if the core was already // initialized - we still have state to set up, and resize the core // to the new swapchain size, etc. - if (_InitializeTerminal()) + if (_InitializeTerminal(false)) { // Only let this succeed once. _layoutUpdatedRevoker.revoke(); @@ -158,12 +158,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto term{ winrt::make_self(content) }; term->_AttachDxgiSwapChainToXaml(reinterpret_cast(term->_core.SwapChainHandle())); content.Reparent(keyBindings); - // if (const auto h{ reinterpret_cast(_core.SwapChainHandle()) }) - // { - // // Reparent will fire off a _core.CloseTerminalRequested, which the - // // old window will listen to and know to remove its old control, - // // leaving us as the owner. - // } + + // 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 + auto r = term->SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [term](auto /*s*/, auto /*e*/) { + // Replace the normal initialize routine with one that will allow up + // to complete initialization even though the Core was already + // initialized. + if (term->_InitializeTerminal(true)) + { + // Only let this succeed once. + term->_layoutUpdatedRevoker.revoke(); + } + }); + term->_layoutUpdatedRevoker.swap(r); + return *term; } @@ -843,7 +852,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation nativePanel->SetSwapChainHandle(swapChainHandle); } - bool TermControl::_InitializeTerminal() + bool TermControl::_InitializeTerminal(const bool reattach) { if (_initializedTerminal) { @@ -869,14 +878,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation // the first paint will be ignored! _core.RendererWarning({ get_weak(), &TermControl::_RendererWarning }); - const auto coreInitialized = _core.Initialize(panelWidth, - panelHeight, - panelScaleX); - if (!coreInitialized) + // If we're re-attaching an existing content, then we want to proceed even though the Terminal was already initialized. + if (!reattach) { - return false; + const auto coreInitialized = _core.Initialize(panelWidth, + panelHeight, + panelScaleX); + if (!coreInitialized) + { + return false; + } + _interactivity.Initialize(); + } + else + { + _core.SizeOrScaleChanged(panelWidth, panelHeight, panelScaleX); } - _interactivity.Initialize(); _core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); _core.EnablePainting(); @@ -2037,9 +2054,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation TSFInputControl().Close(); _autoScrollTimer.Stop(); - _core.Close(); + if (!_detached) + { + _core.Close(); + } } } + void TermControl::Detach() + { + _detached = true; + } // Method Description: // - Scrolls the viewport of the terminal and updates the scroll bar accordingly diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index e489025df52..9004c84bfa6 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -140,6 +140,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void AdjustOpacity(const double opacity, const bool relative); + void Detach(); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -222,6 +224,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool _showMarksInScrollbar{ false }; bool _isBackgroundLight{ false }; + bool _detached{ false }; inline bool _IsClosing() const noexcept { @@ -247,7 +250,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation static bool _isColorLight(til::color bg) noexcept; void _changeBackgroundOpacity(); - bool _InitializeTerminal(); + bool _InitializeTerminal(const bool reattach); void _SetFontSize(int fontSize); void _TappedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e); void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 1a77cffc9b5..23c5dc3f749 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -107,5 +107,7 @@ namespace Microsoft.Terminal.Control Windows.UI.Xaml.Media.Brush BackgroundBrush { get; }; void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode); + + void Detach(); } } From 1cef94e6962f36ecb18b3a804d62d8e8a34f2666 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 11:43:10 -0600 Subject: [PATCH 043/234] 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 f9a6d1161f2..489a9da5ed5 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({ get_weak(), &TermControl::_HyperlinkHandler }); _interactivity.ScrollPositionChanged({ get_weak(), &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 9004c84bfa6..87146222281 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -144,19 +144,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 eea6a7bcca96c7776d5b5228914a1267912be2d4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 12:55:44 -0600 Subject: [PATCH 044/234] 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/ControlCore.cpp | 4 +- src/cascadia/TerminalControl/TermControl.cpp | 73 ++++++++++---------- src/cascadia/TerminalControl/TermControl.h | 30 ++++++++ 3 files changed, 70 insertions(+), 37 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index bdef78d9a48..82c0d43cc55 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -233,7 +233,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _settings->KeyBindings(keyBindings); _setupDispatcherAndCallbacks(); - + const auto actualNewSize = _actualFont.GetSize(); + // Bubble this up, so our new control knows how big we want the font. + _FontSizeChangedHandlers(actualNewSize.width, actualNewSize.height, true); _AttachedHandlers(*this, nullptr); } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 489a9da5ed5..b6834bef743 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({ get_weak(), &TermControl::_ScrollPositionChanged }); - _core.WarningBell({ get_weak(), &TermControl::_coreWarningBell }); - _core.CursorPositionChanged({ get_weak(), &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({ get_weak(), &TermControl::_coreBackgroundColorChanged }); - _core.FontSizeChanged({ get_weak(), &TermControl::_coreFontSizeChanged }); - _core.TransparencyChanged({ get_weak(), &TermControl::_coreTransparencyChanged }); - _core.RaiseNotice({ get_weak(), &TermControl::_coreRaisedNotice }); - _core.HoveredHyperlinkChanged({ get_weak(), &TermControl::_hoveredHyperlinkChanged }); - _core.FoundMatch({ get_weak(), &TermControl::_coreFoundMatch }); - _core.UpdateSelectionMarkers({ get_weak(), &TermControl::_updateSelectionMarkers }); - _core.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler }); - _interactivity.OpenHyperlink({ get_weak(), &TermControl::_HyperlinkHandler }); - _interactivity.ScrollPositionChanged({ get_weak(), &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 @@ -153,13 +153,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _autoScrollTimer.Interval(AutoScrollUpdateInterval); _autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll }); - // _interactivity.Attached([weakThis = get_weak()]{ - // if (auto control{ weakThis.get() }; !control->_IsClosing()) - // { - // CloseTerminalRequested - // } - // }); - _ApplyUISettings(); } @@ -886,7 +879,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 }); // If we're re-attaching an existing content, then we want to proceed even though the Terminal was already initialized. if (!reattach) @@ -905,7 +898,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core.SizeOrScaleChanged(panelWidth, panelHeight, panelScaleX); } - _core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged }); + _revokers.SwapChainChanged = _core.SwapChainChanged(winrt::auto_revoke, { get_weak(), &TermControl::RenderEngineSwapChainChanged }); _core.EnablePainting(); auto bufferHeight = _core.BufferHeight(); @@ -2072,6 +2065,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } void TermControl::Detach() { + _revokers = {}; _detached = true; } @@ -3008,9 +3002,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 87146222281..270053258a7 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -327,6 +327,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 7bad8c9642662cae1a12329f07029f6937ea8ef4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 13:06:06 -0600 Subject: [PATCH 045/234] make pane brushes a member variable, which is good enough --- 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 cdba83967e7..bb4f5713a7b 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); @@ -1417,8 +1414,8 @@ void Pane::UpdateVisuals() // Hey remember when we made a static brush reference so we didn't have to look these up each time? // well, that's on the wrong thread always now. Great work. // - // _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: @@ -1870,7 +1867,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); @@ -2148,7 +2145,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)); @@ -3133,14 +3130,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"); @@ -3150,14 +3147,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 2caa0443d7c..d24984322ca 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 f433d3fc136..e544e5de9d0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4580,15 +4580,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 700aadcb155624dbd69ee196d0b5a795804a56f1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 14:48:20 -0600 Subject: [PATCH 046/234] [ainulindale] This I'm confident fixes some of the x-thread errors, but this doesn't fix the crash --- 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 | 3 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 8 +++++++- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 7 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e544e5de9d0..ca2a801f846 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -129,10 +129,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 93fbca35615..3d5acda23d9 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -73,7 +73,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 d52fa12bceb..dd46138fdb1 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -740,8 +740,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"); @@ -756,9 +763,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 b6b7eea94ca..26d3a45fa19 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -197,6 +197,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); + FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent); #ifdef UNIT_TESTING diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index dfd64bf412a..44ffbbae12b 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -136,6 +136,9 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SystemMenuChangeRequested; event Windows.Foundation.TypedEventHandler ShowWindowChanged; + event Windows.Foundation.TypedEventHandler SettingsChanged; + + event Windows.Foundation.TypedEventHandler RequestMoveContent; void AttachContent(String content, UInt32 tabIndex); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 21440902001..26888e96df2 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -453,7 +453,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 38b28e5bdbf1ede5bfab6589f5bd94c6e7cb9726 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 14:49:09 -0600 Subject: [PATCH 047/234] [foreward]? This is literally never used. This can go in main. --- .../TerminalApp/ActionPaletteItem.cpp | 22 ------------------- src/cascadia/TerminalSettingsModel/Command.h | 3 --- .../TerminalSettingsModel/Command.idl | 2 +- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/src/cascadia/TerminalApp/ActionPaletteItem.cpp b/src/cascadia/TerminalApp/ActionPaletteItem.cpp index 6331a619cbc..ecfd849c5de 100644 --- a/src/cascadia/TerminalApp/ActionPaletteItem.cpp +++ b/src/cascadia/TerminalApp/ActionPaletteItem.cpp @@ -24,27 +24,5 @@ namespace winrt::TerminalApp::implementation Name(command.Name()); KeyChordText(command.KeyChordText()); Icon(command.IconPath()); - - _commandChangedRevoker = command.PropertyChanged(winrt::auto_revoke, [weakThis{ get_weak() }](auto& sender, auto& e) { - auto item{ weakThis.get() }; - auto senderCommand{ sender.try_as() }; - - if (item && senderCommand) - { - auto changedProperty = e.PropertyName(); - if (changedProperty == L"Name") - { - item->Name(senderCommand.Name()); - } - else if (changedProperty == L"KeyChordText") - { - item->KeyChordText(senderCommand.KeyChordText()); - } - else if (changedProperty == L"IconPath") - { - item->Icon(senderCommand.IconPath()); - } - } - }); } } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 4f4dcbe2048..2eb3f6db56c 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -67,9 +67,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation hstring IconPath() const noexcept; void IconPath(const hstring& val); - winrt::Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker propertyChangedRevoker; - - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None); WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs); diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index 374a8ffded6..4a2d3c9f9bb 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -31,7 +31,7 @@ namespace Microsoft.Terminal.Settings.Model ShortcutAction Action; }; - [default_interface] runtimeclass Command : Windows.UI.Xaml.Data.INotifyPropertyChanged + [default_interface] runtimeclass Command { Command(); From e3fa46ff94ff5328b28ae76a68de78a6225a4774 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 15:07:03 -0600 Subject: [PATCH 048/234] This might all be wrong --- src/cascadia/TerminalApp/TerminalWindow.cpp | 3 +- .../TerminalSettingsModel/ActionMap.cpp | 8 +- .../CascadiaSettings.cpp | 8 +- .../TerminalSettingsModel/CascadiaSettings.h | 6 +- .../CascadiaSettingsSerialization.cpp | 118 +++++++++--------- .../TerminalSettingsModel/Command.cpp | 4 +- .../TerminalSettingsModel/EnumMappings.cpp | 4 +- .../TerminalSettingsModel/FolderEntry.cpp | 2 +- .../TerminalSettingsModel/GlobalAppSettings.h | 4 +- .../TerminalSettingsModel/JsonUtils.h | 4 +- .../TerminalSettingsModel/MTSMSettings.h | 2 +- 11 files changed, 82 insertions(+), 81 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index dd46138fdb1..9b991767499 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1164,7 +1164,8 @@ namespace winrt::TerminalApp::implementation bool TerminalWindow::AutoHideWindow() { - return _settings.GlobalSettings().AutoHideWindow(); + const auto& globals{ _settings.GlobalSettings() }; + return globals.AutoHideWindow(); } // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} void TerminalWindow::UpdateSettingsHandler(const winrt::IInspectable& /*sender*/, diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index a3a5124a3d0..82385df5b84 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -132,7 +132,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ALL_SHORTCUT_ACTIONS #undef ON_ALL_ACTIONS - _AvailableActionsCache = single_threaded_map(std::move(availableActions)); + _AvailableActionsCache = multi_threaded_map(std::move(availableActions)); } return _AvailableActionsCache.GetView(); } @@ -181,7 +181,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _PopulateNameMapWithSpecialCommands(nameMap); _PopulateNameMapWithStandardCommands(nameMap); - _NameMapCache = single_threaded_map(std::move(nameMap)); + _NameMapCache = multi_threaded_map(std::move(nameMap)); } return _NameMapCache.GetView(); } @@ -319,8 +319,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } } - _KeyBindingMapCache = single_threaded_map(std::move(keyBindingsMap)); - _GlobalHotkeysCache = single_threaded_map(std::move(globalHotkeys)); + _KeyBindingMapCache = multi_threaded_map(std::move(keyBindingsMap)); + _GlobalHotkeysCache = multi_threaded_map(std::move(globalHotkeys)); } // Method Description: diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 42ec1d28725..90501a9872b 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -101,8 +101,8 @@ Model::CascadiaSettings CascadiaSettings::Copy() const } settings->_globals = _globals->Copy(); - settings->_allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles)); - settings->_activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles)); + settings->_allProfiles = winrt::multi_threaded_observable_vector(std::move(allProfiles)); + settings->_activeProfiles = winrt::multi_threaded_observable_vector(std::move(activeProfiles)); } // load errors @@ -110,7 +110,7 @@ Model::CascadiaSettings CascadiaSettings::Copy() const std::vector warnings{ _warnings.Size() }; _warnings.GetMany(0, warnings); - settings->_warnings = winrt::single_threaded_vector(std::move(warnings)); + settings->_warnings = winrt::multi_threaded_vector(std::move(warnings)); settings->_loadError = _loadError; settings->_deserializationErrorMessage = _deserializationErrorMessage; } @@ -1157,7 +1157,7 @@ void CascadiaSettings::_refreshDefaultTerminals() }(); latch.wait(); - _defaultTerminals = winrt::single_threaded_observable_vector(std::move(result.first)); + _defaultTerminals = winrt::multi_threaded_observable_vector(std::move(result.first)); _currentDefaultTerminal = std::move(result.second); } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index ac9049b4b12..a84f4a242bf 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -170,11 +170,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring _hash; winrt::com_ptr _globals = winrt::make_self(); winrt::com_ptr _baseLayerProfile = winrt::make_self(); - winrt::Windows::Foundation::Collections::IObservableVector _allProfiles = winrt::single_threaded_observable_vector(); - winrt::Windows::Foundation::Collections::IObservableVector _activeProfiles = winrt::single_threaded_observable_vector(); + winrt::Windows::Foundation::Collections::IObservableVector _allProfiles = winrt::multi_threaded_observable_vector(); + winrt::Windows::Foundation::Collections::IObservableVector _activeProfiles = winrt::multi_threaded_observable_vector(); // load errors - winrt::Windows::Foundation::Collections::IVector _warnings = winrt::single_threaded_vector(); + winrt::Windows::Foundation::Collections::IVector _warnings = winrt::multi_threaded_vector(); winrt::Windows::Foundation::IReference _loadError; winrt::hstring _deserializationErrorMessage; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index bc597926773..28e03d72e32 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -255,57 +255,57 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() } } - // Search through app extensions. - // Gets the catalog of extensions with the name "com.microsoft.windows.terminal.settings". - // - // GH#12305: Open() can throw an 0x80070490 "Element not found.". - // It's unclear to me under which circumstances this happens as no one on the team - // was able to reproduce the user's issue, even if the application was run unpackaged. - // The error originates from `CallerIdentity::GetCallingProcessAppId` which returns E_NOT_SET. - // A comment can be found, reading: - // > Gets the "strong" AppId from the process token. This works for UWAs and Centennial apps, - // > strongly named processes where the AppId is stored securely in the process token. [...] - // > E_NOT_SET is returned for processes without strong AppIds. - IVectorView extensions; - try - { - const auto catalog = AppExtensionCatalog::Open(AppExtensionHostName); - extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); - } - CATCH_LOG(); - - if (!extensions) - { - return; - } - - for (const auto& ext : extensions) - { - const auto packageName = ext.Package().Id().FamilyName(); - if (_ignoredNamespaces.count(std::wstring_view{ packageName })) - { - continue; - } - - // Likewise, getting the public folder from an extension is an async operation. - auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync()); - if (!foundFolder) - { - continue; - } - - // the StorageFolder class has its own methods for obtaining the files within the folder - // however, all those methods are Async methods - // you may have noticed that we need to resort to clunky implementations for async operations - // (they are in extractValueFromTaskWithoutMainThreadAwait) - // so for now we will just take the folder path and access the files that way - const auto path = buildPath(foundFolder.Path(), FragmentsSubDirectory); - - if (std::filesystem::is_directory(path)) - { - parseAndLayerFragmentFiles(path, packageName); - } - } + // // Search through app extensions. + // // Gets the catalog of extensions with the name "com.microsoft.windows.terminal.settings". + // // + // // GH#12305: Open() can throw an 0x80070490 "Element not found.". + // // It's unclear to me under which circumstances this happens as no one on the team + // // was able to reproduce the user's issue, even if the application was run unpackaged. + // // The error originates from `CallerIdentity::GetCallingProcessAppId` which returns E_NOT_SET. + // // A comment can be found, reading: + // // > Gets the "strong" AppId from the process token. This works for UWAs and Centennial apps, + // // > strongly named processes where the AppId is stored securely in the process token. [...] + // // > E_NOT_SET is returned for processes without strong AppIds. + // IVectorView extensions; + // try + // { + // const auto catalog = AppExtensionCatalog::Open(AppExtensionHostName); + // extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); + // } + // CATCH_LOG(); + + // if (!extensions) + // { + // return; + // } + + // for (const auto& ext : extensions) + // { + // const auto packageName = ext.Package().Id().FamilyName(); + // if (_ignoredNamespaces.count(std::wstring_view{ packageName })) + // { + // continue; + // } + + // // Likewise, getting the public folder from an extension is an async operation. + // auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync()); + // if (!foundFolder) + // { + // continue; + // } + + // // the StorageFolder class has its own methods for obtaining the files within the folder + // // however, all those methods are Async methods + // // you may have noticed that we need to resort to clunky implementations for async operations + // // (they are in extractValueFromTaskWithoutMainThreadAwait) + // // so for now we will just take the folder path and access the files that way + // const auto path = buildPath(foundFolder.Path(), FragmentsSubDirectory); + + // if (std::filesystem::is_directory(path)) + // { + // parseAndLayerFragmentFiles(path, packageName); + // } + // } } // See FindFragmentsAndMergeIntoUserSettings. @@ -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, @@ -1103,9 +1103,9 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) : _globals = loader.userSettings.globals; _baseLayerProfile = loader.userSettings.baseLayerProfile; - _allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles)); - _activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles)); - _warnings = winrt::single_threaded_vector(std::move(warnings)); + _allProfiles = winrt::multi_threaded_observable_vector(std::move(allProfiles)); + _activeProfiles = winrt::multi_threaded_observable_vector(std::move(activeProfiles)); + _warnings = winrt::multi_threaded_vector(std::move(warnings)); _resolveDefaultProfile(); _resolveNewTabMenuProfiles(); @@ -1312,7 +1312,7 @@ void CascadiaSettings::_resolveNewTabMenuProfiles() const // We keep track of the "remaining profiles" - those that have not yet been resolved // in either a "profile" or "source" entry. They will possibly be assigned to a // "remainingProfiles" entry - auto remainingProfiles = single_threaded_map(std::move(remainingProfilesMap)); + auto remainingProfiles = multi_threaded_map(std::move(remainingProfilesMap)); // We call a recursive helper function to process the entries auto entries = _globals->NewTabMenu(); @@ -1426,7 +1426,7 @@ void CascadiaSettings::_resolveNewTabMenuProfilesSet(const IVector(entry.as()) }; - matchEntry->Profiles(single_threaded_map()); + matchEntry->Profiles(multi_threaded_map()); auto activeProfileCount = gsl::narrow_cast(_activeProfiles.Size()); for (auto profileIndex = 0; profileIndex < activeProfileCount; profileIndex++) diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 8387413ee77..af99f748bf9 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation command->_nestedCommand = _nestedCommand; if (HasNestedCommands()) { - command->_subcommands = winrt::single_threaded_map(); + command->_subcommands = winrt::multi_threaded_map(); for (auto kv : NestedCommands()) { const auto subCmd{ winrt::get_self(kv.Value()) }; @@ -261,7 +261,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (const auto nestedCommandsJson{ json[JsonKey(CommandsKey)] }) { // Initialize our list of subcommands. - result->_subcommands = winrt::single_threaded_map(); + result->_subcommands = winrt::multi_threaded_map(); result->_nestedCommand = true; auto nestedWarnings = Command::LayerJson(result->_subcommands, nestedCommandsJson); // It's possible that the nested commands have some warnings diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index 8b3d936a4a3..621c7c5b0c0 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -17,7 +17,7 @@ using namespace ::Microsoft::Terminal::Settings::Model; winrt::Windows::Foundation::Collections::IMap EnumMappings::name() \ { \ static IMap enumMap = []() { \ - auto map = single_threaded_map(); \ + auto map = multi_threaded_map(); \ for (auto [enumStr, enumVal] : JsonUtils::ConversionTrait::mappings) \ { \ map.Insert(winrt::to_hstring(enumStr), enumVal); \ @@ -54,7 +54,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::Windows::Foundation::Collections::IMap EnumMappings::FontWeight() { static auto enumMap = []() { - auto map = single_threaded_map(); + auto map = multi_threaded_map(); for (auto [enumStr, enumVal] : JsonUtils::ConversionTrait::mappings) { map.Insert(winrt::to_hstring(enumStr), enumVal); diff --git a/src/cascadia/TerminalSettingsModel/FolderEntry.cpp b/src/cascadia/TerminalSettingsModel/FolderEntry.cpp index 633f529a86a..108324547c1 100644 --- a/src/cascadia/TerminalSettingsModel/FolderEntry.cpp +++ b/src/cascadia/TerminalSettingsModel/FolderEntry.cpp @@ -62,7 +62,7 @@ IVector FolderEntry::Entries() const { // We filter the full list of entries from JSON to just include the // non-empty ones. - IVector result{ winrt::single_threaded_vector() }; + IVector result{ winrt::multi_threaded_vector() }; if (_Entries == nullptr) { return result; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 4a594948eba..d8717b42dc8 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -81,7 +81,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::com_ptr _actionMap{ winrt::make_self() }; std::vector _keybindingsWarnings; - Windows::Foundation::Collections::IMap _colorSchemes{ winrt::single_threaded_map() }; - Windows::Foundation::Collections::IMap _themes{ winrt::single_threaded_map() }; + Windows::Foundation::Collections::IMap _colorSchemes{ winrt::multi_threaded_map() }; + Windows::Foundation::Collections::IMap _themes{ winrt::multi_threaded_map() }; }; } diff --git a/src/cascadia/TerminalSettingsModel/JsonUtils.h b/src/cascadia/TerminalSettingsModel/JsonUtils.h index 693ac798462..6c189c121bf 100644 --- a/src/cascadia/TerminalSettingsModel/JsonUtils.h +++ b/src/cascadia/TerminalSettingsModel/JsonUtils.h @@ -481,7 +481,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils winrt::Windows::Foundation::Collections::IVector FromJson(const Json::Value& json) { ConversionTrait> trait; - return winrt::single_threaded_vector(std::move(trait.FromJson(json))); + return winrt::multi_threaded_vector(std::move(trait.FromJson(json))); } bool CanConvert(const Json::Value& json) const @@ -525,7 +525,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils GetValue(*it, val[winrt::to_hstring(it.name())], trait); } - return winrt::single_threaded_map(std::move(val)); + return winrt::multi_threaded_map(std::move(val)); } bool CanConvert(const Json::Value& json) const diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index cb0ed03e3f1..b791414ca12 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -62,7 +62,7 @@ Author(s): 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(winrt::Windows::Foundation::Collections::IVector, NewTabMenu, "newTabMenu", winrt::multi_threaded_vector({ Model::RemainingProfilesEntry{} })) #define MTSM_PROFILE_SETTINGS(X) \ X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \ From 81524ea71eef6e76c766853c7fcccbe87a235126 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 15:07:15 -0600 Subject: [PATCH 049/234] Revert "This might all be wrong" This reverts commit e3fa46ff94ff5328b28ae76a68de78a6225a4774. --- src/cascadia/TerminalApp/TerminalWindow.cpp | 3 +- .../TerminalSettingsModel/ActionMap.cpp | 8 +- .../CascadiaSettings.cpp | 8 +- .../TerminalSettingsModel/CascadiaSettings.h | 6 +- .../CascadiaSettingsSerialization.cpp | 118 +++++++++--------- .../TerminalSettingsModel/Command.cpp | 4 +- .../TerminalSettingsModel/EnumMappings.cpp | 4 +- .../TerminalSettingsModel/FolderEntry.cpp | 2 +- .../TerminalSettingsModel/GlobalAppSettings.h | 4 +- .../TerminalSettingsModel/JsonUtils.h | 4 +- .../TerminalSettingsModel/MTSMSettings.h | 2 +- 11 files changed, 81 insertions(+), 82 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 9b991767499..dd46138fdb1 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1164,8 +1164,7 @@ namespace winrt::TerminalApp::implementation bool TerminalWindow::AutoHideWindow() { - const auto& globals{ _settings.GlobalSettings() }; - return globals.AutoHideWindow(); + return _settings.GlobalSettings().AutoHideWindow(); } // TODO! Arg should be a SettingsLoadEventArgs{ result, warnings, error, settings} void TerminalWindow::UpdateSettingsHandler(const winrt::IInspectable& /*sender*/, diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index 82385df5b84..a3a5124a3d0 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -132,7 +132,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation ALL_SHORTCUT_ACTIONS #undef ON_ALL_ACTIONS - _AvailableActionsCache = multi_threaded_map(std::move(availableActions)); + _AvailableActionsCache = single_threaded_map(std::move(availableActions)); } return _AvailableActionsCache.GetView(); } @@ -181,7 +181,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _PopulateNameMapWithSpecialCommands(nameMap); _PopulateNameMapWithStandardCommands(nameMap); - _NameMapCache = multi_threaded_map(std::move(nameMap)); + _NameMapCache = single_threaded_map(std::move(nameMap)); } return _NameMapCache.GetView(); } @@ -319,8 +319,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } } - _KeyBindingMapCache = multi_threaded_map(std::move(keyBindingsMap)); - _GlobalHotkeysCache = multi_threaded_map(std::move(globalHotkeys)); + _KeyBindingMapCache = single_threaded_map(std::move(keyBindingsMap)); + _GlobalHotkeysCache = single_threaded_map(std::move(globalHotkeys)); } // Method Description: diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 90501a9872b..42ec1d28725 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -101,8 +101,8 @@ Model::CascadiaSettings CascadiaSettings::Copy() const } settings->_globals = _globals->Copy(); - settings->_allProfiles = winrt::multi_threaded_observable_vector(std::move(allProfiles)); - settings->_activeProfiles = winrt::multi_threaded_observable_vector(std::move(activeProfiles)); + settings->_allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles)); + settings->_activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles)); } // load errors @@ -110,7 +110,7 @@ Model::CascadiaSettings CascadiaSettings::Copy() const std::vector warnings{ _warnings.Size() }; _warnings.GetMany(0, warnings); - settings->_warnings = winrt::multi_threaded_vector(std::move(warnings)); + settings->_warnings = winrt::single_threaded_vector(std::move(warnings)); settings->_loadError = _loadError; settings->_deserializationErrorMessage = _deserializationErrorMessage; } @@ -1157,7 +1157,7 @@ void CascadiaSettings::_refreshDefaultTerminals() }(); latch.wait(); - _defaultTerminals = winrt::multi_threaded_observable_vector(std::move(result.first)); + _defaultTerminals = winrt::single_threaded_observable_vector(std::move(result.first)); _currentDefaultTerminal = std::move(result.second); } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index a84f4a242bf..ac9049b4b12 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -170,11 +170,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring _hash; winrt::com_ptr _globals = winrt::make_self(); winrt::com_ptr _baseLayerProfile = winrt::make_self(); - winrt::Windows::Foundation::Collections::IObservableVector _allProfiles = winrt::multi_threaded_observable_vector(); - winrt::Windows::Foundation::Collections::IObservableVector _activeProfiles = winrt::multi_threaded_observable_vector(); + winrt::Windows::Foundation::Collections::IObservableVector _allProfiles = winrt::single_threaded_observable_vector(); + winrt::Windows::Foundation::Collections::IObservableVector _activeProfiles = winrt::single_threaded_observable_vector(); // load errors - winrt::Windows::Foundation::Collections::IVector _warnings = winrt::multi_threaded_vector(); + winrt::Windows::Foundation::Collections::IVector _warnings = winrt::single_threaded_vector(); winrt::Windows::Foundation::IReference _loadError; winrt::hstring _deserializationErrorMessage; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 28e03d72e32..bc597926773 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -255,57 +255,57 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() } } - // // Search through app extensions. - // // Gets the catalog of extensions with the name "com.microsoft.windows.terminal.settings". - // // - // // GH#12305: Open() can throw an 0x80070490 "Element not found.". - // // It's unclear to me under which circumstances this happens as no one on the team - // // was able to reproduce the user's issue, even if the application was run unpackaged. - // // The error originates from `CallerIdentity::GetCallingProcessAppId` which returns E_NOT_SET. - // // A comment can be found, reading: - // // > Gets the "strong" AppId from the process token. This works for UWAs and Centennial apps, - // // > strongly named processes where the AppId is stored securely in the process token. [...] - // // > E_NOT_SET is returned for processes without strong AppIds. - // IVectorView extensions; - // try - // { - // const auto catalog = AppExtensionCatalog::Open(AppExtensionHostName); - // extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); - // } - // CATCH_LOG(); - - // if (!extensions) - // { - // return; - // } - - // for (const auto& ext : extensions) - // { - // const auto packageName = ext.Package().Id().FamilyName(); - // if (_ignoredNamespaces.count(std::wstring_view{ packageName })) - // { - // continue; - // } - - // // Likewise, getting the public folder from an extension is an async operation. - // auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync()); - // if (!foundFolder) - // { - // continue; - // } - - // // the StorageFolder class has its own methods for obtaining the files within the folder - // // however, all those methods are Async methods - // // you may have noticed that we need to resort to clunky implementations for async operations - // // (they are in extractValueFromTaskWithoutMainThreadAwait) - // // so for now we will just take the folder path and access the files that way - // const auto path = buildPath(foundFolder.Path(), FragmentsSubDirectory); - - // if (std::filesystem::is_directory(path)) - // { - // parseAndLayerFragmentFiles(path, packageName); - // } - // } + // Search through app extensions. + // Gets the catalog of extensions with the name "com.microsoft.windows.terminal.settings". + // + // GH#12305: Open() can throw an 0x80070490 "Element not found.". + // It's unclear to me under which circumstances this happens as no one on the team + // was able to reproduce the user's issue, even if the application was run unpackaged. + // The error originates from `CallerIdentity::GetCallingProcessAppId` which returns E_NOT_SET. + // A comment can be found, reading: + // > Gets the "strong" AppId from the process token. This works for UWAs and Centennial apps, + // > strongly named processes where the AppId is stored securely in the process token. [...] + // > E_NOT_SET is returned for processes without strong AppIds. + IVectorView extensions; + try + { + const auto catalog = AppExtensionCatalog::Open(AppExtensionHostName); + extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); + } + CATCH_LOG(); + + if (!extensions) + { + return; + } + + for (const auto& ext : extensions) + { + const auto packageName = ext.Package().Id().FamilyName(); + if (_ignoredNamespaces.count(std::wstring_view{ packageName })) + { + continue; + } + + // Likewise, getting the public folder from an extension is an async operation. + auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync()); + if (!foundFolder) + { + continue; + } + + // the StorageFolder class has its own methods for obtaining the files within the folder + // however, all those methods are Async methods + // you may have noticed that we need to resort to clunky implementations for async operations + // (they are in extractValueFromTaskWithoutMainThreadAwait) + // so for now we will just take the folder path and access the files that way + const auto path = buildPath(foundFolder.Path(), FragmentsSubDirectory); + + if (std::filesystem::is_directory(path)) + { + parseAndLayerFragmentFiles(path, packageName); + } + } } // See FindFragmentsAndMergeIntoUserSettings. @@ -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, @@ -1103,9 +1103,9 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) : _globals = loader.userSettings.globals; _baseLayerProfile = loader.userSettings.baseLayerProfile; - _allProfiles = winrt::multi_threaded_observable_vector(std::move(allProfiles)); - _activeProfiles = winrt::multi_threaded_observable_vector(std::move(activeProfiles)); - _warnings = winrt::multi_threaded_vector(std::move(warnings)); + _allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles)); + _activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles)); + _warnings = winrt::single_threaded_vector(std::move(warnings)); _resolveDefaultProfile(); _resolveNewTabMenuProfiles(); @@ -1312,7 +1312,7 @@ void CascadiaSettings::_resolveNewTabMenuProfiles() const // We keep track of the "remaining profiles" - those that have not yet been resolved // in either a "profile" or "source" entry. They will possibly be assigned to a // "remainingProfiles" entry - auto remainingProfiles = multi_threaded_map(std::move(remainingProfilesMap)); + auto remainingProfiles = single_threaded_map(std::move(remainingProfilesMap)); // We call a recursive helper function to process the entries auto entries = _globals->NewTabMenu(); @@ -1426,7 +1426,7 @@ void CascadiaSettings::_resolveNewTabMenuProfilesSet(const IVector(entry.as()) }; - matchEntry->Profiles(multi_threaded_map()); + matchEntry->Profiles(single_threaded_map()); auto activeProfileCount = gsl::narrow_cast(_activeProfiles.Size()); for (auto profileIndex = 0; profileIndex < activeProfileCount; profileIndex++) diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index af99f748bf9..8387413ee77 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation command->_nestedCommand = _nestedCommand; if (HasNestedCommands()) { - command->_subcommands = winrt::multi_threaded_map(); + command->_subcommands = winrt::single_threaded_map(); for (auto kv : NestedCommands()) { const auto subCmd{ winrt::get_self(kv.Value()) }; @@ -261,7 +261,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (const auto nestedCommandsJson{ json[JsonKey(CommandsKey)] }) { // Initialize our list of subcommands. - result->_subcommands = winrt::multi_threaded_map(); + result->_subcommands = winrt::single_threaded_map(); result->_nestedCommand = true; auto nestedWarnings = Command::LayerJson(result->_subcommands, nestedCommandsJson); // It's possible that the nested commands have some warnings diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index 621c7c5b0c0..8b3d936a4a3 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -17,7 +17,7 @@ using namespace ::Microsoft::Terminal::Settings::Model; winrt::Windows::Foundation::Collections::IMap EnumMappings::name() \ { \ static IMap enumMap = []() { \ - auto map = multi_threaded_map(); \ + auto map = single_threaded_map(); \ for (auto [enumStr, enumVal] : JsonUtils::ConversionTrait::mappings) \ { \ map.Insert(winrt::to_hstring(enumStr), enumVal); \ @@ -54,7 +54,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::Windows::Foundation::Collections::IMap EnumMappings::FontWeight() { static auto enumMap = []() { - auto map = multi_threaded_map(); + auto map = single_threaded_map(); for (auto [enumStr, enumVal] : JsonUtils::ConversionTrait::mappings) { map.Insert(winrt::to_hstring(enumStr), enumVal); diff --git a/src/cascadia/TerminalSettingsModel/FolderEntry.cpp b/src/cascadia/TerminalSettingsModel/FolderEntry.cpp index 108324547c1..633f529a86a 100644 --- a/src/cascadia/TerminalSettingsModel/FolderEntry.cpp +++ b/src/cascadia/TerminalSettingsModel/FolderEntry.cpp @@ -62,7 +62,7 @@ IVector FolderEntry::Entries() const { // We filter the full list of entries from JSON to just include the // non-empty ones. - IVector result{ winrt::multi_threaded_vector() }; + IVector result{ winrt::single_threaded_vector() }; if (_Entries == nullptr) { return result; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index d8717b42dc8..4a594948eba 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -81,7 +81,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::com_ptr _actionMap{ winrt::make_self() }; std::vector _keybindingsWarnings; - Windows::Foundation::Collections::IMap _colorSchemes{ winrt::multi_threaded_map() }; - Windows::Foundation::Collections::IMap _themes{ winrt::multi_threaded_map() }; + Windows::Foundation::Collections::IMap _colorSchemes{ winrt::single_threaded_map() }; + Windows::Foundation::Collections::IMap _themes{ winrt::single_threaded_map() }; }; } diff --git a/src/cascadia/TerminalSettingsModel/JsonUtils.h b/src/cascadia/TerminalSettingsModel/JsonUtils.h index 6c189c121bf..693ac798462 100644 --- a/src/cascadia/TerminalSettingsModel/JsonUtils.h +++ b/src/cascadia/TerminalSettingsModel/JsonUtils.h @@ -481,7 +481,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils winrt::Windows::Foundation::Collections::IVector FromJson(const Json::Value& json) { ConversionTrait> trait; - return winrt::multi_threaded_vector(std::move(trait.FromJson(json))); + return winrt::single_threaded_vector(std::move(trait.FromJson(json))); } bool CanConvert(const Json::Value& json) const @@ -525,7 +525,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils GetValue(*it, val[winrt::to_hstring(it.name())], trait); } - return winrt::multi_threaded_map(std::move(val)); + return winrt::single_threaded_map(std::move(val)); } bool CanConvert(const Json::Value& json) const diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index b791414ca12..cb0ed03e3f1 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -62,7 +62,7 @@ Author(s): 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::multi_threaded_vector({ Model::RemainingProfilesEntry{} })) + X(winrt::Windows::Foundation::Collections::IVector, NewTabMenu, "newTabMenu", winrt::single_threaded_vector({ Model::RemainingProfilesEntry{} })) #define MTSM_PROFILE_SETTINGS(X) \ X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \ From 2122eec18612ee8f06de8c48a3af3445fe6c28cc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 16:57:23 -0600 Subject: [PATCH 050/234] [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 --- 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 65098b5582b..fbe0a21c750 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 ca2a801f846..f68d56a8a2c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -99,36 +99,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; @@ -3316,50 +3286,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 @@ -3370,15 +3296,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 3d5acda23d9..b393c142771 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -300,10 +300,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 47336c0982ef556b6d91e49abf9ade89d0924806 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 3 Feb 2023 09:15:53 -0600 Subject: [PATCH 051/234] [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 052/234] [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 053/234] [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 054/234] [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 055/234] 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 056/234] 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 057/234] 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 058/234] 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 059/234] 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 060/234] 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 061/234] 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 062/234] 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 063/234] 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 064/234] 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 065/234] 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 066/234] 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 067/234] 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 068/234] 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 069/234] 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 070/234] 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 071/234] 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 072/234] 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 073/234] 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 074/234] 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 075/234] 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 076/234] 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 077/234] 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 078/234] 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 079/234] 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 080/234] 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 081/234] 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 082/234] 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 083/234] 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 084/234] 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 085/234] 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 086/234] 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 087/234] 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 088/234] 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 089/234] 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 090/234] 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 091/234] 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 092/234] 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 093/234] 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 094/234] 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 095/234] 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 4eba01f919951934b9b1f0a49fd4a3863c59959f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 16:08:35 -0600 Subject: [PATCH 096/234] [valaquenta] Lifetime manage controls --- src/cascadia/TerminalApp/ContentManager.cpp | 16 +++++++++++++--- src/cascadia/TerminalApp/ContentManager.h | 3 +++ src/cascadia/TerminalControl/ControlCore.idl | 1 - .../TerminalControl/ControlInteractivity.cpp | 9 +++++++++ .../TerminalControl/ControlInteractivity.h | 3 +++ .../TerminalControl/ControlInteractivity.idl | 3 +++ src/cascadia/TerminalControl/TermControl.cpp | 2 +- 7 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 8dd3d0b234f..19555c14e0f 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -27,14 +27,14 @@ namespace winrt namespace winrt::TerminalApp::implementation { // TODO! These all probably need to be weak_refs, don't they, so that the - // contentmanager doesn't just keep every control around indefinitely. + // ContentManager doesn't just keep every control around indefinitely. 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.Closed({ this, &ContentManager::_closedHandler }); _content.Insert(content.Id(), content); return content; } @@ -60,8 +60,18 @@ namespace winrt::TerminalApp::implementation { if (const auto& content{ sender.try_as() }) { - _recentlyDetachedContent.Remove(content.Id()); + _recentlyDetachedContent.TryRemove(content.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); + _recentlyDetachedContent.TryRemove(contentGuid); + } + } } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 19cbff05e32..6754509b784 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -29,5 +29,8 @@ namespace winrt::TerminalApp::implementation void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable e); + + 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 50db7124feb..cabd0550609 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -129,7 +129,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 abb51661a49..bb3b0837b92 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -92,6 +92,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 0afdc6eda3b..705fb469502 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; @@ -93,6 +95,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs); TYPED_EVENT(Attached, IInspectable, IInspectable); + 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 c0165645695..2be8dab8078 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -25,6 +25,7 @@ namespace Microsoft.Terminal.Control Guid Id { get; }; void Reparent(Microsoft.Terminal.Control.IKeyBindings keyBindings); + void Close(); InteractivityAutomationPeer OnCreateAutomationPeer(); @@ -68,6 +69,8 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler ScrollPositionChanged; event Windows.Foundation.TypedEventHandler PasteFromClipboard; + event Windows.Foundation.TypedEventHandler Closed; + event Windows.Foundation.TypedEventHandler Attached; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b6834bef743..0bcb9a4c7b4 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2059,7 +2059,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (!_detached) { - _core.Close(); + _interactivity.Close(); } } } From 7d903dea4a8af9e19fca1f56fb12e22fb58c9ef4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 17:13:59 -0600 Subject: [PATCH 097/234] [silmarillion] Fix moving a tab with panes. Now, the non-first panes don't rebuild themselves on move --- src/cascadia/TerminalApp/ContentManager.cpp | 3 ++- src/cascadia/TerminalApp/Pane.cpp | 2 +- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 19555c14e0f..d2c5707deb5 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -35,7 +35,8 @@ namespace winrt::TerminalApp::implementation { auto content = ControlInteractivity{ settings, unfocusedAppearance, connection }; content.Closed({ this, &ContentManager::_closedHandler }); - _content.Insert(content.Id(), content); + const auto& contentGuid{ content.Id() }; + _content.Insert(contentGuid, content); return content; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 4086687ffe7..004de268d0f 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -180,7 +180,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent) { // if we are a leaf then all there is to do is defer to the parent. - if (!asContent && _IsLeaf()) + if (_IsLeaf()) { if (_lastActive) { diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 6ca9981c898..e05e5521eac 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -442,7 +442,7 @@ namespace winrt::TerminalApp::implementation { // Give initial ids (0 for the child created with this tab, // 1 for the child after the first split. - auto state = _rootPane->BuildStartupActions(0, 1); + auto state = _rootPane->BuildStartupActions(0, 1, asContent); { ActionAndArgs newTabAction{}; From 77346000df21498af0c3453e422403f90da5fe4b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 16:08:35 -0600 Subject: [PATCH 098/234] [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 3988a1c4e518144562e06e8d4754172f2960f7d7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Feb 2023 17:13:59 -0600 Subject: [PATCH 099/234] [silmarillion] Fix moving a tab with panes. Now, the non-first panes don't rebuild themselves on move (cherry picked from commit 7d903dea4a8af9e19fca1f56fb12e22fb58c9ef4) --- src/cascadia/TerminalApp/ContentManager.cpp | 3 ++- src/cascadia/TerminalApp/Pane.cpp | 2 +- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 83e92e35ac6..a935b447544 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -35,7 +35,8 @@ namespace winrt::TerminalApp::implementation { auto content = ControlInteractivity{ settings, unfocusedAppearance, connection }; content.Closed({ this, &ContentManager::_closedHandler }); - _content.Insert(content.Id(), content); + const auto& contentGuid{ content.Id() }; + _content.Insert(contentGuid, content); return content; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 4086687ffe7..004de268d0f 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -180,7 +180,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent) { // if we are a leaf then all there is to do is defer to the parent. - if (!asContent && _IsLeaf()) + if (_IsLeaf()) { if (_lastActive) { diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 6ca9981c898..e05e5521eac 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -442,7 +442,7 @@ namespace winrt::TerminalApp::implementation { // Give initial ids (0 for the child created with this tab, // 1 for the child after the first split. - auto state = _rootPane->BuildStartupActions(0, 1); + auto state = _rootPane->BuildStartupActions(0, 1, asContent); { ActionAndArgs newTabAction{}; From 28735112d207032bc780d0913d479d0b257ee4e0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 7 Feb 2023 11:43:10 -0600 Subject: [PATCH 100/234] 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 101/234] 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 102/234] 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 103/234] 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 e5f98807e2c7775d876212afe686e6a86e001cc9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Feb 2023 11:34:00 -0600 Subject: [PATCH 104/234] cleanup to get ready for review --- src/cascadia/TerminalApp/ContentManager.cpp | 3 - src/cascadia/TerminalApp/TerminalPage.cpp | 78 ++++++++----------- src/cascadia/TerminalApp/TerminalPage.h | 5 +- src/cascadia/TerminalControl/TermControl.h | 12 --- .../TerminalSettingsModel/ActionArgs.cpp | 19 +++-- .../Resources/en-US/Resources.resw | 4 + 6 files changed, 53 insertions(+), 68 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index a935b447544..9ab3d6fbe29 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -26,9 +26,6 @@ namespace winrt } namespace winrt::TerminalApp::implementation { - // TODO! These all probably need to be weak_refs, don't they, so that the - // contentmanager doesn't just keep every control around indefinitely. - ControlInteractivity ContentManager::CreateCore(Microsoft::Terminal::Control::IControlSettings settings, IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index b096d75cebb..078375a089e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1873,8 +1873,8 @@ namespace winrt::TerminalApp::implementation // is the last remaining pane on a tab, that tab will be closed upon moving. // - No move will occur if the tabIdx is the same as the current tab, or if // the specified tab is not a host of terminals (such as the settings tab). - // Arguments: - // - TODO! + // - If the Window is specified, the pane will instead be detached and moved + // to the window with the given name/id. // Return Value: // - true if the pane was successfully moved to the new tab. bool TerminalPage::_MovePane(MovePaneArgs args) @@ -1892,33 +1892,16 @@ namespace winrt::TerminalApp::implementation if (!windowId.empty()) { if (const auto terminalTab{ _GetFocusedTabImpl() }) - { if (const auto pane{ terminalTab->GetActivePane() }) - { auto startupActions = pane->BuildStartupActions(0, 1, true); - auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions.args)) }; - // Json::Value json{ Json::objectValue }; - // SetValueForKey(json, "content", winRtActions); - // Json::StreamWriterBuilder wbuilder; - // auto str = Json::writeString(wbuilder, json); - auto str = ActionAndArgs::Serialize(winRtActions); - auto request = winrt::make_self(args.Window(), - str, - args.TabIndex()); - _RequestMoveContentHandlers(*this, *request); + _DetachPaneFromWindow(pane); + _MoveContent(startupActions.args, args.Window(), args.TabIndex()); + focusedTab->DetachPane(); return true; } } - - //if (const auto& control{ _GetActiveControl() }) - //{ - // const auto currentContentGuid{ control.ContentGuid() }; - // auto request = winrt::make_self(currentContentGuid, args); - // _RequestMovePaneHandlers(*this, *request); - // return true; - //} } // If we are trying to move from the current tab to the current tab do nothing. @@ -1951,20 +1934,37 @@ namespace winrt::TerminalApp::implementation return true; } - void TerminalPage::_DetachTab(const winrt::com_ptr& terminalTab) + void TerminalPage::_DetachPaneFromWindow(std::shared_ptr pane) + { + pane->WalkTree([&](auto p) { + if (const auto& control{ p->GetTerminalControl() }) + { + _manager.Detach(control); + } + }); + } + + void TerminalPage::_DetachTabFromWindow(const winrt::com_ptr& terminalTab) { // Collect all the content we're about to detach. if (const auto rootPane = terminalTab->GetRootPane()) { - rootPane->WalkTree([&](auto p) { - if (const auto& control{ p->GetTerminalControl() }) - { - _manager.Detach(control); - } - }); + _DetachPaneFromWindow(rootPane); } } + void TerminalPage::_MoveContent(std::vector& actions, + const winrt::hstring& windowName, + const uint32_t tabIndex) + { + const auto winRtActions{ winrt::single_threaded_vector(std::move(actions)) }; + const auto str{ ActionAndArgs::Serialize(winRtActions) }; + const auto request = winrt::make_self(windowName, + str, + tabIndex); + _RequestMoveContentHandlers(*this, *request); + } + bool TerminalPage::_MoveTab(MoveTabArgs args) { const auto windowId{ args.Window() }; @@ -1973,20 +1973,9 @@ namespace winrt::TerminalApp::implementation if (const auto terminalTab{ _GetFocusedTabImpl() }) { auto startupActions = terminalTab->BuildStartupActions(true); - _DetachTab(terminalTab); - - auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; - // Json::Value json{ Json::objectValue }; - // SetValueForKey(json, "content", winRtActions); - // Json::StreamWriterBuilder wbuilder; - // auto str = Json::writeString(wbuilder, json); - auto str = ActionAndArgs::Serialize(winRtActions); - auto request = winrt::make_self(args.Window(), - str, - 0); - + _DetachTabFromWindow(terminalTab); + _MoveContent(startupActions, args.Window(), 0); _RemoveTab(*terminalTab); - _RequestMoveContentHandlers(*this, *request); return true; } } @@ -2007,22 +1996,19 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content, uint32_t tabIndex) { - content; tabIndex; auto args = ActionAndArgs::Deserialize(content); // TODO! if the first action is a split pane and tabIndex > tabs.size, // then remove it and insert an equivalent newTab - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); // may need to go to the top of _createNewTabFromContent + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); for (const auto& action : args) { _actionDispatch->DoAction(action); } } - // TODO! look at 87c840b381870e45bcc9e625fb88a8bdf5106420. That's what I was starting here - // Method Description: // - Split the focused pane either horizontally or vertically, and place the // given pane accordingly in the tree diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 730b32a9f41..50a2a4a0421 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -478,8 +478,9 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args); - void _DetachTab(const winrt::com_ptr& terminalTab); - + void _DetachPaneFromWindow(std::shared_ptr pane); + void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); + void _MoveContent(std::vector& actions, const winrt::hstring& windowName, const uint32_t tabIndex); #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp #define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index c4e0a66c8da..9f16b3e02cf 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -148,18 +148,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // clang-format off WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); -<<<<<<< HEAD - 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); - -======= // 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); @@ -168,7 +157,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); ->>>>>>> dev/migrie/oop/3/valaquenta BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index d88077011fa..422c4d2ce5a 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -245,7 +245,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring MovePaneArgs::GenerateName() const { - // TODO! + if (!Window().empty()) + { + return winrt::hstring{ + fmt::format(L"{}, window:{}, tab index:{}", RS_(L"MovePaneCommandKey"), Window(), TabIndex()) + }; + } return winrt::hstring{ fmt::format(L"{}, tab index:{}", RS_(L"MovePaneCommandKey"), TabIndex()) }; @@ -649,6 +654,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring MoveTabArgs::GenerateName() const { + if (!Window().empty()) + { + return winrt::hstring{ + fmt::format(std::wstring_view(RS_(L"MoveTabToWindowCommandKey")), + Window()) + }; + } + winrt::hstring directionString; switch (Direction()) { @@ -663,10 +676,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation fmt::format(std::wstring_view(RS_(L"MoveTabCommandKey")), directionString) }; - - // TODO! - // return winrt::hstring{ - // fmt::format(L"{}, window:{}", RS_(L"MovePaneCommandKey"), Window()) } winrt::hstring ToggleCommandPaletteArgs::GenerateName() const diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 09998b9093f..a4ca72cc3a1 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -166,6 +166,10 @@ Move tab {0} {0} will be replaced with a "forward" / "backward" + + Move tab to window {0} + {0} will be replaced with a user-specified name of a window + forward From f10dfac20f8d8f8ef167a901ef3d39834e0c0401 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Feb 2023 12:45:35 -0600 Subject: [PATCH 105/234] some reliability when moving tabs that currently have output across the boundary --- src/cascadia/Remoting/Monarch.cpp | 24 +++- src/cascadia/TerminalApp/Pane.cpp | 11 +- src/cascadia/TerminalApp/TerminalPage.cpp | 125 +------------------ src/cascadia/TerminalControl/ControlCore.cpp | 37 ++++-- src/cascadia/TerminalControl/ControlCore.h | 2 + src/cascadia/TerminalControl/ControlCore.idl | 2 + src/cascadia/TerminalControl/TermControl.cpp | 24 ++-- src/cascadia/WindowsTerminal/AppHost.cpp | 24 ++-- src/cascadia/WindowsTerminal/AppHost.h | 9 ++ 9 files changed, 92 insertions(+), 166 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index d3201109e22..34298013bea 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1056,6 +1056,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::hstring content, uint32_t tabIndex) { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_MoveContent_Requested", + TraceLoggingWideString(window.c_str(), "window", "The name of the window we tried to move to"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + uint64_t windowId = _lookupPeasantIdForName(window); if (windowId == 0) { @@ -1063,7 +1069,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint32_t temp; if (!Utils::StringToUint(window.c_str(), temp)) { - return; + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_MoveContent_FailedToParseId", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } else { @@ -1075,10 +1084,21 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto request = winrt::make_self(content, tabIndex); targetPeasant.AttachContentToWindow(*request); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_MoveContent_Completed", + TraceLoggingInt64(windowId, "windowId", "The ID of the peasant which we sent the content to"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); } else { - /*TODO! log */ + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_MoveContent_NoWindow", + TraceLoggingInt64(windowId, "windowId", "We could not find a peasant with this ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // TODO! create a window for that name / ID? } } } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 004de268d0f..57f4616c4c3 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -108,7 +108,9 @@ Pane::Pane(std::shared_ptr first, // - Extract the terminal settings from the current (leaf) pane's control // to be used to create an equivalent control // Arguments: -// - +// - asContent: when true, we're trying to serialize this pane for moving across +// windows. In that case, we'll need to fill in the content guid for our new +// terminal args. // Return Value: // - Arguments appropriate for a SplitPane or NewTab action NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const @@ -156,6 +158,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // object. That would work for schemes set by the Terminal, but not ones set // by VT, but that seems good enough. + // Only fill in the ContentGuid if absolutely needed. if (asContent) { args.ContentGuid(_control.ContentGuid()); @@ -172,7 +175,6 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // Arguments: // - currentId: the id to use for the current/first pane // - nextId: the id to use for a new pane if we split -// - asContent: TODO! // Return Value: // - The state from building the startup actions, includes a vector of commands, // the original root pane, the id of the focused pane, and the number of panes @@ -1226,11 +1228,6 @@ void Pane::Shutdown() if (_IsLeaf()) { - // TOODO! if we call Close here, on a control that was moved to another thread, then it's Dispatcher is no longer this thread, and we'll crash. - // Acn we get away with _not_ calling Close? Seems like shutdown is only called for RemoveTab(TerminalTab), so theoretically, removing the old control tree from the UI tree will release the core, calling it's dtor, which will call Close itself. - // Alternatively, we could try and see if there's only one strong ref to a ControlCore and just noop if there's more than one. - // - // I'm bringing this back for a second. _control.Close(); } else diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 078375a089e..82d41e9ae6b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2741,129 +2741,6 @@ namespace winrt::TerminalApp::implementation } } - // <<<<<<< HEAD - // ======= - // static wil::unique_process_information _createHostClassProcess(const winrt::guid& g) - // { - // auto guidStr{ ::Microsoft::Console::Utils::GuidToString(g) }; - - // // Create an event that the content process will use to signal it is - // // ready to go. We won't need the event after this function, so the - // // unique_event will clean up our handle when we leave this scope. The - // // ContentProcess is responsible for cleaning up its own handle. - // wil::unique_event ev{ CreateEvent(nullptr, true, false, nullptr /*L"contentProcessStarted"*/) }; - // // Make sure to mark this handle as inheritable! Even with - // // bInheritHandles=true, this is only inherited when it's explicitly - // // allowed to be. - // SetHandleInformation(ev.get(), HANDLE_FLAG_INHERIT, 1); - - // // god bless, fmt::format will format a HANDLE like `0xa80` - // std::wstring commandline{ - // fmt::format(L"WindowsTerminal.exe --content {} --signal {}", guidStr, ev.get()) - // }; - - // STARTUPINFO siOne{ 0 }; - // siOne.cb = sizeof(STARTUPINFOW); - // wil::unique_process_information piOne; - // auto succeeded = CreateProcessW( - // nullptr, - // commandline.data(), - // nullptr, // lpProcessAttributes - // nullptr, // lpThreadAttributes - // true, // bInheritHandles - // CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags - // nullptr, // lpEnvironment - // nullptr, // startingDirectory - // &siOne, // lpStartupInfo - // &piOne // lpProcessInformation - // ); - // THROW_IF_WIN32_BOOL_FALSE(succeeded); - - // // Wait for the child process to signal that they're ready. - // WaitForSingleObject(ev.get(), INFINITE); - - // return std::move(piOne); - // } - - // PreparedContent TerminalPage::_prepareContentProc(const NewTerminalArgs& newTerminalArgs, - // const bool duplicate) - // { - // PreparedContent preppedContent; - // _evaluateSettings(newTerminalArgs, duplicate, preppedContent.controlSettings, preppedContent.profile); - // preppedContent.initContentProc = (newTerminalArgs && newTerminalArgs.ContentGuid() != winrt::guid{}) ? - // _AttachToContentProcess(newTerminalArgs.ContentGuid()) : - // _CreateNewContentProcess(preppedContent.profile, preppedContent.controlSettings); - // return preppedContent; - // } - - // Windows::Foundation::IAsyncOperation TerminalPage::_CreateNewContentProcess(Profile profile, - // TerminalSettingsCreateResult settings) - // { - // co_await winrt::resume_background(); - // winrt::guid contentGuid{ ::Microsoft::Console::Utils::CreateGuid() }; - // // Spawn a wt.exe, with the guid on the commandline - // auto piContentProcess{ _createHostClassProcess(contentGuid) }; - - // // DebugBreak(); - - // // THIS MUST TAKE PLACE AFTER _createHostClassProcess. - // // * If we're creating a new OOP control, _createHostClassProcess will - // // spawn the process that will actually host the ContentProcess - // // object. - // // * If we're attaching, then that process already exists. - // ContentProcess content{ nullptr }; - // try - // { - // content = create_instance(contentGuid, CLSCTX_LOCAL_SERVER); - // } - // catch (winrt::hresult_error hr) - // { - // co_return nullptr; - // } - - // if (content == nullptr) - // { - // co_return nullptr; - // } - - // TerminalConnection::ConnectionInformation connectInfo{ _CreateConnectionInfoFromSettings(profile, settings.DefaultSettings()) }; - - // // Init the content proc with the focused/unfocused pair - // if (!content.Initialize(settings.DefaultSettings(), settings.UnfocusedSettings(), connectInfo)) - // { - // co_return nullptr; - // } - - // co_return content; - // } - - // Windows::Foundation::IAsyncOperation TerminalPage::_AttachToContentProcess(const winrt::guid contentGuid) - // { - // ContentProcess content{ nullptr }; - // try - // { - // content = create_instance(contentGuid, CLSCTX_LOCAL_SERVER); - // } - // catch (winrt::hresult_error hr) - // { - // } - // co_return content; - // } - - // // INVARIANT: Must be called on UI thread! - // std::shared_ptr TerminalPage::_makePaneFromContent(ContentProcess content, - // TerminalSettingsCreateResult controlSettings, - // Profile profile) - // { - // // Create the XAML control that will be attached to the content process. - // // We're not passing in a connection, because the contentGuid will be used instead - // const auto control = _InitControl(controlSettings, content.Guid()); - // _RegisterTerminalEvents(control); - - // return std::make_shared(profile, control); - // } - - // >>>>>>> 1f2bb760e (Pane officially opened via the serialized actions, via across the process boundary) TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const ITerminalConnection& connection) { // Do any initialization that needs to apply to _every_ TermControl we @@ -2896,6 +2773,8 @@ namespace winrt::TerminalApp::implementation term.WindowVisibilityChanged(_visible); } + // Even in the case of re-attaching content from another window, this + // will correctly update the control's owning HWND if (_hostingHwnd.has_value()) { term.OwningHwnd(reinterpret_cast(*_hostingHwnd)); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 82c0d43cc55..9eb81fc8b6f 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -229,6 +229,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } + void ControlCore::Detach() + { + _tsfTryRedrawCanvas.reset(); + _updatePatternLocations.reset(); + _updateScrollBar.reset(); + } + void ControlCore::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { _settings->KeyBindings(keyBindings); @@ -588,7 +595,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // itself - it was initiated by the mouse wheel, or the scrollbar. _terminal->UserScrollViewport(viewTop); - (*_updatePatternLocations)(); + if (_updatePatternLocations) + { + (*_updatePatternLocations)(); + } } void ControlCore::AdjustOpacity(const double adjustment) @@ -1375,7 +1385,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto update{ winrt::make(viewTop, viewHeight, bufferSize) }; - if (!_inUnitTests) + if (!_inUnitTests && _updateScrollBar) { _updateScrollBar->Run(update); } @@ -1385,14 +1395,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Additionally, start the throttled update of where our links are. - (*_updatePatternLocations)(); + + if (_updatePatternLocations) + { + (*_updatePatternLocations)(); + } } void ControlCore::_terminalCursorPositionChanged() { // When the buffer's cursor moves, start the throttled func to // eventually dispatch a CursorPositionChanged event. - _tsfTryRedrawCanvas->Run(); + if (_tsfTryRedrawCanvas) + { + _tsfTryRedrawCanvas->Run(); + } } void ControlCore::_terminalTaskbarProgressChanged() @@ -1417,15 +1434,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - duration - How long the note should be sustained (in microseconds). void ControlCore::_terminalPlayMidiNote(const int noteNumber, const int velocity, const std::chrono::microseconds duration) { - // TODO! In the OOP2 branches, I left a comment about intentionally conflicting here. // The UI thread might try to acquire the console lock from time to time. // --> Unlock it, so the UI doesn't hang while we're busy. const auto suspension = _terminal->SuspendLock(); - // TODO! GH#1256 This is intentionally here to conflict with https://github.com/microsoft/terminal/pull/13471#pullrequestreview-1039353718 - // When we do tearout, we'll need to also recreate the midi thing - - // TODO! I don't know if taht ^ comment is still relevant, but I'll leave it here in case it is. - // This call will block for the duration, unless shutdown early. _midiAudio.PlayNote(reinterpret_cast(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast(duration)); } @@ -1699,7 +1710,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation _terminal->Write(hstr); // Start the throttled update of where our hyperlinks are. - (*_updatePatternLocations)(); + if (_updatePatternLocations) + { + (*_updatePatternLocations)(); + } } catch (...) { @@ -1972,7 +1986,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (owner != _owningHwnd && _connection) { - // TODO GH#1256 change the midi HWND too if (auto conpty{ _connection.try_as() }) { conpty.ReparentWindow(owner); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 6cc6f3e6057..a6f572b22f1 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -63,6 +63,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation const double compositionScale); void EnablePainting(); + void Detach(); + void UpdateSettings(const Control::IControlSettings& settings, const IControlAppearance& newAppearance); void ApplyAppearance(const bool& focused); Control::IControlSettings Settings() { return *_settings; }; diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index cabd0550609..d69923c5f6f 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -71,6 +71,8 @@ namespace Microsoft.Terminal.Control void UpdateSettings(IControlSettings settings, IControlAppearance appearance); void ApplyAppearance(Boolean focused); + void Detach(); + IControlSettings Settings { get; }; IControlAppearance FocusedAppearance { get; }; IControlAppearance UnfocusedAppearance { get; }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index fb2bb7796c0..e66e2eeb08d 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -69,13 +69,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core = _interactivity.Core(); - // These events might all be triggered by the connection, but that - // should be drained and closed before we complete destruction. So these - // are safe. - _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. _revokers.RendererEnteredErrorState = _core.RendererEnteredErrorState(winrt::auto_revoke, { get_weak(), &TermControl::_RendererEnteredErrorState }); @@ -111,11 +104,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation // in any layout change chain. That gives us great flexibility in finding the right point // at which to initialize our renderer (and our terminal). // Any earlier than the last layout update and we may not know the terminal's starting size. - - // TODO! This certainly needs to be adapted for reparenting. At - // least we don't need to quick return if the core was already - // initialized - we still have state to set up, and resize the core - // to the new swapchain size, etc. if (_InitializeTerminal(false)) { // Only let this succeed once. @@ -150,6 +138,17 @@ 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. + // + // NOTE: _ScrollPositionChanged has to be registered after we set up the + // _updateScrollBar func. Otherwise, we could get a callback from an + // attached content before we set up the throttled func, and that'll A/V + _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 }); + static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000)); _autoScrollTimer.Interval(AutoScrollUpdateInterval); _autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll }); @@ -2069,6 +2068,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::Detach() { _revokers = {}; + _core.Detach(); _detached = true; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index fa2395ef2b5..3d1e348c4bb 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -279,10 +279,7 @@ void AppHost::_HandleCommandlineArgs() _windowLogic.WindowName(_peasant.WindowName()); _windowLogic.WindowId(_peasant.GetID()); - // TODO! add revoker - _peasant.AttachRequested([this](auto&&, Remoting::AttachRequest args) { - _windowLogic.AttachContent(args.Content(), args.TabIndex()); - }); + _revokers.AttachRequested = _peasant.AttachRequested(winrt::auto_revoke, { this, &AppHost::_handleAttach }); } } @@ -391,12 +388,7 @@ void AppHost::Initialize() _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 }); - - // TODO! revoker - // TODO! move to member method - _windowLogic.RequestMoveContent([this](auto&&, winrt::TerminalApp::RequestMoveContentArgs args) { - _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex()); - }); + _revokers.RequestMoveContent = _windowLogic.RequestMoveContent(winrt::auto_revoke, { this, &AppHost::_handleMoveContent }); // BODGY // On certain builds of Windows, when Terminal is set as the default @@ -1211,3 +1203,15 @@ winrt::TerminalApp::TerminalWindow AppHost::Logic() { return _windowLogic; } + +void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& /*sender*/, + winrt::TerminalApp::RequestMoveContentArgs args) +{ + _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex()); +} + +void AppHost::_handleAttach(const winrt::Windows::Foundation::IInspectable& /*sender*/, + winrt::Microsoft::Terminal::Remoting::AttachRequest args) +{ + _windowLogic.AttachContent(args.Content(), args.TabIndex()); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index fc4bb0c7401..6d774dd5ca6 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -118,6 +118,11 @@ class AppHost void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); + void _handleMoveContent(const winrt::Windows::Foundation::IInspectable& sender, + winrt::TerminalApp::RequestMoveContentArgs args); + void _handleAttach(const winrt::Windows::Foundation::IInspectable& sender, + winrt::Microsoft::Terminal::Remoting::AttachRequest args); + winrt::event_token _GetWindowLayoutRequestedToken; // Helper struct. By putting these all into one struct, we can revoke them @@ -131,6 +136,8 @@ 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::Microsoft::Terminal::Remoting::Peasant::AttachRequested_revoker AttachRequested; + winrt::TerminalApp::TerminalWindow::CloseRequested_revoker CloseRequested; winrt::TerminalApp::TerminalWindow::RequestedThemeChanged_revoker RequestedThemeChanged; winrt::TerminalApp::TerminalWindow::FullscreenChanged_revoker FullscreenChanged; @@ -149,8 +156,10 @@ class AppHost winrt::TerminalApp::TerminalWindow::OpenSystemMenu_revoker OpenSystemMenu; winrt::TerminalApp::TerminalWindow::QuitRequested_revoker QuitRequested; winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged; + winrt::TerminalApp::TerminalWindow::RequestMoveContent_revoker RequestMoveContent; 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; From 118bffa6cdcbd1144e267f863ab9b29202530ae9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Feb 2023 15:51:18 -0600 Subject: [PATCH 106/234] 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 107/234] 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 108/234] 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 109/234] 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 110/234] 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 111/234] 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 112/234] 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 113/234] 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 0e0d8572a3b4b05438b31fc0b66def159154c4ec Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Feb 2023 10:21:18 -0600 Subject: [PATCH 114/234] Select the right tab before spliting it; movePane with a single pane works again --- src/cascadia/TerminalApp/Pane.cpp | 40 +++++- src/cascadia/TerminalApp/Pane.h | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 148 ++++++++-------------- 3 files changed, 86 insertions(+), 104 deletions(-) diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 57f4616c4c3..5bce40b2be5 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -179,17 +179,28 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // - The state from building the startup actions, includes a vector of commands, // the original root pane, the id of the focused pane, and the number of panes // created. -Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent) +Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent, const bool asMovePane) { + // TODO! I f'ed something up here recently. Just moving a single pane no longer seems to work. + // + // If we call this directly on a leaf, then CURRENTLY, we'll return {} for + // the actions. Moving that will just move nothing, cause obviously. + // + // But this all works fine for a _tab_, where there's a parent Tab to build + // a newTab action first. + // + // So if we're moving a pane, as content, then we need to special case here where even moving just us returns a splitPane + // if we are a leaf then all there is to do is defer to the parent. - if (_IsLeaf()) + if (!asMovePane && _IsLeaf()) { if (_lastActive) { - return { {}, shared_from_this(), currentId, 0 }; + // empty args, this is the first pane, currentId is + return { .args = {}, .firstPane = shared_from_this(), .focusedPaneId = currentId, .panesCreated = 0 }; } - return { {}, shared_from_this(), std::nullopt, 0 }; + return { .args = {}, .firstPane = shared_from_this(), .focusedPaneId = std::nullopt, .panesCreated = 0 }; } auto buildSplitPane = [&](auto newPane) { @@ -208,7 +219,12 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n if (asContent && _IsLeaf()) { - return { { buildSplitPane(shared_from_this()) }, shared_from_this(), currentId, 1 }; + return { + .args = { buildSplitPane(shared_from_this()) }, + .firstPane = shared_from_this(), + .focusedPaneId = currentId, + .panesCreated = 1 + }; } auto buildMoveFocus = [](auto direction) { @@ -237,7 +253,12 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n focusedPaneId = nextId; } - return { { actionAndArgs }, _firstChild, focusedPaneId, 1 }; + return { + .args = { actionAndArgs }, + .firstPane = _firstChild, + .focusedPaneId = focusedPaneId, + .panesCreated = 1 + }; } // We now need to execute the commands for each side of the tree @@ -274,7 +295,12 @@ Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t n // mutually exclusive. const auto focusedPaneId = firstState.focusedPaneId.has_value() ? firstState.focusedPaneId : secondState.focusedPaneId; - return { actions, firstState.firstPane, focusedPaneId, firstState.panesCreated + secondState.panesCreated + 1 }; + return { + .args = { actions }, + .firstPane = firstState.firstPane, + .focusedPaneId = focusedPaneId, + .panesCreated = firstState.panesCreated + secondState.panesCreated + 1 + }; } // Method Description: diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index d24984322ca..bf4ca4f312c 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -91,7 +91,7 @@ class Pane : public std::enable_shared_from_this std::optional focusedPaneId; uint32_t panesCreated; }; - BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false); + BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false, const bool asMovePane = false); winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const; void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 82d41e9ae6b..2674f4d13cb 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1895,7 +1895,7 @@ namespace winrt::TerminalApp::implementation { if (const auto pane{ terminalTab->GetActivePane() }) { - auto startupActions = pane->BuildStartupActions(0, 1, true); + auto startupActions = pane->BuildStartupActions(0, 1, true, true); _DetachPaneFromWindow(pane); _MoveContent(startupActions.args, args.Window(), args.TabIndex()); focusedTab->DetachPane(); @@ -1996,13 +1996,46 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content, uint32_t tabIndex) { - tabIndex; - auto args = ActionAndArgs::Deserialize(content); - // TODO! if the first action is a split pane and tabIndex > tabs.size, - // then remove it and insert an equivalent newTab + // If the first action is a split pane and tabIndex > tabs.size, + // then remove it and insert an equivalent newTab action instead. + // Otherwise, focus the tab they requested before starting the split. + if (args == nullptr || + args.Size() == 0) + { + co_return; + } + + // Switch to the UI thread before selecting a tab or dispatching actions. co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); + + const auto& firstAction = args.GetAt(0); + const bool firstIsSplitPane{ firstAction.Action() == ShortcutAction::SplitPane }; + + // splitPane allows the user to specify which tab to split. in that + // case, split specifically the requested pane, If there's not enough + // tabs, then just turn this pane into a new tab. If the first action + // is newTab, the index is always going to be 0, so don't do anything in + // that case. + if (firstIsSplitPane && tabIndex < _tabs.Size()) + { + _SelectTab(tabIndex); + } + else + { + const auto& firstAction = args.GetAt(0); + if (firstAction.Action() == ShortcutAction::SplitPane) + { + // Create the equivalent NewTab action. + const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, + Settings::Model::NewTabArgs(firstAction.Args() ? + firstAction.Args().try_as().TerminalArgs() : + nullptr) }; + args.SetAt(0, newAction); + } + } + for (const auto& action : args) { _actionDispatch->DoAction(action); @@ -2103,88 +2136,6 @@ namespace winrt::TerminalApp::implementation } } - // <<<<<<< HEAD - // ======= - // winrt::fire_and_forget TerminalPage::_asyncSplitPaneActiveTab(const SplitDirection splitDirection, - // const float splitSize, - // PreparedContent preppedContent) - // { - // // _GetFocusedTabImpl requires us to be on the UI thread - // co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); - // auto focusedTab{ _GetFocusedTabImpl() }; - - // // Clever hack for a crash in startup, with multiple sub-commands. Say - // // you have the following commandline: - // // - // // wtd nt -p "elevated cmd" ; sp -p "elevated cmd" ; sp -p "Command Prompt" - // // - // // Where "elevated cmd" is an elevated profile. - // // - // // In that scenario, we won't dump off the commandline immediately to an - // // elevated window, because it's got the final unelevated split in it. - // // However, when we get to that command, there won't be a tab yet. So - // // we'd crash right about here. - // // - // // Instead, let's just promote this first split to be a tab instead. - // // Crash avoided, and we don't need to worry about inserting a new-tab - // // command in at the start. - // if (!focusedTab) - // { - // if (_tabs.Size() == 0) - // { - // _createNewTabFromContent(preppedContent); - // } - // else - // { - // // The focused tab isn't a terminal tab - // co_return; - // } - // } - // else - // { - // _asyncSplitPaneOnTab(focusedTab, splitDirection, splitSize, preppedContent); - // } - // } - // winrt::fire_and_forget TerminalPage::_asyncSplitPaneOnTab(winrt::com_ptr tab, - // const SplitDirection splitDirection, - // const float splitSize, - // PreparedContent preppedContent) - // { - // // calculate split type - // const auto contentWidth = ::base::saturated_cast(_tabContent.ActualWidth()); - // const auto contentHeight = ::base::saturated_cast(_tabContent.ActualHeight()); - // const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight }; - - // const auto realSplitType = tab->PreCalculateCanSplit(splitDirection, splitSize, availableSpace); - // if (!realSplitType) - // { - // co_return; - // } - - // // unzoom - // _UnZoomIfNeeded(); - - // co_await winrt::resume_background(); - - // auto content = co_await preppedContent.initContentProc; - - // co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); - - // auto pane = _makePaneFromContent(content, preppedContent.controlSettings, preppedContent.profile); - - // tab->SplitPane(*realSplitType, splitSize, pane); - - // // Manually focus the new pane, if we've already initialized - // if (_startupState == StartupState::Initialized) - // { - // if (const auto control = _GetActiveControl()) - // { - // control.Focus(FocusState::Programmatic); - // } - // } - // } - - // >>>>>>> 1f2bb760e (Pane officially opened via the serialized actions, via across the process boundary) // Method Description: // - Switches the split orientation of the currently focused pane. // Arguments: @@ -2802,6 +2753,20 @@ namespace winrt::TerminalApp::implementation const winrt::TerminalApp::TabBase& sourceTab, TerminalConnection::ITerminalConnection existingConnection) { + // First things first - Check for making a pane from content GUID. + if (newTerminalArgs && + newTerminalArgs.ContentGuid() != winrt::guid{}) + { + // Don't need to worry about duplicating or anything - we'll + // serialize the actual profile's GUID along with the content guid. + const auto& profile = _settings.GetProfileForArgs(newTerminalArgs); + + const auto control = _InitControlFromContent(newTerminalArgs.ContentGuid()); + _RegisterTerminalEvents(control); + auto resultPane = std::make_shared(profile, control); + return resultPane; + } + TerminalSettingsCreateResult controlSettings{ nullptr }; Profile profile{ nullptr }; @@ -2826,15 +2791,6 @@ namespace winrt::TerminalApp::implementation profile = _settings.GetProfileForArgs(newTerminalArgs); controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings); } - // TODO! This should really be even above the block above, I'm just kludging it RN so I get a profile - // We'll probably want to not store just Interactivity's, but a {Profile, Interactivity, etc...} blob in ContentManager - if (newTerminalArgs && newTerminalArgs.ContentGuid() != winrt::guid{}) - { - const auto control = _InitControlFromContent(newTerminalArgs.ContentGuid()); - _RegisterTerminalEvents(control); - auto resultPane = std::make_shared(profile, control); - return resultPane; - } // Try to handle auto-elevation if (_maybeElevate(newTerminalArgs, controlSettings, profile)) From 091660e96dfccbe043ba9a4250c32c0eedf26a58 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Feb 2023 11:02:53 -0600 Subject: [PATCH 115/234] cleanup and comments before the PR --- src/cascadia/Remoting/Monarch.cpp | 6 +++- src/cascadia/TerminalApp/Pane.cpp | 30 +++++++++++-------- src/cascadia/TerminalApp/TerminalPage.cpp | 26 ++++++++++++++-- .../TerminalSettingsModel/ActionArgs.h | 4 --- 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 34298013bea..6c475f71262 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1098,7 +1098,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // TODO! create a window for that name / ID? + // TODO GH#5000 + // + // In the case where window couldn't be found, then create a window + // for that name / ID. Do this as a part of tear-out (different than + // drag/drop) } } } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 5bce40b2be5..aab49b3af4d 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -175,23 +175,27 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // Arguments: // - currentId: the id to use for the current/first pane // - nextId: the id to use for a new pane if we split +// - asContent: We're serializing this set of actions as content actions for +// moving to other windows, so we need to make sure to include ContentGuid's +// in the final actions. +// - asMovePane: only used with asContent. When this is true, we're building +// these actions as a part of moving the pane to another window, but without +// the context of the hosting tab. In that case, we'll want to build a +// splitPane action even if we're just a single leaf, because there's no other +// parent to try and build an action for us. // Return Value: // - The state from building the startup actions, includes a vector of commands, // the original root pane, the id of the focused pane, and the number of panes // created. -Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent, const bool asMovePane) -{ - // TODO! I f'ed something up here recently. Just moving a single pane no longer seems to work. - // - // If we call this directly on a leaf, then CURRENTLY, we'll return {} for - // the actions. Moving that will just move nothing, cause obviously. - // - // But this all works fine for a _tab_, where there's a parent Tab to build - // a newTab action first. - // - // So if we're moving a pane, as content, then we need to special case here where even moving just us returns a splitPane - - // if we are a leaf then all there is to do is defer to the parent. +Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, + uint32_t nextId, + const bool asContent, + const bool asMovePane) +{ + // Normally, if we're a leaf, return an empt set of actions, because the + // parent pane will build the SplitPane action for us. If we're building + // actions for a movePane action though, we'll still need to include + // ourselves. if (!asMovePane && _IsLeaf()) { if (_lastActive) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2674f4d13cb..42281556ac4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1889,6 +1889,8 @@ namespace winrt::TerminalApp::implementation return false; } + // If there was a windowId in the action, try to move it to the + // specified window instead of moving it in our tab row. if (!windowId.empty()) { if (const auto terminalTab{ _GetFocusedTabImpl() }) @@ -1934,6 +1936,8 @@ namespace winrt::TerminalApp::implementation return true; } + // Detach a tree of panes from this terminal. Helper used for moving panes + // and tabs to other windows. void TerminalPage::_DetachPaneFromWindow(std::shared_ptr pane) { pane->WalkTree([&](auto p) { @@ -1946,13 +1950,18 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_DetachTabFromWindow(const winrt::com_ptr& terminalTab) { - // Collect all the content we're about to detach. + // Detach the root pane, which will act like the whole tab got detached. if (const auto rootPane = terminalTab->GetRootPane()) { _DetachPaneFromWindow(rootPane); } } + // Method Description: + // - Serialize these actions to json, and raise them as a RequestMoveContent + // event. Our Window will raise that to the window manager / monarch, who + // will dispatch this blob of json back to the window that should handle + // this. void TerminalPage::_MoveContent(std::vector& actions, const winrt::hstring& windowName, const uint32_t tabIndex) @@ -1967,6 +1976,8 @@ namespace winrt::TerminalApp::implementation bool TerminalPage::_MoveTab(MoveTabArgs args) { + // If there was a windowId in the action, try to move it to the + // specified window instead of moving it in our tab row. const auto windowId{ args.Window() }; if (!windowId.empty()) { @@ -1994,7 +2005,18 @@ namespace winrt::TerminalApp::implementation return true; } - winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content, uint32_t tabIndex) + // Method Description: + // - Called when it is determined that an existing tab or pane should be + // attached to our window. content represents a blob of JSON describing + // some startup actions for resbuilding the specified panes. They will + // include `__content` properties with the GUID of the existing + // ControlInteractivities we should use, rather than starting new ones. + // - _MakePane is already enlightened to use the ContentGuid property to + // reattach instead of create new content, so this method simply needs to + // parse the JSON and pump it into our action handler. Almost the same as + // doing something like `wt -w 0 nt`. + winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content, + uint32_t tabIndex) { auto args = ActionAndArgs::Deserialize(content); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index e668f34e1cd..5f59f597972 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -332,10 +332,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle); JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme); JsonUtils::GetValueForKey(json, ElevateKey, args->_Elevate); - - // TODO! We should probably have the ContentGuid be optional, or at - // least serialized smarter, so we definitely don't write it to the - // file, ever. JsonUtils::GetValueForKey(json, ContentKey, args->_ContentGuid); return *args; } From 1f8766e9727f69d095cad3b25f0e31abee401213 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Feb 2023 11:06:13 -0600 Subject: [PATCH 116/234] shockingly few spel --- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 42281556ac4..7fa95aa5c38 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2008,9 +2008,9 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Called when it is determined that an existing tab or pane should be // attached to our window. content represents a blob of JSON describing - // some startup actions for resbuilding the specified panes. They will + // some startup actions for rebuilding the specified panes. They will // include `__content` properties with the GUID of the existing - // ControlInteractivities we should use, rather than starting new ones. + // ControlInteractivity's we should use, rather than starting new ones. // - _MakePane is already enlightened to use the ContentGuid property to // reattach instead of create new content, so this method simply needs to // parse the JSON and pump it into our action handler. Almost the same as From c2a2caf029de1b51167191f1205240962aa04751 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Feb 2023 12:41:01 -0600 Subject: [PATCH 117/234] Fixes for a bad merge --- src/cascadia/TerminalApp/TerminalPage.h | 2 -- src/cascadia/TerminalControl/ControlInteractivity.idl | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 000bbd4775f..38f2bdf245f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -482,8 +482,6 @@ namespace winrt::TerminalApp::implementation void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); - void _DetachTab(const winrt::com_ptr& terminalTab); - void _DetachPaneFromWindow(std::shared_ptr pane); void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); void _MoveContent(std::vector& actions, const winrt::hstring& windowName, const uint32_t tabIndex); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 0d2b49434f8..2882f4cd819 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -27,8 +27,6 @@ namespace Microsoft.Terminal.Control void Reparent(Microsoft.Terminal.Control.IKeyBindings keyBindings); void Close(); - void Close(); - InteractivityAutomationPeer OnCreateAutomationPeer(); Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference formats); @@ -74,8 +72,5 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler Closed; event Windows.Foundation.TypedEventHandler Attached; - - event Windows.Foundation.TypedEventHandler Closed; - }; } From b41ae7115c7960949a09c38a28212edc3c5e854b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Feb 2023 12:41:21 -0600 Subject: [PATCH 118/234] Everything-is-awful.wav --- src/cascadia/TerminalApp/TabManagement.cpp | 3 +- src/cascadia/TerminalApp/TerminalPage.cpp | 116 ++++++++++++++++++--- src/cascadia/TerminalApp/TerminalPage.h | 2 + 3 files changed, 103 insertions(+), 18 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index f1134efbd36..d8c35b17293 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -709,7 +709,8 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::TabBase TerminalPage::_GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept { uint32_t tabIndexFromControl{}; - if (_tabView.TabItems().IndexOf(tabViewItem, tabIndexFromControl)) + const auto& items{ _tabView.TabItems() }; + if (items.IndexOf(tabViewItem, tabIndexFromControl)) { // If IndexOf returns true, we've actually got an index return _tabs.GetAt(tabIndexFromControl); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 200515a973e..02128083781 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -246,6 +246,11 @@ namespace winrt::TerminalApp::implementation _tabView.TabDragStarting({ this, &TerminalPage::_onTabDragStarting }); _tabView.TabStripDragOver({ this, &TerminalPage::_onTabStripDragOver }); _tabView.TabStripDrop({ this, &TerminalPage::_onTabStripDrop }); + _tabView.TabDroppedOutside({ this, &TerminalPage::_onTabDroppedOutside }); + _tabView.TabDragCompleted({ this, &TerminalPage::_onTabDroppedCompleted }); + // TabRow().DragOver({ this, &TerminalPage::_onTabStripDragOver }); + // TabRow().Drop({ this, &TerminalPage::_onTabStripDrop }); + // TabRow().AllowDrop(true); _CreateNewTabFlyout(); @@ -412,6 +417,11 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e) { + if (!e.DataView().Contains(StandardDataFormats::StorageItems())) + { + co_return; + } + Windows::Foundation::Collections::IVectorView items; try { @@ -4461,6 +4471,12 @@ namespace winrt::TerminalApp::implementation { if (const auto terminalTab{ _GetFocusedTabImpl() }) { + uint32_t tabIndex{}; + if (!_tabs.IndexOf(*terminalTab, tabIndex)) + { + return; + } + auto startupActions = terminalTab->BuildStartupActions(true); auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; auto str = ActionAndArgs::Serialize(winRtActions); @@ -4468,25 +4484,38 @@ namespace winrt::TerminalApp::implementation e.Data().Properties().Insert(L"content", winrt::box_value(str)); e.Data().RequestedOperation(DataPackageOperation::Move); - e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak()](auto&&, auto &&) -> winrt::fire_and_forget { - auto tab = weakTab.get(); - auto page = weakThis.get(); - if (tab && page) + e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak(), e](auto&&, auto args) -> winrt::fire_and_forget { + auto acceptedFormat = args.AcceptedFormatId(); + auto operation = args.Operation(); + acceptedFormat; + operation; + + auto resultObj{ e.Data().Properties().TryLookup(L"result") }; + if (resultObj) { - // TODO! this loop might be able to go outside the - // OperationCompleted. If we put if before the - // OperationCompleted, then we make sure we've got an extra - // reference to the content, if the operation _is_ completed - // before we hook up the Attached handlers. However,idk what - // happens then if the operation never happens. - // - // Collect all the content we're about to detach. - page->_DetachTab(tab); - - co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); - - page->_RemoveTab(*tab); + auto resString{ winrt::unbox_value(resultObj) }; + resString; } + + co_return; + // auto tab = weakTab.get(); + // auto page = weakThis.get(); + // if (tab && page) + // { + // // TODO! this loop might be able to go outside the + // // OperationCompleted. If we put if before the + // // OperationCompleted, then we make sure we've got an extra + // // reference to the content, if the operation _is_ completed + // // before we hook up the Attached handlers. However,idk what + // // happens then if the operation never happens. + // // + // // Collect all the content we're about to detach. + // page->_DetachTabFromWindow(tab); + + // co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); + + // page->_RemoveTab(*tab); + // } }); } } @@ -4506,9 +4535,17 @@ namespace winrt::TerminalApp::implementation { co_return; } + + // winrt::hstring myIdString{ fmt::format(L"{}", this->WindowProperties().WindowId()) } ; + // auto val = winrt::box_value(myIdString); + // e.Data().Properties().Insert(L"result", val); + auto contentString{ winrt::unbox_value(contentObj) }; if (!contentString.empty()) { + e.Handled(true); + e.AcceptedOperation(DataPackageOperation::Move); + auto args = ActionAndArgs::Deserialize(contentString); // TODO! if the first action is a split pane and tabIndex > tabs.size, // then remove it and insert an equivalent newTab @@ -4521,4 +4558,49 @@ namespace winrt::TerminalApp::implementation } } + winrt::fire_and_forget TerminalPage::_onTabDroppedOutside(winrt::Microsoft::UI::Xaml::Controls::TabView sender, + winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e) + { + sender; + e; + + co_return; + } + winrt::fire_and_forget TerminalPage::_onTabDroppedCompleted(winrt::Microsoft::UI::Xaml::Controls::TabView sender, + winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragCompletedEventArgs e) + { + sender; + e; + const auto result = e.DropResult(); + result; + + if (result != DataPackageOperation::Move) + { + co_return; + } + + // auto acceptedFormat = args.AcceptedFormatId(); + // auto operation = args.Operation(); + // acceptedFormat; + // operation; + // + // TODO! settings tabs + + // MUST DO BEFORE _GetTabByTabViewItem because aPaReNtLy you need to be on the UI thread to get the tab index + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); + + auto tabItem = e.Tab(); + auto tab = _GetTabByTabViewItem(tabItem); + auto tabImpl = _GetTerminalTabImpl(tab); + if (tabImpl) + { + // Collect all the content we're about to detach. + _DetachTabFromWindow(tabImpl); + + _RemoveTab(*tabImpl); + } + + co_return; + } + } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 38f2bdf245f..49a07e10dcd 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -481,6 +481,8 @@ namespace winrt::TerminalApp::implementation void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e); void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); + winrt::fire_and_forget _onTabDroppedOutside(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e); + winrt::fire_and_forget _onTabDroppedCompleted(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragCompletedEventArgs e); void _DetachPaneFromWindow(std::shared_ptr pane); void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); From c6cce0fd6be0b45090ffc468455ff2c2ed4f293a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Feb 2023 12:41:31 -0600 Subject: [PATCH 119/234] Revert "Everything-is-awful.wav" This reverts commit b41ae7115c7960949a09c38a28212edc3c5e854b. --- src/cascadia/TerminalApp/TabManagement.cpp | 3 +- src/cascadia/TerminalApp/TerminalPage.cpp | 116 +++------------------ src/cascadia/TerminalApp/TerminalPage.h | 2 - 3 files changed, 18 insertions(+), 103 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index d8c35b17293..f1134efbd36 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -709,8 +709,7 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::TabBase TerminalPage::_GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept { uint32_t tabIndexFromControl{}; - const auto& items{ _tabView.TabItems() }; - if (items.IndexOf(tabViewItem, tabIndexFromControl)) + if (_tabView.TabItems().IndexOf(tabViewItem, tabIndexFromControl)) { // If IndexOf returns true, we've actually got an index return _tabs.GetAt(tabIndexFromControl); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 02128083781..200515a973e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -246,11 +246,6 @@ namespace winrt::TerminalApp::implementation _tabView.TabDragStarting({ this, &TerminalPage::_onTabDragStarting }); _tabView.TabStripDragOver({ this, &TerminalPage::_onTabStripDragOver }); _tabView.TabStripDrop({ this, &TerminalPage::_onTabStripDrop }); - _tabView.TabDroppedOutside({ this, &TerminalPage::_onTabDroppedOutside }); - _tabView.TabDragCompleted({ this, &TerminalPage::_onTabDroppedCompleted }); - // TabRow().DragOver({ this, &TerminalPage::_onTabStripDragOver }); - // TabRow().Drop({ this, &TerminalPage::_onTabStripDrop }); - // TabRow().AllowDrop(true); _CreateNewTabFlyout(); @@ -417,11 +412,6 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e) { - if (!e.DataView().Contains(StandardDataFormats::StorageItems())) - { - co_return; - } - Windows::Foundation::Collections::IVectorView items; try { @@ -4471,12 +4461,6 @@ namespace winrt::TerminalApp::implementation { if (const auto terminalTab{ _GetFocusedTabImpl() }) { - uint32_t tabIndex{}; - if (!_tabs.IndexOf(*terminalTab, tabIndex)) - { - return; - } - auto startupActions = terminalTab->BuildStartupActions(true); auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; auto str = ActionAndArgs::Serialize(winRtActions); @@ -4484,38 +4468,25 @@ namespace winrt::TerminalApp::implementation e.Data().Properties().Insert(L"content", winrt::box_value(str)); e.Data().RequestedOperation(DataPackageOperation::Move); - e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak(), e](auto&&, auto args) -> winrt::fire_and_forget { - auto acceptedFormat = args.AcceptedFormatId(); - auto operation = args.Operation(); - acceptedFormat; - operation; - - auto resultObj{ e.Data().Properties().TryLookup(L"result") }; - if (resultObj) + e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak()](auto&&, auto &&) -> winrt::fire_and_forget { + auto tab = weakTab.get(); + auto page = weakThis.get(); + if (tab && page) { - auto resString{ winrt::unbox_value(resultObj) }; - resString; + // TODO! this loop might be able to go outside the + // OperationCompleted. If we put if before the + // OperationCompleted, then we make sure we've got an extra + // reference to the content, if the operation _is_ completed + // before we hook up the Attached handlers. However,idk what + // happens then if the operation never happens. + // + // Collect all the content we're about to detach. + page->_DetachTab(tab); + + co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); + + page->_RemoveTab(*tab); } - - co_return; - // auto tab = weakTab.get(); - // auto page = weakThis.get(); - // if (tab && page) - // { - // // TODO! this loop might be able to go outside the - // // OperationCompleted. If we put if before the - // // OperationCompleted, then we make sure we've got an extra - // // reference to the content, if the operation _is_ completed - // // before we hook up the Attached handlers. However,idk what - // // happens then if the operation never happens. - // // - // // Collect all the content we're about to detach. - // page->_DetachTabFromWindow(tab); - - // co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); - - // page->_RemoveTab(*tab); - // } }); } } @@ -4535,17 +4506,9 @@ namespace winrt::TerminalApp::implementation { co_return; } - - // winrt::hstring myIdString{ fmt::format(L"{}", this->WindowProperties().WindowId()) } ; - // auto val = winrt::box_value(myIdString); - // e.Data().Properties().Insert(L"result", val); - auto contentString{ winrt::unbox_value(contentObj) }; if (!contentString.empty()) { - e.Handled(true); - e.AcceptedOperation(DataPackageOperation::Move); - auto args = ActionAndArgs::Deserialize(contentString); // TODO! if the first action is a split pane and tabIndex > tabs.size, // then remove it and insert an equivalent newTab @@ -4558,49 +4521,4 @@ namespace winrt::TerminalApp::implementation } } - winrt::fire_and_forget TerminalPage::_onTabDroppedOutside(winrt::Microsoft::UI::Xaml::Controls::TabView sender, - winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e) - { - sender; - e; - - co_return; - } - winrt::fire_and_forget TerminalPage::_onTabDroppedCompleted(winrt::Microsoft::UI::Xaml::Controls::TabView sender, - winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragCompletedEventArgs e) - { - sender; - e; - const auto result = e.DropResult(); - result; - - if (result != DataPackageOperation::Move) - { - co_return; - } - - // auto acceptedFormat = args.AcceptedFormatId(); - // auto operation = args.Operation(); - // acceptedFormat; - // operation; - // - // TODO! settings tabs - - // MUST DO BEFORE _GetTabByTabViewItem because aPaReNtLy you need to be on the UI thread to get the tab index - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); - - auto tabItem = e.Tab(); - auto tab = _GetTabByTabViewItem(tabItem); - auto tabImpl = _GetTerminalTabImpl(tab); - if (tabImpl) - { - // Collect all the content we're about to detach. - _DetachTabFromWindow(tabImpl); - - _RemoveTab(*tabImpl); - } - - co_return; - } - } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 49a07e10dcd..38f2bdf245f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -481,8 +481,6 @@ namespace winrt::TerminalApp::implementation void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e); void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); - winrt::fire_and_forget _onTabDroppedOutside(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e); - winrt::fire_and_forget _onTabDroppedCompleted(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragCompletedEventArgs e); void _DetachPaneFromWindow(std::shared_ptr pane); void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); From 2537342ffb1abd67bfc7b087b3048c12943d7062 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 Feb 2023 11:11:06 -0600 Subject: [PATCH 120/234] On drop, ask the sender to send content to us. The incredibly dumb solution I considered. As previously outlined in https://github.com/microsoft/terminal/issues/5000#issuecomment-1435328038 --- .vscode/settings.json | 4 +- src/cascadia/Remoting/Monarch.cpp | 35 +++++ src/cascadia/Remoting/Monarch.h | 1 + src/cascadia/Remoting/Monarch.idl | 4 +- src/cascadia/Remoting/Peasant.cpp | 6 + src/cascadia/Remoting/Peasant.h | 15 ++ src/cascadia/Remoting/Peasant.idl | 11 +- src/cascadia/Remoting/WindowManager.cpp | 5 + src/cascadia/Remoting/WindowManager.h | 1 + src/cascadia/Remoting/WindowManager.idl | 1 + src/cascadia/TerminalApp/TerminalPage.cpp | 141 +++++++++++++----- src/cascadia/TerminalApp/TerminalPage.h | 18 +++ src/cascadia/TerminalApp/TerminalPage.idl | 10 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 7 + src/cascadia/TerminalApp/TerminalWindow.h | 2 + src/cascadia/TerminalApp/TerminalWindow.idl | 4 +- src/cascadia/WindowsTerminal/AppHost.cpp | 20 ++- src/cascadia/WindowsTerminal/AppHost.h | 10 ++ .../WindowsTerminal/WindowEmperor.cpp | 2 +- 19 files changed, 250 insertions(+), 47 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/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 6c475f71262..0a1ec1a7e1e 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1105,4 +1105,39 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // drag/drop) } } + + // Very similar to the above. Someone came and told us that they were the target of a drag/drop, and they know who started it. + // We will go tell the person who started it that they should send that target the content which was dragged. + void Monarch::RequestSendContent(Remoting::RequestReceiveContentArgs args) + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_SendContent_Requested", + TraceLoggingUInt64(args.SourceWindow(), "source", "The window which started the drag"), + TraceLoggingUInt64(args.TargetWindow(), "target", "The window which was the target of the drop"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + if (auto senderPeasant{ _getPeasant(args.SourceWindow()) }) + { + senderPeasant.SendContent(args); + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_SendContent_Completed", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_SendContent_NoWindow", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // TODO GH#5000 + // + // In the case where window couldn't be found, then create a window + // for that name / ID. Do this as a part of tear-out (different than + // drag/drop) + } + } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index d19c8e71a27..1efc3b6cca8 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -82,6 +82,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVector GetAllWindowLayouts(); void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); + void RequestSendContent(Remoting::RequestReceiveContentArgs args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index f67114fdc0f..027f66d3690 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -41,8 +41,7 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.IReference WindowID; } - [default_interface] runtimeclass QuitAllRequestedArgs - { + [default_interface] runtimeclass QuitAllRequestedArgs { QuitAllRequestedArgs(); Windows.Foundation.IAsyncAction BeforeQuitAllAction; } @@ -71,6 +70,7 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.Collections.IVector GetAllWindowLayouts(); void RequestMoveContent(String window, String content, UInt32 tabIndex); + void RequestSendContent(RequestReceiveContentArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 659de9b72fc..75815bdd25e 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -9,6 +9,7 @@ #include "Peasant.g.cpp" #include "../../types/inc/utils.hpp" #include "AttachRequest.g.cpp" +#include "RequestReceiveContentArgs.g.cpp" using namespace winrt; using namespace winrt::Microsoft::Terminal; @@ -327,4 +328,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } return args->WindowLayoutJson(); } + + void Peasant::SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args) + { + _SendContentRequestedHandlers(*this, args); + } } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index a0fb55dce73..a69942ad540 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -6,6 +6,7 @@ #include "Peasant.g.h" #include "RenameRequestArgs.h" #include "AttachRequest.g.h" +#include "RequestReceiveContentArgs.g.h" namespace RemotingUnitTests { @@ -25,6 +26,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _TabIndex{ tabIndex } {}; }; + struct RequestReceiveContentArgs : RequestReceiveContentArgsT + { + WINRT_PROPERTY(uint64_t, SourceWindow); + WINRT_PROPERTY(uint64_t, TargetWindow); + + public: + RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt) : + _SourceWindow{ src }, + _TargetWindow{ tgt } {}; + }; + struct Peasant : public PeasantT { Peasant(); @@ -52,6 +64,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); winrt::hstring GetWindowLayout(); + void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args); WINRT_PROPERTY(winrt::hstring, WindowName); WINRT_PROPERTY(winrt::hstring, ActiveTabTitle); @@ -70,6 +83,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs); TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); + TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs); private: Peasant(const uint64_t testPID); @@ -87,4 +101,5 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation namespace winrt::Microsoft::Terminal::Remoting::factory_implementation { BASIC_FACTORY(Peasant); + BASIC_FACTORY(RequestReceiveContentArgs); } diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 1257bda4b8c..a9bfce29171 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -55,6 +55,13 @@ namespace Microsoft.Terminal.Remoting String Content { get; }; UInt32 TabIndex { get; }; } + [default_interface] runtimeclass RequestReceiveContentArgs { + RequestReceiveContentArgs(UInt64 src, UInt64 tgt); + + UInt64 SourceWindow { get; }; + UInt64 TargetWindow { get; }; + // TODO! UInt32 TabIndex { get; }; + }; interface IPeasant { @@ -82,7 +89,7 @@ namespace Microsoft.Terminal.Remoting String GetWindowLayout(); void AttachContentToWindow(AttachRequest request); - + void SendContent(RequestReceiveContentArgs args); event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; @@ -98,7 +105,7 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler QuitRequested; event Windows.Foundation.TypedEventHandler AttachRequested; - + event Windows.Foundation.TypedEventHandler SendContentRequested; }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index e5cfe67460e..4fb64628ba6 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -434,4 +434,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.RequestMoveContent(window, content, tabIndex); } + winrt::fire_and_forget WindowManager::RequestSendContent(Remoting::RequestReceiveContentArgs args) + { + co_await winrt::resume_background(); + _monarch.RequestSendContent(args); + } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 2a4df2c42d5..723a17dc3c9 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -46,6 +46,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool DoesQuakeWindowExist(); winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); + winrt::fire_and_forget RequestSendContent(Remoting::RequestReceiveContentArgs args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 3b4ac56d3e6..46a14ea8069 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Remoting Boolean DoesQuakeWindowExist(); void RequestMoveContent(String window, String content, UInt32 tabIndex); + void RequestSendContent(RequestReceiveContentArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 200515a973e..d6fc2035ebd 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -7,6 +7,7 @@ #include "TerminalPage.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" #include "RequestMoveContentArgs.g.cpp" +#include "RequestReceiveContentArgs.g.cpp" #include @@ -4461,39 +4462,46 @@ namespace winrt::TerminalApp::implementation { if (const auto terminalTab{ _GetFocusedTabImpl() }) { - auto startupActions = terminalTab->BuildStartupActions(true); - auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; - auto str = ActionAndArgs::Serialize(winRtActions); + // First: stash the ~serialization of the~ tab we started dragging. + // We're going to be asked for this. + // auto startupActions = terminalTab->BuildStartupActions(true); + // auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; + // _stashedTabDragContent = ActionAndArgs::Serialize(winRtActions); + _stashedDraggedTab = terminalTab; - e.Data().Properties().Insert(L"content", winrt::box_value(str)); + // Into the datapackage, let's stash our own window ID. + + const winrt::hstring id{ fmt::format(L"{}", _WindowProperties.WindowId()) }; + + e.Data().Properties().Insert(L"windowId", winrt::box_value(id)); e.Data().RequestedOperation(DataPackageOperation::Move); - e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak()](auto&&, auto &&) -> winrt::fire_and_forget { - auto tab = weakTab.get(); - auto page = weakThis.get(); - if (tab && page) - { - // TODO! this loop might be able to go outside the - // OperationCompleted. If we put if before the - // OperationCompleted, then we make sure we've got an extra - // reference to the content, if the operation _is_ completed - // before we hook up the Attached handlers. However,idk what - // happens then if the operation never happens. - // - // Collect all the content we're about to detach. - page->_DetachTab(tab); - - co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); - - page->_RemoveTab(*tab); - } - }); + // e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak()](auto&&, auto &&) -> winrt::fire_and_forget { + // auto tab = weakTab.get(); + // auto page = weakThis.get(); + // if (tab && page) + // { + // // TODO! this loop might be able to go outside the + // // OperationCompleted. If we put if before the + // // OperationCompleted, then we make sure we've got an extra + // // reference to the content, if the operation _is_ completed + // // before we hook up the Attached handlers. However,idk what + // // happens then if the operation never happens. + // // + // // Collect all the content we're about to detach. + // page->_DetachTab(tab); + + // co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); + + // page->_RemoveTab(*tab); + // } + // }); } } void TerminalPage::_onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e) { - if (e.DataView().Properties().HasKey(L"content")) + if (e.DataView().Properties().HasKey(L"windowId")) { e.AcceptedOperation(DataPackageOperation::Move); } @@ -4501,24 +4509,83 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e) { - auto contentObj{ e.DataView().Properties().TryLookup(L"content") }; - if (contentObj == nullptr) + // TODO! get PID and make sure it's the same as ours. + + auto windowIdObj{ e.DataView().Properties().TryLookup(L"windowId") }; + if (windowIdObj == nullptr) { + // No windowId? Bail. co_return; } - auto contentString{ winrt::unbox_value(contentObj) }; - if (!contentString.empty()) + const auto windowId{ winrt::unbox_value(windowIdObj) }; + + // Convert the windowId to a number + uint32_t src; + if (!Utils::StringToUint(windowId.c_str(), src)) { - auto args = ActionAndArgs::Deserialize(contentString); - // TODO! if the first action is a split pane and tabIndex > tabs.size, - // then remove it and insert an equivalent newTab + // Failed to convert the windowId to a number. Bail. + co_return; + } - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); // may need to go to the top of _createNewTabFromContent - for (const auto& action : args) - { - _actionDispatch->DoAction(action); - } + // TODO! Figure out where in the tabstrip we're dropping this tab. Add + // that index to the reqest. I believe the WUI sample app has example + // code for this + + auto weakThis{ get_weak() }; + co_await winrt::resume_background(); + if (const auto& page{ weakThis.get() }) + { + // `this` is safe to use + const auto request = winrt::make_self(src, _WindowProperties.WindowId()); + + // This will go up to the monarch, who will then dispatch the request + // back down to the source TerminalPage, who will then perform a + // RequestMoveContent to move their tab to us. + _RequestReceiveContentHandlers(*this, *request); } + + // if (!contentString.empty()) + // { + // auto args = ActionAndArgs::Deserialize(contentString); + // // TODO! if the first action is a split pane and tabIndex > tabs.size, + // // then remove it and insert an equivalent newTab + + // co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); // may need to go to the top of _createNewTabFromContent + // for (const auto& action : args) + // { + // _actionDispatch->DoAction(action); + // } + // } } + winrt::fire_and_forget TerminalPage::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args) + { + // This is called on the source TerminalPage, when the monarch has + // requested that we send our tab to another window. We'll need to + // serialize the tab, and send it to the monarch, who will then send it + // to the destination window. + // validate that we're the source window of the tab in this request + if (args.SourceWindow() != _WindowProperties.WindowId()) + { + co_return; + } + if (!_stashedDraggedTab) + { + co_return; + } + + auto weakThis{ get_weak() }; + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); + if (const auto& page{ weakThis.get() }) + { + // `this` is safe to use + auto startupActions = _stashedDraggedTab->BuildStartupActions(true); + _DetachTabFromWindow(_stashedDraggedTab); + _MoveContent(startupActions, winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, 0); + _RemoveTab(*_stashedDraggedTab); + + // TODO! _whenever_ we close the stashed dragged tab, we should null it. + _stashedDraggedTab = nullptr; + } + } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 38f2bdf245f..aa8482edc22 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -9,6 +9,7 @@ #include "AppCommandlineArgs.h" #include "RenameWindowRequestedArgs.g.h" #include "RequestMoveContentArgs.g.h" +#include "RequestReceiveContentArgs.g.h" #include "Toast.h" #define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); @@ -64,6 +65,17 @@ namespace winrt::TerminalApp::implementation _TabIndex{ tabIndex } {}; }; + struct RequestReceiveContentArgs : RequestReceiveContentArgsT + { + WINRT_PROPERTY(uint64_t, SourceWindow); + WINRT_PROPERTY(uint64_t, TargetWindow); + + public: + RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt) : + _SourceWindow{ src }, + _TargetWindow{ tgt } {}; + }; + struct TerminalPage : TerminalPageT { public: @@ -149,6 +161,7 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget WindowNameChanged(); winrt::fire_and_forget AttachContent(winrt::hstring content, uint32_t tabIndex); + winrt::fire_and_forget SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args); WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); @@ -174,6 +187,7 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(ShowWindowChanged, IInspectable, winrt::Microsoft::Terminal::Control::ShowWindowArgs) TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs); + TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs); WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, TitlebarBrush, _PropertyChangedHandlers, nullptr); @@ -247,6 +261,9 @@ namespace winrt::TerminalApp::implementation TerminalApp::IWindowProperties _WindowProperties{ nullptr }; TerminalApp::ContentManager _manager{ nullptr }; + // winrt::hstring _stashedTabDragContent{}; + winrt::com_ptr _stashedDraggedTab{ nullptr }; + winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); void _ShowAboutDialog(); @@ -501,4 +518,5 @@ namespace winrt::TerminalApp::implementation namespace winrt::TerminalApp::factory_implementation { BASIC_FACTORY(TerminalPage); + BASIC_FACTORY(RequestReceiveContentArgs); } diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 86cd73de912..e3eadd47310 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -29,6 +29,13 @@ namespace TerminalApp String Content { get; }; UInt32 TabIndex { get; }; }; + [default_interface] runtimeclass RequestReceiveContentArgs { + RequestReceiveContentArgs(UInt64 src, UInt64 tgt); + + UInt64 SourceWindow { get; }; + UInt64 TargetWindow { get; }; + // TODO! UInt32 TabIndex { get; }; + }; interface IDialogPresenter { @@ -72,6 +79,7 @@ namespace TerminalApp Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); + void SendContentToOther(RequestReceiveContentArgs args); String WindowName { get; }; UInt64 WindowId { get; }; @@ -96,6 +104,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler ShowWindowChanged; event Windows.Foundation.TypedEventHandler RequestMoveContent; - + event Windows.Foundation.TypedEventHandler RequestReceiveContent; } } diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 712157bd04e..98a4e8a64ef 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1233,6 +1233,13 @@ namespace winrt::TerminalApp::implementation _root->AttachContent(content, tabIndex); } } + void TerminalWindow::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args) + { + if (_root) + { + _root->SendContentToOther(args); + } + } //////////////////////////////////////////////////////////////////////////// diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 85f4e279bba..669220a85e9 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -127,6 +127,7 @@ namespace winrt::TerminalApp::implementation bool IsQuakeWindow() const noexcept; void AttachContent(winrt::hstring content, uint32_t tabIndex); + void SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args); // -------------------------------- WinRT Events --------------------------------- // PropertyChanged is surprisingly not a typed event, so we'll define that one manually. @@ -206,6 +207,7 @@ namespace winrt::TerminalApp::implementation TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs); FORWARDED_TYPED_EVENT(RequestMoveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMoveContentArgs, _root, RequestMoveContent); + FORWARDED_TYPED_EVENT(RequestReceiveContent, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestReceiveContentArgs, _root, RequestReceiveContent); #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 9dd48183cc6..628742289c1 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -139,7 +139,9 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SettingsChanged; event Windows.Foundation.TypedEventHandler RequestMoveContent; - void AttachContent(String content, UInt32 tabIndex); + event Windows.Foundation.TypedEventHandler RequestReceiveContent; + void AttachContent(String content, UInt32 tabIndex); + void SendContentToOther(RequestReceiveContentArgs args); } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index eb1e122fd23..2db0cfc8818 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -384,7 +384,8 @@ void AppHost::Initialize() _revokers.QuitRequested = _windowLogic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll }); _revokers.ShowWindowChanged = _windowLogic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged }); _revokers.RequestMoveContent = _windowLogic.RequestMoveContent(winrt::auto_revoke, { this, &AppHost::_handleMoveContent }); - + _revokers.RequestReceiveContent = _windowLogic.RequestReceiveContent(winrt::auto_revoke, { this, &AppHost::_handleReceiveContent }); + _revokers.SendContentRequested = _peasant.SendContentRequested(winrt::auto_revoke, { this, &AppHost::_handleSendContent }); // BODGY // On certain builds of Windows, when Terminal is set as the default // it will accumulate an unbounded amount of queued animations while @@ -1210,3 +1211,20 @@ void AppHost::_handleAttach(const winrt::Windows::Foundation::IInspectable& /*se { _windowLogic.AttachContent(args.Content(), args.TabIndex()); } + +// Page -> us -> manager -> monarch +// The page wants to tell the monarch that it was the drop target for a drag drop. +// The manager will tell the monarch to tell the _other_ window to send its content to us. +void AppHost::_handleReceiveContent(const winrt::Windows::Foundation::IInspectable& /* sender */, + winrt::TerminalApp::RequestReceiveContentArgs args) +{ + _windowManager.RequestSendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow() }); +} + +// monarch -> Peasant -> us -> Page +// The Monarch was told to tell us to send our dragged content to someone else. +void AppHost::_handleSendContent(const winrt::Windows::Foundation::IInspectable& /* sender */, + winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args) +{ + _windowLogic.SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow() }); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 6d774dd5ca6..02359d72db0 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -123,6 +123,14 @@ class AppHost void _handleAttach(const winrt::Windows::Foundation::IInspectable& sender, winrt::Microsoft::Terminal::Remoting::AttachRequest args); + // Page -> us -> monarch + void _handleReceiveContent(const winrt::Windows::Foundation::IInspectable& sender, + winrt::TerminalApp::RequestReceiveContentArgs args); + + // monarch -> us -> Page + void _handleSendContent(const winrt::Windows::Foundation::IInspectable& sender, + winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args); + winrt::event_token _GetWindowLayoutRequestedToken; // Helper struct. By putting these all into one struct, we can revoke them @@ -157,11 +165,13 @@ class AppHost winrt::TerminalApp::TerminalWindow::QuitRequested_revoker QuitRequested; winrt::TerminalApp::TerminalWindow::ShowWindowChanged_revoker ShowWindowChanged; winrt::TerminalApp::TerminalWindow::RequestMoveContent_revoker RequestMoveContent; + winrt::TerminalApp::TerminalWindow::RequestReceiveContent_revoker RequestReceiveContent; 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; + winrt::Microsoft::Terminal::Remoting::Peasant::SendContentRequested_revoker SendContentRequested; } _revokers{}; }; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index ce78ffff513..fc23db42c2d 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -122,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 0cf13d81b1942b7054eb3c148cd54d6543705442 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 Feb 2023 11:24:30 -0600 Subject: [PATCH 121/234] Notes, cleanup. 6 TODOs remain --- src/cascadia/TerminalApp/TerminalPage.cpp | 89 ++++++++++------------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d6fc2035ebd..3738036bce6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4460,53 +4460,46 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) { + // TODO! Use the `sender` as the active tab, you donkey if (const auto terminalTab{ _GetFocusedTabImpl() }) { - // First: stash the ~serialization of the~ tab we started dragging. + // First: stash the tab we started dragging. // We're going to be asked for this. - // auto startupActions = terminalTab->BuildStartupActions(true); - // auto winRtActions{ winrt::single_threaded_vector(std::move(startupActions)) }; - // _stashedTabDragContent = ActionAndArgs::Serialize(winRtActions); _stashedDraggedTab = terminalTab; - // Into the datapackage, let's stash our own window ID. - + // Into the DataPackage, let's stash our own window ID. const winrt::hstring id{ fmt::format(L"{}", _WindowProperties.WindowId()) }; e.Data().Properties().Insert(L"windowId", winrt::box_value(id)); e.Data().RequestedOperation(DataPackageOperation::Move); - // e.Data().OperationCompleted([weakThis = get_weak(), weakTab = terminalTab->get_weak()](auto&&, auto &&) -> winrt::fire_and_forget { - // auto tab = weakTab.get(); - // auto page = weakThis.get(); - // if (tab && page) - // { - // // TODO! this loop might be able to go outside the - // // OperationCompleted. If we put if before the - // // OperationCompleted, then we make sure we've got an extra - // // reference to the content, if the operation _is_ completed - // // before we hook up the Attached handlers. However,idk what - // // happens then if the operation never happens. - // // - // // Collect all the content we're about to detach. - // page->_DetachTab(tab); - - // co_await wil::resume_foreground(page->Dispatcher(), CoreDispatcherPriority::Normal); - - // page->_RemoveTab(*tab); - // } - // }); - } - } - void TerminalPage::_onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, + // The next thing that will happen: + // * Another TerminalPage will get a TabStripDragOver, then get a + // TabStripDrop + // * This will be handled by the _other_ page asking the monarch + // to ask us to send our content to them. + // * We'll get a TabDroppedOutside to indicate that this tab was + // dropped _not_ on a TabView. + // * This we can't handle yet, and is the last point of TODO GH#5000 + } + } + + void TerminalPage::_onTabStripDragOver(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Windows::UI::Xaml::DragEventArgs e) { + // We must mark that we can accept the drag/drop. The system will never + // call TabStripDrop on us if we don't indicate that we're willing. if (e.DataView().Properties().HasKey(L"windowId")) { e.AcceptedOperation(DataPackageOperation::Move); } } - winrt::fire_and_forget TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, + + // Method Description: + // - Called on the TARGET of a tab drag/drop. We'll unpack the DataPackage + // to find who the tab came from. We'll then ask the Monarch to ask the + // sender to move that tab to us. + winrt::fire_and_forget TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Windows::UI::Xaml::DragEventArgs e) { // TODO! get PID and make sure it's the same as ours. @@ -4527,10 +4520,11 @@ namespace winrt::TerminalApp::implementation co_return; } - // TODO! Figure out where in the tabstrip we're dropping this tab. Add - // that index to the reqest. I believe the WUI sample app has example + // TODO! Figure out where in the tab strip we're dropping this tab. Add + // that index to the request. I believe the WUI sample app has example // code for this + // Get off the drag/drop thread, but we don't need to be on our UI thread. auto weakThis{ get_weak() }; co_await winrt::resume_background(); if (const auto& page{ weakThis.get() }) @@ -4543,27 +4537,17 @@ namespace winrt::TerminalApp::implementation // RequestMoveContent to move their tab to us. _RequestReceiveContentHandlers(*this, *request); } - - // if (!contentString.empty()) - // { - // auto args = ActionAndArgs::Deserialize(contentString); - // // TODO! if the first action is a split pane and tabIndex > tabs.size, - // // then remove it and insert an equivalent newTab - - // co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); // may need to go to the top of _createNewTabFromContent - // for (const auto& action : args) - // { - // _actionDispatch->DoAction(action); - // } - // } } + + // Method Description: + // - This is called on the drag/drop SOURCE TerminalPage, when the monarch has + // requested that we send our tab to another window. We'll need to + // serialize the tab, and send it to the monarch, who will then send it to + // the destination window. + // - Fortunately, sending the tab is basically just a MoveTab action, so we + // can largely reuse that. winrt::fire_and_forget TerminalPage::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args) { - // This is called on the source TerminalPage, when the monarch has - // requested that we send our tab to another window. We'll need to - // serialize the tab, and send it to the monarch, who will then send it - // to the destination window. - // validate that we're the source window of the tab in this request if (args.SourceWindow() != _WindowProperties.WindowId()) { @@ -4574,17 +4558,18 @@ namespace winrt::TerminalApp::implementation co_return; } + // must do the work of adding/removing tabs on the UI thread. auto weakThis{ get_weak() }; co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); if (const auto& page{ weakThis.get() }) { - // `this` is safe to use + // `this` is safe to use in here. auto startupActions = _stashedDraggedTab->BuildStartupActions(true); _DetachTabFromWindow(_stashedDraggedTab); _MoveContent(startupActions, winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, 0); _RemoveTab(*_stashedDraggedTab); - // TODO! _whenever_ we close the stashed dragged tab, we should null it. + // TODO! _whenever_ we close the stashed dragged tab, we should null it. I think that's just _RemoveTab, but to the dilligence you donkey _stashedDraggedTab = nullptr; } } From 6586c4a8160e9a0bb36b344c45bfab33a46f7960 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 Feb 2023 14:38:24 -0600 Subject: [PATCH 122/234] Use the correct tab when dragging ; check the PID too 4 TODOs left --- src/cascadia/TerminalApp/TerminalPage.cpp | 44 ++++++++++++++++++----- src/cascadia/TerminalApp/TerminalPage.h | 5 ++- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 3738036bce6..23dcd1234ae 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1953,12 +1953,15 @@ namespace winrt::TerminalApp::implementation }); } - void TerminalPage::_DetachTabFromWindow(const winrt::com_ptr& terminalTab) + void TerminalPage::_DetachTabFromWindow(const winrt::com_ptr& tab) { - // Detach the root pane, which will act like the whole tab got detached. - if (const auto rootPane = terminalTab->GetRootPane()) + if (const auto terminalTab = tab.try_as()) { - _DetachPaneFromWindow(rootPane); + // Detach the root pane, which will act like the whole tab got detached. + if (const auto rootPane = terminalTab->GetRootPane()) + { + _DetachPaneFromWindow(rootPane); + } } } @@ -4460,17 +4463,24 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) { - // TODO! Use the `sender` as the active tab, you donkey - if (const auto terminalTab{ _GetFocusedTabImpl() }) + // Get the tab impl from this event. + auto tvi = _GetTabByTabViewItem(e.Tab()); + winrt::com_ptr tabImpl; + tabImpl.copy_from(winrt::get_self(tvi)); + if (tabImpl) { // First: stash the tab we started dragging. // We're going to be asked for this. - _stashedDraggedTab = terminalTab; + _stashedDraggedTab = tabImpl; // Into the DataPackage, let's stash our own window ID. const winrt::hstring id{ fmt::format(L"{}", _WindowProperties.WindowId()) }; + // Get our PID + const winrt::hstring pid{ fmt::format(L"{}", GetCurrentProcessId()) }; + e.Data().Properties().Insert(L"windowId", winrt::box_value(id)); + e.Data().Properties().Insert(L"pid", winrt::box_value(pid)); e.Data().RequestedOperation(DataPackageOperation::Move); // The next thing that will happen: @@ -4502,7 +4512,25 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Windows::UI::Xaml::DragEventArgs e) { - // TODO! get PID and make sure it's the same as ours. + // Get the PID and make sure it is the same as ours. + if (auto pidObj{ e.DataView().Properties().TryLookup(L"pid") }) + { + const auto pidStr{ winrt::unbox_value(pidObj) }; + uint32_t pidNum; + if (Utils::StringToUint(pidStr.c_str(), pidNum)) + { + if (pidNum != GetCurrentProcessId()) + { + // The PID doesn't match ours. We can't handle this drop. + co_return; + } + } + } + else + { + // No PID? We can't handle this drop. Bail. + co_return; + } auto windowIdObj{ e.DataView().Properties().TryLookup(L"windowId") }; if (windowIdObj == nullptr) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index aa8482edc22..cef07bf5703 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -261,8 +261,7 @@ namespace winrt::TerminalApp::implementation TerminalApp::IWindowProperties _WindowProperties{ nullptr }; TerminalApp::ContentManager _manager{ nullptr }; - // winrt::hstring _stashedTabDragContent{}; - winrt::com_ptr _stashedDraggedTab{ nullptr }; + winrt::com_ptr _stashedDraggedTab{ nullptr }; winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); @@ -500,7 +499,7 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); void _DetachPaneFromWindow(std::shared_ptr pane); - void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); + void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); void _MoveContent(std::vector& actions, const winrt::hstring& windowName, const uint32_t tabIndex); #pragma region ActionHandlers From d67815bf0c55a4a2738fc006a55b250c9d80568c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 08:27:35 -0600 Subject: [PATCH 123/234] Try to drag/drop to the rigt place in the tab row, only to discover that we ALWAYS drag the 0th tab now. Peculiar. --- src/cascadia/Remoting/Peasant.h | 6 ++-- src/cascadia/Remoting/Peasant.idl | 4 +-- src/cascadia/TerminalApp/TabManagement.cpp | 3 +- src/cascadia/TerminalApp/TerminalPage.cpp | 41 ++++++++++++++++++---- src/cascadia/TerminalApp/TerminalPage.h | 6 ++-- src/cascadia/TerminalApp/TerminalPage.idl | 4 +-- src/cascadia/WindowsTerminal/AppHost.cpp | 4 +-- 7 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index a69942ad540..80110d5d7b6 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -30,11 +30,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WINRT_PROPERTY(uint64_t, SourceWindow); WINRT_PROPERTY(uint64_t, TargetWindow); + WINRT_PROPERTY(uint32_t, TabIndex); public: - RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt) : + RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) : _SourceWindow{ src }, - _TargetWindow{ tgt } {}; + _TargetWindow{ tgt }, + _TabIndex{ tabIndex } {}; }; struct Peasant : public PeasantT diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index a9bfce29171..7bda611bb80 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -56,11 +56,11 @@ namespace Microsoft.Terminal.Remoting UInt32 TabIndex { get; }; } [default_interface] runtimeclass RequestReceiveContentArgs { - RequestReceiveContentArgs(UInt64 src, UInt64 tgt); + RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex); UInt64 SourceWindow { get; }; UInt64 TargetWindow { get; }; - // TODO! UInt32 TabIndex { get; }; + UInt32 TabIndex { get; }; }; interface IPeasant diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index f1134efbd36..5283786da63 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -709,7 +709,8 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::TabBase TerminalPage::_GetTabByTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem) const noexcept { uint32_t tabIndexFromControl{}; - if (_tabView.TabItems().IndexOf(tabViewItem, tabIndexFromControl)) + const auto items{ _tabView.TabItems() }; + if (items.IndexOf(tabViewItem, tabIndexFromControl)) { // If IndexOf returns true, we've actually got an index return _tabs.GetAt(tabIndexFromControl); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 23dcd1234ae..50b73545b4a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4464,9 +4464,15 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) { // Get the tab impl from this event. - auto tvi = _GetTabByTabViewItem(e.Tab()); + auto eventTab = e.Tab(); + // auto eventTabGood = e.Item().try_as(); + auto iconBad = eventTab.IconSource(); + iconBad; + // auto iconGood = eventTabGood.IconSource(); + // iconGood; + auto tabBase = _GetTabByTabViewItem(eventTab); winrt::com_ptr tabImpl; - tabImpl.copy_from(winrt::get_self(tvi)); + tabImpl.copy_from(winrt::get_self(tabBase)); if (tabImpl) { // First: stash the tab we started dragging. @@ -4503,6 +4509,11 @@ namespace winrt::TerminalApp::implementation { e.AcceptedOperation(DataPackageOperation::Move); } + + // You may think to yourself, this is a great place to increase the + // width of the TabView artificially, to make room for the new tab item. + // However, we'll never get a message that the tab left the tab view + // (without being dropped). So there's no good way to resize back down. } // Method Description: @@ -4552,13 +4563,31 @@ namespace winrt::TerminalApp::implementation // that index to the request. I believe the WUI sample app has example // code for this - // Get off the drag/drop thread, but we don't need to be on our UI thread. + // We need to be on OUR UI thread to figure out where we dropped auto weakThis{ get_weak() }; - co_await winrt::resume_background(); + co_await wil::resume_foreground(Dispatcher()); if (const auto& page{ weakThis.get() }) { + // First we need to get the position in the List to drop to + auto index = -1; + + // Determine which items in the list our pointer is between. + for (auto i = 0u; i < _tabView.TabItems().Size(); i++) + { + if (auto item{ _tabView.ContainerFromIndex(i).try_as() }) + { + const auto posX{ e.GetPosition(item).X }; + const auto itemWidth{ item.ActualWidth() }; + if (posX - itemWidth < 0) + { + index = i; + break; + } + } + } + // `this` is safe to use - const auto request = winrt::make_self(src, _WindowProperties.WindowId()); + const auto request = winrt::make_self(src, _WindowProperties.WindowId(), index); // This will go up to the monarch, who will then dispatch the request // back down to the source TerminalPage, who will then perform a @@ -4594,7 +4623,7 @@ namespace winrt::TerminalApp::implementation // `this` is safe to use in here. auto startupActions = _stashedDraggedTab->BuildStartupActions(true); _DetachTabFromWindow(_stashedDraggedTab); - _MoveContent(startupActions, winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, 0); + _MoveContent(startupActions, winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, args.TabIndex()); _RemoveTab(*_stashedDraggedTab); // TODO! _whenever_ we close the stashed dragged tab, we should null it. I think that's just _RemoveTab, but to the dilligence you donkey diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index cef07bf5703..513c804804c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -69,11 +69,13 @@ namespace winrt::TerminalApp::implementation { WINRT_PROPERTY(uint64_t, SourceWindow); WINRT_PROPERTY(uint64_t, TargetWindow); + WINRT_PROPERTY(uint32_t, TabIndex); public: - RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt) : + RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) : _SourceWindow{ src }, - _TargetWindow{ tgt } {}; + _TargetWindow{ tgt }, + _TabIndex{ tabIndex } {}; }; struct TerminalPage : TerminalPageT diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index e3eadd47310..ab19acea668 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -30,11 +30,11 @@ namespace TerminalApp UInt32 TabIndex { get; }; }; [default_interface] runtimeclass RequestReceiveContentArgs { - RequestReceiveContentArgs(UInt64 src, UInt64 tgt); + RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex); UInt64 SourceWindow { get; }; UInt64 TargetWindow { get; }; - // TODO! UInt32 TabIndex { get; }; + UInt32 TabIndex { get; }; }; interface IDialogPresenter diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 2db0cfc8818..2cc8c0b5190 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1218,7 +1218,7 @@ void AppHost::_handleAttach(const winrt::Windows::Foundation::IInspectable& /*se void AppHost::_handleReceiveContent(const winrt::Windows::Foundation::IInspectable& /* sender */, winrt::TerminalApp::RequestReceiveContentArgs args) { - _windowManager.RequestSendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow() }); + _windowManager.RequestSendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow(), args.TabIndex() }); } // monarch -> Peasant -> us -> Page @@ -1226,5 +1226,5 @@ void AppHost::_handleReceiveContent(const winrt::Windows::Foundation::IInspectab void AppHost::_handleSendContent(const winrt::Windows::Foundation::IInspectable& /* sender */, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args) { - _windowLogic.SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow() }); + _windowLogic.SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow(), args.TabIndex() }); } From b5dbae92442be528e1525b71ea07c307edfbc5ee Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 09:36:57 -0600 Subject: [PATCH 124/234] OMEGA BODGE --- src/cascadia/TerminalApp/TabBase.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp index 52734c9f31e..54b25c48b08 100644 --- a/src/cascadia/TerminalApp/TabBase.cpp +++ b/src/cascadia/TerminalApp/TabBase.cpp @@ -264,6 +264,19 @@ namespace winrt::TerminalApp::implementation tab->_RequestFocusActiveControlHandlers(); } }); + + // BODGY: When the tab is drag/dropped, the TabView gets a + // TabDragStarting. However, the way it is implemented[^1], the + // TabViewItem needs either an Item or a Content for the event to + // include the correct TabViewItem. Otherwise, it will just return the + // first TabViewItem in the TabView with the same Content as the dragged + // tab (which, if the Content is null, will be the _first_ tab). + // + // So here, we'll stick an empty border in, just so that every tab has a + // Content which is not equal to the others. + // + // [^1]: microsoft-ui-xaml/blob/92fbfcd55f05c92ac65569f5d284c5b36492091e/dev/TabView/TabView.cpp#L751-L758 + TabViewItem().Content(winrt::WUX::Controls::Border{}); } std::optional TabBase::GetTabColor() From 9ec9da3da22a98302b24406a77077ad438f3b717 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 09:38:55 -0600 Subject: [PATCH 125/234] ACTUALLY put the tab in the right place after dropping 1 TODOs left --- src/cascadia/TerminalApp/TerminalPage.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 50b73545b4a..9f0e85cca02 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2070,6 +2070,21 @@ namespace winrt::TerminalApp::implementation { _actionDispatch->DoAction(action); } + + if (!firstIsSplitPane && tabIndex != -1) + { + // Move the currently active tab to the requested index Use the + // currently focused tab index, because we don't know if the new tab + // opened at the end of the list, or adjacent to the previously + // active tab. This is affected by the user's "newTabPosition" + // setting. + if (auto focusedTabIndex = _GetFocusedTabIndex()) + { + const auto source = *focusedTabIndex; + _TryMoveTab(source, tabIndex); + } + // else: This shouldn't really be possible, because the tab we _just_ opened should be active. + } } // Method Description: @@ -4559,9 +4574,9 @@ namespace winrt::TerminalApp::implementation co_return; } - // TODO! Figure out where in the tab strip we're dropping this tab. Add - // that index to the request. I believe the WUI sample app has example - // code for this + // Figure out where in the tab strip we're dropping this tab. Add that + // index to the request. This is largely taken from the WinUIq sample + // app. // We need to be on OUR UI thread to figure out where we dropped auto weakThis{ get_weak() }; From 70450afe7e9ea7a8028fb0e2fbe5d79df594a387 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 10:24:22 -0600 Subject: [PATCH 126/234] 0 TODOs left --- src/cascadia/TerminalApp/TabManagement.cpp | 5 +++++ src/cascadia/TerminalApp/TerminalPage.cpp | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 5283786da63..0d546d39349 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -512,6 +512,11 @@ namespace winrt::TerminalApp::implementation _mruTabs.RemoveAt(mruIndex); } + if (_stashedDraggedTab && *_stashedDraggedTab == tab) + { + _stashedDraggedTab = nullptr; + } + _tabs.RemoveAt(tabIndex); _tabView.TabItems().RemoveAt(tabIndex); _UpdateTabIndices(); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 9f0e85cca02..1bea7505aa5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2071,6 +2071,10 @@ namespace winrt::TerminalApp::implementation _actionDispatch->DoAction(action); } + // After handling all the actions, then re-check the tabIndex. We might + // have been called as a part of a tab drag/drop. In that case, the + // tabIndex is actually relevant, and we need to move the tab we just + // made into position. if (!firstIsSplitPane && tabIndex != -1) { // Move the currently active tab to the requested index Use the @@ -4639,10 +4643,8 @@ namespace winrt::TerminalApp::implementation auto startupActions = _stashedDraggedTab->BuildStartupActions(true); _DetachTabFromWindow(_stashedDraggedTab); _MoveContent(startupActions, winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, args.TabIndex()); + // _RemoveTab will make sure to null out the _stashedDraggedTab _RemoveTab(*_stashedDraggedTab); - - // TODO! _whenever_ we close the stashed dragged tab, we should null it. I think that's just _RemoveTab, but to the dilligence you donkey - _stashedDraggedTab = nullptr; } } } From 614ec58c316c88eb7019147f2178d8193d9bd104 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 10:25:50 -0600 Subject: [PATCH 127/234] spel? I think that's it? --- 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 1bea7505aa5..d6b54cf4628 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4579,7 +4579,7 @@ namespace winrt::TerminalApp::implementation } // Figure out where in the tab strip we're dropping this tab. Add that - // index to the request. This is largely taken from the WinUIq sample + // index to the request. This is largely taken from the WinUI sample // app. // We need to be on OUR UI thread to figure out where we dropped From b1a867db6c498ec804ce407890d3f710717cd536 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 12:26:05 -0600 Subject: [PATCH 128/234] [silmarillion] Disable the UIA renderer before detaching --- src/cascadia/TerminalControl/ControlCore.cpp | 4 ++++ src/cascadia/TerminalControl/ControlCore.h | 1 + src/cascadia/TerminalControl/ControlCore.idl | 2 -- .../TerminalControl/ControlInteractivity.cpp | 20 ++++++++++++++++++- .../TerminalControl/ControlInteractivity.h | 1 + .../TerminalControl/ControlInteractivity.idl | 1 + src/cascadia/TerminalControl/TermControl.cpp | 2 +- src/renderer/base/renderer.cpp | 14 +++++++++++++ src/renderer/base/renderer.hpp | 1 + 9 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 9eb81fc8b6f..38de95593ed 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1687,6 +1687,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // _renderer will always exist since it's introduced in the ctor _renderer->AddRenderEngine(pEngine); } + void ControlCore::DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine) + { + _renderer->RemoveRenderEngine(pEngine); + } bool ControlCore::IsInReadOnlyMode() const { diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index a6f572b22f1..6e8eb9f95a6 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -196,6 +196,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool& selectionNeedsToBeCopied); void AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine); + void DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine); bool IsInReadOnlyMode() const; void ToggleReadOnlyMode(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index d69923c5f6f..cabd0550609 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -71,8 +71,6 @@ namespace Microsoft.Terminal.Control void UpdateSettings(IControlSettings settings, IControlAppearance appearance); void ApplyAppearance(Boolean focused); - void Detach(); - IControlSettings Settings { get; }; IControlAppearance FocusedAppearance { get; }; IControlAppearance UnfocusedAppearance { get; }; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index bb3b0837b92..77f4b1de9fc 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -61,9 +61,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _guid; } + void ControlInteractivity::Detach() + { + if (_uiaEngine) + { + LOG_IF_FAILED(_uiaEngine->Disable()); + } + + _core->Detach(); + } + void ControlInteractivity::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { _core->Reparent(keyBindings); + + /*if (_uiaEngine) + { + _uiaEngine->Reparent(); + }*/ } // Method Description: @@ -677,7 +692,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation try { const auto autoPeer = winrt::make_self(this); - + if (_uiaEngine) + { + _core->DetachUiaEngine(_uiaEngine.get()); + } _uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get()); _core->AttachUiaEngine(_uiaEngine.get()); return *autoPeer; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 705fb469502..f67545d4c31 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -45,6 +45,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore Core(); void Close(); + void Detach(); Control::InteractivityAutomationPeer OnCreateAutomationPeer(); ::Microsoft::Console::Render::IRenderData* GetRenderData() const; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 2882f4cd819..a60c2069fef 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -25,6 +25,7 @@ namespace Microsoft.Terminal.Control Guid Id { get; }; void Reparent(Microsoft.Terminal.Control.IKeyBindings keyBindings); + void Detach(); void Close(); InteractivityAutomationPeer OnCreateAutomationPeer(); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index e66e2eeb08d..4482db48056 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2068,7 +2068,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::Detach() { _revokers = {}; - _core.Detach(); + _interactivity.Detach(); _detached = true; } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 0739a9281bb..f3b9517d899 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -1306,6 +1306,20 @@ void Renderer::AddRenderEngine(_In_ IRenderEngine* const pEngine) THROW_HR_MSG(E_UNEXPECTED, "engines array is full"); } +void Renderer::RemoveRenderEngine(_In_ IRenderEngine* const pEngine) +{ + THROW_HR_IF_NULL(E_INVALIDARG, pEngine); + + for (auto& p : _engines) + { + if (p == pEngine) + { + p = nullptr; + return; + } + } +} + // Method Description: // - Registers a callback for when the background color is changed // Arguments: diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index f85b279abeb..e41101f7bf5 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -82,6 +82,7 @@ namespace Microsoft::Console::Render void WaitUntilCanRender(); void AddRenderEngine(_In_ IRenderEngine* const pEngine); + void RemoveRenderEngine(_In_ IRenderEngine* const pEngine); void SetBackgroundColorChangedCallback(std::function pfn); void SetFrameColorChangedCallback(std::function pfn); From 76497251509d90dd658f3a5b48d5af036c7c5528 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 16:51:16 -0600 Subject: [PATCH 129/234] [ainulindale] The Terminal shouldn't crash on a WM_SETTINGCHANGE --- src/cascadia/WindowsTerminal/AppHost.cpp | 10 +++++++++- src/cascadia/WindowsTerminal/AppHost.h | 4 ++++ src/cascadia/WindowsTerminal/WindowEmperor.cpp | 7 ++++++- src/cascadia/WindowsTerminal/WindowThread.cpp | 2 +- src/cascadia/WindowsTerminal/WindowThread.h | 1 + 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 2cc8c0b5190..c815dd86b9b 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -335,7 +335,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 }); @@ -1228,3 +1228,11 @@ void AppHost::_handleSendContent(const winrt::Windows::Foundation::IInspectable& { _windowLogic.SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow(), args.TabIndex() }); } + +// 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 02359d72db0..0b9566531e6 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; @@ -123,6 +125,8 @@ class AppHost void _handleAttach(const winrt::Windows::Foundation::IInspectable& sender, winrt::Microsoft::Terminal::Remoting::AttachRequest args); + void _requestUpdateSettings(); + // Page -> us -> monarch void _handleReceiveContent(const winrt::Windows::Foundation::IInspectable& sender, winrt::TerminalApp::RequestReceiveContentArgs args); diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index fc23db42c2d..72c55c127ad 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -122,10 +122,15 @@ 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(); }); + 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 9a4739659bb6f03efa2b7d9e3c03770b59406758 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 16:51:16 -0600 Subject: [PATCH 130/234] [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 d2a5a4aab7223649984158c53709585b35780c5c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Feb 2023 12:26:05 -0600 Subject: [PATCH 131/234] [silmarillion] Disable the UIA renderer before detaching (cherry picked from commit b1a867db6c498ec804ce407890d3f710717cd536) --- src/cascadia/TerminalControl/ControlCore.cpp | 4 ++++ src/cascadia/TerminalControl/ControlCore.h | 1 + src/cascadia/TerminalControl/ControlCore.idl | 2 -- .../TerminalControl/ControlInteractivity.cpp | 20 ++++++++++++++++++- .../TerminalControl/ControlInteractivity.h | 1 + .../TerminalControl/ControlInteractivity.idl | 2 ++ src/cascadia/TerminalControl/TermControl.cpp | 2 +- src/renderer/base/renderer.cpp | 14 +++++++++++++ src/renderer/base/renderer.hpp | 1 + 9 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 9eb81fc8b6f..38de95593ed 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1687,6 +1687,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // _renderer will always exist since it's introduced in the ctor _renderer->AddRenderEngine(pEngine); } + void ControlCore::DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine) + { + _renderer->RemoveRenderEngine(pEngine); + } bool ControlCore::IsInReadOnlyMode() const { diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index a6f572b22f1..6e8eb9f95a6 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -196,6 +196,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool& selectionNeedsToBeCopied); void AttachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine); + void DetachUiaEngine(::Microsoft::Console::Render::IRenderEngine* const pEngine); bool IsInReadOnlyMode() const; void ToggleReadOnlyMode(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index d69923c5f6f..cabd0550609 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -71,8 +71,6 @@ namespace Microsoft.Terminal.Control void UpdateSettings(IControlSettings settings, IControlAppearance appearance); void ApplyAppearance(Boolean focused); - void Detach(); - IControlSettings Settings { get; }; IControlAppearance FocusedAppearance { get; }; IControlAppearance UnfocusedAppearance { get; }; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index bb3b0837b92..77f4b1de9fc 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -61,9 +61,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _guid; } + void ControlInteractivity::Detach() + { + if (_uiaEngine) + { + LOG_IF_FAILED(_uiaEngine->Disable()); + } + + _core->Detach(); + } + void ControlInteractivity::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { _core->Reparent(keyBindings); + + /*if (_uiaEngine) + { + _uiaEngine->Reparent(); + }*/ } // Method Description: @@ -677,7 +692,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation try { const auto autoPeer = winrt::make_self(this); - + if (_uiaEngine) + { + _core->DetachUiaEngine(_uiaEngine.get()); + } _uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get()); _core->AttachUiaEngine(_uiaEngine.get()); return *autoPeer; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 705fb469502..f67545d4c31 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -45,6 +45,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore Core(); void Close(); + void Detach(); Control::InteractivityAutomationPeer OnCreateAutomationPeer(); ::Microsoft::Console::Render::IRenderData* GetRenderData() const; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index ee0fe4f2fdd..ab29fb7a36e 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -26,6 +26,8 @@ namespace Microsoft.Terminal.Control Guid Id { get; }; void Reparent(Microsoft.Terminal.Control.IKeyBindings keyBindings); + void Detach(); + void Close(); InteractivityAutomationPeer OnCreateAutomationPeer(); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index e66e2eeb08d..4482db48056 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2068,7 +2068,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::Detach() { _revokers = {}; - _core.Detach(); + _interactivity.Detach(); _detached = true; } diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 0739a9281bb..f3b9517d899 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -1306,6 +1306,20 @@ void Renderer::AddRenderEngine(_In_ IRenderEngine* const pEngine) THROW_HR_MSG(E_UNEXPECTED, "engines array is full"); } +void Renderer::RemoveRenderEngine(_In_ IRenderEngine* const pEngine) +{ + THROW_HR_IF_NULL(E_INVALIDARG, pEngine); + + for (auto& p : _engines) + { + if (p == pEngine) + { + p = nullptr; + return; + } + } +} + // Method Description: // - Registers a callback for when the background color is changed // Arguments: diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index f85b279abeb..e41101f7bf5 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -82,6 +82,7 @@ namespace Microsoft::Console::Render void WaitUntilCanRender(); void AddRenderEngine(_In_ IRenderEngine* const pEngine); + void RemoveRenderEngine(_In_ IRenderEngine* const pEngine); void SetBackgroundColorChangedCallback(std::function pfn); void SetFrameColorChangedCallback(std::function pfn); From c79f27c3dac686c5f30b6448831d856a61fd57ca Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Feb 2023 08:32:18 -0600 Subject: [PATCH 132/234] 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 133/234] 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 055cdfedd0204ea1dc8fbd6c58575a82f795316d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Feb 2023 14:03:25 -0600 Subject: [PATCH 134/234] moveTab to a nonexistent window creates that window. This was actually easy enough that I'm tempted to include it in the prior branches --- src/cascadia/Remoting/Monarch.cpp | 2 + src/cascadia/Remoting/Monarch.h | 9 +++ src/cascadia/Remoting/Monarch.idl | 3 + src/cascadia/TerminalApp/TerminalWindow.cpp | 74 +++++++++++++++++---- src/cascadia/TerminalApp/TerminalWindow.h | 2 + src/cascadia/TerminalApp/TerminalWindow.idl | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 13 +++- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 8 files changed, 88 insertions(+), 19 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 0a1ec1a7e1e..2be5b100c43 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1103,6 +1103,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // In the case where window couldn't be found, then create a window // for that name / ID. Do this as a part of tear-out (different than // drag/drop) + auto request = winrt::make_self(window, content); + _RequestNewWindowHandlers(*this, *request); } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 1efc3b6cca8..8ae59839d60 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -48,12 +48,21 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _args{ command.Commandline() }, _CurrentDirectory{ command.CurrentDirectory() } {}; + WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content) : + _Id{ nullptr }, + _WindowName{ window }, + _args{}, + _CurrentDirectory{}, + _Content{ content } {}; + 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); + WINRT_PROPERTY(winrt::hstring, Content); + WINRT_PROPERTY(Windows::Foundation::IReference, InitialBounds); private: winrt::com_array _args; diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 027f66d3690..021eba7a4d7 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -26,6 +26,9 @@ namespace Microsoft.Terminal.Remoting String[] Commandline { get; }; String CurrentDirectory { get; }; + + String Content { get; }; + Windows.Foundation.IReference InitialBounds { get; }; } [default_interface] runtimeclass SummonWindowSelectionArgs { diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 98a4e8a64ef..7f3e94f2537 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -142,25 +142,36 @@ namespace winrt::TerminalApp::implementation _root->WindowProperties(*this); _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()) + // Pass in information about the initial state of the window. + // * If we were suppost to start from serialized "content", do that, + // * If we were supposed to load from a persisted layout, do that + // instead. + // * if we have commandline arguments, Pass commandline args into the + // TerminalPage. + if (!_initialContentArgs.empty()) { - if (layout.TabLayout().Size() > 0) + _root->SetStartupActions(_initialContentArgs); + } + else + { + auto foundLayout = false; + if (const auto& layout = LoadPersistedLayout()) { - std::vector actions; - for (const auto& a : layout.TabLayout()) + if (layout.TabLayout().Size() > 0) { - actions.emplace_back(a); + std::vector actions; + for (const auto& a : layout.TabLayout()) + { + actions.emplace_back(a); + } + _root->SetStartupActions(actions); + foundLayout = true; } - _root->SetStartupActions(actions); - foundLayout = true; } - } - if (!foundLayout) - { - _root->SetStartupActions(_appArgs.GetStartupActions()); + if (!foundLayout) + { + _root->SetStartupActions(_appArgs.GetStartupActions()); + } } // Check if we were started as a COM server for inbound connections of console sessions @@ -1008,6 +1019,41 @@ namespace winrt::TerminalApp::implementation return result; } + void TerminalWindow::SetStartupContent(winrt::hstring content) + { + try + { + auto args = ActionAndArgs::Deserialize(content); + if (args == nullptr || + args.Size() == 0) + { + return; + } + + const auto& firstAction = args.GetAt(0); + const bool firstIsSplitPane{ firstAction.Action() == ShortcutAction::SplitPane }; + if (firstIsSplitPane) + { + // Create the equivalent NewTab action. + const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, + Settings::Model::NewTabArgs(firstAction.Args() ? + firstAction.Args().try_as().TerminalArgs() : + nullptr) }; + args.SetAt(0, newAction); + } + + for (const auto& action : args) + { + _initialContentArgs.push_back(action); + } + // _initialContentArgs = std::vector{ std::move(args) }; + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + } + } + // Method Description: // - Parse the provided commandline arguments into actions, and try to // perform them immediately. diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 669220a85e9..8519e5775eb 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -53,6 +53,7 @@ namespace winrt::TerminalApp::implementation bool HasCommandlineArguments() const noexcept; int32_t SetStartupCommandline(array_view actions); + void SetStartupContent(winrt::hstring content); int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); void SetSettingsStartupArgs(const std::vector& actions); winrt::hstring ParseCommandlineMessage(); @@ -168,6 +169,7 @@ namespace winrt::TerminalApp::implementation TerminalApp::SettingsLoadEventArgs _initialLoadResult{ nullptr }; TerminalApp::ContentManager _manager{ nullptr }; + std::vector _initialContentArgs; 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 628742289c1..2377f03adb2 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -58,7 +58,7 @@ namespace TerminalApp Boolean HasCommandlineArguments(); Int32 SetStartupCommandline(String[] commands); - + void SetStartupContent(String json); Int32 ExecuteCommandline(String[] commands, String cwd); String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index c815dd86b9b..b40d149e77d 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -38,7 +38,7 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, _windowLogic{ nullptr }, _window{ nullptr } { - _HandleCommandlineArgs(); + _HandleCommandlineArgs(args); // _HandleCommandlineArgs will create a _windowLogic _useNonClientArea = _windowLogic.GetShowTabsInTitlebar(); @@ -169,7 +169,7 @@ void AppHost::s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResu // - // Return Value: // - -void AppHost::_HandleCommandlineArgs() +void AppHost::_HandleCommandlineArgs(const Remoting::WindowRequestedArgs& windowArgs) { // 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. @@ -178,7 +178,13 @@ void AppHost::_HandleCommandlineArgs() if (_peasant) { const auto& args{ _peasant.InitialArgs() }; - if (args) + + if (!windowArgs.Content().empty()) + { + _windowLogic.SetStartupContent(windowArgs.Content()); + _shouldCreateWindow = true; // TODO! I don't think we actually use this anymore + } + else if (args) { const auto result = _windowLogic.SetStartupCommandline(args.Commandline()); const auto message = _windowLogic.ParseCommandlineMessage(); @@ -207,6 +213,7 @@ void AppHost::_HandleCommandlineArgs() return; } + // After handling the initial args, hookup the callback for handling // future commandline invocations. When our peasant is told to execute a // commandline (in the future), it'll trigger this callback, that we'll diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 0b9566531e6..c9256699ac9 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -46,7 +46,7 @@ class AppHost void _preInit(); - void _HandleCommandlineArgs(); + void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); winrt::Microsoft::Terminal::Settings::Model::LaunchPosition _GetWindowLaunchPosition(); void _HandleCreateWindow(const HWND hwnd, til::rect proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); From b9dc8c3f5bc36b49a1cfa3299c224b8992c0be96 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Feb 2023 14:25:29 -0600 Subject: [PATCH 135/234] Really, I did it on the first try --- src/cascadia/TerminalApp/TerminalPage.cpp | 30 +++++++++++++++++++++++ src/cascadia/TerminalApp/TerminalPage.h | 1 + 2 files changed, 31 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f0b01035afd..1fb47a64e07 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -247,6 +247,7 @@ namespace winrt::TerminalApp::implementation _tabView.TabDragStarting({ this, &TerminalPage::_onTabDragStarting }); _tabView.TabStripDragOver({ this, &TerminalPage::_onTabStripDragOver }); _tabView.TabStripDrop({ this, &TerminalPage::_onTabStripDrop }); + _tabView.TabDroppedOutside({ this, &TerminalPage::_onTabDroppedOutside }); _CreateNewTabFlyout(); @@ -4647,4 +4648,33 @@ namespace winrt::TerminalApp::implementation _RemoveTab(*_stashedDraggedTab); } } + + winrt::fire_and_forget TerminalPage::_onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, + winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e) + { + // This is called when a tab FROM OUR WINDOW was dropped outside the + // tabview. We already know which tab was being dragged. We'll just + // invoke a moveTab action with the target window being -1. That will + // force the creation of a new window. + + if (!_stashedDraggedTab) + { + co_return; + } + + // must do the work of adding/removing tabs on the UI thread. + auto weakThis{ get_weak() }; + co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); + if (const auto& page{ weakThis.get() }) + { + // `this` is safe to use in here. + auto startupActions = _stashedDraggedTab->BuildStartupActions(true); + _DetachTabFromWindow(_stashedDraggedTab); + // -1 is the magic number for "new window" + // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. + _MoveContent(startupActions, winrt::hstring{ L"-1" }, 0); + // _RemoveTab will make sure to null out the _stashedDraggedTab + _RemoveTab(*_stashedDraggedTab); + } + } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 513c804804c..c5f88ff188d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -499,6 +499,7 @@ namespace winrt::TerminalApp::implementation void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e); void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); + winrt::fire_and_forget _onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e); void _DetachPaneFromWindow(std::shared_ptr pane); void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); From e1472095e210ebb956c0d8b10014ad5eed620a89 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Feb 2023 16:54:17 -0600 Subject: [PATCH 136/234] events aren't really scaled correctly, but it does work --- src/cascadia/Remoting/Monarch.cpp | 5 ++-- src/cascadia/Remoting/Monarch.h | 7 +++--- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/Remoting/WindowManager.cpp | 5 ++-- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/Remoting/WindowManager.idl | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 20 ++++++++++++++-- src/cascadia/TerminalApp/TerminalPage.h | 6 ++++- src/cascadia/TerminalApp/TerminalPage.idl | 1 + src/cascadia/TerminalApp/TerminalWindow.cpp | 25 ++++++++++++++++++-- src/cascadia/TerminalApp/TerminalWindow.h | 3 ++- src/cascadia/TerminalApp/TerminalWindow.idl | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 26 ++++++++++++++++++--- 13 files changed, 86 insertions(+), 20 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 2be5b100c43..af94694c410 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1054,7 +1054,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Monarch::RequestMoveContent(winrt::hstring window, winrt::hstring content, - uint32_t tabIndex) + uint32_t tabIndex, + Windows::Foundation::IReference windowBounds) { TraceLoggingWrite(g_hRemotingProvider, "Monarch_MoveContent_Requested", @@ -1103,7 +1104,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // In the case where window couldn't be found, then create a window // for that name / ID. Do this as a part of tear-out (different than // drag/drop) - auto request = winrt::make_self(window, content); + auto request = winrt::make_self(window == L"-1" ? L"" : window, content, windowBounds); _RequestNewWindowHandlers(*this, *request); } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 8ae59839d60..8a3a6e561b9 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -48,12 +48,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _args{ command.Commandline() }, _CurrentDirectory{ command.CurrentDirectory() } {}; - WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content) : + WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content, Windows::Foundation::IReference bounds) : _Id{ nullptr }, _WindowName{ window }, _args{}, _CurrentDirectory{}, - _Content{ content } {}; + _Content{ content }, + _InitialBounds{ bounds } {}; void Commandline(const winrt::array_view& value) { _args = { value.begin(), value.end() }; }; winrt::com_array Commandline() { return winrt::com_array{ _args.begin(), _args.end() }; } @@ -90,7 +91,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVectorView GetPeasantInfos(); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); - void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); + void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference windowBounds); void RequestSendContent(Remoting::RequestReceiveContentArgs args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 021eba7a4d7..8f549b4fd74 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -72,7 +72,7 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.Collections.IVectorView GetPeasantInfos { get; }; Windows.Foundation.Collections.IVector GetAllWindowLayouts(); - void RequestMoveContent(String window, String content, UInt32 tabIndex); + void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference bounds); void RequestSendContent(RequestReceiveContentArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 8b810ed7738..fe5738afef4 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -453,10 +453,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::fire_and_forget WindowManager::RequestMoveContent(winrt::hstring window, winrt::hstring content, - uint32_t tabIndex) + uint32_t tabIndex, + Windows::Foundation::IReference windowBounds) { co_await winrt::resume_background(); - _monarch.RequestMoveContent(window, content, tabIndex); + _monarch.RequestMoveContent(window, content, tabIndex, windowBounds); } winrt::fire_and_forget WindowManager::RequestSendContent(Remoting::RequestReceiveContentArgs args) diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index c38d085c975..7387c859b60 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -45,7 +45,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVector GetAllWindowLayouts(); bool DoesQuakeWindowExist(); - winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); + winrt::fire_and_forget RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference windowBounds); winrt::fire_and_forget RequestSendContent(Remoting::RequestReceiveContentArgs args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index af26b8181c1..b9b473c56a7 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -27,7 +27,7 @@ namespace Microsoft.Terminal.Remoting Boolean DoesQuakeWindowExist(); - void RequestMoveContent(String window, String content, UInt32 tabIndex); + void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference bounds); void RequestSendContent(RequestReceiveContentArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 1fb47a64e07..2487f290a41 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1973,13 +1973,18 @@ namespace winrt::TerminalApp::implementation // this. void TerminalPage::_MoveContent(std::vector& actions, const winrt::hstring& windowName, - const uint32_t tabIndex) + const uint32_t tabIndex, + Windows::Foundation::IReference dragPoint) { const auto winRtActions{ winrt::single_threaded_vector(std::move(actions)) }; const auto str{ ActionAndArgs::Serialize(winRtActions) }; const auto request = winrt::make_self(windowName, str, tabIndex); + if (dragPoint) + { + request->WindowPosition(dragPoint); + } _RequestMoveContentHandlers(*this, *request); } @@ -4652,6 +4657,10 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::_onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e) { + // Get the curremt pointer point from the corewindow + auto pointerPoint{ CoreWindow::GetForCurrentThread().PointerPosition() }; + pointerPoint; + // This is called when a tab FROM OUR WINDOW was dropped outside the // tabview. We already know which tab was being dragged. We'll just // invoke a moveTab action with the target window being -1. That will @@ -4672,7 +4681,14 @@ namespace winrt::TerminalApp::implementation _DetachTabFromWindow(_stashedDraggedTab); // -1 is the magic number for "new window" // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. - _MoveContent(startupActions, winrt::hstring{ L"-1" }, 0); + + // til::size actualSize{ til::math::rounding, ActualWidth(), ActualHeight() }; + // til::rect bounds{ til::point{ til::math::rounding, pointerPoint }, actualSize }; + // Windows::Foundation::Rect rect{ static_cast(bounds.left), + // static_cast(bounds.top), + // static_cast(bounds.width()), + // static_cast(bounds.height()) }; + _MoveContent(startupActions, winrt::hstring{ L"-1" }, 0, pointerPoint); // _RemoveTab will make sure to null out the _stashedDraggedTab _RemoveTab(*_stashedDraggedTab); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index c5f88ff188d..f31dab4226e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -57,6 +57,7 @@ namespace winrt::TerminalApp::implementation WINRT_PROPERTY(winrt::hstring, Window); WINRT_PROPERTY(winrt::hstring, Content); WINRT_PROPERTY(uint32_t, TabIndex); + WINRT_PROPERTY(Windows::Foundation::IReference, WindowPosition); public: RequestMoveContentArgs(const winrt::hstring window, const winrt::hstring content, uint32_t tabIndex) : @@ -503,7 +504,10 @@ namespace winrt::TerminalApp::implementation void _DetachPaneFromWindow(std::shared_ptr pane); void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); - void _MoveContent(std::vector& actions, const winrt::hstring& windowName, const uint32_t tabIndex); + void _MoveContent(std::vector& actions, + const winrt::hstring& windowName, + const uint32_t tabIndex, + Windows::Foundation::IReference dragPoint = nullptr); #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 ab19acea668..ce59aa00ea3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -28,6 +28,7 @@ namespace TerminalApp String Window { get; }; String Content { get; }; UInt32 TabIndex { get; }; + Windows.Foundation.IReference WindowPosition { get; }; }; [default_interface] runtimeclass RequestReceiveContentArgs { RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 7f3e94f2537..29884cc885a 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -626,6 +626,17 @@ namespace winrt::TerminalApp::implementation commandlineSize.height); } + if (_contentBounds) + { + // If we're being launched in a snapped mode, then we'll need to + // resize ourselves to fit the content bounds of the window. + proposedSize = { + _contentBounds.Value().Width / scale, + _contentBounds.Value().Height / scale + }; + return proposedSize; + } + // 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. @@ -711,12 +722,21 @@ namespace winrt::TerminalApp::implementation } } - // Commandline args trump everything else + // Commandline args trump everything except for content bounds (tear-out) if (_appArgs.GetPosition().has_value()) { initialPosition = _appArgs.GetPosition().value(); } + if (_contentBounds) + { + // If the user has specified a contentBounds, then we should use that + // to determine the initial position of the window. This is used when + // the user is dragging a tab out of the window, to create a new + // window. + const til::rect bounds = {til::math::rounding, _contentBounds.Value()} ; + initialPosition = { bounds.left, bounds.top }; + } return { initialPosition.X ? initialPosition.X.Value() : defaultInitialX, initialPosition.Y ? initialPosition.Y.Value() : defaultInitialY @@ -1019,8 +1039,9 @@ namespace winrt::TerminalApp::implementation return result; } - void TerminalWindow::SetStartupContent(winrt::hstring content) + void TerminalWindow::SetStartupContent(winrt::hstring content, Windows::Foundation::IReference bounds) { + _contentBounds = bounds; try { auto args = ActionAndArgs::Deserialize(content); diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 8519e5775eb..7ceafec6431 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -53,7 +53,7 @@ namespace winrt::TerminalApp::implementation bool HasCommandlineArguments() const noexcept; int32_t SetStartupCommandline(array_view actions); - void SetStartupContent(winrt::hstring content); + void SetStartupContent(winrt::hstring content, Windows::Foundation::IReference contentBounds); int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); void SetSettingsStartupArgs(const std::vector& actions); winrt::hstring ParseCommandlineMessage(); @@ -158,6 +158,7 @@ namespace winrt::TerminalApp::implementation ::TerminalApp::AppCommandlineArgs _appArgs; bool _gotSettingsStartupActions{ false }; std::vector _settingsStartupArgs{}; + Windows::Foundation::IReference _contentBounds{ nullptr }; winrt::hstring _WindowName{}; uint64_t _WindowId{ 0 }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 2377f03adb2..eb482889f1d 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -58,7 +58,7 @@ namespace TerminalApp Boolean HasCommandlineArguments(); Int32 SetStartupCommandline(String[] commands); - void SetStartupContent(String json); + void SetStartupContent(String json, Windows.Foundation.IReference bounds); Int32 ExecuteCommandline(String[] commands, String cwd); String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index b40d149e77d..54cdb465022 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -181,7 +181,7 @@ void AppHost::_HandleCommandlineArgs(const Remoting::WindowRequestedArgs& window if (!windowArgs.Content().empty()) { - _windowLogic.SetStartupContent(windowArgs.Content()); + _windowLogic.SetStartupContent(windowArgs.Content(), windowArgs.InitialBounds()); _shouldCreateWindow = true; // TODO! I don't think we actually use this anymore } else if (args) @@ -213,7 +213,6 @@ void AppHost::_HandleCommandlineArgs(const Remoting::WindowRequestedArgs& window return; } - // After handling the initial args, hookup the callback for handling // future commandline invocations. When our peasant is told to execute a // commandline (in the future), it'll trigger this callback, that we'll @@ -1210,7 +1209,28 @@ winrt::TerminalApp::TerminalWindow AppHost::Logic() void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::TerminalApp::RequestMoveContentArgs args) { - _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex()); + if (args.WindowPosition() && _window) + { + const til::rect window{ + _window->GetWindowRect()}; + + // const auto dpi = _window->GetCurrentDpi(); + // const auto nonClientArea = _window->GetNonClientFrame(dpi); + + // // The nonClientArea adjustment is negative, so subtract that out. + // // This way we save the user-visible location of the terminal. + // pos.X = window.left - nonClientArea.left; + // pos.Y = window.top; + winrt::Windows::Foundation::Rect rect{ static_cast(args.WindowPosition().Value().X), + static_cast(args.WindowPosition().Value().Y), + static_cast(window.width()), + static_cast(window.height()) }; + _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex(), { rect }); + } + else + { + _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex(), nullptr); + } } void AppHost::_handleAttach(const winrt::Windows::Foundation::IInspectable& /*sender*/, From fa1140dc07326b4074ac95714eede06c8d374dac Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Feb 2023 17:08:46 -0600 Subject: [PATCH 137/234] better scaling --- src/cascadia/Remoting/Monarch.cpp | 11 +++--- src/cascadia/TerminalApp/TerminalWindow.cpp | 26 ++++++++------ src/cascadia/WindowsTerminal/AppHost.cpp | 40 +++++++++++++-------- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index af94694c410..a43f879982e 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1099,12 +1099,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // TODO GH#5000 - // // In the case where window couldn't be found, then create a window - // for that name / ID. Do this as a part of tear-out (different than - // drag/drop) - auto request = winrt::make_self(window == L"-1" ? L"" : window, content, windowBounds); + // for that name / ID. + // + // Don't let the window literally be named "-1", because that's silly + auto request = winrt::make_self(window == L"-1" ? L"" : window, + content, + windowBounds); _RequestNewWindowHandlers(*this, *request); } } diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 29884cc885a..6d1fb58a0ce 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -628,11 +628,12 @@ namespace winrt::TerminalApp::implementation if (_contentBounds) { - // If we're being launched in a snapped mode, then we'll need to - // resize ourselves to fit the content bounds of the window. + // If we've been created as a torn-out window, then we'll need to + // use that size instead. _contentBounds is in raw pixels, so + // convert it to _our_ DIPs. proposedSize = { - _contentBounds.Value().Width / scale, - _contentBounds.Value().Height / scale + _contentBounds.Value().Width * scale, + _contentBounds.Value().Height * scale }; return proposedSize; } @@ -730,11 +731,14 @@ namespace winrt::TerminalApp::implementation if (_contentBounds) { - // If the user has specified a contentBounds, then we should use that - // to determine the initial position of the window. This is used when - // the user is dragging a tab out of the window, to create a new - // window. - const til::rect bounds = {til::math::rounding, _contentBounds.Value()} ; + // If the user has specified a contentBounds, then we should use + // that to determine the initial position of the window. This is + // used when the user is dragging a tab out of the window, to create + // a new window. + // + // contentBounds is in screen pixels, but that's okay! we want to + // return screen pixels out of here. Nailed it. + const til::rect bounds = { til::math::rounding, _contentBounds.Value() }; initialPosition = { bounds.left, bounds.top }; } return { @@ -1039,7 +1043,8 @@ namespace winrt::TerminalApp::implementation return result; } - void TerminalWindow::SetStartupContent(winrt::hstring content, Windows::Foundation::IReference bounds) + void TerminalWindow::SetStartupContent(winrt::hstring content, + Windows::Foundation::IReference bounds) { _contentBounds = bounds; try @@ -1067,7 +1072,6 @@ namespace winrt::TerminalApp::implementation { _initialContentArgs.push_back(action); } - // _initialContentArgs = std::vector{ std::move(args) }; } catch (...) { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 54cdb465022..bcb19836dac 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1211,21 +1211,31 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& { if (args.WindowPosition() && _window) { - const til::rect window{ - _window->GetWindowRect()}; - - // const auto dpi = _window->GetCurrentDpi(); - // const auto nonClientArea = _window->GetNonClientFrame(dpi); - - // // The nonClientArea adjustment is negative, so subtract that out. - // // This way we save the user-visible location of the terminal. - // pos.X = window.left - nonClientArea.left; - // pos.Y = window.top; - winrt::Windows::Foundation::Rect rect{ static_cast(args.WindowPosition().Value().X), - static_cast(args.WindowPosition().Value().Y), - static_cast(window.width()), - static_cast(window.height()) }; - _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex(), { rect }); + // The WindowPosition is in DIPs. We need to convert it to pixels. + const til::point dragPositionInDips{ til::math::rounding, args.WindowPosition().Value() }; + const auto scale = _window->GetCurrentDpiScale(); + + const til::point dragPositionInPixels{ + til::math::rounding, + dragPositionInDips.x * scale, + dragPositionInDips.y * scale, + }; + + // Fortunately, the window position is already in pixels. + const til::rect windowBoundsInPixels{ + _window->GetWindowRect() + }; + + // Use the drag event as the new position, and the size of the actual window. + winrt::Windows::Foundation::Rect rect{ static_cast(dragPositionInPixels.x), + static_cast(dragPositionInPixels.y), + static_cast(windowBoundsInPixels.width()), + static_cast(windowBoundsInPixels.height()) }; + + _windowManager.RequestMoveContent(args.Window(), + args.Content(), + args.TabIndex(), + { rect }); } else { From 01388c95b0c8f524177d321d590931aee0c931ab Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 10:05:29 -0600 Subject: [PATCH 138/234] some todo notes --- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++++ src/cascadia/WindowsTerminal/AppHost.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2487f290a41..1f0ecc50af8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4504,6 +4504,10 @@ namespace winrt::TerminalApp::implementation // We're going to be asked for this. _stashedDraggedTab = tabImpl; + // TODO! Stash the offset from where we started the drag to the + // tab's origin. We'll use that offset in the future to help + // position the dropped window. + // Into the DataPackage, let's stash our own window ID. const winrt::hstring id{ fmt::format(L"{}", _WindowProperties.WindowId()) }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index bcb19836dac..ae4726fa872 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1214,18 +1214,20 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& // The WindowPosition is in DIPs. We need to convert it to pixels. const til::point dragPositionInDips{ til::math::rounding, args.WindowPosition().Value() }; const auto scale = _window->GetCurrentDpiScale(); - + const til::point dragPositionInPixels{ til::math::rounding, dragPositionInDips.x * scale, dragPositionInDips.y * scale, - }; + }; // Fortunately, the window position is already in pixels. const til::rect windowBoundsInPixels{ _window->GetWindowRect() }; + // TODO!: adjust for the non-client bounds + // Use the drag event as the new position, and the size of the actual window. winrt::Windows::Foundation::Rect rect{ static_cast(dragPositionInPixels.x), static_cast(dragPositionInPixels.y), From 74260ce6e130fe3f51ba36a1d97a06c9912478d9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 10:09:46 -0600 Subject: [PATCH 139/234] some gross dead code --- src/cascadia/TerminalApp/TerminalPage.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f0b01035afd..b4996ec5954 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4482,13 +4482,6 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) { - // Get the tab impl from this event. - auto eventTab = e.Tab(); - // auto eventTabGood = e.Item().try_as(); - auto iconBad = eventTab.IconSource(); - iconBad; - // auto iconGood = eventTabGood.IconSource(); - // iconGood; auto tabBase = _GetTabByTabViewItem(eventTab); winrt::com_ptr tabImpl; tabImpl.copy_from(winrt::get_self(tabBase)); From b589d092e9cae72186549ca953e9da5b6bdc206d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 11:22:29 -0600 Subject: [PATCH 140/234] 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 141/234] 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 142/234] 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 143/234] 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 4f8d64ec6fe390bad1c9e86c0b10d8d2aba52612 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 13:58:10 -0600 Subject: [PATCH 144/234] non-client borders accounted for: [X] --- src/cascadia/WindowsTerminal/AppHost.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index ae4726fa872..411a93a4b03 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1215,18 +1215,24 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& const til::point dragPositionInDips{ til::math::rounding, args.WindowPosition().Value() }; const auto scale = _window->GetCurrentDpiScale(); - const til::point dragPositionInPixels{ + til::point dragPositionInPixels{ til::math::rounding, dragPositionInDips.x * scale, dragPositionInDips.y * scale, }; // Fortunately, the window position is already in pixels. - const til::rect windowBoundsInPixels{ + til::rect windowBoundsInPixels{ _window->GetWindowRect() }; - // TODO!: adjust for the non-client bounds + // Adjust for the non-client bounds + const auto dpi = _window->GetCurrentDpi(); + const auto nonClientArea = _window->GetNonClientFrame(dpi); + dragPositionInPixels.x -= nonClientArea.left; + dragPositionInPixels.y -= nonClientArea.top; + windowBoundsInPixels.right -= nonClientArea.width(); + windowBoundsInPixels.bottom -= nonClientArea.height(); // Use the drag event as the new position, and the size of the actual window. winrt::Windows::Foundation::Rect rect{ static_cast(dragPositionInPixels.x), From 08acc90e5f439ef21d2791b7adfa0d0338cf1da9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 14:26:00 -0600 Subject: [PATCH 145/234] I'm amazing. Say it. I'm amazing --- src/cascadia/TerminalApp/TerminalPage.cpp | 30 ++++++++++++++++------- src/cascadia/TerminalApp/TerminalPage.h | 4 ++- src/cascadia/WindowsTerminal/AppHost.cpp | 14 ++++++++--- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 1f0ecc50af8..d7315d6d920 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4504,10 +4504,22 @@ namespace winrt::TerminalApp::implementation // We're going to be asked for this. _stashedDraggedTab = tabImpl; - // TODO! Stash the offset from where we started the drag to the + // Stash the offset from where we started the drag to the // tab's origin. We'll use that offset in the future to help // position the dropped window. + // First, the position of the pointer, from the CoreWindow + const til::point pointerPosition{ til::math::rounding, CoreWindow::GetForCurrentThread().PointerPosition() }; + // Next, the position of the tab itself: + const til::point tabPosition{ til::math::rounding, eventTab.TransformToVisual(nullptr).TransformPoint({ 0, 0 }) }; + // Now, we need to add the origin of our CoreWindow to the tab + // position. + const auto& coreWindowBounds{ CoreWindow::GetForCurrentThread().Bounds() }; + const til::point windowOrigin{ til::math::rounding, coreWindowBounds.X, coreWindowBounds.Y }; + const auto realTabPosition = windowOrigin + tabPosition; + // Subtract the two to get the offset. + _dragOffset = til::point{ pointerPosition - realTabPosition }; + // Into the DataPackage, let's stash our own window ID. const winrt::hstring id{ fmt::format(L"{}", _WindowProperties.WindowId()) }; @@ -4683,16 +4695,16 @@ namespace winrt::TerminalApp::implementation // `this` is safe to use in here. auto startupActions = _stashedDraggedTab->BuildStartupActions(true); _DetachTabFromWindow(_stashedDraggedTab); + + // We need to convert the pointer point to a point that we can use + // to position the new window. We'll use the drag offset from before + // so that the tab in the new window is positioned so that it's + // basically still directly under the cursor. + til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; + // -1 is the magic number for "new window" // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. - - // til::size actualSize{ til::math::rounding, ActualWidth(), ActualHeight() }; - // til::rect bounds{ til::point{ til::math::rounding, pointerPoint }, actualSize }; - // Windows::Foundation::Rect rect{ static_cast(bounds.left), - // static_cast(bounds.top), - // static_cast(bounds.width()), - // static_cast(bounds.height()) }; - _MoveContent(startupActions, winrt::hstring{ L"-1" }, 0, pointerPoint); + _MoveContent(startupActions, winrt::hstring{ L"-1" }, 0, adjusted.to_winrt_point()); // _RemoveTab will make sure to null out the _stashedDraggedTab _RemoveTab(*_stashedDraggedTab); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index f31dab4226e..da3d1ae86f0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -265,8 +265,10 @@ namespace winrt::TerminalApp::implementation TerminalApp::ContentManager _manager{ nullptr }; winrt::com_ptr _stashedDraggedTab{ nullptr }; + til::point _dragOffset{ 0, 0 }; - 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(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 411a93a4b03..fdccab4152a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1206,6 +1206,16 @@ winrt::TerminalApp::TerminalWindow AppHost::Logic() return _windowLogic; } +// Method Description: +// - Raised from Page -> us -> manager -> monarch +// - Called when the user attemtpts to move a tab or pane to another window. +// `args` will contain info about the structure of the content being moved, +// and where it should go. +// - If the WindowPosition is filled in, then the user was dragging a tab out of +// this window and dropping it in empty space, indicating it should create a +// new window. In that case, we'll make some adjustments using that info and +// our own window info, so that the new window will be created in the right +// place and with the same size. void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::TerminalApp::RequestMoveContentArgs args) { @@ -1222,9 +1232,7 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& }; // Fortunately, the window position is already in pixels. - til::rect windowBoundsInPixels{ - _window->GetWindowRect() - }; + til::rect windowBoundsInPixels{ _window->GetWindowRect() }; // Adjust for the non-client bounds const auto dpi = _window->GetCurrentDpi(); From 17dbc7b5ce3c994564f4012c6f8bfd1c5fe664f5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Feb 2023 14:41:56 -0600 Subject: [PATCH 146/234] That's it. Ship the PR --- src/cascadia/Remoting/Monarch.cpp | 10 ++++------ src/cascadia/TerminalApp/TerminalPage.cpp | 3 ++- src/cascadia/TerminalControl/TermControl.cpp | 18 ------------------ src/cascadia/WindowsTerminal/WindowEmperor.cpp | 2 +- 4 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index a43f879982e..4db4e3fac10 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1132,16 +1132,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } else { + // We couldn't find the peasant that started the drag. Well that + // sure is weird, but that would indicate that the sender closed + // after starting the drag. No matter. We can just do nothing. + TraceLoggingWrite(g_hRemotingProvider, "Monarch_SendContent_NoWindow", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // TODO GH#5000 - // - // In the case where window couldn't be found, then create a window - // for that name / ID. Do this as a part of tear-out (different than - // drag/drop) } } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index d7315d6d920..4f9596a5a53 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4537,7 +4537,8 @@ namespace winrt::TerminalApp::implementation // to ask us to send our content to them. // * We'll get a TabDroppedOutside to indicate that this tab was // dropped _not_ on a TabView. - // * This we can't handle yet, and is the last point of TODO GH#5000 + // * This will be handled by _onTabDroppedOutside, which will + // raise a MoveContent (to a new window) event. } } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 79b3e66f902..270730f87b3 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1199,12 +1199,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation return true; } - // TODO: GH#5000 - // The Core owning the keybindings is weird. That's for sure. In the - // future, we may want to pass the keybindings into the control - // separately, so the control can have a pointer to an in-proc - // Keybindings object, rather than routing through the ControlCore. - // (see GH#5000) auto bindings = _core.Settings().KeyBindings(); if (!bindings) { @@ -2807,18 +2801,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation IControlSettings TermControl::Settings() const { - // TODO: GH#5000 - // We still need this in a couple places: - // - Pane.cpp uses this for parsing out the StartingTitle, Commandline, - // etc for Pane::GetTerminalArgsForPane. - // - TerminalTab::_CreateToolTipTitle uses the ProfileName for the - // tooltip for the tab. - // - // These both happen on the UI thread right now. In the future, when we - // have to hop across the process boundary to get at the core settings, - // it may make sense to cache these values inside the TermControl - // itself, so it can do the hop once when it's first setup, rather than - // when it's needed by the UI thread. return _core.Settings(); } 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 147/234] 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 148/234] 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 149/234] 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 150/234] 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 151/234] 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 fda16631ccc55ff6737d4d333d3a2c03ef8390c0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 13:20:58 -0600 Subject: [PATCH 152/234] Fix a UiaEngine race that would absolutely block self-host --- src/cascadia/TerminalControl/ControlCore.cpp | 7 +++++++ .../TerminalControl/ControlInteractivity.cpp | 19 +++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 2b294b68a65..e09235f740e 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -231,6 +231,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::Detach() { + // Disable the renderer, so that it doesn't try to start any new frames + // for our engines while we're not attached to anything. + _renderer->WaitForPaintCompletionAndDisable(INFINITE); + _tsfTryRedrawCanvas.reset(); _updatePatternLocations.reset(); _updateScrollBar.reset(); @@ -243,6 +247,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto actualNewSize = _actualFont.GetSize(); // Bubble this up, so our new control knows how big we want the font. _FontSizeChangedHandlers(actualNewSize.width, actualNewSize.height, true); + + // Turn the rendering back on now that we're ready to go. + _renderer->EnablePainting(); _AttachedHandlers(*this, nullptr); } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 77f4b1de9fc..bf6b36d4824 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -65,20 +65,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (_uiaEngine) { + // There's a potential race here where we've removed the TermControl + // from the UI tree, but the UIA engine is in the middle of a paint, + // and the UIA engine will try to dispatch to the + // TermControlAutomationPeer, which (is now)/(will very soon be) gone. + // + // To alleviate, make sure to disable the UIA engine and remove it, + // and ALSO disable the renderer. Core.Detach will take care of the + // WaitForPaintCompletionAndDisable (which will stop the renderer + // after all current engines are done painting). + // + // Simply disabling the UIA engine is not enough, because it's + // possible that it had already started presenting here. LOG_IF_FAILED(_uiaEngine->Disable()); + _core->DetachUiaEngine(_uiaEngine.get()); } - _core->Detach(); } void ControlInteractivity::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { _core->Reparent(keyBindings); - - /*if (_uiaEngine) - { - _uiaEngine->Reparent(); - }*/ } // Method Description: From 7591b0f404a4ebae261b8dd571d12ec85d625802 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 13:20:58 -0600 Subject: [PATCH 153/234] Fix a UiaEngine race that would absolutely block self-host (cherry picked from commit fda16631ccc55ff6737d4d333d3a2c03ef8390c0) --- src/cascadia/TerminalControl/ControlCore.cpp | 7 +++++++ .../TerminalControl/ControlInteractivity.cpp | 19 +++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 2b294b68a65..e09235f740e 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -231,6 +231,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::Detach() { + // Disable the renderer, so that it doesn't try to start any new frames + // for our engines while we're not attached to anything. + _renderer->WaitForPaintCompletionAndDisable(INFINITE); + _tsfTryRedrawCanvas.reset(); _updatePatternLocations.reset(); _updateScrollBar.reset(); @@ -243,6 +247,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto actualNewSize = _actualFont.GetSize(); // Bubble this up, so our new control knows how big we want the font. _FontSizeChangedHandlers(actualNewSize.width, actualNewSize.height, true); + + // Turn the rendering back on now that we're ready to go. + _renderer->EnablePainting(); _AttachedHandlers(*this, nullptr); } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 77f4b1de9fc..bf6b36d4824 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -65,20 +65,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (_uiaEngine) { + // There's a potential race here where we've removed the TermControl + // from the UI tree, but the UIA engine is in the middle of a paint, + // and the UIA engine will try to dispatch to the + // TermControlAutomationPeer, which (is now)/(will very soon be) gone. + // + // To alleviate, make sure to disable the UIA engine and remove it, + // and ALSO disable the renderer. Core.Detach will take care of the + // WaitForPaintCompletionAndDisable (which will stop the renderer + // after all current engines are done painting). + // + // Simply disabling the UIA engine is not enough, because it's + // possible that it had already started presenting here. LOG_IF_FAILED(_uiaEngine->Disable()); + _core->DetachUiaEngine(_uiaEngine.get()); } - _core->Detach(); } void ControlInteractivity::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { _core->Reparent(keyBindings); - - /*if (_uiaEngine) - { - _uiaEngine->Reparent(); - }*/ } // Method Description: From 3aa083ba9a4000eb6339d409316bfb3c3aaf1844 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 14:00:09 -0600 Subject: [PATCH 154/234] 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 491fa62a5acc58111554736c63e3667c1e593788 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 2 Mar 2023 16:53:06 -0600 Subject: [PATCH 155/234] I'm not dead yet --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 9c7daa3c39f..882c40a49a3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4533,6 +4533,8 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) { + // Get the tab impl from this event. + auto eventTab = e.Tab(); auto tabBase = _GetTabByTabViewItem(eventTab); winrt::com_ptr tabImpl; tabImpl.copy_from(winrt::get_self(tabBase)); From 35c7474abe61aab08598d10b5e62923d3d978471 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Mar 2023 13:25:45 -0600 Subject: [PATCH 156/234] [ainulindale] Prevent a race between the Emperor and the window logic when updating settings --- 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 | 12 ++++-- 9 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 0013bdc8f9c..4bc1be1bc6b 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 1a203b935a7..a315da48e21 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -66,6 +66,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 144e3916da6..2f6fd5adf7d 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -43,8 +43,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); @@ -54,6 +56,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 942397cf9a4..65e924da5d4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -102,37 +102,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 4a0ab9e131c..b5c15b2a6b4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -88,7 +88,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 f871393c5be..857cf95359b 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -265,7 +265,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 @@ -338,12 +338,6 @@ namespace winrt::TerminalApp::implementation { return _settings.GlobalSettings().AlwaysShowNotificationIcon(); } - bool TerminalWindow::RequestsTrayIcon() - { - return _settings.GlobalSettings().AlwaysShowNotificationIcon() || - _settings.GlobalSettings().MinimizeToNotificationArea() || - IsQuakeWindow(); - } bool TerminalWindow::GetShowTitleInTitlebar() { @@ -793,14 +787,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 9d5de7c517c..a6f927a3421 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -107,7 +107,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 8810d52c014..154cd56eaef 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 4025fe4bd7f..71bcb8bc416 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(); }); @@ -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 9a9fa4392a918708bafc5192dbc347cf4053feaf Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 6 Mar 2023 13:25:45 -0600 Subject: [PATCH 157/234] [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 158/234] 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 159/234] 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 160/234] 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 161/234] .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 162/234] 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 163/234] 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 164/234] 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 165/234] 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 166/234] 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 167/234] 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 168/234] 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 169/234] 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 170/234] 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 171/234] 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 172/234] 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 173/234] 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 174/234] 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 175/234] 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 176/234] 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 177/234] 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 178/234] 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 179/234] 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 180/234] 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 181/234] 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 6107495ea8676ff4aa3e50f429869e3072507e95 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Mar 2023 12:56:06 -0500 Subject: [PATCH 182/234] minor pr nits --- src/cascadia/Remoting/Peasant.idl | 1 - src/cascadia/TerminalApp/ContentManager.h | 3 +++ src/cascadia/TerminalApp/TerminalPage.cpp | 14 ++++++-------- src/cascadia/TerminalApp/TerminalTab.cpp | 6 ++++-- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 -- .../Resources/en-US/Resources.resw | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 1257bda4b8c..a26604e4bf6 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -98,7 +98,6 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler QuitRequested; event Windows.Foundation.TypedEventHandler AttachRequested; - }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index c656231047c..859e81a679f 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -18,6 +18,9 @@ Class Name: other threads. - When you want to create a new TermControl, call CreateCore to instantiate a new content with a GUID for later reparenting. +- Detach can be used to temporarily remove a content from its hosted + TermControl. After detaching, you can still use LookupCore & + TermControl::AttachContent to re-attach to the content. --*/ #pragma once diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 9ddc1808eea..04096f7b9ff 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1990,7 +1990,7 @@ namespace winrt::TerminalApp::implementation { if (const auto terminalTab{ _GetFocusedTabImpl() }) { - auto startupActions = terminalTab->BuildStartupActions(true); + const auto startupActions = terminalTab->BuildStartupActions(true); _DetachTabFromWindow(terminalTab); _MoveContent(startupActions, args.Window(), 0); _RemoveTab(*terminalTab); @@ -1998,13 +1998,13 @@ namespace winrt::TerminalApp::implementation } } - auto direction = args.Direction(); + const auto direction = args.Direction(); if (direction != MoveTabDirection::None) { if (auto focusedTabIndex = _GetFocusedTabIndex()) { - auto currentTabIndex = focusedTabIndex.value(); - auto delta = direction == MoveTabDirection::Forward ? 1 : -1; + const auto currentTabIndex = focusedTabIndex.value(); + const auto delta = direction == MoveTabDirection::Forward ? 1 : -1; _TryMoveTab(currentTabIndex, currentTabIndex + delta); } } @@ -2053,8 +2053,7 @@ namespace winrt::TerminalApp::implementation } else { - const auto& firstAction = args.GetAt(0); - if (firstAction.Action() == ShortcutAction::SplitPane) + if (firstIsSplitPane) { // Create the equivalent NewTab action. const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, @@ -2792,8 +2791,7 @@ namespace winrt::TerminalApp::implementation const auto control = _InitControlFromContent(newTerminalArgs.ContentGuid()); _RegisterTerminalEvents(control); - auto resultPane = std::make_shared(profile, control); - return resultPane; + return std::make_shared(profile, control); } TerminalSettingsCreateResult controlSettings{ nullptr }; diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index e05e5521eac..aca79d1f74b 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -784,7 +784,9 @@ namespace winrt::TerminalApp::implementation bool TerminalTab::FocusPane(const uint32_t id) { if (_rootPane == nullptr) + { return false; + } _changingActivePane = true; const auto res = _rootPane->FocusPane(id); _changingActivePane = false; @@ -907,7 +909,7 @@ namespace winrt::TerminalApp::implementation } }); - events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget { co_await wil::resume_foreground(dispatcher); // Check if Tab's lifetime has expired if (auto tab{ weakThis.get() }) @@ -1112,7 +1114,7 @@ namespace winrt::TerminalApp::implementation // Add a Closed event handler to the Pane. If the pane closes out from // underneath us, and it's zoomed, we want to be able to make sure to // update our state accordingly to un-zoom that pane. See GH#7252. - auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) -> winrt::fire_and_forget { + auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget { if (auto tab{ weakThis.get() }) { if (tab->_zoomedPane) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 61397421ea9..03f7aff911e 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1204,8 +1204,6 @@ namespace winrt::TerminalApp::implementation } } - //////////////////////////////////////////////////////////////////////////// - bool TerminalWindow::ShouldImmediatelyHandoffToElevated() { return _root != nullptr ? _root->ShouldImmediatelyHandoffToElevated(_settings) : false; diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index a4ca72cc3a1..526687ad79d 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -167,7 +167,7 @@ {0} will be replaced with a "forward" / "backward" - Move tab to window {0} + Move tab to window "{0}" {0} will be replaced with a user-specified name of a window From d886c656b6c38bab4a6019b1b773b2cb1c6a7f87 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Mar 2023 13:00:41 -0500 Subject: [PATCH 183/234] This actually can't be const --- src/cascadia/TerminalApp/TerminalPage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 04096f7b9ff..fdde4c7f219 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1969,6 +1969,8 @@ namespace winrt::TerminalApp::implementation // event. Our Window will raise that to the window manager / monarch, who // will dispatch this blob of json back to the window that should handle // this. + // - `actions` will be emptied into a winrt IVector as a part of this method + // and should be expected to be empty after this call. void TerminalPage::_MoveContent(std::vector& actions, const winrt::hstring& windowName, const uint32_t tabIndex) @@ -1990,7 +1992,7 @@ namespace winrt::TerminalApp::implementation { if (const auto terminalTab{ _GetFocusedTabImpl() }) { - const auto startupActions = terminalTab->BuildStartupActions(true); + auto startupActions = terminalTab->BuildStartupActions(true); _DetachTabFromWindow(terminalTab); _MoveContent(startupActions, args.Window(), 0); _RemoveTab(*terminalTab); From 1cc3100a6cb1c0d77ca378d368ca731dbc2bb005 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 13 Mar 2023 13:04:26 -0500 Subject: [PATCH 184/234] halt, explain yourself --- src/cascadia/TerminalApp/SettingsTab.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp index 16b4ab454fd..7f060566059 100644 --- a/src/cascadia/TerminalApp/SettingsTab.cpp +++ b/src/cascadia/TerminalApp/SettingsTab.cpp @@ -47,8 +47,11 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Creates a list of actions that can be run to recreate the state of this tab // Arguments: - // - - // Return Value: + // - asContent: unused. There's nothing different we need to do when + // serializing the settings tab for moving to another window. If we ever + // really want to support opening the SUI to a specific page, we can + // re-evaluate including that arg in this action then. + // Return Value: // - The list of actions. std::vector SettingsTab::BuildStartupActions(const bool /*asContent*/) const { From e41c8353d628aeaab9cfff5ee3652f21d785ed04 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Mar 2023 05:02:44 -0500 Subject: [PATCH 185/234] [an unexpected party] This was a bad merge conflict that resulted in windows entirely failing to start --- src/cascadia/TerminalApp/TerminalWindow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index c0eab3f26be..a7cdec53b9a 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -165,6 +165,9 @@ namespace winrt::TerminalApp::implementation } _root->SetStartupActions(actions); } + else { + _root->SetStartupActions(_appArgs.GetStartupActions()); + } } // Check if we were started as a COM server for inbound connections of console sessions From 3bc25ab5399ea3abbde936fa52f6dc14b1cbe5c8 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 14 Mar 2023 05:53:19 -0500 Subject: [PATCH 186/234] *shakes fist* VS --- src/cascadia/TerminalApp/TerminalWindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index a7cdec53b9a..9356ed00cc4 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -165,7 +165,8 @@ namespace winrt::TerminalApp::implementation } _root->SetStartupActions(actions); } - else { + else + { _root->SetStartupActions(_appArgs.GetStartupActions()); } } From 5b3dc083e03c000b7ed24406e25f7624290ad62c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Mar 2023 08:42:41 -0500 Subject: [PATCH 187/234] 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 188/234] [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 189/234] [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 f60c6d20a6b9d50f4f4edc04b63ae707dbae563a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 16 Mar 2023 14:00:48 -0500 Subject: [PATCH 190/234] [party] tear out of maximized doesn't open maximized (cherry picked from commit 805b880dacec73a3daf7a612308280c352a9e2ae) --- src/cascadia/TerminalApp/TerminalWindow.cpp | 11 +++--- src/cascadia/WindowsTerminal/AppHost.cpp | 39 ++++++++++++++++----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 9356ed00cc4..8f5c251cd68 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -638,13 +638,12 @@ namespace winrt::TerminalApp::implementation if (_contentBounds) { // If we've been created as a torn-out window, then we'll need to - // use that size instead. _contentBounds is in raw pixels, so - // convert it to _our_ DIPs. - proposedSize = { - _contentBounds.Value().Width * scale, - _contentBounds.Value().Height * scale + // use that size instead. _contentBounds is in raw pixels. Huzzah! + // Just return that. + return { + _contentBounds.Value().Width, + _contentBounds.Value().Height }; - return proposedSize; } // GH#2061 - If the global setting "Always show tab bar" is diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 7b27721f14f..48a4e080363 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1243,20 +1243,43 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& // Fortunately, the window position is already in pixels. til::rect windowBoundsInPixels{ _window->GetWindowRect() }; + til::size windowSize{ windowBoundsInPixels.size() }; - // Adjust for the non-client bounds const auto dpi = _window->GetCurrentDpi(); - const auto nonClientArea = _window->GetNonClientFrame(dpi); - dragPositionInPixels.x -= nonClientArea.left; - dragPositionInPixels.y -= nonClientArea.top; - windowBoundsInPixels.right -= nonClientArea.width(); - windowBoundsInPixels.bottom -= nonClientArea.height(); + const auto nonClientFrame = _window->GetNonClientFrame(dpi); + + // If this window is maximized, you don't _really_ want the new window + // showing up at the same size (the size of a maximized window). You + // want it to just make a normal-sized window. This logic was taken out + // of AppHost::_HandleCreateWindow. + if (IsZoomed(_window->GetHandle())) + { + auto initialSize = _windowLogic.GetLaunchDimensions(dpi); + + const auto islandWidth = Utils::ClampToShortMax(static_cast(ceil(initialSize.Width)), 1); + const auto islandHeight = Utils::ClampToShortMax(static_cast(ceil(initialSize.Height)), 1); + + // Get the size of a window we'd need to host that client rect. This will + // add the titlebar space. + const til::size nonClientSize{ _window->GetTotalNonClientExclusiveSize(dpi) }; + + auto adjustedWidth = islandWidth + nonClientSize.width; + auto adjustedHeight = islandHeight + nonClientSize.height; + + windowSize = til::size{ Utils::ClampToShortMax(adjustedWidth, 1), + Utils::ClampToShortMax(adjustedHeight, 1) }; + } + + // Adjust for the non-client bounds + dragPositionInPixels.x -= nonClientFrame.left; + dragPositionInPixels.y -= nonClientFrame.top; + windowSize = windowSize - nonClientFrame.size(); // Use the drag event as the new position, and the size of the actual window. winrt::Windows::Foundation::Rect rect{ static_cast(dragPositionInPixels.x), static_cast(dragPositionInPixels.y), - static_cast(windowBoundsInPixels.width()), - static_cast(windowBoundsInPixels.height()) }; + static_cast(windowSize.width), + static_cast(windowSize.height) }; _windowManager.RequestMoveContent(args.Window(), args.Content(), From dea194dd770ef5dd7a34a3f2899a56771e907d4e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Mar 2023 09:29:24 -0500 Subject: [PATCH 191/234] [party] actually DON'T respect the launch mode during tearout (cherry picked from commit b107f7d20094bd60ee9042640e1514a982967329) --- src/cascadia/TerminalApp/TerminalWindow.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 8f5c251cd68..0375e7a10cc 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -693,6 +693,11 @@ namespace winrt::TerminalApp::implementation // - LaunchMode enum that indicates the launch mode LaunchMode TerminalWindow::GetLaunchMode() { + if (_contentBounds) + { + return LaunchMode::DefaultMode; + } + // 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(); @@ -757,8 +762,11 @@ namespace winrt::TerminalApp::implementation 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(); + // If + // * the position has been specified on the commandline, + // * We're opening the window as a part of tearout (and _contentBounds were set) + // then don't center on launch + return !_contentBounds && _settings.GlobalSettings().CenterOnLaunch() && !_appArgs.GetPosition().has_value(); } // Method Description: From 11f995793cafb7fa535893848f3faacaea915a91 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 17 Mar 2023 11:50:06 -0500 Subject: [PATCH 192/234] [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 8a9d24824351c13a31f5bf142f05eae2978730f2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 20 Mar 2023 14:41:22 -0500 Subject: [PATCH 193/234] some nits --- src/cascadia/TerminalApp/TerminalPage.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e97acbc5bb9..e3e493ba352 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2094,7 +2094,7 @@ namespace winrt::TerminalApp::implementation // opened at the end of the list, or adjacent to the previously // active tab. This is affected by the user's "newTabPosition" // setting. - if (auto focusedTabIndex = _GetFocusedTabIndex()) + if (const auto focusedTabIndex = _GetFocusedTabIndex()) { const auto source = *focusedTabIndex; _TryMoveTab(source, tabIndex); @@ -4601,8 +4601,8 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) { // Get the tab impl from this event. - auto eventTab = e.Tab(); - auto tabBase = _GetTabByTabViewItem(eventTab); + const auto& eventTab = e.Tab(); + const auto& tabBase = _GetTabByTabViewItem(eventTab); winrt::com_ptr tabImpl; tabImpl.copy_from(winrt::get_self(tabBase)); if (tabImpl) @@ -4656,7 +4656,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::DragEventArgs e) { // Get the PID and make sure it is the same as ours. - if (auto pidObj{ e.DataView().Properties().TryLookup(L"pid") }) + if (const auto& pidObj{ e.DataView().Properties().TryLookup(L"pid") }) { const auto pidStr{ winrt::unbox_value(pidObj) }; uint32_t pidNum; @@ -4675,7 +4675,7 @@ namespace winrt::TerminalApp::implementation co_return; } - auto windowIdObj{ e.DataView().Properties().TryLookup(L"windowId") }; + const auto& windowIdObj{ e.DataView().Properties().TryLookup(L"windowId") }; if (windowIdObj == nullptr) { // No windowId? Bail. @@ -4706,7 +4706,7 @@ namespace winrt::TerminalApp::implementation // Determine which items in the list our pointer is between. for (auto i = 0u; i < _tabView.TabItems().Size(); i++) { - if (auto item{ _tabView.ContainerFromIndex(i).try_as() }) + if (const auto& item{ _tabView.ContainerFromIndex(i).try_as() }) { const auto posX{ e.GetPosition(item).X }; const auto itemWidth{ item.ActualWidth() }; From b02b93537925f1127f24af9a41c9af4c2ca4b75f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Mar 2023 06:54:53 -0500 Subject: [PATCH 194/234] 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 195/234] 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 196/234] 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 197/234] 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 87e5283c243dfbf75a6caa70dc9c60bf5b65f75b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 21 Mar 2023 12:57:20 -0500 Subject: [PATCH 198/234] all my homies love numeric ids --- src/cascadia/TerminalApp/ContentManager.cpp | 6 +++--- src/cascadia/TerminalApp/ContentManager.h | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 857ba9807ad..9a80e5bca80 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -51,7 +51,7 @@ namespace winrt::TerminalApp::implementation { control.Detach(); content.Attached({ get_weak(), &ContentManager::_finalizeDetach }); - _recentlyDetachedContent.Insert(contentId, content); + _recentlyDetachedContent.emplace(contentId, content); } } @@ -60,7 +60,7 @@ namespace winrt::TerminalApp::implementation { if (const auto& content{ sender.try_as() }) { - _recentlyDetachedContent.Remove(content.Id()); + _recentlyDetachedContent.erase(content.Id()); } } @@ -71,7 +71,7 @@ namespace winrt::TerminalApp::implementation { const auto& contentId{ content.Id() }; _content.erase(contentId); - _recentlyDetachedContent.TryRemove(contentId); + _recentlyDetachedContent.erase(contentId); } } } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index af4549d253f..0ecee8af4d6 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -42,10 +42,7 @@ namespace winrt::TerminalApp::implementation private: std::unordered_map _content; - - Windows::Foundation::Collections::IMap _recentlyDetachedContent{ - winrt::multi_threaded_map() - }; + std::unordered_map _recentlyDetachedContent; void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable e); From 697b420d9b607b5057ec2d2e6196c43cf959c22e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 Mar 2023 11:35:33 -0500 Subject: [PATCH 199/234] 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); } From 9179992d50eaa852e3a02a2a3cfeda73bf14ec5b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 Mar 2023 13:10:31 -0500 Subject: [PATCH 200/234] Fix `wt -w _quake` by not throwing when setting the window name If we get initialized with a window name, this will be called before XAML is stood up, and constructing a PropertyChangedEventArgs will throw. So don't. --- src/cascadia/TerminalApp/TerminalWindow.cpp | 15 +++++++++++++-- src/cascadia/WindowsTerminal/AppHost.cpp | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 796f5c9e0e9..8b8e580948e 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1155,6 +1155,10 @@ namespace winrt::TerminalApp::implementation { const auto oldIsQuakeMode = _WindowProperties->IsQuakeWindow(); _WindowProperties->WindowName(name); + if (!_root) + { + return; + } const auto newIsQuakeMode = _WindowProperties->IsQuakeWindow(); if (newIsQuakeMode != oldIsQuakeMode) { @@ -1218,8 +1222,15 @@ namespace winrt::TerminalApp::implementation if (_WindowName != value) { _WindowName = value; - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowName" }); - _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); + // If we get initialized with a window name, this will be called + // before XAML is stood up, and constructing a + // PropertyChangedEventArgs will throw. + try + { + _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowName" }); + _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); + } + CATCH_LOG(); } } uint64_t WindowProperties::WindowId() const noexcept diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index f190940e837..aa062a4d722 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -299,6 +299,7 @@ void AppHost::Initialize() withWindow->Initialize(_window->GetHandle()); } + if (_useNonClientArea) { // Register our callback for when the app's non-client content changes. From 362679536af2af76c4956a6061bf96be5dd749b7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 22 Mar 2023 14:46:28 -0500 Subject: [PATCH 201/234] runformat --- src/cascadia/TerminalApp/TerminalTab.cpp | 4 ++-- src/cascadia/WindowsTerminal/AppHost.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index aca79d1f74b..d06b815e04a 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -909,7 +909,7 @@ namespace winrt::TerminalApp::implementation } }); - events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto &&) -> winrt::fire_and_forget { + events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { co_await wil::resume_foreground(dispatcher); // Check if Tab's lifetime has expired if (auto tab{ weakThis.get() }) @@ -1114,7 +1114,7 @@ namespace winrt::TerminalApp::implementation // Add a Closed event handler to the Pane. If the pane closes out from // underneath us, and it's zoomed, we want to be able to make sure to // update our state accordingly to un-zoom that pane. See GH#7252. - auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto && /*e*/) -> winrt::fire_and_forget { + auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) -> winrt::fire_and_forget { if (auto tab{ weakThis.get() }) { if (tab->_zoomedPane) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index aa062a4d722..f190940e837 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -299,7 +299,6 @@ void AppHost::Initialize() withWindow->Initialize(_window->GetHandle()); } - if (_useNonClientArea) { // Register our callback for when the app's non-client content changes. From f898d39ce96d9ae01c6c655cf89feb7a2013dbef Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Mar 2023 16:17:33 -0500 Subject: [PATCH 202/234] this was a lot of nits --- src/cascadia/TerminalApp/TerminalPage.cpp | 7 +++---- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 17 ++++------------- src/cascadia/WindowsTerminal/AppHost.h | 2 -- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 74303653209..71c041a6df8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4787,9 +4787,8 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::_onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e) { - // Get the curremt pointer point from the corewindow - auto pointerPoint{ CoreWindow::GetForCurrentThread().PointerPosition() }; - pointerPoint; + // Get the current pointer point from the CoreWindow + const auto& pointerPoint{ CoreWindow::GetForCurrentThread().PointerPosition() }; // This is called when a tab FROM OUR WINDOW was dropped outside the // tabview. We already know which tab was being dragged. We'll just @@ -4814,7 +4813,7 @@ namespace winrt::TerminalApp::implementation // to position the new window. We'll use the drag offset from before // so that the tab in the new window is positioned so that it's // basically still directly under the cursor. - til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; + const til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; // -1 is the magic number for "new window" // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 82d1264285e..46faee9a8fd 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -737,7 +737,7 @@ namespace winrt::TerminalApp::implementation { // If // * the position has been specified on the commandline, - // * We're opening the window as a part of tearout (and _contentBounds were set) + // * We're opening the window as a part of tear out (and _contentBounds were set) // then don't center on launch return !_contentBounds && _settings.GlobalSettings().CenterOnLaunch() && !_appArgs.GetPosition().has_value(); } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index c2f552900d6..11a757c1b77 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -157,8 +157,6 @@ void AppHost::s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResu // the WindowManager, to ask if we should become a window process. // - If we should create a window, then pass the arguments to the app logic for // processing. -// - If we shouldn't become a window, set _shouldCreateWindow to false and exit -// immediately. // - If the logic determined there's an error while processing that commandline, // display a message box to the user with the text of the error, and exit. // * We display a message box because we're a Win32 application (not a @@ -182,7 +180,6 @@ void AppHost::_HandleCommandlineArgs(const Remoting::WindowRequestedArgs& window if (!windowArgs.Content().empty()) { _windowLogic.SetStartupContent(windowArgs.Content(), windowArgs.InitialBounds()); - _shouldCreateWindow = true; // TODO! I don't think we actually use this anymore } else if (args) { @@ -208,7 +205,6 @@ void AppHost::_HandleCommandlineArgs(const Remoting::WindowRequestedArgs& window // then we're going to exit immediately. if (_windowLogic.ShouldImmediatelyHandoffToElevated()) { - _shouldCreateWindow = false; _windowLogic.HandoffToElevated(); return; } @@ -802,11 +798,6 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta) } } -bool AppHost::HasWindow() -{ - return _shouldCreateWindow; -} - // Method Description: // - Event handler for the Peasant::ExecuteCommandlineRequested event. Take the // provided commandline args, and attempt to parse them and perform the @@ -1229,7 +1220,7 @@ winrt::TerminalApp::TerminalWindow AppHost::Logic() // Method Description: // - Raised from Page -> us -> manager -> monarch -// - Called when the user attemtpts to move a tab or pane to another window. +// - Called when the user attempts to move a tab or pane to another window. // `args` will contain info about the structure of the content being moved, // and where it should go. // - If the WindowPosition is filled in, then the user was dragging a tab out of @@ -1265,7 +1256,7 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& // of AppHost::_HandleCreateWindow. if (IsZoomed(_window->GetHandle())) { - auto initialSize = _windowLogic.GetLaunchDimensions(dpi); + const auto initialSize = _windowLogic.GetLaunchDimensions(dpi); const auto islandWidth = Utils::ClampToShortMax(static_cast(ceil(initialSize.Width)), 1); const auto islandHeight = Utils::ClampToShortMax(static_cast(ceil(initialSize.Height)), 1); @@ -1274,8 +1265,8 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& // add the titlebar space. const til::size nonClientSize{ _window->GetTotalNonClientExclusiveSize(dpi) }; - auto adjustedWidth = islandWidth + nonClientSize.width; - auto adjustedHeight = islandHeight + nonClientSize.height; + const auto adjustedWidth = islandWidth + nonClientSize.width; + const auto adjustedHeight = islandHeight + nonClientSize.height; windowSize = til::size{ Utils::ClampToShortMax(adjustedWidth, 1), Utils::ClampToShortMax(adjustedHeight, 1) }; diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 9f7da5b9ceb..dc2c3da91cc 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -21,7 +21,6 @@ class AppHost bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); - bool HasWindow(); winrt::TerminalApp::TerminalWindow Logic(); static void s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& message); @@ -39,7 +38,6 @@ class AppHost winrt::com_ptr _desktopManager{ nullptr }; - bool _shouldCreateWindow{ false }; bool _useNonClientArea{ false }; std::shared_ptr> _showHideWindowThrottler; From 8677169b42fd34197166bccb3ca7072f623e8b80 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Mar 2023 16:23:30 -0500 Subject: [PATCH 203/234] classic --- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index bc7a7c6664f..0599aa980af 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -85,6 +85,8 @@ namespace RemotingUnitTests winrt::hstring GetWindowLayout() DIE; void RequestQuitAll() DIE; void Quit() DIE; + void AttachContentToWindow(Remoting::AttachRequest) DIE; + void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs) DIE; TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs); TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); @@ -96,6 +98,8 @@ namespace RemotingUnitTests TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, Remoting::GetWindowLayoutArgs); + TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); + TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs); }; // Same idea. @@ -114,6 +118,8 @@ namespace RemotingUnitTests bool DoesQuakeWindowExist() DIE; winrt::Windows::Foundation::Collections::IVectorView GetPeasantInfos() DIE; winrt::Windows::Foundation::Collections::IVector GetAllWindowLayouts() DIE; + void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t, winrt::Windows::Foundation::IReference) DIE; + void RequestSendContent(Remoting::RequestReceiveContentArgs) DIE; TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); From 7ecf006944521304497e25b3af15b09c7620bb50 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Mar 2023 16:35:53 -0500 Subject: [PATCH 204/234] seriously, fixed for real this time --- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index bc7a7c6664f..111dfb5877a 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -85,6 +85,7 @@ namespace RemotingUnitTests winrt::hstring GetWindowLayout() DIE; void RequestQuitAll() DIE; void Quit() DIE; + void AttachContentToWindow(Remoting::AttachRequest) DIE; TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs); TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); @@ -96,6 +97,7 @@ namespace RemotingUnitTests TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, Remoting::GetWindowLayoutArgs); + TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); }; // Same idea. @@ -114,6 +116,7 @@ namespace RemotingUnitTests bool DoesQuakeWindowExist() DIE; winrt::Windows::Foundation::Collections::IVectorView GetPeasantInfos() DIE; winrt::Windows::Foundation::Collections::IVector GetAllWindowLayouts() DIE; + void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t) DIE; TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); From d20694b92b857331ecd3080975e9cf2484b86a17 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 23 Mar 2023 16:38:24 -0500 Subject: [PATCH 205/234] hey they still pass --- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 111dfb5877a..058a6c0793c 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -86,6 +86,7 @@ namespace RemotingUnitTests void RequestQuitAll() DIE; void Quit() DIE; void AttachContentToWindow(Remoting::AttachRequest) DIE; + void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs) DIE; TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs); TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); @@ -98,6 +99,7 @@ namespace RemotingUnitTests TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, Remoting::GetWindowLayoutArgs); TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest); + TYPED_EVENT(SendContentRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs); }; // Same idea. @@ -117,6 +119,7 @@ namespace RemotingUnitTests winrt::Windows::Foundation::Collections::IVectorView GetPeasantInfos() DIE; winrt::Windows::Foundation::Collections::IVector GetAllWindowLayouts() DIE; void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t) DIE; + void RequestSendContent(Remoting::RequestReceiveContentArgs) DIE; TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); From 923cb24e6d6e5c25e792ed70f745c1baf97bb2f4 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 24 Mar 2023 16:55:49 +0100 Subject: [PATCH 206/234] wip --- .../WindowsTerminal/WindowEmperor.cpp | 35 ++++++++++++------- src/cascadia/WindowsTerminal/WindowEmperor.h | 4 ++- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 75fe386d6c6..26394e68812 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -114,33 +114,44 @@ void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; std::weak_ptr weakThis{ weak_from_this() }; + _windowStartedHandlerPreXAML(); + std::thread t([weakThis, window]() { - window->CreateHost(); + try { + const auto cleanup = wil::scope_exit([&]() { + if (auto self{ weakThis.lock() }) + { + self->_windowExitedHandler(window->Peasant().GetID()); + } + }); - if (auto self{ weakThis.lock() }) - { - self->_windowStartedHandler(window); - } + window->CreateHost(); - window->RunMessagePump(); + if (auto self{ weakThis.lock() }) + { + self->_windowStartedHandler(window); + } - if (auto self{ weakThis.lock() }) - { - self->_windowExitedHandler(window->Peasant().GetID()); - } + window->RunMessagePump(); + } CATCH_LOG() }); LOG_IF_FAILED(SetThreadDescription(t.native_handle(), L"Window Thread")); t.detach(); } +void WindowEmperor::_windowStartedHandlerPreXAML() +{ + _windowThreadInstances.fetch_add(1, std::memory_order_relaxed); +} + // 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) +void WindowEmperor::_windowStartedHandlerPostXAML(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 @@ -181,7 +192,7 @@ void WindowEmperor::_windowExitedHandler(uint64_t senderID) return w->Peasant().GetID() == senderID; }); - if (lockedWindows->size() == 0) + if (_windowThreadInstances.fetch_sub(1, std::memory_order_relaxed) == 1) { _close(); } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 9ed30a41cea..d82b5a4a804 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -41,6 +41,7 @@ class WindowEmperor : public std::enable_shared_from_this winrt::Microsoft::Terminal::Remoting::WindowManager _manager; til::shared_mutex>> _windows; + std::atomic _windowThreadInstances; std::optional> _getWindowLayoutThrottler; @@ -51,7 +52,8 @@ class WindowEmperor : public std::enable_shared_from_this std::unique_ptr _notificationIcon; - void _windowStartedHandler(const std::shared_ptr& sender); + void _windowStartedHandlerPreXAML(); + void _windowStartedHandlerPostXAML(const std::shared_ptr& sender); void _windowExitedHandler(uint64_t senderID); void _becomeMonarch(); From 1591df0c276833faaff65ee705274faa5af7a3bd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 24 Mar 2023 15:25:43 -0500 Subject: [PATCH 207/234] yea that was a typo --- 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 26394e68812..eff02340176 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -129,7 +129,7 @@ void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& if (auto self{ weakThis.lock() }) { - self->_windowStartedHandler(window); + self->_windowStartedHandlerPostXAML(window); } window->RunMessagePump(); From 70fd182e69c710abd99cf74811089a257ddc2159 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 27 Mar 2023 17:14:07 -0500 Subject: [PATCH 208/234] Smaller PR nits --- src/cascadia/TerminalApp/ContentManager.cpp | 8 ++-- src/cascadia/TerminalApp/ContentManager.h | 8 ++-- src/cascadia/TerminalApp/Pane.cpp | 6 ++- src/cascadia/TerminalApp/TerminalPage.cpp | 47 +++++++++---------- src/cascadia/TerminalApp/TerminalPage.h | 10 ++-- src/cascadia/TerminalControl/ControlCore.cpp | 2 +- src/cascadia/TerminalControl/ControlCore.h | 2 +- .../TerminalControl/ControlInteractivity.cpp | 4 +- .../TerminalControl/ControlInteractivity.h | 2 +- .../TerminalControl/ControlInteractivity.idl | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 23 ++++++--- src/cascadia/TerminalControl/TermControl.h | 7 ++- .../TerminalSettingsModel/ActionAndArgs.cpp | 2 +- .../TerminalSettingsModel/ActionAndArgs.h | 2 +- 14 files changed, 71 insertions(+), 54 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index 48aee2a5b41..e0232b75eeb 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -50,8 +50,8 @@ namespace winrt::TerminalApp::implementation } } - void ContentManager::_finalizeDetach(winrt::Windows::Foundation::IInspectable sender, - winrt::Windows::Foundation::IInspectable e) + void ContentManager::_finalizeDetach(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable&) { if (const auto& content{ sender.try_as() }) { @@ -59,8 +59,8 @@ namespace winrt::TerminalApp::implementation } } - void ContentManager::_closedHandler(winrt::Windows::Foundation::IInspectable sender, - winrt::Windows::Foundation::IInspectable e) + void ContentManager::_closedHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable&) { if (const auto& content{ sender.try_as() }) { diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 0278438292d..21a6009ae63 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -44,10 +44,10 @@ namespace winrt::TerminalApp::implementation std::unordered_map _content; std::unordered_map _recentlyDetachedContent; - void _finalizeDetach(winrt::Windows::Foundation::IInspectable sender, - winrt::Windows::Foundation::IInspectable e); + void _finalizeDetach(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); - void _closedHandler(winrt::Windows::Foundation::IInspectable sender, - winrt::Windows::Foundation::IInspectable e); + void _closedHandler(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& e); }; } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 184760bf7a7..99b42f1ab39 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -158,7 +158,9 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // object. That would work for schemes set by the Terminal, but not ones set // by VT, but that seems good enough. - // Only fill in the ContentGuid if absolutely needed. + // Only fill in the ContentId if absolutely needed. If you fill in a number + // here (even 0), we'll serialize that number, AND treat that action as an + // "attach existing" rather than a "create" if (asContent) { args.ContentId(_control.ContentId()); @@ -176,7 +178,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const // - currentId: the id to use for the current/first pane // - nextId: the id to use for a new pane if we split // - asContent: We're serializing this set of actions as content actions for -// moving to other windows, so we need to make sure to include ContentGuid's +// moving to other windows, so we need to make sure to include ContentId's // in the final actions. // - asMovePane: only used with asContent. When this is true, we're building // these actions as a part of moving the pane to another window, but without diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 5ef541ce3e2..f84c3d5c3d8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1910,7 +1910,7 @@ namespace winrt::TerminalApp::implementation { auto startupActions = pane->BuildStartupActions(0, 1, true, true); _DetachPaneFromWindow(pane); - _MoveContent(startupActions.args, args.Window(), args.TabIndex()); + _MoveContent(std::move(startupActions.args), args.Window(), args.TabIndex()); focusedTab->DetachPane(); return true; } @@ -1975,7 +1975,7 @@ namespace winrt::TerminalApp::implementation // this. // - `actions` will be emptied into a winrt IVector as a part of this method // and should be expected to be empty after this call. - void TerminalPage::_MoveContent(std::vector& actions, + void TerminalPage::_MoveContent(std::vector&& actions, const winrt::hstring& windowName, const uint32_t tabIndex) { @@ -1998,7 +1998,7 @@ namespace winrt::TerminalApp::implementation { auto startupActions = terminalTab->BuildStartupActions(true); _DetachTabFromWindow(terminalTab); - _MoveContent(startupActions, args.Window(), 0); + _MoveContent(std::move(startupActions), args.Window(), 0); _RemoveTab(*terminalTab); return true; } @@ -2024,7 +2024,7 @@ namespace winrt::TerminalApp::implementation // some startup actions for rebuilding the specified panes. They will // include `__content` properties with the GUID of the existing // ControlInteractivity's we should use, rather than starting new ones. - // - _MakePane is already enlightened to use the ContentGuid property to + // - _MakePane is already enlightened to use the ContentId property to // reattach instead of create new content, so this method simply needs to // parse the JSON and pump it into our action handler. Almost the same as // doing something like `wt -w 0 nt`. @@ -2033,9 +2033,6 @@ namespace winrt::TerminalApp::implementation { auto args = ActionAndArgs::Deserialize(content); - // If the first action is a split pane and tabIndex > tabs.size, - // then remove it and insert an equivalent newTab action instead. - // Otherwise, focus the tab they requested before starting the split. if (args == nullptr || args.Size() == 0) { @@ -2048,11 +2045,13 @@ namespace winrt::TerminalApp::implementation const auto& firstAction = args.GetAt(0); const bool firstIsSplitPane{ firstAction.Action() == ShortcutAction::SplitPane }; - // splitPane allows the user to specify which tab to split. in that - // case, split specifically the requested pane, If there's not enough - // tabs, then just turn this pane into a new tab. If the first action - // is newTab, the index is always going to be 0, so don't do anything in - // that case. + // `splitPane` allows the user to specify which tab to split. In that + // case, split specifically the requested pane. + // + // If there's not enough tabs, then just turn this pane into a new tab. + // + // If the first action is `newTab`, the index is always going to be 0, + // so don't do anything in that case. if (firstIsSplitPane && tabIndex < _tabs.Size()) { _SelectTab(tabIndex); @@ -2739,17 +2738,17 @@ namespace winrt::TerminalApp::implementation } } - TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const ITerminalConnection& connection) + TermControl TerminalPage::_CreateNewControlAndContent(const TerminalSettingsCreateResult& settings, const ITerminalConnection& connection) { // 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. const auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection); - return _InitControl(TermControl{ content }); + return _SetupControl(TermControl{ content }); } - TermControl TerminalPage::_InitControlFromContent(const uint64_t& contentId) + TermControl TerminalPage::_AttachControlToContent(const uint64_t& contentId) { if (const auto& content{ _manager.TryLookupCore(contentId) }) { @@ -2758,12 +2757,12 @@ namespace winrt::TerminalApp::implementation // don't, then when we move the content to another thread, and it // tries to handle a key, it'll callback on the original page's // stack, inevitably resulting in a wrong_thread - return _InitControl(TermControl::AttachContent(content, *_bindings)); + return _SetupControl(TermControl::AttachContent(content, *_bindings)); } return nullptr; } - TermControl TerminalPage::_InitControl(const TermControl& term) + TermControl TerminalPage::_SetupControl(const TermControl& term) { // GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now. if (_visible) @@ -2777,6 +2776,8 @@ namespace winrt::TerminalApp::implementation { term.OwningHwnd(reinterpret_cast(*_hostingHwnd)); } + + _RegisterTerminalEvents(term); return term; } @@ -2800,7 +2801,7 @@ namespace winrt::TerminalApp::implementation const winrt::TerminalApp::TabBase& sourceTab, TerminalConnection::ITerminalConnection existingConnection) { - // First things first - Check for making a pane from content GUID. + // First things first - Check for making a pane from content ID. if (newTerminalArgs && newTerminalArgs.ContentId() != 0) { @@ -2808,8 +2809,8 @@ namespace winrt::TerminalApp::implementation // serialize the actual profile's GUID along with the content guid. const auto& profile = _settings.GetProfileForArgs(newTerminalArgs); - const auto control = _InitControlFromContent(newTerminalArgs.ContentId()); - _RegisterTerminalEvents(control); + const auto control = _AttachControlToContent(newTerminalArgs.ContentId()); + return std::make_shared(profile, control); } @@ -2864,15 +2865,13 @@ namespace winrt::TerminalApp::implementation } } - const auto control = _InitControl(controlSettings, connection); - _RegisterTerminalEvents(control); + const auto control = _CreateNewControlAndContent(controlSettings, connection); auto resultPane = std::make_shared(profile, control); if (debugConnection) // this will only be set if global debugging is on and tap is active { - auto newControl = _InitControl(controlSettings, debugConnection); - _RegisterTerminalEvents(newControl); + auto newControl = _CreateNewControlAndContent(controlSettings, debugConnection); // Split (auto) with the debug tap. auto debugPane = std::make_shared(profile, newControl); diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 76df9b39e1b..98bc0c79806 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -406,10 +406,10 @@ namespace winrt::TerminalApp::implementation void _Find(const TerminalTab& tab); - winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, - const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); - winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Control::TermControl& term); - winrt::Microsoft::Terminal::Control::TermControl _InitControlFromContent(const uint64_t& contentGuid); + winrt::Microsoft::Terminal::Control::TermControl _CreateNewControlAndContent(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, + const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); + winrt::Microsoft::Terminal::Control::TermControl _SetupControl(const winrt::Microsoft::Terminal::Control::TermControl& term); + winrt::Microsoft::Terminal::Control::TermControl _AttachControlToContent(const uint64_t& contentGuid); std::shared_ptr _MakePane(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr, const winrt::TerminalApp::TabBase& sourceTab = nullptr, @@ -484,7 +484,7 @@ namespace winrt::TerminalApp::implementation void _DetachPaneFromWindow(std::shared_ptr pane); void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); - void _MoveContent(std::vector& actions, const winrt::hstring& windowName, const uint32_t tabIndex); + void _MoveContent(std::vector&& actions, const winrt::hstring& windowName, const uint32_t tabIndex); void _ContextMenuOpened(const IInspectable& sender, const IInspectable& args); void _SelectionMenuOpened(const IInspectable& sender, const IInspectable& args); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 8e0fb9a6d84..6bc7d24799e 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -240,7 +240,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _updateScrollBar.reset(); } - void ControlCore::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) + void ControlCore::AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { _settings->KeyBindings(keyBindings); _setupDispatcherAndCallbacks(); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 76abb0e6e43..f9047676176 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -76,7 +76,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme); uint64_t SwapChainHandle() const; - void Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); + void AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 3018ff4aabd..b626eb77ce1 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -85,9 +85,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core->Detach(); } - void ControlInteractivity::Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) + void ControlInteractivity::AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { - _core->Reparent(keyBindings); + _core->AttachToNewControl(keyBindings); } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 446171d03b1..dee2fe9beed 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -89,7 +89,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ManglePathsForWsl(); uint64_t Id(); - void Reparent(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); + void AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index ea8ca8b2a18..47a2920f0eb 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -25,7 +25,7 @@ namespace Microsoft.Terminal.Control UInt64 Id { get; }; - void Reparent(Microsoft.Terminal.Control.IKeyBindings keyBindings); + void AttachToNewControl(Microsoft.Terminal.Control.IKeyBindings keyBindings); void Detach(); void Close(); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 6551aa9eb09..3e0902d895c 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -106,7 +106,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // in any layout change chain. That gives us great flexibility in finding the right point // at which to initialize our renderer (and our terminal). // Any earlier than the last layout update and we may not know the terminal's starting size. - if (_InitializeTerminal(false)) + if (_InitializeTerminal(InitializeReason::Create)) { // Only let this succeed once. _layoutUpdatedRevoker.revoke(); @@ -211,11 +211,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation }); } - Control::TermControl TermControl::AttachContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings) + // Function Description: + // - Static hepler for building a new TermControl from an already existing + // content. We'll attach the existing swapchain to this new control's + // SwapChainPanel. The IKeyBindings might belong to a non-agile object on + // a new thread, so we'll hook up the core to these new bindings. + // Arguments: + // - content: The pre-existing ControlInteractivity to connect to. + // - keybindings: The new IKeyBindings instance to use for this control. + // Return Value: + // - The newly constructed TermControl. + Control::TermControl TermControl::AttachContent(Control::ControlInteractivity content, + const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { const auto term{ winrt::make_self(content) }; term->_AttachDxgiSwapChainToXaml(reinterpret_cast(term->_core.SwapChainHandle())); - content.Reparent(keyBindings); + content.AttachToNewControl(keyBindings); // 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 @@ -223,7 +234,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Replace the normal initialize routine with one that will allow up // to complete initialization even though the Core was already // initialized. - if (term->_InitializeTerminal(true)) + if (term->_InitializeTerminal(InitializeReason::Reattach)) { // Only let this succeed once. term->_layoutUpdatedRevoker.revoke(); @@ -911,7 +922,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation nativePanel->SetSwapChainHandle(swapChainHandle); } - bool TermControl::_InitializeTerminal(const bool reattach) + bool TermControl::_InitializeTerminal(const InitializeReason reason) { if (_initializedTerminal) { @@ -938,7 +949,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.RendererWarning = _core.RendererWarning(winrt::auto_revoke, { get_weak(), &TermControl::_RendererWarning }); // If we're re-attaching an existing content, then we want to proceed even though the Terminal was already initialized. - if (!reattach) + if (reason == InitializeReason::Create) { const auto coreInitialized = _core.Initialize(panelWidth, panelHeight, diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 86d271f9686..3d6e8bc023b 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -259,7 +259,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation static bool _isColorLight(til::color bg) noexcept; void _changeBackgroundOpacity(); - bool _InitializeTerminal(const bool reattach); + enum InitializeReason : bool + { + Create, + Reattach + }; + bool _InitializeTerminal(const InitializeReason reason); void _SetFontSize(int fontSize); void _TappedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e); void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e); diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 5115b9f4fac..5453319df9d 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -428,7 +428,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return found != GeneratedActionNames.end() ? found->second : L""; } - winrt::hstring ActionAndArgs::Serialize(winrt::Windows::Foundation::Collections::IVector args) + winrt::hstring ActionAndArgs::Serialize(const winrt::Windows::Foundation::Collections::IVector& args) { Json::Value json{ Json::objectValue }; JsonUtils::SetValueForKey(json, "actions", args); diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h index 5fa40aea67f..a2afefaa493 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.h @@ -16,7 +16,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::vector& warnings); static Json::Value ToJson(const Model::ActionAndArgs& val); - static winrt::hstring Serialize(winrt::Windows::Foundation::Collections::IVector args); + static winrt::hstring Serialize(const winrt::Windows::Foundation::Collections::IVector& args); static winrt::Windows::Foundation::Collections::IVector Deserialize(winrt::hstring content); ActionAndArgs() = default; From b0343786815ef6295e2671d9b173d4347e9ad636 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 11:09:14 -0500 Subject: [PATCH 209/234] null out _interactivity when a control detaches --- src/cascadia/TerminalControl/TermControl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 3e0902d895c..1956020884b 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2135,7 +2135,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::Detach() { _revokers = {}; - _interactivity.Detach(); + + Control::ControlInteractivity old{ nullptr }; + std::swap(old, _interactivity); + old.Detach(); + _detached = true; } From ec0fe79c02b8b2ddbf32349beecd98e826285ed4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 11:14:06 -0500 Subject: [PATCH 210/234] This is safe now? @lhecker to sanity check --- src/cascadia/TerminalControl/ControlCore.cpp | 31 ++++++++++++------ src/cascadia/TerminalControl/ControlCore.h | 2 ++ src/cascadia/TerminalControl/TermControl.cpp | 33 +++++++++++--------- src/renderer/atlas/AtlasEngine.api.cpp | 5 --- src/renderer/atlas/AtlasEngine.h | 1 - src/renderer/dx/DxRenderer.cpp | 4 --- src/renderer/dx/DxRenderer.hpp | 1 - src/renderer/inc/IRenderEngine.hpp | 1 - 8 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 6bc7d24799e..525380a01f0 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -332,9 +332,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // the first paint will be ignored! _renderEngine->SetWarningCallback(std::bind(&ControlCore::_rendererWarning, this, std::placeholders::_1)); - // Tell the DX Engine to notify us when the swap chain changes. - // We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems) - _renderEngine->SetCallback([this](auto handle) { _renderEngineSwapChainChanged(handle); }); + // Tell the render engine to notify us when the swap chain changes. + // We do this after we initially set the swapchain so as to avoid + // unnecessary callbacks (and locking problems) + _renderEngine->SetCallback([this](HANDLE handle) { + _renderEngineSwapChainChanged(handle); + }); _renderEngine->SetRetroTerminalEffect(_settings->RetroTerminalEffect()); _renderEngine->SetPixelShaderPath(_settings->PixelShaderPath()); @@ -1551,6 +1554,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle) { + // `handle` is a weak ref to a HANDLE that's ultimately owned by the + // render engine's own unique_handle. We'll add another ref to it here. + // This will make sure that we always have a valid HANDLE to give to + // callers of our own SwapChainHandle method, even if the renderer is + // currently in the process of discarding this value and creating a new + // one. Callers should have already set up the SwapChainChanged + // callback, so this all works out. + _lastSwapChainHandle.attach(handle); + + // Now bubble the event up to the control. _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(handle))); } @@ -1744,13 +1757,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation uint64_t ControlCore::SwapChainHandle() const { - // This is called by: - // * TermControl::RenderEngineSwapChainChanged, who is only registered - // after Core::Initialize() is called. - // * TermControl::_InitializeTerminal, after the call to Initialize, for - // _AttachDxgiSwapChainToXaml. - // In both cases, we'll have a _renderEngine by then. - return _renderEngine ? reinterpret_cast(_renderEngine->GetSwapChainHandle()) : 0u; + // This is only ever called by TermControl::AttachContent, which occurs + // when we're taking an existing core and moving it to a new control. + // Otherwise, we only ever use the value from the SwapChainChanged + // event. + return reinterpret_cast(_lastSwapChainHandle.get()); } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index f9047676176..fa263b65888 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -265,6 +265,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::unique_ptr<::Microsoft::Console::Render::IRenderEngine> _renderEngine{ nullptr }; std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr }; + winrt::handle _lastSwapChainHandle{ nullptr }; + FontInfoDesired _desiredFont; FontInfo _actualFont; winrt::hstring _actualFontFaceName; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 1956020884b..8315c622927 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -72,6 +72,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation // This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here. _revokers.RendererEnteredErrorState = _core.RendererEnteredErrorState(winrt::auto_revoke, { get_weak(), &TermControl::_RendererEnteredErrorState }); + // IMPORTANT! Set this callback up sooner rather than later. If we do it + // 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! + _revokers.RendererWarning = _core.RendererWarning(winrt::auto_revoke, { get_weak(), &TermControl::_RendererWarning }); + // ALSO IMPORTANT: Make sure to set this callback up in the ctor, so + // that we won't miss any swap chain changes. + _revokers.SwapChainChanged = _core.SwapChainChanged(winrt::auto_revoke, { get_weak(), &TermControl::RenderEngineSwapChainChanged }); + // 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. @@ -855,16 +864,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) { - // This event is only registered during terminal initialization, - // so we don't need to check _initializedTerminal. + // This event comes in on the render thread, not the UI thread. + const auto weakThis{ get_weak() }; - // Create a copy of the swap chain HANDLE in args, since we don't own that parameter. - // By the time we return from the co_await below, it might be deleted already. winrt::handle handle; - const auto processHandle = GetCurrentProcess(); - const auto sourceHandle = reinterpret_cast(winrt::unbox_value(args)); - THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(processHandle, sourceHandle, processHandle, handle.put(), 0, FALSE, DUPLICATE_SAME_ACCESS)); + + // Add a ref to the handle passed to us, so that the HANDLE will remain + // valid to the other side of the co_await. + handle.attach(reinterpret_cast(winrt::unbox_value(args))); co_await wil::resume_foreground(Dispatcher()); @@ -872,6 +880,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _AttachDxgiSwapChainToXaml(handle.get()); } + // Detach from the handle. If you don't do this, we'll CloseHandle() on + // the handle when `handle` goes out of scope, resulting in the swap + // chain being closed. + handle.detach(); } // Method Description: @@ -942,12 +954,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation return false; } - // IMPORTANT! Set this callback up sooner rather than later. If we do it - // 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! - _revokers.RendererWarning = _core.RendererWarning(winrt::auto_revoke, { get_weak(), &TermControl::_RendererWarning }); - // If we're re-attaching an existing content, then we want to proceed even though the Terminal was already initialized. if (reason == InitializeReason::Create) { @@ -965,7 +971,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _core.SizeOrScaleChanged(panelWidth, panelHeight, panelScaleX); } - _revokers.SwapChainChanged = _core.SwapChainChanged(winrt::auto_revoke, { get_weak(), &TermControl::RenderEngineSwapChainChanged }); _core.EnablePainting(); auto bufferHeight = _core.BufferHeight(); diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 1b118eb83f5..5cf54d18583 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -341,11 +341,6 @@ void AtlasEngine::SetCallback(std::function pfn) noexcept _api.swapChainChangedCallback = std::move(pfn); } -HANDLE AtlasEngine::GetSwapChainHandle() noexcept -{ - return _api.swapChainHandle.get(); -} - void AtlasEngine::EnableTransparentBackground(const bool isTransparent) noexcept { if (_api.enableTransparentBackground != isTransparent) diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 97f77da24e4..146d8053d1d 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -68,7 +68,6 @@ namespace Microsoft::Console::Render // DxRenderer - setter void SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override; void SetCallback(std::function pfn) noexcept override; - HANDLE GetSwapChainHandle() noexcept override; void EnableTransparentBackground(const bool isTransparent) noexcept override; void SetForceFullRepaintRendering(bool enable) noexcept override; [[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 38a4a55548f..70e6e01806e 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -987,10 +987,6 @@ void DxEngine::SetCallback(std::function pfn) noexcept { _pfn = std::move(pfn); } -HANDLE DxEngine::GetSwapChainHandle() noexcept -{ - return _swapChainHandle.get(); -} void DxEngine::SetWarningCallback(std::function pfn) noexcept { diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 2a36f0f0fba..9dc9fdff741 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -58,7 +58,6 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT SetWindowSize(const til::size pixels) noexcept override; void SetCallback(std::function pfn) noexcept override; - HANDLE GetSwapChainHandle() noexcept override; void SetWarningCallback(std::function pfn) noexcept override; bool GetRetroTerminalEffect() const noexcept override; diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 66898e1a991..05d6b33eed1 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -100,7 +100,6 @@ namespace Microsoft::Console::Render // DxRenderer - setter virtual void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept {} virtual void SetCallback(std::function pfn) noexcept {} - virtual HANDLE GetSwapChainHandle() noexcept { return nullptr; } virtual void EnableTransparentBackground(const bool isTransparent) noexcept {} virtual void SetForceFullRepaintRendering(bool enable) noexcept {} [[nodiscard]] virtual HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; } From 6b8c54f60d4267d2704b10d29ef536806e551dff Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 11:43:34 -0500 Subject: [PATCH 211/234] spel --- src/cascadia/TerminalControl/TermControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 8315c622927..6e54d40726e 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -221,7 +221,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Function Description: - // - Static hepler for building a new TermControl from an already existing + // - Static helper for building a new TermControl from an already existing // content. We'll attach the existing swapchain to this new control's // SwapChainPanel. The IKeyBindings might belong to a non-agile object on // a new thread, so we'll hook up the core to these new bindings. From 56c83d002ddb2a5e56cb1e9b2635ab5a9a5248b5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 11:44:08 -0500 Subject: [PATCH 212/234] yea this'll work - warn if the user tries to get cheeky and put __content in their settings --- .../Resources/en-US/Resources.resw | 4 ++++ src/cascadia/TerminalApp/TerminalWindow.cpp | 1 + .../TerminalSettingsModel/ActionArgs.h | 23 +++++++++++++++++++ .../TerminalWarnings.idl | 1 + 4 files changed, 29 insertions(+) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 2d7a385fbb2..955a84b576a 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -762,6 +762,10 @@ The "newTabMenu" field contains more than one entry of type "remainingProfiles". Only the first one will be considered. {Locked="newTabMenu"} {Locked="remainingProfiles"} + + The "__content" property is reserved for internal use. Ignoring any actions that contained that value + {Locked="__content"} + Open a dialog containing product information diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 8b8e580948e..2d7d59bc604 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -52,6 +52,7 @@ static const std::array settingsLoadWarningsLabels{ USES_RESOURCE(L"FailedToParseSubCommands"), USES_RESOURCE(L"UnknownTheme"), USES_RESOURCE(L"DuplicateRemainingProfilesEntry"), + USES_RESOURCE(L"InvalidUseOfContent"), }; static_assert(settingsLoadWarningsLabels.size() == static_cast(SettingsLoadWarnings::WARNINGS_SIZE)); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 738e30bd138..1355a806b23 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -451,6 +451,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); args->_TerminalArgs = NewTerminalArgs::FromJson(json); + + // Don't let the user specify the __content property in their + // settings. That's an internal-use-only property. + if (args->_TerminalArgs.ContentId()) + { + return { nullptr, { SettingsLoadWarnings::InvalidUseOfContent } }; + } return { *args, {} }; } static Json::Value ToJson(const IActionArgs& val) @@ -530,6 +537,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return { nullptr, { SettingsLoadWarnings::InvalidSplitSize } }; } + + // Don't let the user specify the __content property in their + // settings. That's an internal-use-only property. + if (args->_TerminalArgs.ContentId()) + { + return { nullptr, { SettingsLoadWarnings::InvalidUseOfContent } }; + } + return { *args, {} }; } static Json::Value ToJson(const IActionArgs& val) @@ -589,6 +604,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); args->_TerminalArgs = NewTerminalArgs::FromJson(json); + + // Don't let the user specify the __content property in their + // settings. That's an internal-use-only property. + if (args->_TerminalArgs.ContentId()) + { + return { nullptr, { SettingsLoadWarnings::InvalidUseOfContent } }; + } + return { *args, {} }; } static Json::Value ToJson(const IActionArgs& val) diff --git a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl index 5d3f676609a..a82acf0d5db 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl @@ -23,6 +23,7 @@ namespace Microsoft.Terminal.Settings.Model FailedToParseSubCommands, UnknownTheme, DuplicateRemainingProfilesEntry, + InvalidUseOfContent, WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder. }; From 45374e8c9bb2e7f1352ec21fd07c434be50be813 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 12:03:21 -0500 Subject: [PATCH 213/234] wrap these bits up as a method so it's a wee bit more sane --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- src/cascadia/TerminalControl/ControlCore.cpp | 3 +++ src/cascadia/TerminalControl/TermControl.cpp | 24 ++++++++++++-------- src/cascadia/TerminalControl/TermControl.h | 4 +++- src/cascadia/TerminalControl/TermControl.idl | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f84c3d5c3d8..6cbaa4ac33c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2757,7 +2757,7 @@ namespace winrt::TerminalApp::implementation // don't, then when we move the content to another thread, and it // tries to handle a key, it'll callback on the original page's // stack, inevitably resulting in a wrong_thread - return _SetupControl(TermControl::AttachContent(content, *_bindings)); + return _SetupControl(TermControl::NewControlByAttachingContent(content, *_bindings)); } return nullptr; } diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 525380a01f0..8b0e90a69f2 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -235,6 +235,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // for our engines while we're not attached to anything. _renderer->WaitForPaintCompletionAndDisable(INFINITE); + // Clear out any throttled funcs that we had wired up to run on this UI + // thread. These will be recreated in _setupDispatcherAndCallbacks, when + // we're re-attached to a new control (on a possibly new UI thread). _tsfTryRedrawCanvas.reset(); _updatePatternLocations.reset(); _updateScrollBar.reset(); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 6e54d40726e..c0e84977943 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -230,28 +230,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - keybindings: The new IKeyBindings instance to use for this control. // Return Value: // - The newly constructed TermControl. - Control::TermControl TermControl::AttachContent(Control::ControlInteractivity content, - const Microsoft::Terminal::Control::IKeyBindings& keyBindings) + Control::TermControl TermControl::NewControlByAttachingContent(Control::ControlInteractivity content, + const Microsoft::Terminal::Control::IKeyBindings& keyBindings) { const auto term{ winrt::make_self(content) }; - term->_AttachDxgiSwapChainToXaml(reinterpret_cast(term->_core.SwapChainHandle())); - content.AttachToNewControl(keyBindings); + term->_initializeForAttach(keyBindings); + return *term; + } + + void TermControl::_initializeForAttach(const Microsoft::Terminal::Control::IKeyBindings& keyBindings) + { + _AttachDxgiSwapChainToXaml(reinterpret_cast(_core.SwapChainHandle())); + _interactivity.AttachToNewControl(keyBindings); // 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 - auto r = term->SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [term](auto /*s*/, auto /*e*/) { + auto r = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) { // Replace the normal initialize routine with one that will allow up // to complete initialization even though the Core was already // initialized. - if (term->_InitializeTerminal(InitializeReason::Reattach)) + if (_InitializeTerminal(InitializeReason::Reattach)) { // Only let this succeed once. - term->_layoutUpdatedRevoker.revoke(); + _layoutUpdatedRevoker.revoke(); } }); - term->_layoutUpdatedRevoker.swap(r); - - return *term; + _layoutUpdatedRevoker.swap(r); } uint64_t TermControl::ContentId() const diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 3d6e8bc023b..56767936fb2 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -29,7 +29,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection); - static Control::TermControl AttachContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings); + static Control::TermControl NewControlByAttachingContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings); winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings); winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance); @@ -247,6 +247,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _closing; } + void _initializeForAttach(const Microsoft::Terminal::Control::IKeyBindings& keyBindings); + void _UpdateSettingsFromUIThread(); void _UpdateAppearanceFromUIThread(Control::IControlAppearance newAppearance); void _ApplyUISettings(); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 70ca0e0f54a..b622db628e9 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -24,7 +24,7 @@ namespace Microsoft.Terminal.Control IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); - static TermControl AttachContent(ControlInteractivity content, Microsoft.Terminal.Control.IKeyBindings keyBindings); + static TermControl NewControlByAttachingContent(ControlInteractivity content, Microsoft.Terminal.Control.IKeyBindings keyBindings); static Windows.Foundation.Size GetProposedDimensions(IControlSettings settings, UInt32 dpi, From 31e904a7ca4ba8d3e1a73c5feec65d967e005460 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 15:48:40 -0500 Subject: [PATCH 214/234] There's no reason that this should have entirely broken moving panes. That doesn't make sense. --- src/cascadia/TerminalControl/ControlCore.cpp | 23 ++++++++++--- src/cascadia/TerminalControl/ControlCore.h | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 36 +++++++------------- src/cascadia/TerminalControl/TermControl.h | 2 +- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 8b0e90a69f2..0b58ffd730a 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1555,19 +1555,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation _RendererWarningHandlers(*this, winrt::make(hr)); } - void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle) + winrt::fire_and_forget ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle) { - // `handle` is a weak ref to a HANDLE that's ultimately owned by the + // `sourceHandle` is a weak ref to a HANDLE that's ultimately owned by the // render engine's own unique_handle. We'll add another ref to it here. // This will make sure that we always have a valid HANDLE to give to // callers of our own SwapChainHandle method, even if the renderer is // currently in the process of discarding this value and creating a new // one. Callers should have already set up the SwapChainChanged // callback, so this all works out. - _lastSwapChainHandle.attach(handle); - // Now bubble the event up to the control. - _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(handle))); + winrt::handle duplicatedHandle; + const auto processHandle = GetCurrentProcess(); + THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(processHandle, sourceHandle, processHandle, duplicatedHandle.put(), 0, FALSE, DUPLICATE_SAME_ACCESS)); + + const auto weakThis{ get_weak() }; + + co_await wil::resume_foreground(_dispatcher); + + if (auto core{ weakThis.get() }) + { + // `this` is safe to use now + + _lastSwapChainHandle = (std::move(duplicatedHandle)); + // Now bubble the event up to the control. + _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(_lastSwapChainHandle.get()))); + } } void ControlCore::_rendererBackgroundColorChanged() diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index fa263b65888..8f7e40e6242 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -328,7 +328,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation #pragma region RendererCallbacks void _rendererWarning(const HRESULT hr); - void _renderEngineSwapChainChanged(const HANDLE handle); + winrt::fire_and_forget _renderEngineSwapChainChanged(const HANDLE handle); void _rendererBackgroundColorChanged(); void _rendererTabColorChanged(); #pragma endregion diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index c0e84977943..841b72a9cd4 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -866,28 +866,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ConnectionState(); } - winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) + void TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) { - // This event comes in on the render thread, not the UI thread. - - const auto weakThis{ get_weak() }; - - winrt::handle handle; - - // Add a ref to the handle passed to us, so that the HANDLE will remain - // valid to the other side of the co_await. - handle.attach(reinterpret_cast(winrt::unbox_value(args))); - - co_await wil::resume_foreground(Dispatcher()); - - if (auto control{ weakThis.get() }) - { - _AttachDxgiSwapChainToXaml(handle.get()); - } - // Detach from the handle. If you don't do this, we'll CloseHandle() on - // the handle when `handle` goes out of scope, resulting in the swap - // chain being closed. - handle.detach(); + // This event comes in on the UI thread + HANDLE h = reinterpret_cast(winrt::unbox_value(args)); + _AttachDxgiSwapChainToXaml(h); } // Method Description: @@ -1818,7 +1801,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation // GH#5421: Enable the UiaEngine before checking for the SearchBox // That way, new selections are notified to automation clients. // The _uiaEngine lives in _interactivity, so call into there to enable it. - _interactivity.GotFocus(); + + if (_interactivity) + { + _interactivity.GotFocus(); + } // If the searchbox is focused, we don't want TSFInputControl to think // it has focus so it doesn't intercept IME input. We also don't want the @@ -1873,7 +1860,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // This will disable the accessibility notifications, because the // UiaEngine lives in ControlInteractivity - _interactivity.LostFocus(); + if (_interactivity) + { + _interactivity.LostFocus(); + } if (TSFInputControl() != nullptr) { diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 56767936fb2..f06a50f3373 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -95,7 +95,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ToggleShaderEffects(); - winrt::fire_and_forget RenderEngineSwapChainChanged(IInspectable sender, IInspectable args); + void RenderEngineSwapChainChanged(IInspectable sender, IInspectable args); void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle); winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args); From a86fd20b6d4ddd5f51f3d85891ad162fccc23693 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 15:48:53 -0500 Subject: [PATCH 215/234] Revert "There's no reason that this should have entirely broken moving panes. That doesn't make sense." This reverts commit 31e904a7ca4ba8d3e1a73c5feec65d967e005460. --- src/cascadia/TerminalControl/ControlCore.cpp | 23 +++---------- src/cascadia/TerminalControl/ControlCore.h | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 36 +++++++++++++------- src/cascadia/TerminalControl/TermControl.h | 2 +- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 0b58ffd730a..8b0e90a69f2 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1555,32 +1555,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation _RendererWarningHandlers(*this, winrt::make(hr)); } - winrt::fire_and_forget ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle) + void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle) { - // `sourceHandle` is a weak ref to a HANDLE that's ultimately owned by the + // `handle` is a weak ref to a HANDLE that's ultimately owned by the // render engine's own unique_handle. We'll add another ref to it here. // This will make sure that we always have a valid HANDLE to give to // callers of our own SwapChainHandle method, even if the renderer is // currently in the process of discarding this value and creating a new // one. Callers should have already set up the SwapChainChanged // callback, so this all works out. + _lastSwapChainHandle.attach(handle); - winrt::handle duplicatedHandle; - const auto processHandle = GetCurrentProcess(); - THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(processHandle, sourceHandle, processHandle, duplicatedHandle.put(), 0, FALSE, DUPLICATE_SAME_ACCESS)); - - const auto weakThis{ get_weak() }; - - co_await wil::resume_foreground(_dispatcher); - - if (auto core{ weakThis.get() }) - { - // `this` is safe to use now - - _lastSwapChainHandle = (std::move(duplicatedHandle)); - // Now bubble the event up to the control. - _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(_lastSwapChainHandle.get()))); - } + // Now bubble the event up to the control. + _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(handle))); } void ControlCore::_rendererBackgroundColorChanged() diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 8f7e40e6242..fa263b65888 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -328,7 +328,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation #pragma region RendererCallbacks void _rendererWarning(const HRESULT hr); - winrt::fire_and_forget _renderEngineSwapChainChanged(const HANDLE handle); + void _renderEngineSwapChainChanged(const HANDLE handle); void _rendererBackgroundColorChanged(); void _rendererTabColorChanged(); #pragma endregion diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 841b72a9cd4..c0e84977943 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -866,11 +866,28 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ConnectionState(); } - void TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) + winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) { - // This event comes in on the UI thread - HANDLE h = reinterpret_cast(winrt::unbox_value(args)); - _AttachDxgiSwapChainToXaml(h); + // This event comes in on the render thread, not the UI thread. + + const auto weakThis{ get_weak() }; + + winrt::handle handle; + + // Add a ref to the handle passed to us, so that the HANDLE will remain + // valid to the other side of the co_await. + handle.attach(reinterpret_cast(winrt::unbox_value(args))); + + co_await wil::resume_foreground(Dispatcher()); + + if (auto control{ weakThis.get() }) + { + _AttachDxgiSwapChainToXaml(handle.get()); + } + // Detach from the handle. If you don't do this, we'll CloseHandle() on + // the handle when `handle` goes out of scope, resulting in the swap + // chain being closed. + handle.detach(); } // Method Description: @@ -1801,11 +1818,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // GH#5421: Enable the UiaEngine before checking for the SearchBox // That way, new selections are notified to automation clients. // The _uiaEngine lives in _interactivity, so call into there to enable it. - - if (_interactivity) - { - _interactivity.GotFocus(); - } + _interactivity.GotFocus(); // If the searchbox is focused, we don't want TSFInputControl to think // it has focus so it doesn't intercept IME input. We also don't want the @@ -1860,10 +1873,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // This will disable the accessibility notifications, because the // UiaEngine lives in ControlInteractivity - if (_interactivity) - { - _interactivity.LostFocus(); - } + _interactivity.LostFocus(); if (TSFInputControl() != nullptr) { diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index f06a50f3373..56767936fb2 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -95,7 +95,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ToggleShaderEffects(); - void RenderEngineSwapChainChanged(IInspectable sender, IInspectable args); + winrt::fire_and_forget RenderEngineSwapChainChanged(IInspectable sender, IInspectable args); void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle); winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args); From 56a52d9bf47e0012219ad857054b8d9c455e6fa0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 16:41:00 -0500 Subject: [PATCH 216/234] This is a real low point. Yea this one hurts --- src/cascadia/TerminalApp/Resources/en-US/Resources.resw | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 955a84b576a..95749614ea2 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -763,7 +763,7 @@ {Locked="newTabMenu"} {Locked="remainingProfiles"} - The "__content" property is reserved for internal use. Ignoring any actions that contained that value + The "__content" property is reserved for internal use. Ignoring that value from any actions that have it set. {Locked="__content"} diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 1355a806b23..576e9e1255a 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -456,7 +456,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - return { nullptr, { SettingsLoadWarnings::InvalidUseOfContent } }; + args->_TerminalArgs.ContentId(0); + return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } return { *args, {} }; } @@ -542,7 +543,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - return { nullptr, { SettingsLoadWarnings::InvalidUseOfContent } }; + args->_TerminalArgs.ContentId(0); + return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } return { *args, {} }; @@ -609,7 +611,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - return { nullptr, { SettingsLoadWarnings::InvalidUseOfContent } }; + args->_TerminalArgs.ContentId(0); + return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } return { *args, {} }; From 0391a26dce622688da7e32ccd4a635a8154ba90f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 16:41:10 -0500 Subject: [PATCH 217/234] Revert "Revert "There's no reason that this should have entirely broken moving panes. That doesn't make sense."" This reverts commit a86fd20b6d4ddd5f51f3d85891ad162fccc23693. --- src/cascadia/TerminalControl/ControlCore.cpp | 23 ++++++++++--- src/cascadia/TerminalControl/ControlCore.h | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 36 +++++++------------- src/cascadia/TerminalControl/TermControl.h | 2 +- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 8b0e90a69f2..0b58ffd730a 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1555,19 +1555,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation _RendererWarningHandlers(*this, winrt::make(hr)); } - void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle) + winrt::fire_and_forget ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle) { - // `handle` is a weak ref to a HANDLE that's ultimately owned by the + // `sourceHandle` is a weak ref to a HANDLE that's ultimately owned by the // render engine's own unique_handle. We'll add another ref to it here. // This will make sure that we always have a valid HANDLE to give to // callers of our own SwapChainHandle method, even if the renderer is // currently in the process of discarding this value and creating a new // one. Callers should have already set up the SwapChainChanged // callback, so this all works out. - _lastSwapChainHandle.attach(handle); - // Now bubble the event up to the control. - _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(handle))); + winrt::handle duplicatedHandle; + const auto processHandle = GetCurrentProcess(); + THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(processHandle, sourceHandle, processHandle, duplicatedHandle.put(), 0, FALSE, DUPLICATE_SAME_ACCESS)); + + const auto weakThis{ get_weak() }; + + co_await wil::resume_foreground(_dispatcher); + + if (auto core{ weakThis.get() }) + { + // `this` is safe to use now + + _lastSwapChainHandle = (std::move(duplicatedHandle)); + // Now bubble the event up to the control. + _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(_lastSwapChainHandle.get()))); + } } void ControlCore::_rendererBackgroundColorChanged() diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index fa263b65888..8f7e40e6242 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -328,7 +328,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation #pragma region RendererCallbacks void _rendererWarning(const HRESULT hr); - void _renderEngineSwapChainChanged(const HANDLE handle); + winrt::fire_and_forget _renderEngineSwapChainChanged(const HANDLE handle); void _rendererBackgroundColorChanged(); void _rendererTabColorChanged(); #pragma endregion diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index c0e84977943..841b72a9cd4 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -866,28 +866,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.ConnectionState(); } - winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) + void TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable args) { - // This event comes in on the render thread, not the UI thread. - - const auto weakThis{ get_weak() }; - - winrt::handle handle; - - // Add a ref to the handle passed to us, so that the HANDLE will remain - // valid to the other side of the co_await. - handle.attach(reinterpret_cast(winrt::unbox_value(args))); - - co_await wil::resume_foreground(Dispatcher()); - - if (auto control{ weakThis.get() }) - { - _AttachDxgiSwapChainToXaml(handle.get()); - } - // Detach from the handle. If you don't do this, we'll CloseHandle() on - // the handle when `handle` goes out of scope, resulting in the swap - // chain being closed. - handle.detach(); + // This event comes in on the UI thread + HANDLE h = reinterpret_cast(winrt::unbox_value(args)); + _AttachDxgiSwapChainToXaml(h); } // Method Description: @@ -1818,7 +1801,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation // GH#5421: Enable the UiaEngine before checking for the SearchBox // That way, new selections are notified to automation clients. // The _uiaEngine lives in _interactivity, so call into there to enable it. - _interactivity.GotFocus(); + + if (_interactivity) + { + _interactivity.GotFocus(); + } // If the searchbox is focused, we don't want TSFInputControl to think // it has focus so it doesn't intercept IME input. We also don't want the @@ -1873,7 +1860,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // This will disable the accessibility notifications, because the // UiaEngine lives in ControlInteractivity - _interactivity.LostFocus(); + if (_interactivity) + { + _interactivity.LostFocus(); + } if (TSFInputControl() != nullptr) { diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 56767936fb2..f06a50f3373 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -95,7 +95,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ToggleShaderEffects(); - winrt::fire_and_forget RenderEngineSwapChainChanged(IInspectable sender, IInspectable args); + void RenderEngineSwapChainChanged(IInspectable sender, IInspectable args); void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle); winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args); From 4184a1838060596534c778cb6cff2ca6bf8f60b9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 16:52:39 -0500 Subject: [PATCH 218/234] this was a crash --- src/cascadia/TerminalApp/TerminalPage.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 6cbaa4ac33c..013e766cec1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4310,7 +4310,10 @@ namespace winrt::TerminalApp::implementation if (auto terminalTab{ _GetTerminalTabImpl(tab) }) { // The root pane will propagate the theme change to all its children. - terminalTab->GetRootPane()->UpdateResources(_paneResources); + if (const auto& rootPane{ terminalTab->GetRootPane() }) + { + rootPane->UpdateResources(_paneResources); + } } } } From b1043f57cb4d6564dc86e9aef5ab4cbe9cb6f94a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 16:57:46 -0500 Subject: [PATCH 219/234] just nits --- src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Peasant.cpp | 2 +- src/cascadia/Remoting/Peasant.h | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 37 +++++++++++------------ src/cascadia/TerminalApp/TerminalPage.h | 4 +-- 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 4304ed0868d..a62493ecb4a 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1112,7 +1112,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Very similar to the above. Someone came and told us that they were the target of a drag/drop, and they know who started it. // We will go tell the person who started it that they should send that target the content which was dragged. - void Monarch::RequestSendContent(Remoting::RequestReceiveContentArgs args) + void Monarch::RequestSendContent(const Remoting::RequestReceiveContentArgs& args) { TraceLoggingWrite(g_hRemotingProvider, "Monarch_SendContent_Requested", diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index f8d47920268..54c31e98e27 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -82,7 +82,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVector GetAllWindowLayouts(); void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex); - void RequestSendContent(Remoting::RequestReceiveContentArgs args); + void RequestSendContent(const Remoting::RequestReceiveContentArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 75815bdd25e..94f8406c00c 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -329,7 +329,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return args->WindowLayoutJson(); } - void Peasant::SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args) + void Peasant::SendContent(const Remoting::RequestReceiveContentArgs& args) { _SendContentRequestedHandlers(*this, args); } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 80110d5d7b6..26a82a17fef 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -66,7 +66,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); winrt::hstring GetWindowLayout(); - void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args); + void SendContent(const winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs& args); WINRT_PROPERTY(winrt::hstring, WindowName); WINRT_PROPERTY(winrt::hstring, ActiveTabTitle); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index af1e77d6dce..1df8e842e6a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4597,12 +4597,12 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::_onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, - winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e) + void TerminalPage::_onTabDragStarting(const winrt::Microsoft::UI::Xaml::Controls::TabView&, + const winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs& e) { // Get the tab impl from this event. - const auto& eventTab = e.Tab(); - const auto& tabBase = _GetTabByTabViewItem(eventTab); + const auto eventTab = e.Tab(); + const auto tabBase = _GetTabByTabViewItem(eventTab); winrt::com_ptr tabImpl; tabImpl.copy_from(winrt::get_self(tabBase)); if (tabImpl) @@ -4612,7 +4612,7 @@ namespace winrt::TerminalApp::implementation _stashedDraggedTab = tabImpl; // Into the DataPackage, let's stash our own window ID. - const winrt::hstring id{ fmt::format(L"{}", _WindowProperties.WindowId()) }; + const auto id{ _WindowProperties.WindowId() }; // Get our PID const winrt::hstring pid{ fmt::format(L"{}", GetCurrentProcessId()) }; @@ -4632,12 +4632,15 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::_onTabStripDragOver(winrt::Windows::Foundation::IInspectable /*sender*/, - winrt::Windows::UI::Xaml::DragEventArgs e) + void TerminalPage::_onTabStripDragOver(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::UI::Xaml::DragEventArgs& e) { // We must mark that we can accept the drag/drop. The system will never // call TabStripDrop on us if we don't indicate that we're willing. - if (e.DataView().Properties().HasKey(L"windowId")) + const auto& props{ e.DataView().Properties() }; + if (props.HasKey(L"windowId") && + props.HasKey(L"pid") && + (winrt::unbox_value_or(props.TryLookup(L"pid"), 0u) == GetCurrentProcessId())) { e.AcceptedOperation(DataPackageOperation::Move); } @@ -4658,15 +4661,11 @@ namespace winrt::TerminalApp::implementation // Get the PID and make sure it is the same as ours. if (const auto& pidObj{ e.DataView().Properties().TryLookup(L"pid") }) { - const auto pidStr{ winrt::unbox_value(pidObj) }; - uint32_t pidNum; - if (Utils::StringToUint(pidStr.c_str(), pidNum)) + const auto pid{ winrt::unbox_value_or(pidObj, 0u) }; + if (pid != GetCurrentProcessId()) { - if (pidNum != GetCurrentProcessId()) - { - // The PID doesn't match ours. We can't handle this drop. - co_return; - } + // The PID doesn't match ours. We can't handle this drop. + co_return; } } else @@ -4708,9 +4707,9 @@ namespace winrt::TerminalApp::implementation { if (const auto& item{ _tabView.ContainerFromIndex(i).try_as() }) { - const auto posX{ e.GetPosition(item).X }; - const auto itemWidth{ item.ActualWidth() }; - if (posX - itemWidth < 0) + const auto posX{ e.GetPosition(item).X }; // The point of the drop, relative to the tab + const auto itemWidth{ item.ActualWidth() }; // The right of the tab + if (posX < itemWidth) { index = i; break; diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 9a9ea20174b..05390392ce8 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -500,8 +500,8 @@ namespace winrt::TerminalApp::implementation 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); - void _onTabDragStarting(winrt::Microsoft::UI::Xaml::Controls::TabView sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs e); - void _onTabStripDragOver(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); + void _onTabDragStarting(const winrt::Microsoft::UI::Xaml::Controls::TabView& sender, const winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs& e); + void _onTabStripDragOver(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::DragEventArgs& e); winrt::fire_and_forget _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); void _DetachPaneFromWindow(std::shared_ptr pane); From ecaca168385922e361e5d0ff445712ca5c08999a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 28 Mar 2023 17:01:40 -0500 Subject: [PATCH 220/234] sure --- src/cascadia/TerminalControl/ControlCore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 0b58ffd730a..d81e4b4a6bb 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1577,7 +1577,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // `this` is safe to use now - _lastSwapChainHandle = (std::move(duplicatedHandle)); + _lastSwapChainHandle = std::move(duplicatedHandle); // Now bubble the event up to the control. _SwapChainChangedHandlers(*this, winrt::box_value(reinterpret_cast(_lastSwapChainHandle.get()))); } From ef4651a8906839360eaaf3be7bf8dc85eefb877d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 09:22:26 -0500 Subject: [PATCH 221/234] [alkallabeth] I got distracted and totally botched that, didn't I --- src/cascadia/TerminalApp/TerminalPage.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f586a3a3006..387ecf35bf1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4639,10 +4639,10 @@ namespace winrt::TerminalApp::implementation const auto id{ _WindowProperties.WindowId() }; // Get our PID - const winrt::hstring pid{ fmt::format(L"{}", GetCurrentProcessId()) }; + const auto pid{ GetCurrentProcessId() }; e.Data().Properties().Insert(L"windowId", winrt::box_value(id)); - e.Data().Properties().Insert(L"pid", winrt::box_value(pid)); + e.Data().Properties().Insert(L"pid", winrt::box_value(pid)); e.Data().RequestedOperation(DataPackageOperation::Move); // The next thing that will happen: @@ -4705,15 +4705,7 @@ namespace winrt::TerminalApp::implementation // No windowId? Bail. co_return; } - const auto windowId{ winrt::unbox_value(windowIdObj) }; - - // Convert the windowId to a number - uint32_t src; - if (!Utils::StringToUint(windowId.c_str(), src)) - { - // Failed to convert the windowId to a number. Bail. - co_return; - } + const uint64_t src{ winrt::unbox_value(windowIdObj) }; // Figure out where in the tab strip we're dropping this tab. Add that // index to the request. This is largely taken from the WinUI sample From 3152ff98c47bac8a089ce43d19e907933e486e93 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 10:03:12 -0500 Subject: [PATCH 222/234] [silmarillion] Yikes I've really had a hard time with this. --- src/cascadia/TerminalApp/Resources/en-US/Resources.resw | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 95749614ea2..2d34da2f610 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -763,7 +763,7 @@ {Locked="newTabMenu"} {Locked="remainingProfiles"} - The "__content" property is reserved for internal use. Ignoring that value from any actions that have it set. + The "__content" property is reserved for internal use {Locked="__content"} diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 576e9e1255a..7f2fde239e3 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -456,7 +456,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - args->_TerminalArgs.ContentId(0); return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } return { *args, {} }; @@ -543,7 +542,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - args->_TerminalArgs.ContentId(0); return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } @@ -611,7 +609,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - args->_TerminalArgs.ContentId(0); return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } From 3f5a559d4bbc00d228156620f5fe5f489019e219 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 10:08:30 -0500 Subject: [PATCH 223/234] [silmarillion] Wow, we didn't need _recentlyDetached at all, did we --- src/cascadia/TerminalApp/ContentManager.cpp | 12 ------------ src/cascadia/TerminalApp/ContentManager.h | 4 ---- 2 files changed, 16 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index e0232b75eeb..edddc0b8ecd 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -45,17 +45,6 @@ namespace winrt::TerminalApp::implementation if (const auto& content{ TryLookupCore(contentId) }) { control.Detach(); - content.Attached({ get_weak(), &ContentManager::_finalizeDetach }); - _recentlyDetachedContent.emplace(contentId, content); - } - } - - void ContentManager::_finalizeDetach(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable&) - { - if (const auto& content{ sender.try_as() }) - { - _recentlyDetachedContent.erase(content.Id()); } } @@ -66,7 +55,6 @@ namespace winrt::TerminalApp::implementation { const auto& contentId{ content.Id() }; _content.erase(contentId); - _recentlyDetachedContent.erase(contentId); } } } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 21a6009ae63..38c0cbb7d30 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -42,10 +42,6 @@ namespace winrt::TerminalApp::implementation private: std::unordered_map _content; - std::unordered_map _recentlyDetachedContent; - - void _finalizeDetach(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& e); void _closedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); From 1f0b958b82a374868f5acd5339b710faf31d50c5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 10:03:12 -0500 Subject: [PATCH 224/234] [silmarillion] Yikes I've really had a hard time with this. (cherry picked from commit 3152ff98c47bac8a089ce43d19e907933e486e93) --- src/cascadia/TerminalApp/Resources/en-US/Resources.resw | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 95749614ea2..2d34da2f610 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -763,7 +763,7 @@ {Locked="newTabMenu"} {Locked="remainingProfiles"} - The "__content" property is reserved for internal use. Ignoring that value from any actions that have it set. + The "__content" property is reserved for internal use {Locked="__content"} diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 576e9e1255a..7f2fde239e3 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -456,7 +456,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - args->_TerminalArgs.ContentId(0); return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } return { *args, {} }; @@ -543,7 +542,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - args->_TerminalArgs.ContentId(0); return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } @@ -611,7 +609,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // settings. That's an internal-use-only property. if (args->_TerminalArgs.ContentId()) { - args->_TerminalArgs.ContentId(0); return { *args, { SettingsLoadWarnings::InvalidUseOfContent } }; } From 08732fedcf167aa7e12308888753d9b4ed7dd53f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 10:08:30 -0500 Subject: [PATCH 225/234] [silmarillion] Wow, we didn't need _recentlyDetached at all, did we (cherry picked from commit 3f5a559d4bbc00d228156620f5fe5f489019e219) --- src/cascadia/TerminalApp/ContentManager.cpp | 12 ------------ src/cascadia/TerminalApp/ContentManager.h | 4 ---- 2 files changed, 16 deletions(-) diff --git a/src/cascadia/TerminalApp/ContentManager.cpp b/src/cascadia/TerminalApp/ContentManager.cpp index e0232b75eeb..edddc0b8ecd 100644 --- a/src/cascadia/TerminalApp/ContentManager.cpp +++ b/src/cascadia/TerminalApp/ContentManager.cpp @@ -45,17 +45,6 @@ namespace winrt::TerminalApp::implementation if (const auto& content{ TryLookupCore(contentId) }) { control.Detach(); - content.Attached({ get_weak(), &ContentManager::_finalizeDetach }); - _recentlyDetachedContent.emplace(contentId, content); - } - } - - void ContentManager::_finalizeDetach(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable&) - { - if (const auto& content{ sender.try_as() }) - { - _recentlyDetachedContent.erase(content.Id()); } } @@ -66,7 +55,6 @@ namespace winrt::TerminalApp::implementation { const auto& contentId{ content.Id() }; _content.erase(contentId); - _recentlyDetachedContent.erase(contentId); } } } diff --git a/src/cascadia/TerminalApp/ContentManager.h b/src/cascadia/TerminalApp/ContentManager.h index 21a6009ae63..38c0cbb7d30 100644 --- a/src/cascadia/TerminalApp/ContentManager.h +++ b/src/cascadia/TerminalApp/ContentManager.h @@ -42,10 +42,6 @@ namespace winrt::TerminalApp::implementation private: std::unordered_map _content; - std::unordered_map _recentlyDetachedContent; - - void _finalizeDetach(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& e); void _closedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e); From 8344c01350370a0d84e860d926220e8bd1275922 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 09:22:26 -0500 Subject: [PATCH 226/234] [alkallabeth] I got distracted and totally botched that, didn't I (cherry picked from commit ef4651a8906839360eaaf3be7bf8dc85eefb877d) --- src/cascadia/TerminalApp/TerminalPage.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 191b22aa9dd..87417615ee3 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4617,10 +4617,10 @@ namespace winrt::TerminalApp::implementation const auto id{ _WindowProperties.WindowId() }; // Get our PID - const winrt::hstring pid{ fmt::format(L"{}", GetCurrentProcessId()) }; + const auto pid{ GetCurrentProcessId() }; e.Data().Properties().Insert(L"windowId", winrt::box_value(id)); - e.Data().Properties().Insert(L"pid", winrt::box_value(pid)); + e.Data().Properties().Insert(L"pid", winrt::box_value(pid)); e.Data().RequestedOperation(DataPackageOperation::Move); // The next thing that will happen: @@ -4682,15 +4682,7 @@ namespace winrt::TerminalApp::implementation // No windowId? Bail. co_return; } - const auto windowId{ winrt::unbox_value(windowIdObj) }; - - // Convert the windowId to a number - uint32_t src; - if (!Utils::StringToUint(windowId.c_str(), src)) - { - // Failed to convert the windowId to a number. Bail. - co_return; - } + const uint64_t src{ winrt::unbox_value(windowIdObj) }; // Figure out where in the tab strip we're dropping this tab. Add that // index to the request. This is largely taken from the WinUI sample From e02637d1671976cbf1f008a8507e5ee23cdd6317 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 10:36:45 -0500 Subject: [PATCH 227/234] Fix the positioning of the dropped tab --- src/cascadia/TerminalApp/TerminalPage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 87417615ee3..efe73161ec0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4703,7 +4703,8 @@ namespace winrt::TerminalApp::implementation { const auto posX{ e.GetPosition(item).X }; // The point of the drop, relative to the tab const auto itemWidth{ item.ActualWidth() }; // The right of the tab - if (posX < itemWidth) + // If the drag point is on the left half of the tab, then insert here. + if (posX < itemWidth / 2) { index = i; break; From 0c6eb3528b929492d313dac04aff021f7d3056e9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 29 Mar 2023 11:45:14 -0500 Subject: [PATCH 228/234] tiny const ref nits --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.h | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 4 ++-- src/cascadia/TerminalApp/TerminalWindow.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2614e4a8da0..a7082c1a3d5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1987,7 +1987,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_MoveContent(std::vector&& actions, const winrt::hstring& windowName, const uint32_t tabIndex, - Windows::Foundation::IReference dragPoint) + const Windows::Foundation::IReference& dragPoint) { const auto winRtActions{ winrt::single_threaded_vector(std::move(actions)) }; const auto str{ ActionAndArgs::Serialize(winRtActions) }; diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 4e268962afe..4677e81d773 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -512,7 +512,7 @@ namespace winrt::TerminalApp::implementation void _MoveContent(std::vector&& actions, const winrt::hstring& windowName, const uint32_t tabIndex, - Windows::Foundation::IReference dragPoint = nullptr); + const Windows::Foundation::IReference& dragPoint = nullptr); void _ContextMenuOpened(const IInspectable& sender, const IInspectable& args); void _SelectionMenuOpened(const IInspectable& sender, const IInspectable& args); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 1f41488f8bc..4064cb3d86d 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1050,8 +1050,8 @@ namespace winrt::TerminalApp::implementation return result; } - void TerminalWindow::SetStartupContent(winrt::hstring content, - Windows::Foundation::IReference bounds) + void TerminalWindow::SetStartupContent(const winrt::hstring& content, + const Windows::Foundation::IReference& bounds) { _contentBounds = bounds; try diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 406a60c3ab8..b91350ff688 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -74,7 +74,7 @@ namespace winrt::TerminalApp::implementation bool HasCommandlineArguments() const noexcept; int32_t SetStartupCommandline(array_view actions); - void SetStartupContent(winrt::hstring content, Windows::Foundation::IReference contentBounds); + void SetStartupContent(const winrt::hstring& content, const Windows::Foundation::IReference& contentBounds); int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); void SetSettingsStartupArgs(const std::vector& actions); winrt::hstring ParseCommandlineMessage(); From bf5d6c35391dc66d465af499520ce7cbb7fb470e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 30 Mar 2023 08:50:10 -0500 Subject: [PATCH 229/234] shuffle some code into helpers --- src/cascadia/TerminalApp/TerminalPage.cpp | 85 +++++++++++++-------- src/cascadia/TerminalApp/TerminalPage.h | 7 +- src/cascadia/TerminalApp/TerminalPage.idl | 2 +- src/cascadia/TerminalApp/TerminalWindow.cpp | 78 ++++++++++++------- src/cascadia/TerminalApp/TerminalWindow.h | 4 + 5 files changed, 116 insertions(+), 60 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index a7082c1a3d5..26aa818afa1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1987,16 +1987,16 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_MoveContent(std::vector&& actions, const winrt::hstring& windowName, const uint32_t tabIndex, - const Windows::Foundation::IReference& dragPoint) + const std::optional& dragPoint) { const auto winRtActions{ winrt::single_threaded_vector(std::move(actions)) }; const auto str{ ActionAndArgs::Serialize(winRtActions) }; const auto request = winrt::make_self(windowName, str, tabIndex); - if (dragPoint) + if (dragPoint.has_value()) { - request->WindowPosition(dragPoint); + request->WindowPosition(dragPoint->to_winrt_point()); } _RequestMoveContentHandlers(*this, *request); } @@ -2032,6 +2032,11 @@ namespace winrt::TerminalApp::implementation return true; } + uint32_t TerminalPage::NumberOfTabs() const + { + return _tabs.Size(); + } + // Method Description: // - Called when it is determined that an existing tab or pane should be // attached to our window. content represents a blob of JSON describing @@ -2042,10 +2047,10 @@ namespace winrt::TerminalApp::implementation // reattach instead of create new content, so this method simply needs to // parse the JSON and pump it into our action handler. Almost the same as // doing something like `wt -w 0 nt`. - winrt::fire_and_forget TerminalPage::AttachContent(winrt::hstring content, + winrt::fire_and_forget TerminalPage::AttachContent(IVector args, uint32_t tabIndex) { - auto args = ActionAndArgs::Deserialize(content); + // auto args = ActionAndArgs::Deserialize(content); if (args == nullptr || args.Size() == 0) @@ -2070,18 +2075,18 @@ namespace winrt::TerminalApp::implementation { _SelectTab(tabIndex); } - else - { - if (firstIsSplitPane) - { - // Create the equivalent NewTab action. - const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, - Settings::Model::NewTabArgs(firstAction.Args() ? - firstAction.Args().try_as().TerminalArgs() : - nullptr) }; - args.SetAt(0, newAction); - } - } + // else + // { + // if (firstIsSplitPane) + // { + // // Create the equivalent NewTab action. + // const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, + // Settings::Model::NewTabArgs(firstAction.Args() ? + // firstAction.Args().try_as().TerminalArgs() : + // nullptr) }; + // args.SetAt(0, newAction); + // } + // } for (const auto& action : args) { @@ -4770,11 +4775,13 @@ namespace winrt::TerminalApp::implementation if (const auto& page{ weakThis.get() }) { // `this` is safe to use in here. - auto startupActions = _stashedDraggedTab->BuildStartupActions(true); - _DetachTabFromWindow(_stashedDraggedTab); - _MoveContent(std::move(startupActions), winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, args.TabIndex()); - // _RemoveTab will make sure to null out the _stashedDraggedTab - _RemoveTab(*_stashedDraggedTab); + // auto startupActions = _stashedDraggedTab->BuildStartupActions(true); + // _DetachTabFromWindow(_stashedDraggedTab); + // _MoveContent(std::move(startupActions), winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, args.TabIndex()); + // // _RemoveTab will make sure to null out the _stashedDraggedTab + // _RemoveTab(*_stashedDraggedTab); + + _sendDraggedTabToWindow(winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, args.TabIndex(), std::nullopt); } } @@ -4800,21 +4807,39 @@ namespace winrt::TerminalApp::implementation if (const auto& page{ weakThis.get() }) { // `this` is safe to use in here. - auto startupActions = _stashedDraggedTab->BuildStartupActions(true); - _DetachTabFromWindow(_stashedDraggedTab); + // auto startupActions = _stashedDraggedTab->BuildStartupActions(true); + // _DetachTabFromWindow(_stashedDraggedTab); // We need to convert the pointer point to a point that we can use // to position the new window. We'll use the drag offset from before // so that the tab in the new window is positioned so that it's // basically still directly under the cursor. const til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; - - // -1 is the magic number for "new window" - // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. - _MoveContent(std::move(startupActions), winrt::hstring{ L"-1" }, 0, adjusted.to_winrt_point()); - // _RemoveTab will make sure to null out the _stashedDraggedTab - _RemoveTab(*_stashedDraggedTab); + _sendDraggedTabToWindow(winrt::hstring{ L"-1" }, 0, adjusted); + // // -1 is the magic number for "new window" + // // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. + // _MoveContent(std::move(startupActions), winrt::hstring{ L"-1" }, 0, adjusted.to_winrt_point()); + // // _RemoveTab will make sure to null out the _stashedDraggedTab + // _RemoveTab(*_stashedDraggedTab); } } + void TerminalPage::_sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional dragPoint) + { + auto startupActions = _stashedDraggedTab->BuildStartupActions(true); + _DetachTabFromWindow(_stashedDraggedTab); + + // // We need to convert the pointer point to a point that we can use + // // to position the new window. We'll use the drag offset from before + // // so that the tab in the new window is positioned so that it's + // // basically still directly under the cursor. + // const til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; + + // -1 is the magic number for "new window" + // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. + _MoveContent(std::move(startupActions), windowId, tabIndex, dragPoint); + // _RemoveTab will make sure to null out the _stashedDraggedTab + _RemoveTab(*_stashedDraggedTab); + } + } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 4677e81d773..14b60b03a42 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -163,9 +163,11 @@ namespace winrt::TerminalApp::implementation bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); - winrt::fire_and_forget AttachContent(winrt::hstring content, uint32_t tabIndex); + winrt::fire_and_forget AttachContent(Windows::Foundation::Collections::IVector args, uint32_t tabIndex); winrt::fire_and_forget SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args); + uint32_t NumberOfTabs() const; + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); // -------------------------------- WinRT Events --------------------------------- @@ -512,7 +514,8 @@ namespace winrt::TerminalApp::implementation void _MoveContent(std::vector&& actions, const winrt::hstring& windowName, const uint32_t tabIndex, - const Windows::Foundation::IReference& dragPoint = nullptr); + const std::optional& dragPoint = std::nullopt); + void _sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional dragPoint); void _ContextMenuOpened(const IInspectable& sender, const IInspectable& args); void _SelectionMenuOpened(const IInspectable& sender, const IInspectable& args); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 3bfbbb44b1c..cf04986551b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -78,7 +78,7 @@ namespace TerminalApp String KeyboardServiceDisabledText { get; }; TaskbarState TaskbarState{ get; }; - void AttachContent(String content, UInt32 tabIndex); + // void AttachContent(String content, UInt32 tabIndex); Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 4064cb3d86d..d97009fd305 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -23,6 +23,7 @@ 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 winrt::Windows::Foundation::Collections; using namespace ::TerminalApp; namespace winrt @@ -1054,35 +1055,12 @@ namespace winrt::TerminalApp::implementation const Windows::Foundation::IReference& bounds) { _contentBounds = bounds; - try - { - auto args = ActionAndArgs::Deserialize(content); - if (args == nullptr || - args.Size() == 0) - { - return; - } - const auto& firstAction = args.GetAt(0); - const bool firstIsSplitPane{ firstAction.Action() == ShortcutAction::SplitPane }; - if (firstIsSplitPane) - { - // Create the equivalent NewTab action. - const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, - Settings::Model::NewTabArgs(firstAction.Args() ? - firstAction.Args().try_as().TerminalArgs() : - nullptr) }; - args.SetAt(0, newAction); - } + const auto& args = _contentStringToActions(content, true); - for (const auto& action : args) - { - _initialContentArgs.push_back(action); - } - } - catch (...) + for (const auto& action : args) { - LOG_CAUGHT_EXCEPTION(); + _initialContentArgs.push_back(action); } } @@ -1253,11 +1231,57 @@ namespace winrt::TerminalApp::implementation _WindowProperties->WindowId(id); } + IVector TerminalWindow::_contentStringToActions(const winrt::hstring& content, const bool replaceFirstWithNewTab) + { + try + { + const auto& args = ActionAndArgs::Deserialize(content); + if (args == nullptr || + args.Size() == 0) + { + return args; + } + + const auto& firstAction = args.GetAt(0); + const bool firstIsSplitPane{ firstAction.Action() == ShortcutAction::SplitPane }; + if (replaceFirstWithNewTab && + firstIsSplitPane) + { + // Create the equivalent NewTab action. + const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, + Settings::Model::NewTabArgs(firstAction.Args() ? + firstAction.Args().try_as().TerminalArgs() : + nullptr) }; + args.SetAt(0, newAction); + } + + return args; + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + } + + return nullptr; + } + void TerminalWindow::AttachContent(winrt::hstring content, uint32_t tabIndex) { if (_root) { - _root->AttachContent(content, tabIndex); + // `splitPane` allows the user to specify which tab to split. In that + // case, split specifically the requested pane. + // + // If there's not enough tabs, then just turn this pane into a new tab. + // + // If the first action is `newTab`, the index is always going to be 0, + // so don't do anything in that case. + + const bool replaceFirstWithNewTab = tabIndex >= _root->NumberOfTabs(); + + const auto& args = _contentStringToActions(content, replaceFirstWithNewTab); + + _root->AttachContent(args, tabIndex); } } void TerminalWindow::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args) diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index b91350ff688..adb2c9b6638 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -196,6 +196,10 @@ namespace winrt::TerminalApp::implementation void _RefreshThemeRoutine(); void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void _OpenSettingsUI(); + + winrt::Windows::Foundation::Collections::IVector _contentStringToActions(const winrt::hstring& content, + const bool replaceFirstWithNewTab); + // 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. From 721a0ec4fe33ac57493c2596c8a8d32a77a6541e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 30 Mar 2023 08:52:20 -0500 Subject: [PATCH 230/234] spell? --- src/cascadia/TerminalControl/TermControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 841b72a9cd4..fcca688c2c4 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -226,7 +226,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // SwapChainPanel. The IKeyBindings might belong to a non-agile object on // a new thread, so we'll hook up the core to these new bindings. // Arguments: - // - content: The pre-existing ControlInteractivity to connect to. + // - content: The preexisting ControlInteractivity to connect to. // - keybindings: The new IKeyBindings instance to use for this control. // Return Value: // - The newly constructed TermControl. From c606439d271228dc4b8e0f9b4f49aaf813a9df7b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 30 Mar 2023 09:40:30 -0500 Subject: [PATCH 231/234] more dedupes and nits --- src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/Remoting/Monarch.h | 4 +-- src/cascadia/TerminalApp/TerminalPage.cpp | 27 +++++++++------------ src/cascadia/TerminalApp/TerminalWindow.cpp | 8 +++++- src/cascadia/WindowsTerminal/AppHost.cpp | 23 ++++++++---------- 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 374756a508e..3466e84d009 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1059,7 +1059,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Monarch::RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, - Windows::Foundation::IReference windowBounds) + const Windows::Foundation::IReference& windowBounds) { TraceLoggingWrite(g_hRemotingProvider, "Monarch_MoveContent_Requested", diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index e8ee1aec632..ef311fad4e1 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -48,7 +48,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _args{ command.Commandline() }, _CurrentDirectory{ command.CurrentDirectory() } {}; - WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content, Windows::Foundation::IReference bounds) : + WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content, const Windows::Foundation::IReference& bounds) : _Id{ 0u }, _WindowName{ window }, _args{}, @@ -91,7 +91,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Windows::Foundation::Collections::IVectorView GetPeasantInfos(); Windows::Foundation::Collections::IVector GetAllWindowLayouts(); - void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference windowBounds); + void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, const Windows::Foundation::IReference& windowBounds); void RequestSendContent(const Remoting::RequestReceiveContentArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 26aa818afa1..ad140fbc5d2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4775,18 +4775,15 @@ namespace winrt::TerminalApp::implementation if (const auto& page{ weakThis.get() }) { // `this` is safe to use in here. - // auto startupActions = _stashedDraggedTab->BuildStartupActions(true); - // _DetachTabFromWindow(_stashedDraggedTab); - // _MoveContent(std::move(startupActions), winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, args.TabIndex()); - // // _RemoveTab will make sure to null out the _stashedDraggedTab - // _RemoveTab(*_stashedDraggedTab); - _sendDraggedTabToWindow(winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, args.TabIndex(), std::nullopt); + _sendDraggedTabToWindow(winrt::hstring{ fmt::format(L"{}", args.TargetWindow()) }, + args.TabIndex(), + std::nullopt); } } - winrt::fire_and_forget TerminalPage::_onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, - winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e) + winrt::fire_and_forget TerminalPage::_onTabDroppedOutside(winrt::IInspectable sender, + winrt::MUX::Controls::TabViewTabDroppedOutsideEventArgs e) { // Get the current pointer point from the CoreWindow const auto& pointerPoint{ CoreWindow::GetForCurrentThread().PointerPosition() }; @@ -4807,24 +4804,22 @@ namespace winrt::TerminalApp::implementation if (const auto& page{ weakThis.get() }) { // `this` is safe to use in here. - // auto startupActions = _stashedDraggedTab->BuildStartupActions(true); - // _DetachTabFromWindow(_stashedDraggedTab); // We need to convert the pointer point to a point that we can use // to position the new window. We'll use the drag offset from before // so that the tab in the new window is positioned so that it's // basically still directly under the cursor. + + // -1 is the magic number for "new window" + // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. const til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; _sendDraggedTabToWindow(winrt::hstring{ L"-1" }, 0, adjusted); - // // -1 is the magic number for "new window" - // // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. - // _MoveContent(std::move(startupActions), winrt::hstring{ L"-1" }, 0, adjusted.to_winrt_point()); - // // _RemoveTab will make sure to null out the _stashedDraggedTab - // _RemoveTab(*_stashedDraggedTab); } } - void TerminalPage::_sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional dragPoint) + void TerminalPage::_sendDraggedTabToWindow(const winrt::hstring& windowId, + const uint32_t tabIndex, + std::optional dragPoint) { auto startupActions = _stashedDraggedTab->BuildStartupActions(true); _DetachTabFromWindow(_stashedDraggedTab); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index d97009fd305..453eb4b375d 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -1231,7 +1231,13 @@ namespace winrt::TerminalApp::implementation _WindowProperties->WindowId(id); } - IVector TerminalWindow::_contentStringToActions(const winrt::hstring& content, const bool replaceFirstWithNewTab) + // Method Description: + // - Deserialize this string of content into a list of actions to perform. + // If replaceFirstWithNewTab is true and the first serialized action is a + // `splitPane` action, we'll attempt to replace that action with the + // equivalent `newTab` action. + IVector TerminalWindow::_contentStringToActions(const winrt::hstring& content, + const bool replaceFirstWithNewTab) { try { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 11a757c1b77..3776017f25e 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1231,6 +1231,9 @@ winrt::TerminalApp::TerminalWindow AppHost::Logic() void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::TerminalApp::RequestMoveContentArgs args) { + winrt::Windows::Foundation::Rect rect{}; + winrt::Windows::Foundation::IReference windowBoundsReference{ nullptr }; + if (args.WindowPosition() && _window) { // The WindowPosition is in DIPs. We need to convert it to pixels. @@ -1278,20 +1281,14 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& windowSize = windowSize - nonClientFrame.size(); // Use the drag event as the new position, and the size of the actual window. - winrt::Windows::Foundation::Rect rect{ static_cast(dragPositionInPixels.x), - static_cast(dragPositionInPixels.y), - static_cast(windowSize.width), - static_cast(windowSize.height) }; - - _windowManager.RequestMoveContent(args.Window(), - args.Content(), - args.TabIndex(), - { rect }); - } - else - { - _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex(), nullptr); + rect = winrt::Windows::Foundation::Rect{ static_cast(dragPositionInPixels.x), + static_cast(dragPositionInPixels.y), + static_cast(windowSize.width), + static_cast(windowSize.height) }; + windowBoundsReference = rect; } + + _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex(), windowBoundsReference); } void AppHost::_handleAttach(const winrt::Windows::Foundation::IInspectable& /*sender*/, From ac5c8fa8d83d7925a186ee24401b3103822cd3c4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 30 Mar 2023 10:20:04 -0500 Subject: [PATCH 232/234] last nits --- src/cascadia/TerminalApp/TabManagement.cpp | 4 +-- src/cascadia/TerminalApp/TerminalPage.cpp | 24 +++++++---------- src/cascadia/TerminalApp/TerminalPage.h | 7 +++-- .../WindowsTerminal/WindowEmperor.cpp | 26 +++++++++++++------ src/cascadia/WindowsTerminal/WindowEmperor.h | 1 - 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 560a531650b..457cf34c1ca 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -512,9 +512,9 @@ namespace winrt::TerminalApp::implementation _mruTabs.RemoveAt(mruIndex); } - if (_stashedDraggedTab && *_stashedDraggedTab == tab) + if (_stashed.draggedTab && *_stashed.draggedTab == tab) { - _stashedDraggedTab = nullptr; + _stashed.draggedTab = nullptr; } _tabs.RemoveAt(tabIndex); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index ad140fbc5d2..8fb92e730ee 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4622,7 +4622,7 @@ namespace winrt::TerminalApp::implementation { // First: stash the tab we started dragging. // We're going to be asked for this. - _stashedDraggedTab = tabImpl; + _stashed.draggedTab = tabImpl; // Stash the offset from where we started the drag to the // tab's origin. We'll use that offset in the future to help @@ -4638,7 +4638,7 @@ namespace winrt::TerminalApp::implementation const til::point windowOrigin{ til::math::rounding, coreWindowBounds.X, coreWindowBounds.Y }; const auto realTabPosition = windowOrigin + tabPosition; // Subtract the two to get the offset. - _dragOffset = til::point{ pointerPosition - realTabPosition }; + _stashed.dragOffset = til::point{ pointerPosition - realTabPosition }; // Into the DataPackage, let's stash our own window ID. const auto id{ _WindowProperties.WindowId() }; @@ -4764,7 +4764,7 @@ namespace winrt::TerminalApp::implementation { co_return; } - if (!_stashedDraggedTab) + if (!_stashed.draggedTab) { co_return; } @@ -4793,7 +4793,7 @@ namespace winrt::TerminalApp::implementation // invoke a moveTab action with the target window being -1. That will // force the creation of a new window. - if (!_stashedDraggedTab) + if (!_stashed.draggedTab) { co_return; } @@ -4812,7 +4812,7 @@ namespace winrt::TerminalApp::implementation // -1 is the magic number for "new window" // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. - const til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; + const til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _stashed.dragOffset; _sendDraggedTabToWindow(winrt::hstring{ L"-1" }, 0, adjusted); } } @@ -4821,20 +4821,14 @@ namespace winrt::TerminalApp::implementation const uint32_t tabIndex, std::optional dragPoint) { - auto startupActions = _stashedDraggedTab->BuildStartupActions(true); - _DetachTabFromWindow(_stashedDraggedTab); - - // // We need to convert the pointer point to a point that we can use - // // to position the new window. We'll use the drag offset from before - // // so that the tab in the new window is positioned so that it's - // // basically still directly under the cursor. - // const til::point adjusted = til::point{ til::math::rounding, pointerPoint } - _dragOffset; + auto startupActions = _stashed.draggedTab->BuildStartupActions(true); + _DetachTabFromWindow(_stashed.draggedTab); // -1 is the magic number for "new window" // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. _MoveContent(std::move(startupActions), windowId, tabIndex, dragPoint); - // _RemoveTab will make sure to null out the _stashedDraggedTab - _RemoveTab(*_stashedDraggedTab); + // _RemoveTab will make sure to null out the _stashed.draggedTab + _RemoveTab(*_stashed.draggedTab); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 14b60b03a42..efc68f5f6b2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -266,8 +266,11 @@ namespace winrt::TerminalApp::implementation TerminalApp::ContentManager _manager{ nullptr }; - winrt::com_ptr _stashedDraggedTab{ nullptr }; - til::point _dragOffset{ 0, 0 }; + struct StashedDragData + { + winrt::com_ptr draggedTab{ nullptr }; + til::point dragOffset{ 0, 0 }; + } _stashed; winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::NewConnection_revoker _newConnectionRevoker; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index eff02340176..3343004aa0b 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -114,10 +114,24 @@ void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& auto window{ std::make_shared(_app.Logic(), args, _manager, peasant) }; std::weak_ptr weakThis{ weak_from_this() }; - _windowStartedHandlerPreXAML(); + // Increment our count of window instances _now_, immediately. We're + // starting a window now, we shouldn't exit (due to having 0 windows) till + // this window has a chance to actually start. + // * We can't just put the window immediately into _windows right now, + // because there are multiple async places where we iterate over all + // _windows assuming that they have initialized and we can call methods + // that might hit the TerminalPage. + // * If we don't somehow track this window now, before it has been actually + // started, there's a possible race. As an example, it would be possible + // to drag a tab out of the single window, which would create a new + // window, but have the original window exit before the new window has + // started, causing the app to exit. + // Hence: increment the number of total windows now. + _windowThreadInstances.fetch_add(1, std::memory_order_relaxed); std::thread t([weakThis, window]() { - try { + try + { const auto cleanup = wil::scope_exit([&]() { if (auto self{ weakThis.lock() }) { @@ -133,18 +147,14 @@ void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& } window->RunMessagePump(); - } CATCH_LOG() + } + CATCH_LOG() }); LOG_IF_FAILED(SetThreadDescription(t.native_handle(), L"Window Thread")); t.detach(); } -void WindowEmperor::_windowStartedHandlerPreXAML() -{ - _windowThreadInstances.fetch_add(1, std::memory_order_relaxed); -} - // 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. diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index d82b5a4a804..73e3f1ebbe1 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -52,7 +52,6 @@ class WindowEmperor : public std::enable_shared_from_this std::unique_ptr _notificationIcon; - void _windowStartedHandlerPreXAML(); void _windowStartedHandlerPostXAML(const std::shared_ptr& sender); void _windowExitedHandler(uint64_t senderID); From ced78fb72d16d2aa1664c3eff4c613af016d5cf4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 30 Mar 2023 16:01:24 -0500 Subject: [PATCH 233/234] derp --- src/cascadia/TerminalApp/TerminalPage.cpp | 16 ---------------- src/cascadia/TerminalApp/TerminalWindow.cpp | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 8fb92e730ee..9b0abe8904a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2050,8 +2050,6 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::AttachContent(IVector args, uint32_t tabIndex) { - // auto args = ActionAndArgs::Deserialize(content); - if (args == nullptr || args.Size() == 0) { @@ -2075,18 +2073,6 @@ namespace winrt::TerminalApp::implementation { _SelectTab(tabIndex); } - // else - // { - // if (firstIsSplitPane) - // { - // // Create the equivalent NewTab action. - // const auto newAction = Settings::Model::ActionAndArgs{ Settings::Model::ShortcutAction::NewTab, - // Settings::Model::NewTabArgs(firstAction.Args() ? - // firstAction.Args().try_as().TerminalArgs() : - // nullptr) }; - // args.SetAt(0, newAction); - // } - // } for (const auto& action : args) { @@ -4824,8 +4810,6 @@ namespace winrt::TerminalApp::implementation auto startupActions = _stashed.draggedTab->BuildStartupActions(true); _DetachTabFromWindow(_stashed.draggedTab); - // -1 is the magic number for "new window" - // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. _MoveContent(std::move(startupActions), windowId, tabIndex, dragPoint); // _RemoveTab will make sure to null out the _stashed.draggedTab _RemoveTab(*_stashed.draggedTab); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 453eb4b375d..dd984c6c126 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -146,7 +146,7 @@ namespace winrt::TerminalApp::implementation _dialog = ContentDialog{}; // Pass in information about the initial state of the window. - // * If we were suppost to start from serialized "content", do that, + // * If we were supposed to start from serialized "content", do that, // * If we were supposed to load from a persisted layout, do that // instead. // * if we have commandline arguments, Pass commandline args into the From 94865b80ce09766567ad02cbd4447a217c2c50b3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 31 Mar 2023 14:03:33 -0500 Subject: [PATCH 234/234] cammants --- src/cascadia/TerminalApp/TerminalPage.idl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index cf04986551b..5c8a151b909 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -78,7 +78,6 @@ namespace TerminalApp String KeyboardServiceDisabledText { get; }; TaskbarState TaskbarState{ get; }; - // void AttachContent(String content, UInt32 tabIndex); Windows.UI.Xaml.Media.Brush TitlebarBrush { get; }; void WindowActivated(Boolean activated);