From 1c7da000fcacc1e076d4538316435b873db3ee90 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 25 Feb 2021 10:39:25 -0600 Subject: [PATCH 01/25] Rebase all the changes on main The history of this had gotten way, way too long. It included everything since I started working on this --- src/cascadia/Remoting/FindTargetWindowArgs.h | 1 + src/cascadia/Remoting/Monarch.cpp | 39 ++++++++- src/cascadia/Remoting/Monarch.h | 1 + src/cascadia/Remoting/Monarch.idl | 3 +- src/cascadia/Remoting/Peasant.h | 2 + src/cascadia/Remoting/Peasant.idl | 1 + .../Remoting/ProposeCommandlineResult.h | 1 + src/cascadia/Remoting/WindowManager.cpp | 35 +++++++- src/cascadia/Remoting/WindowManager.h | 3 +- .../TerminalApp/AppCommandlineArgs.cpp | 20 ++--- src/cascadia/TerminalApp/AppCommandlineArgs.h | 4 +- src/cascadia/TerminalApp/AppLogic.cpp | 82 ++++++++++++++----- src/cascadia/TerminalApp/AppLogic.h | 17 +++- src/cascadia/TerminalApp/AppLogic.idl | 8 +- src/cascadia/WindowsTerminal/AppHost.cpp | 3 +- src/cascadia/inc/WindowingBehavior.h | 9 +- 16 files changed, 179 insertions(+), 50 deletions(-) diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.h b/src/cascadia/Remoting/FindTargetWindowArgs.h index 628004aa212..309793afd4e 100644 --- a/src/cascadia/Remoting/FindTargetWindowArgs.h +++ b/src/cascadia/Remoting/FindTargetWindowArgs.h @@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr); GETSET_PROPERTY(int, ResultTargetWindow, -1); + GETSET_PROPERTY(winrt::hstring, ResultTargetWindowName, L""); public: FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) : diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 628b8d01a37..553f61cfdd7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -147,6 +147,33 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } + uint64_t Monarch::_lookupPeasantIdForName(const winrt::hstring& name) + { + if (name.empty()) + { + return 0; + } + + for (const auto& [id, p] : _peasants) + { + try + { + auto otherName = p.WindowName(); + if (otherName == name) + { + return id; + } + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + // Remove the peasant from the list of peasants + _peasants.erase(id); + } + } + return 0; + } + // Method Description: // - Handler for the `Peasant::WindowActivated` event. We'll make a in-proc // copy of the WindowActivatedArgs from the peasant. That way, we won't @@ -400,6 +427,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // After the event was handled, ResultTargetWindow() will be filled with // the parsed result. const auto targetWindow = findWindowArgs->ResultTargetWindow(); + const auto& targetWindowName = findWindowArgs->ResultTargetWindowName(); TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline", @@ -410,6 +438,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // that goes with it. Alternatively, if we were given a magic windowing // constant, we can use that to look up an appropriate peasant. if (targetWindow >= 0 || + targetWindow == WindowingBehaviorUseName || targetWindow == WindowingBehaviorUseExisting || targetWindow == WindowingBehaviorUseAnyExisting) { @@ -429,6 +458,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation case WindowingBehaviorUseAnyExisting: windowID = _getMostRecentPeasantID(false); break; + case WindowingBehaviorUseName: + windowID = _lookupPeasantIdForName(targetWindowName); + break; default: windowID = ::base::saturated_cast(targetWindow); break; @@ -449,7 +481,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (auto targetPeasant{ _getPeasant(windowID) }) { auto result{ winrt::make_self(false) }; - + result->WindowName(targetWindowName); try { // This will raise the peasant's ExecuteCommandlineRequested @@ -497,6 +529,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto result{ winrt::make_self(true) }; result->Id(windowID); + result->WindowName(targetWindowName); return *result; } } @@ -508,6 +541,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // In this case, no usable ID was provided. Return { true, nullopt } - return winrt::make(true); + auto result = winrt::make_self(true); + result->WindowName(targetWindowName); + return *result; } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 8c62e14866b..52399ad5971 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -67,6 +67,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop); + uint64_t _lookupPeasantIdForName(const winrt::hstring& name); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 3d44666b0f1..836cb7ee9d4 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -9,11 +9,12 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass FindTargetWindowArgs { CommandlineArgs Args { get; }; Int32 ResultTargetWindow; + String ResultTargetWindowName; } [default_interface] runtimeclass ProposeCommandlineResult { Windows.Foundation.IReference Id { get; }; - // TODO:projects/5 - also return the name here, if the name was set on the commandline + String WindowName { get; }; Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 4d2bb24a01b..3b680d15ccf 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -26,6 +26,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); + GETSET_PROPERTY(winrt::hstring, WindowName, L""); + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 8d092d3d423..2ef4cb89086 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -33,6 +33,7 @@ namespace Microsoft.Terminal.Remoting Boolean ExecuteCommandline(CommandlineArgs args); void ActivateWindow(WindowActivatedArgs args); WindowActivatedArgs GetLastActivatedArgs(); + String WindowName { get; }; event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.h b/src/cascadia/Remoting/ProposeCommandlineResult.h index 39b1cd6071b..b095be75b8b 100644 --- a/src/cascadia/Remoting/ProposeCommandlineResult.h +++ b/src/cascadia/Remoting/ProposeCommandlineResult.h @@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { public: GETSET_PROPERTY(Windows::Foundation::IReference, Id); + GETSET_PROPERTY(winrt::hstring, WindowName, L""); GETSET_PROPERTY(bool, ShouldCreateWindow, true); public: diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 01a422a690d..b5b77b19411 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -5,6 +5,8 @@ #include "WindowManager.h" #include "MonarchFactory.h" #include "CommandlineArgs.h" +#include "../inc/WindowingBehavior.h" +#include "FindTargetWindowArgs.h" #include "WindowManager.g.cpp" #include "../../types/inc/utils.hpp" @@ -69,6 +71,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Otherwise, the King will tell us if we should make a new window _shouldCreateWindow = _isKing; std::optional givenID; + winrt::hstring givenName = L""; // TODO:MG If we're the king, we might STILL WANT TO GET THE NAME. How do we get the name? if (!_isKing) { // The monarch may respond back "you should be a new @@ -86,7 +89,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { 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) @@ -95,6 +98,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "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)); } else @@ -103,6 +107,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "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)); } } @@ -110,9 +115,29 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // 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". + auto findWindowArgs{ winrt::make_self(args) }; + _raiseFindTargetWindowRequested(nullptr, *findWindowArgs); + + auto responseId = findWindowArgs->ResultTargetWindow(); + if (responseId > 0) + { + givenID = ::base::saturated_cast(responseId); + } + 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(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)); } @@ -120,7 +145,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // If we should create a new window, then instantiate our Peasant // instance, and tell that peasant to handle that commandline. - _createOurPeasant({ givenID }); + _createOurPeasant({ givenID }, givenName); // Spawn a thread to wait on the monarch, and handle the election if (!_isKing) @@ -208,13 +233,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return (ourPID == kingPID); } - Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID) + Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID, + const winrt::hstring& givenName) { auto p = winrt::make_self(); if (givenID) { p->AssignID(givenID.value()); } + + // If the name wasn't specified, this will be an empty string. + p->WindowName(givenName); _peasant = *p; // Try to add us to the monarch. If that fails, try to find a monarch diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index bc257ef06b6..8e5678369b2 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -54,7 +54,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _createMonarch(); void _createMonarchAndCallbacks(); bool _areWeTheKing(); - winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional givenID); + winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional givenID, + const winrt::hstring& givenName); bool _performElection(); void _createPeasantThread(); diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index c6782252125..030fe2780cc 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -185,9 +185,9 @@ void AppCommandlineArgs::_buildParser() maximized->excludes(fullscreen); focus->excludes(fullscreen); - _app.add_option, int>("-w,--window", - _windowTarget, - RS_A(L"CmdWindowTargetArgDesc")); + _app.add_option("-w,--window", + _windowTarget, + RS_A(L"CmdWindowTargetArgDesc")); // Subcommands _buildNewTabParser(); @@ -854,16 +854,16 @@ void AppCommandlineArgs::FullResetState() _exitMessage = ""; _shouldExitEarly = false; - _windowTarget = std::nullopt; + _windowTarget = ""; } -std::optional AppCommandlineArgs::GetTargetWindow() const noexcept +std::string_view AppCommandlineArgs::GetTargetWindow() const noexcept { - // If the user provides _any_ negative number, then treat it as -1, for "use a new window". - if (_windowTarget.has_value() && *_windowTarget < 0) - { - return { -1 }; - } + // // If the user provides _any_ negative number, then treat it as -1, for "use a new window". + // if (_windowTarget.has_value() && *_windowTarget < 0) + // { + // return { -1 }; + // } return _windowTarget; } diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index a09bd36280b..9c8662293e1 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -44,7 +44,7 @@ class TerminalApp::AppCommandlineArgs final void DisableHelpInExitMessage(); void FullResetState(); - std::optional GetTargetWindow() const noexcept; + std::string_view GetTargetWindow() const noexcept; private: static const std::wregex _commandDelimiterRegex; @@ -106,7 +106,7 @@ class TerminalApp::AppCommandlineArgs final std::string _exitMessage; bool _shouldExitEarly{ false }; - std::optional _windowTarget{ std::nullopt }; + std::string _windowTarget{ "" }; // Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand); diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index c47c9a6eee0..29cf75a5b95 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -5,6 +5,7 @@ #include "AppLogic.h" #include "../inc/WindowingBehavior.h" #include "AppLogic.g.cpp" +#include "FindTargetWindowResult.g.cpp" #include #include @@ -1218,14 +1219,15 @@ namespace winrt::TerminalApp::implementation // - WindowingBehaviorUseAnyExisting: We should handle the args "in the current // window ON ANY DESKTOP" // - anything else: We should handle the commandline in the window with the given ID. - int32_t AppLogic::FindTargetWindow(array_view args) + TerminalApp::FindTargetWindowResult AppLogic::FindTargetWindow(array_view args) { + // TODO:MG Add tests for this method. Localtests? probably. return AppLogic::_doFindTargetWindow(args, _settings.GlobalSettings().WindowingBehavior()); } // The main body of this function is a static helper, to facilitate unit-testing - int32_t AppLogic::_doFindTargetWindow(array_view args, - const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior) + TerminalApp::FindTargetWindowResult AppLogic::_doFindTargetWindow(array_view args, + const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior) { ::TerminalApp::AppCommandlineArgs appArgs; const auto result = appArgs.ParseArgs(args); @@ -1233,31 +1235,67 @@ namespace winrt::TerminalApp::implementation { if (!appArgs.GetExitMessage().empty()) { - return WindowingBehaviorUseNew; + return winrt::make(WindowingBehaviorUseNew, L""); } - const auto parsedTarget = appArgs.GetTargetWindow(); - if (parsedTarget.has_value()) - { - // parsedTarget might be -1, if the user explicitly requested -1 - // (or any other negative number) on the commandline. So the set - // of possible values here is {-1, 0, ℤ+} - return *parsedTarget; - } - else + const std::string parsedTarget{ appArgs.GetTargetWindow() }; + + // If the user did not provide any value on the commandline, + // then lookup our windowing behavior to determine what to do + // now. + if (parsedTarget.empty()) { - // If the user did not provide any value on the commandline, - // then lookup our windowing behavior to determine what to do - // now. + int32_t windowId = WindowingBehaviorUseNew; switch (windowingBehavior) { + case WindowingMode::UseNew: + windowId = WindowingBehaviorUseNew; case WindowingMode::UseExisting: - return WindowingBehaviorUseExisting; + windowId = WindowingBehaviorUseExisting; case WindowingMode::UseAnyExisting: - return WindowingBehaviorUseAnyExisting; - case WindowingMode::UseNew: - default: - return WindowingBehaviorUseNew; + windowId = WindowingBehaviorUseAnyExisting; + } + return winrt::make(windowId, L""); + } + + // Here, the user _has_ provided a window-id on the commandline. + // What is it? Let's start by checking if it's an int, for the + // window's ID: + try + { + int32_t windowId = ::base::saturated_cast(std::stoi(parsedTarget)); + + // If the user provides _any_ negative number, then treat it as + // -1, for "use a new window". + if (windowId < 0) + { + windowId = -1; + } + + // Hooray! This is a valid integer. The set of possible values + // here is {-1, 0, ℤ+}. Let's return that window ID. + return winrt::make(windowId, L""); + } + catch (...) + { + // Value was not a valid int. It could be any other string to + // use as a title though! + // + // First, check the reserved keywords: + if (parsedTarget == "new") + { + return winrt::make(WindowingBehaviorUseNew, L""); + } + else if (parsedTarget == "last") + { + return winrt::make(WindowingBehaviorUseExisting, L""); + } + else + { + // The string they provided wasn't an int, it wasn't "new" + // or "last", so whatever it is, that's the name they get. + winrt::hstring winrtName{ til::u8u16(parsedTarget) }; + return winrt::make(WindowingBehaviorUseName, winrtName); } } } @@ -1273,7 +1311,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 WindowingBehaviorUseNew; + return winrt::make(WindowingBehaviorUseNew, L""); } // Method Description: diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index d011738c977..0c83d0afe95 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -4,6 +4,7 @@ #pragma once #include "AppLogic.g.h" +#include "FindTargetWindowResult.g.h" #include "TerminalPage.h" #include "Jumplist.h" #include "../../cascadia/inc/cppwinrt_utils.h" @@ -18,6 +19,16 @@ namespace TerminalAppLocalTests namespace winrt::TerminalApp::implementation { + struct FindTargetWindowResult : FindTargetWindowResultT + { + GETSET_PROPERTY(int32_t, WindowId, -1); + GETSET_PROPERTY(winrt::hstring, WindowName, L""); + + public: + FindTargetWindowResult(const int32_t id, const winrt::hstring& name) : + _WindowId{ id }, _WindowName{ name } {}; + }; + struct AppLogic : AppLogicT { public: @@ -38,7 +49,7 @@ namespace winrt::TerminalApp::implementation int32_t SetStartupCommandline(array_view actions); int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); - int32_t FindTargetWindow(array_view actions); + TerminalApp::FindTargetWindowResult FindTargetWindow(array_view actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); @@ -98,8 +109,8 @@ namespace winrt::TerminalApp::implementation ::TerminalApp::AppCommandlineArgs _appArgs; ::TerminalApp::AppCommandlineArgs _settingsAppArgs; int _ParseArgs(winrt::array_view& args); - static int32_t _doFindTargetWindow(winrt::array_view args, - const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior); + 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(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 44a7260f312..bc2d4546486 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -13,6 +13,12 @@ namespace TerminalApp Int64 Y; }; + [default_interface] runtimeclass FindTargetWindowResult + { + Int32 WindowId { get; }; + String WindowName { get; }; + }; + [default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter { AppLogic(); @@ -57,7 +63,7 @@ namespace TerminalApp UInt64 GetLastActiveControlTaskbarState(); UInt64 GetLastActiveControlTaskbarProgress(); - Int32 FindTargetWindow(String[] args); + FindTargetWindowResult FindTargetWindow(String[] args); // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 4381654fc04..6a1ba759804 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -566,7 +566,8 @@ void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& const Remoting::FindTargetWindowArgs& args) { const auto targetWindow = _logic.FindTargetWindow(args.Args().Commandline()); - args.ResultTargetWindow(targetWindow); + args.ResultTargetWindow(targetWindow.WindowId()); + args.ResultTargetWindowName(targetWindow.WindowName()); } winrt::fire_and_forget AppHost::_WindowActivated() diff --git a/src/cascadia/inc/WindowingBehavior.h b/src/cascadia/inc/WindowingBehavior.h index 0a6b55bd34e..bce36f7a693 100644 --- a/src/cascadia/inc/WindowingBehavior.h +++ b/src/cascadia/inc/WindowingBehavior.h @@ -4,7 +4,8 @@ Licensed under the MIT license. --*/ #pragma once -constexpr int64_t WindowingBehaviorUseCurrent{ 0 }; -constexpr int64_t WindowingBehaviorUseNew{ -1 }; -constexpr int64_t WindowingBehaviorUseExisting{ -2 }; -constexpr int64_t WindowingBehaviorUseAnyExisting{ -3 }; +constexpr int32_t WindowingBehaviorUseCurrent{ 0 }; +constexpr int32_t WindowingBehaviorUseNew{ -1 }; +constexpr int32_t WindowingBehaviorUseExisting{ -2 }; +constexpr int32_t WindowingBehaviorUseAnyExisting{ -3 }; +constexpr int32_t WindowingBehaviorUseName{ -4 }; From 97818c68e608b96de2eecf44ac7f3007eff98dd0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 25 Feb 2021 11:39:28 -0600 Subject: [PATCH 02/25] fix a bug and fix the tests --- .../CommandlineTest.cpp | 84 +++++++++---------- src/cascadia/TerminalApp/AppLogic.cpp | 3 + 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp index 4cab5a32d9d..8f31206b536 100644 --- a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp +++ b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp @@ -1595,58 +1595,58 @@ namespace TerminalAppLocalTests { { std::vector args{ L"wt.exe" }; - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew)); + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId()); } { std::vector args{ L"wt.exe", L"-w", L"-1" }; - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew)); + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); } { std::vector args{ L"wt.exe", L"-w", L"-12345" }; - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew)); + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); } { std::vector args{ L"wt.exe", L"-w", L"0" }; - VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew)); + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); } { std::vector args{ L"wt.exe", L"new-tab" }; - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew)); + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId()); } } @@ -1657,14 +1657,14 @@ namespace TerminalAppLocalTests // This is a little helper to make sure that these args _always_ return // UseNew, regardless of the windowing behavior. auto testHelper = [](auto&& args) { - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew)); + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); }; testHelper(std::vector{ L"wt.exe", L"--help" }); @@ -1680,14 +1680,14 @@ namespace TerminalAppLocalTests // This is a little helper to make sure that these args _always_ return // UseNew, regardless of the windowing behavior. auto testHelper = [](auto&& args) { - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew)); + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, - appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting)); + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); }; testHelper(std::vector{ L"wt.exe", L"--version" }); diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 29cf75a5b95..a7eb6e06bca 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1250,10 +1250,13 @@ namespace winrt::TerminalApp::implementation { case WindowingMode::UseNew: windowId = WindowingBehaviorUseNew; + break; case WindowingMode::UseExisting: windowId = WindowingBehaviorUseExisting; + break; case WindowingMode::UseAnyExisting: windowId = WindowingBehaviorUseAnyExisting; + break; } return winrt::make(windowId, L""); } From 2ec94159746125a0b28a4ee38ee1dbb0740169d8 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 25 Feb 2021 14:39:43 -0600 Subject: [PATCH 03/25] fix tests --- .../CommandlineTest.cpp | 78 +++++++++++++++++++ src/cascadia/TerminalApp/AppLogic.cpp | 7 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp index 8f31206b536..a593aed03ce 100644 --- a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp +++ b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp @@ -1597,56 +1597,127 @@ namespace TerminalAppLocalTests std::vector args{ L"wt.exe" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); } { std::vector args{ L"wt.exe", L"-w", L"-1" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + } + { + std::vector args{ L"wt.exe", L"-w", L"new" }; + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); } { std::vector args{ L"wt.exe", L"-w", L"-12345" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + } + { + std::vector args{ L"wt.exe", L"-w", L"12345" }; + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(12345, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(12345, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(12345, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); } { std::vector args{ L"wt.exe", L"-w", L"0" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + } + { + std::vector args{ L"wt.exe", L"-w", L"last" }; + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); } { std::vector args{ L"wt.exe", L"new-tab" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); + } + { + std::vector args{ L"wt.exe", L"-w", L"new-tab" }; + auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); + VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId()); + VERIFY_ARE_EQUAL(L"new-tab", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId()); + VERIFY_ARE_EQUAL(L"new-tab", result.WindowName()); + + result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); + VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId()); + VERIFY_ARE_EQUAL(L"new-tab", result.WindowName()); } } @@ -1659,17 +1730,21 @@ namespace TerminalAppLocalTests auto testHelper = [](auto&& args) { auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); }; testHelper(std::vector{ L"wt.exe", L"--help" }); testHelper(std::vector{ L"wt.exe", L"new-tab", L"--help" }); testHelper(std::vector{ L"wt.exe", L"-w", L"0", L"new-tab", L"--help" }); + testHelper(std::vector{ L"wt.exe", L"-w", L"foo", L"new-tab", L"--help" }); testHelper(std::vector{ L"wt.exe", L"new-tab", L";", L"--help" }); } @@ -1682,12 +1757,15 @@ namespace TerminalAppLocalTests auto testHelper = [](auto&& args) { auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); + VERIFY_ARE_EQUAL(L"", result.WindowName()); }; testHelper(std::vector{ L"wt.exe", L"--version" }); diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index a7eb6e06bca..56f1964291b 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1221,7 +1221,12 @@ namespace winrt::TerminalApp::implementation // - anything else: We should handle the commandline in the window with the given ID. TerminalApp::FindTargetWindowResult AppLogic::FindTargetWindow(array_view args) { - // TODO:MG Add tests for this method. Localtests? probably. + if (!_loadedInitialSettings) + { + // Load settings if we haven't already + LoadSettings(); + } + return AppLogic::_doFindTargetWindow(args, _settings.GlobalSettings().WindowingBehavior()); } From e13e1e7fe55657b084a41d4860df26ec80c97f22 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 25 Feb 2021 16:59:44 -0600 Subject: [PATCH 04/25] Good ole Java --- src/cascadia/Remoting/Monarch.cpp | 31 ++- .../UnitTests_Remoting/RemotingTests.cpp | 213 ++++++++++++++++++ 2 files changed, 240 insertions(+), 4 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 553f61cfdd7..cf98bdb1d4b 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -154,6 +154,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return 0; } + std::vector peasantsToErase{}; + uint64_t result = 0; for (const auto& [id, p] : _peasants) { try @@ -161,17 +163,38 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto otherName = p.WindowName(); if (otherName == name) { - return id; + result = id; + break; } } catch (...) { LOG_CAUGHT_EXCEPTION(); - // Remove the peasant from the list of peasants - _peasants.erase(id); + // Normally, we'd just erase the peasant here. However, we can't + // erase from the map while we're iterating over it like this. + // Instead, pull a good ole Java and collect this id for removal + // later. + peasantsToErase.push_back(id); + + // // Remove the peasant from the list of peasants + // _peasants.erase(id); + // // Remove the peasant from the list of MRU windows. They're dead. + // // They can't be the MRU anymore. + // _clearOldMruEntries(id); } } - return 0; + + // Remove the dead peasants we came across while iterating. + for (const auto& id : peasantsToErase) + { + // Remove the peasant from the list of peasants + _peasants.erase(id); + // Remove the peasant from the list of MRU windows. They're dead. + // They can't be the MRU anymore. + _clearOldMruEntries(id); + } + + return result; } // Method Description: diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 81856c5422f..01b837b4634 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -50,6 +50,7 @@ namespace RemotingUnitTests DeadPeasant() = default; void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; }; uint64_t GetID() { throw winrt::hresult_error{}; }; + winrt::hstring WindowName() { throw winrt::hresult_error{}; }; uint64_t GetPID() { throw winrt::hresult_error{}; }; bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { throw winrt::hresult_error{}; } @@ -85,6 +86,12 @@ namespace RemotingUnitTests TEST_METHOD(GetMostRecentAnyDesktop); TEST_METHOD(MostRecentIsDead); + TEST_METHOD(GetPeasantsByName); + TEST_METHOD(AddNamedPeasantsToNewMonarch); + TEST_METHOD(LookupNamedPeasantWhenOthersDied); + TEST_METHOD(LookupNamedPeasantWhenItDied); + TEST_METHOD(GetMruPeasantAfterNameLookupForDeadPeasant); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -978,4 +985,210 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); } + void RemotingTests::GetPeasantsByName() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + + p1->WindowName(L"one"); + p2->WindowName(L"two"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + VERIFY_ARE_EQUAL(L"one", p1->WindowName()); + VERIFY_ARE_EQUAL(L"two", p2->WindowName()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + VERIFY_ARE_EQUAL(L"one", p1->WindowName()); + VERIFY_ARE_EQUAL(L"two", p2->WindowName()); + + VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); + + Log::Comment(L"Rename p2"); + + p2->WindowName(L"foo"); + + VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"two")); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"foo")); + } + + void RemotingTests::AddNamedPeasantsToNewMonarch() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + const auto monarch3PID = 45678u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + com_ptr m3; + m3.attach(new Remoting::implementation::Monarch(monarch3PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + VERIFY_IS_NOT_NULL(m3); + + p1->WindowName(L"one"); + p2->WindowName(L"two"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + VERIFY_ARE_EQUAL(L"one", p1->WindowName()); + VERIFY_ARE_EQUAL(L"two", p2->WindowName()); + + Log::Comment(L"When the peasants go to a new monarch, make sure they have the same name"); + m3->AddPeasant(*p1); + m3->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + VERIFY_ARE_EQUAL(L"one", p1->WindowName()); + VERIFY_ARE_EQUAL(L"two", p2->WindowName()); + + VERIFY_ARE_EQUAL(p1->GetID(), m3->_lookupPeasantIdForName(L"one")); + VERIFY_ARE_EQUAL(p2->GetID(), m3->_lookupPeasantIdForName(L"two")); + } + + void RemotingTests::LookupNamedPeasantWhenOthersDied() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + p1->WindowName(L"one"); + p2->WindowName(L"two"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + + VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); + + VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); + + Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch."); + RemotingTests::_killPeasant(m0, p1->GetID()); + + // By killing 1, then looking for "two", we happen to iterate over the + // corpse of 1 when looking for the peasant named "two". This causes us + // to remove 1 while looking for "two". Technically, we shouldn't be + // relying on any sort of ordering for an unordered_map iterator, but + // this one just so happens to work. + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); + + Log::Comment(L"Peasant 1 should have been pruned"); + VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); + } + + void RemotingTests::LookupNamedPeasantWhenItDied() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + p1->WindowName(L"one"); + p2->WindowName(L"two"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + + VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); + + VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); + + Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch."); + RemotingTests::_killPeasant(m0, p1->GetID()); + + VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"one")); + + Log::Comment(L"Peasant 1 should have been pruned"); + VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); + } + void RemotingTests::GetMruPeasantAfterNameLookupForDeadPeasant() + { + // This test is trying to hit the catch in Monarch::_lookupPeasantIdForName. + // + // We need to: + // * add some peasants, + // * make one the mru, then make a named two the mru + // * then kill two + // * then try to get the mru peasant -> it should be one + + VERIFY_ARE_EQUAL(true, false); + } + } From 136ce6d983c8b2b7031576f54d760e7999cf35d7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 25 Feb 2021 17:08:58 -0600 Subject: [PATCH 05/25] finish that test --- .../UnitTests_Remoting/RemotingTests.cpp | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 01b837b4634..6cea9c533dd 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -1188,7 +1188,59 @@ namespace RemotingUnitTests // * then kill two // * then try to get the mru peasant -> it should be one - VERIFY_ARE_EQUAL(true, false); + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + p1->WindowName(L"one"); + p2->WindowName(L"two"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + + VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); + + VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + guid1, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + Log::Comment(L"Kill peasant 2."); + RemotingTests::_killPeasant(m0, p2->GetID()); + + VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false)); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(true)); } } From ec97c43af43b67e099981ae3ad87578bca1ab843 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 26 Feb 2021 09:57:49 -0600 Subject: [PATCH 06/25] macros are life --- .../TerminalApp/AppActionHandlers.cpp | 9 +- .../TerminalApp/ShortcutActionDispatch.cpp | 279 ++++-------------- .../TerminalApp/ShortcutActionDispatch.h | 93 +++--- .../TerminalApp/ShortcutActionDispatch.idl | 94 +++--- src/cascadia/TerminalApp/TerminalPage.cpp | 93 +++--- src/cascadia/TerminalApp/TerminalPage.h | 94 +++--- .../TerminalSettingsModel/ActionAndArgs.cpp | 3 + .../TerminalSettingsModel/KeyMapping.idl | 3 +- .../Resources/en-US/Resources.resw | 3 + 9 files changed, 260 insertions(+), 411 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index c7b75fc605e..1266f624926 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -544,8 +544,8 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::_HandleOpenTabSearch(const IInspectable& /*sender*/, - const ActionEventArgs& args) + void TerminalPage::_HandleTabSearch(const IInspectable& /*sender*/, + const ActionEventArgs& args) { CommandPalette().SetTabs(_tabs, _mruTabs); CommandPalette().EnableTabSearchMode(); @@ -668,4 +668,9 @@ namespace winrt::TerminalApp::implementation actionArgs.Handled(true); } + void TerminalPage::_HandleIdentifyWindows(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + args.Handled(false); + } } diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp index 90eede9d057..98b4f68139f 100644 --- a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp +++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp @@ -10,6 +10,13 @@ using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::TerminalApp; +#define ACTION_CASE(action) \ + case ShortcutAction::action: \ + { \ + _##action##Handlers(*this, eventArgs); \ + break; \ + } + namespace winrt::TerminalApp::implementation { // Method Description: @@ -29,99 +36,24 @@ namespace winrt::TerminalApp::implementation switch (action) { - case ShortcutAction::CopyText: - { - _CopyTextHandlers(*this, eventArgs); - break; - } - case ShortcutAction::PasteText: - { - _PasteTextHandlers(*this, eventArgs); - break; - } - case ShortcutAction::OpenNewTabDropdown: - { - _OpenNewTabDropdownHandlers(*this, eventArgs); - break; - } - case ShortcutAction::DuplicateTab: - { - _DuplicateTabHandlers(*this, eventArgs); - break; - } - case ShortcutAction::OpenSettings: - { - _OpenSettingsHandlers(*this, eventArgs); - break; - } - - case ShortcutAction::NewTab: - { - _NewTabHandlers(*this, eventArgs); - break; - } - case ShortcutAction::CloseWindow: - { - _CloseWindowHandlers(*this, eventArgs); - break; - } - case ShortcutAction::CloseTab: - { - _CloseTabHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ClosePane: - { - _ClosePaneHandlers(*this, eventArgs); - break; - } - - case ShortcutAction::ScrollUp: - { - _ScrollUpHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ScrollDown: - { - _ScrollDownHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ScrollUpPage: - { - _ScrollUpPageHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ScrollDownPage: - { - _ScrollDownPageHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ScrollToTop: - { - _ScrollToTopHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ScrollToBottom: - { - _ScrollToBottomHandlers(*this, eventArgs); - break; - } - case ShortcutAction::NextTab: - { - _NextTabHandlers(*this, eventArgs); - break; - } - case ShortcutAction::PrevTab: - { - _PrevTabHandlers(*this, eventArgs); - break; - } - - case ShortcutAction::SendInput: - { - _SendInputHandlers(*this, eventArgs); - break; - } + ACTION_CASE(CopyText); + ACTION_CASE(PasteText); + ACTION_CASE(OpenNewTabDropdown); + ACTION_CASE(DuplicateTab); + ACTION_CASE(OpenSettings); + ACTION_CASE(NewTab); + ACTION_CASE(CloseWindow); + ACTION_CASE(CloseTab); + ACTION_CASE(ClosePane); + ACTION_CASE(ScrollUp); + ACTION_CASE(ScrollDown); + ACTION_CASE(ScrollUpPage); + ACTION_CASE(ScrollDownPage); + ACTION_CASE(ScrollToTop); + ACTION_CASE(ScrollToBottom); + ACTION_CASE(NextTab); + ACTION_CASE(PrevTab); + ACTION_CASE(SendInput); case ShortcutAction::SplitVertical: case ShortcutAction::SplitHorizontal: @@ -131,140 +63,33 @@ namespace winrt::TerminalApp::implementation break; } - case ShortcutAction::TogglePaneZoom: - { - _TogglePaneZoomHandlers(*this, eventArgs); - break; - } - - case ShortcutAction::SwitchToTab: - { - _SwitchToTabHandlers(*this, eventArgs); - break; - } - - case ShortcutAction::ResizePane: - { - _ResizePaneHandlers(*this, eventArgs); - break; - } - - case ShortcutAction::MoveFocus: - { - _MoveFocusHandlers(*this, eventArgs); - break; - } - - case ShortcutAction::AdjustFontSize: - { - _AdjustFontSizeHandlers(*this, eventArgs); - break; - } - case ShortcutAction::Find: - { - _FindHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ResetFontSize: - { - _ResetFontSizeHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ToggleShaderEffects: - { - _ToggleShaderEffectsHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ToggleFocusMode: - { - _ToggleFocusModeHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ToggleFullscreen: - { - _ToggleFullscreenHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ToggleAlwaysOnTop: - { - _ToggleAlwaysOnTopHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ToggleCommandPalette: - { - _ToggleCommandPaletteHandlers(*this, eventArgs); - break; - } - case ShortcutAction::SetColorScheme: - { - _SetColorSchemeHandlers(*this, eventArgs); - break; - } - case ShortcutAction::SetTabColor: - { - _SetTabColorHandlers(*this, eventArgs); - break; - } - case ShortcutAction::OpenTabColorPicker: - { - _OpenTabColorPickerHandlers(*this, eventArgs); - break; - } - case ShortcutAction::RenameTab: - { - _RenameTabHandlers(*this, eventArgs); - break; - } - case ShortcutAction::OpenTabRenamer: - { - _OpenTabRenamerHandlers(*this, eventArgs); - break; - } - case ShortcutAction::ExecuteCommandline: - { - _ExecuteCommandlineHandlers(*this, eventArgs); - break; - } - case ShortcutAction::CloseOtherTabs: - { - _CloseOtherTabsHandlers(*this, eventArgs); - break; - } - case ShortcutAction::CloseTabsAfter: - { - _CloseTabsAfterHandlers(*this, eventArgs); - break; - } - case ShortcutAction::MoveTab: - { - _MoveTabHandlers(*this, eventArgs); - break; - } - case ShortcutAction::TabSearch: - { - _TabSearchHandlers(*this, eventArgs); - break; - } - case ShortcutAction::BreakIntoDebugger: - { - _BreakIntoDebuggerHandlers(*this, eventArgs); - break; - } - case ShortcutAction::FindMatch: - { - _FindMatchHandlers(*this, eventArgs); - break; - } - case ShortcutAction::TogglePaneReadOnly: - { - _TogglePaneReadOnlyHandlers(*this, eventArgs); - break; - } - case ShortcutAction::NewWindow: - { - _NewWindowHandlers(*this, eventArgs); - break; - } + ACTION_CASE(TogglePaneZoom); + ACTION_CASE(SwitchToTab); + ACTION_CASE(ResizePane); + ACTION_CASE(MoveFocus); + ACTION_CASE(AdjustFontSize); + ACTION_CASE(Find); + ACTION_CASE(ResetFontSize); + ACTION_CASE(ToggleShaderEffects); + ACTION_CASE(ToggleFocusMode); + ACTION_CASE(ToggleFullscreen); + ACTION_CASE(ToggleAlwaysOnTop); + ACTION_CASE(ToggleCommandPalette); + ACTION_CASE(SetColorScheme); + ACTION_CASE(SetTabColor); + ACTION_CASE(OpenTabColorPicker); + ACTION_CASE(RenameTab); + ACTION_CASE(OpenTabRenamer); + ACTION_CASE(ExecuteCommandline); + ACTION_CASE(CloseOtherTabs); + ACTION_CASE(CloseTabsAfter); + ACTION_CASE(MoveTab); + ACTION_CASE(TabSearch); + ACTION_CASE(BreakIntoDebugger); + ACTION_CASE(FindMatch); + ACTION_CASE(TogglePaneReadOnly); + ACTION_CASE(NewWindow); + ACTION_CASE(IdentifyWindows); default: return false; } diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.h b/src/cascadia/TerminalApp/ShortcutActionDispatch.h index bdcf59e7a17..92312495d84 100644 --- a/src/cascadia/TerminalApp/ShortcutActionDispatch.h +++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.h @@ -13,6 +13,8 @@ namespace TerminalAppLocalTests class KeyBindingsTests; } +#define DECLARE_ACTION(action) TYPED_EVENT(action, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); + namespace winrt::TerminalApp::implementation { struct ShortcutActionDispatch : ShortcutActionDispatchT @@ -22,51 +24,52 @@ namespace winrt::TerminalApp::implementation bool DoAction(const Microsoft::Terminal::Settings::Model::ActionAndArgs& actionAndArgs); // clang-format off - TYPED_EVENT(CopyText, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(PasteText, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(OpenNewTabDropdown, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(DuplicateTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(NewTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(CloseWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(CloseTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ClosePane, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(SwitchToTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(NextTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(PrevTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(SendInput, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(SplitPane, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(TogglePaneZoom, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(AdjustFontSize, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ResetFontSize, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ScrollUp, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ScrollDown, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ScrollUpPage, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ScrollDownPage, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ScrollToTop, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ScrollToBottom, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(OpenSettings, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ResizePane, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(Find, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(MoveFocus, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ToggleShaderEffects, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ToggleFocusMode, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ToggleFullscreen, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ToggleAlwaysOnTop, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ToggleCommandPalette, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(SetColorScheme, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(SetTabColor, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(OpenTabColorPicker, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(RenameTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(OpenTabRenamer, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(ExecuteCommandline, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(CloseOtherTabs, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(CloseTabsAfter, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(TabSearch, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(MoveTab, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(BreakIntoDebugger, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(FindMatch, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(TogglePaneReadOnly, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); - TYPED_EVENT(NewWindow, TerminalApp::ShortcutActionDispatch, Microsoft::Terminal::Settings::Model::ActionEventArgs); + DECLARE_ACTION(CopyText); + DECLARE_ACTION(PasteText); + DECLARE_ACTION(OpenNewTabDropdown); + DECLARE_ACTION(DuplicateTab); + DECLARE_ACTION(NewTab); + DECLARE_ACTION(CloseWindow); + DECLARE_ACTION(CloseTab); + DECLARE_ACTION(ClosePane); + DECLARE_ACTION(SwitchToTab); + DECLARE_ACTION(NextTab); + DECLARE_ACTION(PrevTab); + DECLARE_ACTION(SendInput); + DECLARE_ACTION(SplitPane); + DECLARE_ACTION(TogglePaneZoom); + DECLARE_ACTION(AdjustFontSize); + DECLARE_ACTION(ResetFontSize); + DECLARE_ACTION(ScrollUp); + DECLARE_ACTION(ScrollDown); + DECLARE_ACTION(ScrollUpPage); + DECLARE_ACTION(ScrollDownPage); + DECLARE_ACTION(ScrollToTop); + DECLARE_ACTION(ScrollToBottom); + DECLARE_ACTION(OpenSettings); + DECLARE_ACTION(ResizePane); + DECLARE_ACTION(Find); + DECLARE_ACTION(MoveFocus); + DECLARE_ACTION(ToggleShaderEffects); + DECLARE_ACTION(ToggleFocusMode); + DECLARE_ACTION(ToggleFullscreen); + DECLARE_ACTION(ToggleAlwaysOnTop); + DECLARE_ACTION(ToggleCommandPalette); + DECLARE_ACTION(SetColorScheme); + DECLARE_ACTION(SetTabColor); + DECLARE_ACTION(OpenTabColorPicker); + DECLARE_ACTION(RenameTab); + DECLARE_ACTION(OpenTabRenamer); + DECLARE_ACTION(ExecuteCommandline); + DECLARE_ACTION(CloseOtherTabs); + DECLARE_ACTION(CloseTabsAfter); + DECLARE_ACTION(TabSearch); + DECLARE_ACTION(MoveTab); + DECLARE_ACTION(BreakIntoDebugger); + DECLARE_ACTION(FindMatch); + DECLARE_ACTION(TogglePaneReadOnly); + DECLARE_ACTION(NewWindow); + DECLARE_ACTION(IdentifyWindows); // clang-format on private: diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.idl b/src/cascadia/TerminalApp/ShortcutActionDispatch.idl index 4a8b366b945..870a2175aa9 100644 --- a/src/cascadia/TerminalApp/ShortcutActionDispatch.idl +++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.idl @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +#define ACTION_EVENT(name) event Windows.Foundation.TypedEventHandler name + namespace TerminalApp { [default_interface] runtimeclass ShortcutActionDispatch { @@ -8,50 +10,52 @@ namespace TerminalApp Boolean DoAction(Microsoft.Terminal.Settings.Model.ActionAndArgs actionAndArgs); - event Windows.Foundation.TypedEventHandler CopyText; - event Windows.Foundation.TypedEventHandler PasteText; - event Windows.Foundation.TypedEventHandler NewTab; - event Windows.Foundation.TypedEventHandler OpenNewTabDropdown; - event Windows.Foundation.TypedEventHandler DuplicateTab; - event Windows.Foundation.TypedEventHandler CloseWindow; - event Windows.Foundation.TypedEventHandler CloseTab; - event Windows.Foundation.TypedEventHandler ClosePane; - event Windows.Foundation.TypedEventHandler SwitchToTab; - event Windows.Foundation.TypedEventHandler NextTab; - event Windows.Foundation.TypedEventHandler PrevTab; - event Windows.Foundation.TypedEventHandler SendInput; - event Windows.Foundation.TypedEventHandler SplitPane; - event Windows.Foundation.TypedEventHandler TogglePaneZoom; - event Windows.Foundation.TypedEventHandler AdjustFontSize; - event Windows.Foundation.TypedEventHandler ResetFontSize; - event Windows.Foundation.TypedEventHandler ScrollUp; - event Windows.Foundation.TypedEventHandler ScrollDown; - event Windows.Foundation.TypedEventHandler ScrollUpPage; - event Windows.Foundation.TypedEventHandler ScrollDownPage; - event Windows.Foundation.TypedEventHandler ScrollToTop; - event Windows.Foundation.TypedEventHandler ScrollToBottom; - event Windows.Foundation.TypedEventHandler OpenSettings; - event Windows.Foundation.TypedEventHandler ResizePane; - event Windows.Foundation.TypedEventHandler Find; - event Windows.Foundation.TypedEventHandler MoveFocus; - event Windows.Foundation.TypedEventHandler ToggleShaderEffects; - event Windows.Foundation.TypedEventHandler ToggleFocusMode; - event Windows.Foundation.TypedEventHandler ToggleFullscreen; - event Windows.Foundation.TypedEventHandler ToggleAlwaysOnTop; - event Windows.Foundation.TypedEventHandler ToggleCommandPalette; - event Windows.Foundation.TypedEventHandler SetColorScheme; - event Windows.Foundation.TypedEventHandler SetTabColor; - event Windows.Foundation.TypedEventHandler OpenTabColorPicker; - event Windows.Foundation.TypedEventHandler RenameTab; - event Windows.Foundation.TypedEventHandler OpenTabRenamer; - event Windows.Foundation.TypedEventHandler ExecuteCommandline; - event Windows.Foundation.TypedEventHandler CloseOtherTabs; - event Windows.Foundation.TypedEventHandler CloseTabsAfter; - event Windows.Foundation.TypedEventHandler TabSearch; - event Windows.Foundation.TypedEventHandler MoveTab; - event Windows.Foundation.TypedEventHandler BreakIntoDebugger; - event Windows.Foundation.TypedEventHandler FindMatch; - event Windows.Foundation.TypedEventHandler TogglePaneReadOnly; - event Windows.Foundation.TypedEventHandler NewWindow; + ACTION_EVENT(CopyText); + ACTION_EVENT(PasteText); + ACTION_EVENT(NewTab); + ACTION_EVENT(OpenNewTabDropdown); + ACTION_EVENT(DuplicateTab); + ACTION_EVENT(CloseWindow); + ACTION_EVENT(CloseTab); + ACTION_EVENT(ClosePane); + ACTION_EVENT(SwitchToTab); + ACTION_EVENT(NextTab); + ACTION_EVENT(PrevTab); + ACTION_EVENT(SendInput); + ACTION_EVENT(SplitPane); + ACTION_EVENT(TogglePaneZoom); + ACTION_EVENT(AdjustFontSize); + ACTION_EVENT(ResetFontSize); + ACTION_EVENT(ScrollUp); + ACTION_EVENT(ScrollDown); + ACTION_EVENT(ScrollUpPage); + ACTION_EVENT(ScrollDownPage); + ACTION_EVENT(ScrollToTop); + ACTION_EVENT(ScrollToBottom); + ACTION_EVENT(OpenSettings); + ACTION_EVENT(ResizePane); + ACTION_EVENT(Find); + ACTION_EVENT(MoveFocus); + ACTION_EVENT(ToggleShaderEffects); + ACTION_EVENT(ToggleFocusMode); + ACTION_EVENT(ToggleFullscreen); + ACTION_EVENT(ToggleAlwaysOnTop); + ACTION_EVENT(ToggleCommandPalette); + ACTION_EVENT(SetColorScheme); + ACTION_EVENT(SetTabColor); + ACTION_EVENT(OpenTabColorPicker); + ACTION_EVENT(RenameTab); + ACTION_EVENT(OpenTabRenamer); + ACTION_EVENT(ExecuteCommandline); + ACTION_EVENT(CloseOtherTabs); + ACTION_EVENT(CloseTabsAfter); + ACTION_EVENT(TabSearch); + ACTION_EVENT(MoveTab); + ACTION_EVENT(BreakIntoDebugger); + ACTION_EVENT(FindMatch); + ACTION_EVENT(TogglePaneReadOnly); + ACTION_EVENT(NewWindow); + ACTION_EVENT(IdentifyWindows); + } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 42c771a229b..f6e9399f47a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -32,6 +32,8 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace ::TerminalApp; using namespace ::Microsoft::Console; +#define HOOKUP_ACTION(action) _actionDispatch->action({ this, &TerminalPage::_Handle##action }); + namespace winrt { namespace MUX = Microsoft::UI::Xaml; @@ -1113,51 +1115,52 @@ namespace winrt::TerminalApp::implementation // Hook up the ShortcutActionDispatch object's events to our handlers. // They should all be hooked up here, regardless of whether or not // there's an actual keychord for them. - _actionDispatch->OpenNewTabDropdown({ this, &TerminalPage::_HandleOpenNewTabDropdown }); - _actionDispatch->DuplicateTab({ this, &TerminalPage::_HandleDuplicateTab }); - _actionDispatch->CloseTab({ this, &TerminalPage::_HandleCloseTab }); - _actionDispatch->ClosePane({ this, &TerminalPage::_HandleClosePane }); - _actionDispatch->CloseWindow({ this, &TerminalPage::_HandleCloseWindow }); - _actionDispatch->ScrollUp({ this, &TerminalPage::_HandleScrollUp }); - _actionDispatch->ScrollDown({ this, &TerminalPage::_HandleScrollDown }); - _actionDispatch->NextTab({ this, &TerminalPage::_HandleNextTab }); - _actionDispatch->PrevTab({ this, &TerminalPage::_HandlePrevTab }); - _actionDispatch->SendInput({ this, &TerminalPage::_HandleSendInput }); - _actionDispatch->SplitPane({ this, &TerminalPage::_HandleSplitPane }); - _actionDispatch->TogglePaneZoom({ this, &TerminalPage::_HandleTogglePaneZoom }); - _actionDispatch->ScrollUpPage({ this, &TerminalPage::_HandleScrollUpPage }); - _actionDispatch->ScrollDownPage({ this, &TerminalPage::_HandleScrollDownPage }); - _actionDispatch->ScrollToTop({ this, &TerminalPage::_HandleScrollToTop }); - _actionDispatch->ScrollToBottom({ this, &TerminalPage::_HandleScrollToBottom }); - _actionDispatch->OpenSettings({ this, &TerminalPage::_HandleOpenSettings }); - _actionDispatch->PasteText({ this, &TerminalPage::_HandlePasteText }); - _actionDispatch->NewTab({ this, &TerminalPage::_HandleNewTab }); - _actionDispatch->SwitchToTab({ this, &TerminalPage::_HandleSwitchToTab }); - _actionDispatch->ResizePane({ this, &TerminalPage::_HandleResizePane }); - _actionDispatch->MoveFocus({ this, &TerminalPage::_HandleMoveFocus }); - _actionDispatch->CopyText({ this, &TerminalPage::_HandleCopyText }); - _actionDispatch->AdjustFontSize({ this, &TerminalPage::_HandleAdjustFontSize }); - _actionDispatch->Find({ this, &TerminalPage::_HandleFind }); - _actionDispatch->ResetFontSize({ this, &TerminalPage::_HandleResetFontSize }); - _actionDispatch->ToggleShaderEffects({ this, &TerminalPage::_HandleToggleShaderEffects }); - _actionDispatch->ToggleFocusMode({ this, &TerminalPage::_HandleToggleFocusMode }); - _actionDispatch->ToggleFullscreen({ this, &TerminalPage::_HandleToggleFullscreen }); - _actionDispatch->ToggleAlwaysOnTop({ this, &TerminalPage::_HandleToggleAlwaysOnTop }); - _actionDispatch->ToggleCommandPalette({ this, &TerminalPage::_HandleToggleCommandPalette }); - _actionDispatch->SetColorScheme({ this, &TerminalPage::_HandleSetColorScheme }); - _actionDispatch->SetTabColor({ this, &TerminalPage::_HandleSetTabColor }); - _actionDispatch->OpenTabColorPicker({ this, &TerminalPage::_HandleOpenTabColorPicker }); - _actionDispatch->RenameTab({ this, &TerminalPage::_HandleRenameTab }); - _actionDispatch->OpenTabRenamer({ this, &TerminalPage::_HandleOpenTabRenamer }); - _actionDispatch->ExecuteCommandline({ this, &TerminalPage::_HandleExecuteCommandline }); - _actionDispatch->CloseOtherTabs({ this, &TerminalPage::_HandleCloseOtherTabs }); - _actionDispatch->CloseTabsAfter({ this, &TerminalPage::_HandleCloseTabsAfter }); - _actionDispatch->TabSearch({ this, &TerminalPage::_HandleOpenTabSearch }); - _actionDispatch->MoveTab({ this, &TerminalPage::_HandleMoveTab }); - _actionDispatch->BreakIntoDebugger({ this, &TerminalPage::_HandleBreakIntoDebugger }); - _actionDispatch->FindMatch({ this, &TerminalPage::_HandleFindMatch }); - _actionDispatch->TogglePaneReadOnly({ this, &TerminalPage::_HandleTogglePaneReadOnly }); - _actionDispatch->NewWindow({ this, &TerminalPage::_HandleNewWindow }); + HOOKUP_ACTION(OpenNewTabDropdown); + HOOKUP_ACTION(DuplicateTab); + HOOKUP_ACTION(CloseTab); + HOOKUP_ACTION(ClosePane); + HOOKUP_ACTION(CloseWindow); + HOOKUP_ACTION(ScrollUp); + HOOKUP_ACTION(ScrollDown); + HOOKUP_ACTION(NextTab); + HOOKUP_ACTION(PrevTab); + HOOKUP_ACTION(SendInput); + HOOKUP_ACTION(SplitPane); + HOOKUP_ACTION(TogglePaneZoom); + HOOKUP_ACTION(ScrollUpPage); + HOOKUP_ACTION(ScrollDownPage); + HOOKUP_ACTION(ScrollToTop); + HOOKUP_ACTION(ScrollToBottom); + HOOKUP_ACTION(OpenSettings); + HOOKUP_ACTION(PasteText); + HOOKUP_ACTION(NewTab); + HOOKUP_ACTION(SwitchToTab); + HOOKUP_ACTION(ResizePane); + HOOKUP_ACTION(MoveFocus); + HOOKUP_ACTION(CopyText); + HOOKUP_ACTION(AdjustFontSize); + HOOKUP_ACTION(Find); + HOOKUP_ACTION(ResetFontSize); + HOOKUP_ACTION(ToggleShaderEffects); + HOOKUP_ACTION(ToggleFocusMode); + HOOKUP_ACTION(ToggleFullscreen); + HOOKUP_ACTION(ToggleAlwaysOnTop); + HOOKUP_ACTION(ToggleCommandPalette); + HOOKUP_ACTION(SetColorScheme); + HOOKUP_ACTION(SetTabColor); + HOOKUP_ACTION(OpenTabColorPicker); + HOOKUP_ACTION(RenameTab); + HOOKUP_ACTION(OpenTabRenamer); + HOOKUP_ACTION(ExecuteCommandline); + HOOKUP_ACTION(CloseOtherTabs); + HOOKUP_ACTION(CloseTabsAfter); + HOOKUP_ACTION(TabSearch); + HOOKUP_ACTION(MoveTab); + HOOKUP_ACTION(BreakIntoDebugger); + HOOKUP_ACTION(FindMatch); + HOOKUP_ACTION(TogglePaneReadOnly); + HOOKUP_ACTION(NewWindow); + HOOKUP_ACTION(IdentifyWindows); } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index d26e025516b..a919e95b361 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -12,6 +12,8 @@ #include "AppCommandlineArgs.h" +#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); + static constexpr uint32_t DefaultRowsToScroll{ 3 }; static constexpr std::wstring_view TabletInputServiceKey{ L"TabletInputService" }; // fwdecl unittest classes @@ -275,52 +277,52 @@ namespace winrt::TerminalApp::implementation #pragma region ActionHandlers // These are all defined in AppActionHandlers.cpp - void _HandleOpenNewTabDropdown(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleDuplicateTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleCloseTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleClosePane(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleScrollUp(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleScrollDown(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleNextTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandlePrevTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleSendInput(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleSplitPane(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleTogglePaneZoom(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleScrollUpPage(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleScrollDownPage(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleScrollToTop(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleScrollToBottom(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleOpenSettings(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandlePasteText(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleNewTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleSwitchToTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleResizePane(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleMoveFocus(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleCopyText(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleCloseWindow(const IInspectable&, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleAdjustFontSize(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleFind(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleResetFontSize(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleToggleShaderEffects(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleToggleFocusMode(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleToggleFullscreen(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleToggleAlwaysOnTop(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleSetColorScheme(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleSetTabColor(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleOpenTabColorPicker(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleRenameTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleOpenTabRenamer(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleExecuteCommandline(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleToggleCommandPalette(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleCloseOtherTabs(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleCloseTabsAfter(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleOpenTabSearch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleMoveTab(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleBreakIntoDebugger(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleFindMatch(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleTogglePaneReadOnly(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - void _HandleNewWindow(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); - + DECLARE_ACTION_HANDLER(OpenNewTabDropdown); + DECLARE_ACTION_HANDLER(DuplicateTab); + DECLARE_ACTION_HANDLER(CloseTab); + DECLARE_ACTION_HANDLER(ClosePane); + DECLARE_ACTION_HANDLER(ScrollUp); + DECLARE_ACTION_HANDLER(ScrollDown); + DECLARE_ACTION_HANDLER(NextTab); + DECLARE_ACTION_HANDLER(PrevTab); + DECLARE_ACTION_HANDLER(SendInput); + DECLARE_ACTION_HANDLER(SplitPane); + DECLARE_ACTION_HANDLER(TogglePaneZoom); + DECLARE_ACTION_HANDLER(ScrollUpPage); + DECLARE_ACTION_HANDLER(ScrollDownPage); + DECLARE_ACTION_HANDLER(ScrollToTop); + DECLARE_ACTION_HANDLER(ScrollToBottom); + DECLARE_ACTION_HANDLER(OpenSettings); + DECLARE_ACTION_HANDLER(PasteText); + DECLARE_ACTION_HANDLER(NewTab); + DECLARE_ACTION_HANDLER(SwitchToTab); + DECLARE_ACTION_HANDLER(ResizePane); + DECLARE_ACTION_HANDLER(MoveFocus); + DECLARE_ACTION_HANDLER(CopyText); + DECLARE_ACTION_HANDLER(CloseWindow); + DECLARE_ACTION_HANDLER(AdjustFontSize); + DECLARE_ACTION_HANDLER(Find); + DECLARE_ACTION_HANDLER(ResetFontSize); + DECLARE_ACTION_HANDLER(ToggleShaderEffects); + DECLARE_ACTION_HANDLER(ToggleFocusMode); + DECLARE_ACTION_HANDLER(ToggleFullscreen); + DECLARE_ACTION_HANDLER(ToggleAlwaysOnTop); + DECLARE_ACTION_HANDLER(SetColorScheme); + DECLARE_ACTION_HANDLER(SetTabColor); + DECLARE_ACTION_HANDLER(OpenTabColorPicker); + DECLARE_ACTION_HANDLER(RenameTab); + DECLARE_ACTION_HANDLER(OpenTabRenamer); + DECLARE_ACTION_HANDLER(ExecuteCommandline); + DECLARE_ACTION_HANDLER(ToggleCommandPalette); + DECLARE_ACTION_HANDLER(CloseOtherTabs); + DECLARE_ACTION_HANDLER(CloseTabsAfter); + DECLARE_ACTION_HANDLER(TabSearch); + DECLARE_ACTION_HANDLER(MoveTab); + DECLARE_ACTION_HANDLER(BreakIntoDebugger); + DECLARE_ACTION_HANDLER(FindMatch); + DECLARE_ACTION_HANDLER(TogglePaneReadOnly); + DECLARE_ACTION_HANDLER(NewWindow); + DECLARE_ACTION_HANDLER(IdentifyWindows); // Make sure to hook new actions up in _RegisterActionCallbacks! #pragma endregion diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 7069d448b42..bd9daf8f9dc 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -53,6 +53,7 @@ static constexpr std::string_view BreakIntoDebuggerKey{ "breakIntoDebugger" }; static constexpr std::string_view FindMatchKey{ "findMatch" }; static constexpr std::string_view TogglePaneReadOnlyKey{ "toggleReadOnlyMode" }; static constexpr std::string_view NewWindowKey{ "newWindow" }; +static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" }; static constexpr std::string_view ActionKey{ "action" }; @@ -119,6 +120,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { FindMatchKey, ShortcutAction::FindMatch }, { TogglePaneReadOnlyKey, ShortcutAction::TogglePaneReadOnly }, { NewWindowKey, ShortcutAction::NewWindow }, + { IdentifyWindowsKey, ShortcutAction::IdentifyWindows }, }; using ParseResult = std::tuple>; @@ -322,6 +324,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::FindMatch, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::TogglePaneReadOnly, RS_(L"TogglePaneReadOnlyCommandKey") }, { ShortcutAction::NewWindow, RS_(L"NewWindowCommandKey") }, + { ShortcutAction::IdentifyWindows, RS_(L"IdentifyWindowsCommandKey") }, }; }(); diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.idl b/src/cascadia/TerminalSettingsModel/KeyMapping.idl index a51a601afee..56aff56fed5 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.idl +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.idl @@ -54,7 +54,8 @@ namespace Microsoft.Terminal.Settings.Model BreakIntoDebugger, TogglePaneReadOnly, FindMatch, - NewWindow + NewWindow, + IdentifyWindows }; [default_interface] runtimeclass ActionAndArgs { diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index f8a588553f5..f9fe7361b29 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -244,6 +244,9 @@ New window + + Identify windows + Next tab From fa26f7f569ad843997cff960590d4abfcf38f1b3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 26 Feb 2021 12:29:52 -0600 Subject: [PATCH 07/25] THIS NEEDS TO GO TO THE PARENT --- src/cascadia/Remoting/WindowManager.cpp | 27 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index b5b77b19411..cbf36830f77 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -127,18 +127,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation 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)); } 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(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)); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline_AsMonarch", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline_AsMonarch", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } } if (_shouldCreateWindow) From a391455410f7f242c82670405526a5bd74ed589f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 26 Feb 2021 12:34:58 -0600 Subject: [PATCH 08/25] Plumb the events up and down WE LIVE IN A MAD WORLD. Teaching tips are exactly the UI we want, but they just don't fucking work man. WE want them to light dismiss, but if the window is inactive, and you have ILDE=true, then the tip immediately dismisses itself. So inactive windows need to not enable light dismiss. ALSO we need inactive windows to not focus something when the tip dismisses itself. Like, focus was _nowhere_ when we started. We need to toss the focus back to _nowhere_. --- src/cascadia/Remoting/Monarch.cpp | 24 ++++++++++++++ src/cascadia/Remoting/Monarch.h | 3 ++ src/cascadia/Remoting/Peasant.cpp | 31 +++++++++++++++++++ src/cascadia/Remoting/Peasant.h | 4 +++ src/cascadia/Remoting/Peasant.idl | 4 +++ .../TerminalApp/AppActionHandlers.cpp | 3 +- src/cascadia/TerminalApp/AppLogic.cpp | 8 +++++ src/cascadia/TerminalApp/AppLogic.h | 2 ++ src/cascadia/TerminalApp/AppLogic.idl | 2 ++ src/cascadia/TerminalApp/TerminalPage.cpp | 18 +++++++++++ src/cascadia/TerminalApp/TerminalPage.h | 5 +++ src/cascadia/TerminalApp/TerminalPage.idl | 5 +++ src/cascadia/TerminalApp/TerminalPage.xaml | 13 +++++++- src/cascadia/WindowsTerminal/AppHost.cpp | 20 ++++++++++++ src/cascadia/WindowsTerminal/AppHost.h | 4 +++ 15 files changed, 144 insertions(+), 2 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cf98bdb1d4b..ac737baf443 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -75,6 +75,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto newPeasantsId = peasant.GetID(); // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows }); _peasants[newPeasantsId] = peasant; @@ -568,4 +569,27 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation result->WindowName(targetWindowName); return *result; } + + void Monarch::_identifyWindows(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + // Notify all the peasants to display their ID. + for (const auto& [id, p] : _peasants) + { + try + { + p.DisplayWindowId(); + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + // If this fails, we don't _really_ care. Just movoe on to the + // next one. Someone else will clean up the dead peasant. + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_identifyWindows_Failed", + TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } + } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 52399ad5971..0357a830e6f 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -74,6 +74,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _doHandleActivatePeasant(const winrt::com_ptr& args); void _clearOldMruEntries(const uint64_t peasantID); + void _identifyWindows(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + friend class RemotingUnitTests::RemotingTests; }; } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index ee34887c2a9..01d7c5dfcbc 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -117,4 +117,35 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _lastActivatedArgs; } + void Peasant::DisplayWindowId() + { + // Not worried about try/catching this. The handler is in AppHost, which + // is in-proc for us. + _DisplayWindowIdRequestedHandlers(*this, nullptr); + } + + void Peasant::RequestIdentifyWindows() + { + bool successfullyNotified = false; + + try + { + // Try/catch this, because the other side of this event is handled + // by the monarch. The monarch might have died. If they have, this + // will throw an exception. Just eat it, the election thread will + // handle hooking up the new one. + _IdentifyWindowsRequestedHandlers(*this, nullptr); + successfullyNotified = true; + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + } + TraceLoggingWrite(g_hRemotingProvider, + "Peasant_RequestIdentifyWindows", + TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), + TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 3b680d15ccf..90120d7fff4 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -22,6 +22,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); + void RequestIdentifyWindows(); + void DisplayWindowId(); winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); @@ -30,6 +32,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); + TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); private: Peasant(const uint64_t testPID); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 2ef4cb89086..1c3ff912300 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -34,9 +34,13 @@ namespace Microsoft.Terminal.Remoting void ActivateWindow(WindowActivatedArgs args); WindowActivatedArgs GetLastActivatedArgs(); String WindowName { get; }; + void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested + void DisplayWindowId(); // Tells us to display its own ID (which causes a DisplayWindowIdRequested to be raised) event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; + event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; + event Windows.Foundation.TypedEventHandler DisplayWindowIdRequested; }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 1266f624926..301169b4393 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -671,6 +671,7 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleIdentifyWindows(const IInspectable& /*sender*/, const ActionEventArgs& args) { - args.Handled(false); + _IdentifyWindowsRequestedHandlers(*this, nullptr); + args.Handled(true); } } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 56f1964291b..0675d28d91c 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1368,6 +1368,14 @@ namespace winrt::TerminalApp::implementation return _root ? _root->AlwaysOnTop() : false; } + void AppLogic::IdentifyWindow() + { + if (_root) + { + _root->IdentifyWindow(); + } + } + // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 0c83d0afe95..21e41690a82 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -56,6 +56,7 @@ namespace winrt::TerminalApp::implementation bool FocusMode() const; bool Fullscreen() const; bool AlwaysOnTop() const; + void IdentifyWindow(); Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); bool CenterOnLaunch(); @@ -146,6 +147,7 @@ namespace winrt::TerminalApp::implementation 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); #ifdef UNIT_TESTING friend class TerminalAppLocalTests::CommandlineTest; diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index bc2d4546486..2c0f56f3890 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -47,6 +47,7 @@ namespace TerminalApp Boolean FocusMode { get; }; Boolean Fullscreen { get; }; Boolean AlwaysOnTop { get; }; + void IdentifyWindow(); Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); Boolean CenterOnLaunch { get; }; @@ -78,5 +79,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler AlwaysOnTopChanged; event Windows.Foundation.TypedEventHandler RaiseVisualBell; event Windows.Foundation.TypedEventHandler SetTaskbarProgress; + event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; } } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f6e9399f47a..5f0d836dccc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3290,6 +3290,24 @@ namespace winrt::TerminalApp::implementation } } + winrt::fire_and_forget TerminalPage::IdentifyWindow() + { + auto weakThis{ get_weak() }; + co_await winrt::resume_foreground(Dispatcher()); + if (auto page{ weakThis.get() }) + { + page->WindowIdToast().IsOpen(true); + } + } + winrt::hstring TerminalPage::WindowName() + { + return L""; + } + uint64_t TerminalPage::WindowId() + { + return 42; + } + // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index a919e95b361..feb8d629f56 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -83,6 +83,10 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); + winrt::fire_and_forget IdentifyWindow(); + winrt::hstring WindowName(); + uint64_t WindowId(); + winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial, const winrt::hstring cwd = L""); @@ -97,6 +101,7 @@ namespace winrt::TerminalApp::implementation DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RaiseVisualBell, _raiseVisualBellHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTaskbarProgress, _setTaskbarProgressHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(Initialized, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs); + TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable); private: friend struct TerminalPageT; // for Xaml to bind events diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index b2580d158c4..a025595b7a2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -22,6 +22,10 @@ namespace TerminalApp Boolean Fullscreen { get; }; Boolean AlwaysOnTop { get; }; + void IdentifyWindow(); + String WindowName { get; }; + UInt64 WindowId { 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. @@ -40,5 +44,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler AlwaysOnTopChanged; event Windows.Foundation.TypedEventHandler Initialized; event Windows.Foundation.TypedEventHandler SetTaskbarProgress; + event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; } } diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index a2e23782c57..d6915d12874 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -122,7 +122,7 @@ the MIT License. See LICENSE in the project root for license information. --> x:Name="CommandPalette" Grid.Row="1" Visibility="Collapsed" - PreviewKeyDown ="_KeyDownHandler" + PreviewKeyDown="_KeyDownHandler" VerticalAlignment="Stretch" /> IsOpen="False" Severity="Warning" Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"/> + + + + + + + diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6a1ba759804..7c133cd68be 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -194,6 +194,8 @@ void AppHost::_HandleCommandlineArgs() // commandline (in the future), it'll trigger this callback, that we'll // use to send the actions to the app. peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); + + peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId }); } } @@ -244,6 +246,7 @@ void AppHost::Initialize() _logic.TitleChanged({ this, &AppHost::AppTitleChanged }); _logic.LastTabClosed({ this, &AppHost::LastTabClosed }); _logic.SetTaskbarProgress({ this, &AppHost::SetTaskbarProgress }); + _logic.IdentifyWindowsRequested({ this, &AppHost::_IdentifyWindowsRequested }); _window->UpdateTitle(_logic.Title()); @@ -603,3 +606,20 @@ GUID AppHost::_CurrentDesktopGuid() CATCH_LOG(); return currentDesktopGuid; } + +winrt::fire_and_forget AppHost::_IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) +{ + co_await winrt::resume_background(); + + if (auto peasant{ _windowManager.CurrentWindow() }) + { + peasant.RequestIdentifyWindows(); + } +} + +void AppHost::_DisplayWindowId(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) +{ + _logic.IdentifyWindow(); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index c8782d61e31..723a4604836 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -51,5 +51,9 @@ class AppHost void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + winrt::fire_and_forget _IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + void _DisplayWindowId(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); GUID _CurrentDesktopGuid(); }; From 001f545b9f482e16476fdc93c38729155d4b87dd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 1 Mar 2021 11:37:39 -0600 Subject: [PATCH 09/25] Bind the labels to the actual TerminalPage object --- patch.diff | 1745 +++++++++++++++++ .../Resources/en-US/Resources.resw | 5 + .../TerminalApp/TerminalAppLib.vcxproj | 10 +- src/cascadia/TerminalApp/TerminalPage.cpp | 18 +- src/cascadia/TerminalApp/TerminalPage.h | 9 +- src/cascadia/TerminalApp/TerminalPage.idl | 18 +- src/cascadia/TerminalApp/TerminalPage.xaml | 19 +- .../TerminalApp/WindowNameConverter.cpp | 51 + .../TerminalApp/WindowNameConverter.h | 11 + 9 files changed, 1865 insertions(+), 21 deletions(-) create mode 100644 patch.diff create mode 100644 src/cascadia/TerminalApp/WindowNameConverter.cpp create mode 100644 src/cascadia/TerminalApp/WindowNameConverter.h diff --git a/patch.diff b/patch.diff new file mode 100644 index 00000000000..4fda47e4210 --- /dev/null +++ b/patch.diff @@ -0,0 +1,1745 @@ +From f33c69d8b4171ed43adc09113f2bd9975fdf4422 Mon Sep 17 00:00:00 2001 +From: Mike Griese +Date: Fri, 17 Jul 2020 13:01:07 -0500 +Subject: [PATCH] Add a scratch island project for testing + +--- + OpenConsole.sln | 38 ++ + src/tools/ScratchIsland/AppHost.cpp | 176 +++++ + src/tools/ScratchIsland/AppHost.h | 24 + + src/tools/ScratchIsland/BaseWindow.h | 228 +++++++ + src/tools/ScratchIsland/IslandWindow.cpp | 604 ++++++++++++++++++ + src/tools/ScratchIsland/IslandWindow.h | 77 +++ + src/tools/ScratchIsland/ScratchIsland.def | 1 + + .../ScratchIsland/ScratchIsland.manifest | 24 + + src/tools/ScratchIsland/ScratchIsland.rc | 94 +++ + src/tools/ScratchIsland/ScratchIsland.vcxproj | 164 +++++ + src/tools/ScratchIsland/main.cpp | 54 ++ + src/tools/ScratchIsland/packages.config | 8 + + src/tools/ScratchIsland/pch.cpp | 4 + + src/tools/ScratchIsland/pch.h | 74 +++ + src/tools/ScratchIsland/resource.h | 24 + + 15 files changed, 1594 insertions(+) + create mode 100644 src/tools/ScratchIsland/AppHost.cpp + create mode 100644 src/tools/ScratchIsland/AppHost.h + create mode 100644 src/tools/ScratchIsland/BaseWindow.h + create mode 100644 src/tools/ScratchIsland/IslandWindow.cpp + create mode 100644 src/tools/ScratchIsland/IslandWindow.h + create mode 100644 src/tools/ScratchIsland/ScratchIsland.def + create mode 100644 src/tools/ScratchIsland/ScratchIsland.manifest + create mode 100644 src/tools/ScratchIsland/ScratchIsland.rc + create mode 100644 src/tools/ScratchIsland/ScratchIsland.vcxproj + create mode 100644 src/tools/ScratchIsland/main.cpp + create mode 100644 src/tools/ScratchIsland/packages.config + create mode 100644 src/tools/ScratchIsland/pch.cpp + create mode 100644 src/tools/ScratchIsland/pch.h + create mode 100644 src/tools/ScratchIsland/resource.h + +diff --git a/OpenConsole.sln b/OpenConsole.sln +index dcae33515..5099f457d 100644 +--- a/OpenConsole.sln ++++ b/OpenConsole.sln +@@ -321,6 +321,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchWinRTClient", "src\t + {D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4} + EndProjectSection + EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchIsland", "src\tools\ScratchIsland\ScratchIsland.vcxproj", "{23a1f736-cd19-4196-980f-84bcd50cf783}" ++ ProjectSection(ProjectDependencies) = postProject ++ {06382349-D62A-4C7D-A7D3-9CA817EAE092} = {06382349-D62A-4C7D-A7D3-9CA817EAE092} ++ {D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4} ++ EndProjectSection ++EndProject + Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}" + EndProject + Global +@@ -2087,6 +2093,37 @@ Global + {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64 + {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32 + {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32 ++ ++ ++ ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.ActiveCfg = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.Build.0 = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.ActiveCfg = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.Build.0 = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.Build.0 = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.Build.0 = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.ActiveCfg = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.Build.0 = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.ActiveCfg = Release|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.Build.0 = Release|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|Any CPU.ActiveCfg = Debug|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|ARM64.ActiveCfg = Debug|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.ActiveCfg = Debug|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.Build.0 = Debug|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.ActiveCfg = Debug|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.Build.0 = Debug|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|Any CPU.ActiveCfg = Release|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|ARM64.ActiveCfg = Release|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.ActiveCfg = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.Build.0 = Release|x64 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.ActiveCfg = Release|Win32 ++ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE +@@ -2169,6 +2206,7 @@ Global + {1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202} + {D46D9547-F085-4645-B8F7-E8CD21559AB4} = {A10C4720-DCA4-4640-9749-67F4314F527C} + {06382349-D62A-4C7D-A7D3-9CA817EAE092} = {A10C4720-DCA4-4640-9749-67F4314F527C} ++ {23a1f736-cd19-4196-980f-84bcd50cf783} = {A10C4720-DCA4-4640-9749-67F4314F527C} + {506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution +diff --git a/src/tools/ScratchIsland/AppHost.cpp b/src/tools/ScratchIsland/AppHost.cpp +new file mode 100644 +index 000000000..d1ec7b004 +--- /dev/null ++++ b/src/tools/ScratchIsland/AppHost.cpp +@@ -0,0 +1,176 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT license. ++ ++#include "pch.h" ++#include "AppHost.h" ++#include "../types/inc/Viewport.hpp" ++#include "../types/inc/utils.hpp" ++#include "../types/inc/User32Utils.hpp" ++ ++#include "resource.h" ++ ++using namespace winrt::Windows::UI; ++using namespace winrt::Windows::UI::Composition; ++using namespace winrt::Windows::UI::Xaml; ++using namespace winrt::Windows::UI::Xaml::Hosting; ++using namespace winrt::Windows::Foundation::Numerics; ++using namespace ::Microsoft::Console; ++using namespace ::Microsoft::Console::Types; ++ ++AppHost::AppHost() noexcept : ++ _window{ nullptr } ++{ ++ _window = std::make_unique(); ++ ++ // Tell the window to callback to us when it's about to handle a WM_CREATE ++ auto pfn = std::bind(&AppHost::_HandleCreateWindow, ++ this, ++ std::placeholders::_1, ++ std::placeholders::_2); ++ _window->SetCreateCallback(pfn); ++ ++ _window->MakeWindow(); ++} ++ ++AppHost::~AppHost() ++{ ++ // destruction order is important for proper teardown here ++ _window = nullptr; ++} ++ ++// Method Description: ++// - Initializes the XAML island, creates the terminal app, and sets the ++// island's content to that of the terminal app's content. Also registers some ++// callbacks with TermApp. ++// !!! IMPORTANT!!! ++// This must be called *AFTER* WindowsXamlManager::InitializeForCurrentThread. ++// If it isn't, then we won't be able to create the XAML island. ++// Arguments: ++// - ++// Return Value: ++// - ++void AppHost::Initialize() ++{ ++ _window->Initialize(); ++ ++ // Set up the content of the application. If the app has a custom titlebar, ++ // set that content as well. ++ winrt::Windows::UI::Xaml::Controls::Grid g; ++ // TODO: INITIALIZE THIS UI HERE ++ _window->SetContent(g); ++ ++ _window->OnAppInitialized(); ++} ++ ++// Method Description: ++// - Resize the window we're about to create to the appropriate dimensions, as ++// specified in the settings. This will be called during the handling of ++// WM_CREATE. We'll load the settings for the app, then get the proposed size ++// of the terminal from the app. Using that proposed size, we'll resize the ++// window we're creating, so that it'll match the values in the settings. ++// Arguments: ++// - hwnd: The HWND of the window we're about to create. ++// - proposedRect: The location and size of the window that we're about to ++// create. We'll use this rect to determine which monitor the window is about ++// to appear on. ++// - launchMode: A LaunchMode enum reference that indicates the launch mode ++// Return Value: ++// - None ++void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) ++{ ++ // Acquire the actual initial position ++ winrt::Windows::Foundation::Point initialPosition{ (float)proposedRect.left, (float)proposedRect.top }; ++ proposedRect.left = gsl::narrow_cast(initialPosition.X); ++ proposedRect.top = gsl::narrow_cast(initialPosition.Y); ++ ++ long adjustedHeight = 0; ++ long adjustedWidth = 0; ++ ++ // Find nearest monitor. ++ HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); ++ ++ // Get nearest monitor information ++ MONITORINFO monitorInfo; ++ monitorInfo.cbSize = sizeof(MONITORINFO); ++ GetMonitorInfo(hmon, &monitorInfo); ++ ++ // This API guarantees that dpix and dpiy will be equal, but neither is an ++ // optional parameter so give two UINTs. ++ UINT dpix = USER_DEFAULT_SCREEN_DPI; ++ UINT dpiy = USER_DEFAULT_SCREEN_DPI; ++ // If this fails, we'll use the default of 96. ++ GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); ++ ++ // We need to check if the top left point of the titlebar of the window is within any screen ++ RECT offScreenTestRect; ++ offScreenTestRect.left = proposedRect.left; ++ offScreenTestRect.top = proposedRect.top; ++ offScreenTestRect.right = offScreenTestRect.left + 1; ++ offScreenTestRect.bottom = offScreenTestRect.top + 1; ++ ++ bool isTitlebarIntersectWithMonitors = false; ++ EnumDisplayMonitors( ++ nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL { ++ auto intersectWithMonitor = reinterpret_cast(lParam); ++ *intersectWithMonitor = true; ++ // Continue the enumeration ++ return FALSE; ++ }, ++ reinterpret_cast(&isTitlebarIntersectWithMonitors)); ++ ++ if (!isTitlebarIntersectWithMonitors) ++ { ++ // If the title bar is out-of-screen, we set the initial position to ++ // the top left corner of the nearest monitor ++ proposedRect.left = monitorInfo.rcWork.left; ++ proposedRect.top = monitorInfo.rcWork.top; ++ } ++ ++ winrt::Windows::Foundation::Size initialSize{ 800, 600 }; ++ ++ const short islandWidth = Utils::ClampToShortMax( ++ static_cast(ceil(initialSize.Width)), 1); ++ const short 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 auto nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix); ++ adjustedWidth = islandWidth + nonClientSize.cx; ++ adjustedHeight = islandHeight + nonClientSize.cy; ++ ++ const COORD origin{ gsl::narrow(proposedRect.left), ++ gsl::narrow(proposedRect.top) }; ++ const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1), ++ Utils::ClampToShortMax(adjustedHeight, 1) }; ++ ++ const auto newPos = Viewport::FromDimensions(origin, dimensions); ++ bool succeeded = SetWindowPos(hwnd, ++ nullptr, ++ newPos.Left(), ++ newPos.Top(), ++ newPos.Width(), ++ newPos.Height(), ++ SWP_NOACTIVATE | SWP_NOZORDER); ++ ++ // Refresh the dpi of HWND because the dpi where the window will launch may be different ++ // at this time ++ _window->RefreshCurrentDPI(); ++ ++ // If we can't resize the window, that's really okay. We can just go on with ++ // the originally proposed window size. ++ LOG_LAST_ERROR_IF(!succeeded); ++} ++ ++// Method Description: ++// - Called when the app wants to change its theme. We'll forward this to the ++// IslandWindow, so it can update the root UI element of the entire XAML tree. ++// Arguments: ++// - sender: unused ++// - arg: the ElementTheme to use as the new theme for the UI ++// Return Value: ++// - ++void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& arg) ++{ ++ _window->OnApplicationThemeChanged(arg); ++} +diff --git a/src/tools/ScratchIsland/AppHost.h b/src/tools/ScratchIsland/AppHost.h +new file mode 100644 +index 000000000..e4a957cd8 +--- /dev/null ++++ b/src/tools/ScratchIsland/AppHost.h +@@ -0,0 +1,24 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT license. ++ ++#include "pch.h" ++ ++#include "IslandWindow.h" ++ ++class AppHost ++{ ++public: ++ AppHost() noexcept; ++ virtual ~AppHost(); ++ ++ void Initialize(); ++ ++private: ++ bool _useNonClientArea{ false }; ++ ++ std::unique_ptr _window; ++ ++ void _HandleCreateWindow(const HWND hwnd, RECT proposedRect); ++ void _UpdateTheme(const winrt::Windows::Foundation::IInspectable&, ++ const winrt::Windows::UI::Xaml::ElementTheme& arg); ++}; +diff --git a/src/tools/ScratchIsland/BaseWindow.h b/src/tools/ScratchIsland/BaseWindow.h +new file mode 100644 +index 000000000..d1a8b86d7 +--- /dev/null ++++ b/src/tools/ScratchIsland/BaseWindow.h +@@ -0,0 +1,228 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT license. ++ ++#pragma once ++ ++// Custom window messages ++#define CM_UPDATE_TITLE (WM_USER) ++ ++#include ++ ++template ++class BaseWindow ++{ ++public: ++ virtual ~BaseWindow() = 0; ++ static T* GetThisFromHandle(HWND const window) noexcept ++ { ++ return reinterpret_cast(GetWindowLongPtr(window, GWLP_USERDATA)); ++ } ++ ++ [[nodiscard]] static LRESULT __stdcall WndProc(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); ++ T* that = static_cast(cs->lpCreateParams); ++ WINRT_ASSERT(that); ++ WINRT_ASSERT(!that->_window); ++ that->_window = wil::unique_hwnd(window); ++ ++ return that->_OnNcCreate(wparam, lparam); ++ } ++ else if (T* that = GetThisFromHandle(window)) ++ { ++ return that->MessageHandler(message, wparam, lparam); ++ } ++ ++ return DefWindowProc(window, message, wparam, lparam); ++ } ++ ++ [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept ++ { ++ switch (message) ++ { ++ case WM_DPICHANGED: ++ { ++ return HandleDpiChange(_window.get(), wparam, lparam); ++ } ++ ++ case WM_DESTROY: ++ { ++ PostQuitMessage(0); ++ return 0; ++ } ++ ++ case WM_SIZE: ++ { ++ UINT width = LOWORD(lparam); ++ UINT height = HIWORD(lparam); ++ ++ switch (wparam) ++ { ++ case SIZE_MAXIMIZED: ++ [[fallthrough]]; ++ case SIZE_RESTORED: ++ if (_minimized) ++ { ++ _minimized = false; ++ OnRestore(); ++ } ++ ++ // We always need to fire the resize event, even when we're transitioning from minimized. ++ // We might be transitioning directly from minimized to maximized, and we'll need ++ // to trigger any size-related content changes. ++ OnResize(width, height); ++ break; ++ case SIZE_MINIMIZED: ++ if (!_minimized) ++ { ++ _minimized = true; ++ OnMinimize(); ++ } ++ break; ++ default: ++ // do nothing. ++ break; ++ } ++ break; ++ } ++ case CM_UPDATE_TITLE: ++ { ++ SetWindowTextW(_window.get(), _title.c_str()); ++ break; ++ } ++ } ++ ++ return DefWindowProc(_window.get(), message, wparam, lparam); ++ } ++ ++ // DPI Change handler. on WM_DPICHANGE resize the window ++ [[nodiscard]] LRESULT HandleDpiChange(const HWND hWnd, const WPARAM wParam, const LPARAM lParam) ++ { ++ _inDpiChange = true; ++ const HWND hWndStatic = GetWindow(hWnd, GW_CHILD); ++ if (hWndStatic != nullptr) ++ { ++ const UINT uDpi = HIWORD(wParam); ++ ++ // Resize the window ++ auto lprcNewScale = reinterpret_cast(lParam); ++ ++ SetWindowPos(hWnd, nullptr, lprcNewScale->left, lprcNewScale->top, lprcNewScale->right - lprcNewScale->left, lprcNewScale->bottom - lprcNewScale->top, SWP_NOZORDER | SWP_NOACTIVATE); ++ ++ _currentDpi = uDpi; ++ } ++ _inDpiChange = false; ++ return 0; ++ } ++ ++ virtual void OnResize(const UINT width, const UINT height) = 0; ++ virtual void OnMinimize() = 0; ++ virtual void OnRestore() = 0; ++ ++ RECT GetWindowRect() const noexcept ++ { ++ RECT rc = { 0 }; ++ ::GetWindowRect(_window.get(), &rc); ++ return rc; ++ } ++ ++ HWND GetHandle() const noexcept ++ { ++ return _window.get(); ++ } ++ ++ float GetCurrentDpiScale() const noexcept ++ { ++ const auto dpi = ::GetDpiForWindow(_window.get()); ++ const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); ++ return scale; ++ } ++ ++ //// Gets the physical size of the client area of the HWND in _window ++ SIZE GetPhysicalSize() const noexcept ++ { ++ RECT rect = {}; ++ GetClientRect(_window.get(), &rect); ++ const auto windowsWidth = rect.right - rect.left; ++ const auto windowsHeight = rect.bottom - rect.top; ++ return SIZE{ windowsWidth, windowsHeight }; ++ } ++ ++ //// Gets the logical (in DIPs) size of a physical size specified by the parameter physicalSize ++ //// Remarks: ++ //// XAML coordinate system is always in Display Independent Pixels (a.k.a DIPs or Logical). However Win32 GDI (because of legacy reasons) ++ //// in DPI mode "Per-Monitor and Per-Monitor (V2) DPI Awareness" is always in physical pixels. ++ //// The formula to transform is: ++ //// logical = (physical / dpi) + 0.5 // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75 ++ //// See also: ++ //// https://docs.microsoft.com/en-us/windows/desktop/LearnWin32/dpi-and-device-independent-pixels ++ //// https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows#per-monitor-and-per-monitor-v2-dpi-awareness ++ winrt::Windows::Foundation::Size GetLogicalSize(const SIZE physicalSize) const noexcept ++ { ++ const auto scale = GetCurrentDpiScale(); ++ // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75 ++ const auto logicalWidth = (physicalSize.cx / scale) + 0.5f; ++ const auto logicalHeight = (physicalSize.cy / scale) + 0.5f; ++ return winrt::Windows::Foundation::Size(logicalWidth, logicalHeight); ++ } ++ ++ winrt::Windows::Foundation::Size GetLogicalSize() const noexcept ++ { ++ return GetLogicalSize(GetPhysicalSize()); ++ } ++ ++ // Method Description: ++ // - Sends a message to our message loop to update the title of the window. ++ // Arguments: ++ // - newTitle: a string to use as the new title of the window. ++ // Return Value: ++ // - ++ void UpdateTitle(std::wstring_view newTitle) ++ { ++ _title = newTitle; ++ PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast(nullptr)); ++ } ++ ++ // Method Description: ++ // Reset the current dpi of the window. This method is only called after we change the ++ // initial launch position. This makes sure the dpi is consistent with the monitor on which ++ // the window will launch ++ void RefreshCurrentDPI() ++ { ++ _currentDpi = GetDpiForWindow(_window.get()); ++ } ++ ++protected: ++ using base_type = BaseWindow; ++ wil::unique_hwnd _window; ++ ++ unsigned int _currentDpi = 0; ++ bool _inDpiChange = false; ++ ++ std::wstring _title = L""; ++ ++ bool _minimized = false; ++ ++ // Method Description: ++ // - This method is called when the window receives the WM_NCCREATE message. ++ // Return Value: ++ // - The value returned from the window proc. ++ virtual [[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept ++ { ++ SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast(this)); ++ ++ EnableNonClientDpiScaling(_window.get()); ++ _currentDpi = GetDpiForWindow(_window.get()); ++ ++ return DefWindowProc(_window.get(), WM_NCCREATE, wParam, lParam); ++ }; ++}; ++ ++template ++inline BaseWindow::~BaseWindow() ++{ ++} +diff --git a/src/tools/ScratchIsland/IslandWindow.cpp b/src/tools/ScratchIsland/IslandWindow.cpp +new file mode 100644 +index 000000000..3813c6e70 +--- /dev/null ++++ b/src/tools/ScratchIsland/IslandWindow.cpp +@@ -0,0 +1,604 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT license. ++ ++#include "pch.h" ++#include "IslandWindow.h" ++#include "../types/inc/Viewport.hpp" ++#include "resource.h" ++ ++extern "C" IMAGE_DOS_HEADER __ImageBase; ++ ++using namespace winrt::Windows::UI; ++using namespace winrt::Windows::UI::Composition; ++using namespace winrt::Windows::UI::Xaml; ++using namespace winrt::Windows::UI::Xaml::Hosting; ++using namespace winrt::Windows::Foundation::Numerics; ++using namespace ::Microsoft::Console::Types; ++ ++#define XAML_HOSTING_WINDOW_CLASS_NAME L"SCRATCH_ISLAND_CLASS" ++ ++IslandWindow::IslandWindow() noexcept : ++ _interopWindowHandle{ nullptr }, ++ _rootGrid{ nullptr }, ++ _source{ nullptr }, ++ _pfnCreateCallback{ nullptr } ++{ ++} ++ ++IslandWindow::~IslandWindow() ++{ ++ _source.Close(); ++} ++ ++// Method Description: ++// - Create the actual window that we'll use for the application. ++// Arguments: ++// - ++// Return Value: ++// - ++void IslandWindow::MakeWindow() noexcept ++{ ++ WNDCLASS wc{}; ++ wc.hCursor = LoadCursor(nullptr, IDC_ARROW); ++ wc.hInstance = reinterpret_cast(&__ImageBase); ++ wc.lpszClassName = XAML_HOSTING_WINDOW_CLASS_NAME; ++ wc.style = CS_HREDRAW | CS_VREDRAW; ++ wc.lpfnWndProc = WndProc; ++ wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON)); ++ RegisterClass(&wc); ++ WINRT_ASSERT(!_window); ++ ++ // Create the window with the default size here - During the creation of the ++ // window, the system will give us a chance to set its size in WM_CREATE. ++ // WM_CREATE will be handled synchronously, before CreateWindow returns. ++ WINRT_VERIFY(CreateWindowEx(_alwaysOnTop ? WS_EX_TOPMOST : 0, ++ wc.lpszClassName, ++ L"Scratch Island", ++ WS_OVERLAPPEDWINDOW, ++ CW_USEDEFAULT, ++ CW_USEDEFAULT, ++ CW_USEDEFAULT, ++ CW_USEDEFAULT, ++ nullptr, ++ nullptr, ++ wc.hInstance, ++ this)); ++ ++ WINRT_ASSERT(_window); ++} ++ ++// Method Description: ++// - Called when no tab is remaining to close the window. ++// Arguments: ++// - ++// Return Value: ++// - ++void IslandWindow::Close() ++{ ++ PostQuitMessage(0); ++} ++ ++// Method Description: ++// - Set a callback to be called when we process a WM_CREATE message. This gives ++// the AppHost a chance to resize the window to the proper size. ++// Arguments: ++// - pfn: a function to be called during the handling of WM_CREATE. It takes two ++// parameters: ++// * HWND: the HWND of the window that's being created. ++// * RECT: The position on the screen that the system has proposed for our ++// window. ++// Return Value: ++// - ++void IslandWindow::SetCreateCallback(std::function pfn) noexcept ++{ ++ _pfnCreateCallback = pfn; ++} ++ ++// Method Description: ++// - Handles a WM_CREATE message. Calls our create callback, if one's been set. ++// Arguments: ++// - wParam: unused ++// - lParam: the lParam of a WM_CREATE, which is a pointer to a CREATESTRUCTW ++// Return Value: ++// - ++void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexcept ++{ ++ // Get proposed window rect from create structure ++ CREATESTRUCTW* pcs = reinterpret_cast(lParam); ++ RECT rc; ++ rc.left = pcs->x; ++ rc.top = pcs->y; ++ rc.right = rc.left + pcs->cx; ++ rc.bottom = rc.top + pcs->cy; ++ ++ if (_pfnCreateCallback) ++ { ++ _pfnCreateCallback(_window.get(), rc); ++ } ++ ++ int nCmdShow = SW_SHOW; ++ ++ ShowWindow(_window.get(), nCmdShow); ++ ++ UpdateWindow(_window.get()); ++} ++ ++// Method Description: ++// - Handles a WM_SIZING message, which occurs when user drags a window border ++// or corner. It intercepts this resize action and applies 'snapping' i.e. ++// aligns the terminal's size to its cell grid. We're given the window size, ++// which we then adjust based on the terminal's properties (like font size). ++// Arguments: ++// - wParam: Specifies which edge of the window is being dragged. ++// - lParam: Pointer to the requested window rectangle (this is, the one that ++// originates from current drag action). It also acts as the return value ++// (it's a ref parameter). ++// Return Value: ++// - ++LRESULT IslandWindow::_OnSizing(const WPARAM /*wParam*/, const LPARAM /*lParam*/) ++{ ++ // If we haven't been given the callback that would adjust the dimension, ++ // then we can't do anything, so just bail out. ++ return FALSE; ++} ++ ++void IslandWindow::Initialize() ++{ ++ const bool initialized = (_interopWindowHandle != nullptr); ++ ++ _source = DesktopWindowXamlSource{}; ++ ++ auto interop = _source.as(); ++ winrt::check_hresult(interop->AttachToWindow(_window.get())); ++ ++ // stash the child interop handle so we can resize it when the main hwnd is resized ++ interop->get_WindowHandle(&_interopWindowHandle); ++ ++ _rootGrid = winrt::Windows::UI::Xaml::Controls::Grid(); ++ _source.Content(_rootGrid); ++} ++ ++void IslandWindow::OnSize(const UINT width, const UINT height) ++{ ++ // update the interop window size ++ SetWindowPos(_interopWindowHandle, nullptr, 0, 0, width, height, SWP_SHOWWINDOW); ++ ++ if (_rootGrid) ++ { ++ const auto size = GetLogicalSize(); ++ _rootGrid.Width(size.Width); ++ _rootGrid.Height(size.Height); ++ } ++} ++ ++[[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept ++{ ++ switch (message) ++ { ++ case WM_CREATE: ++ { ++ _HandleCreateWindow(wparam, lparam); ++ return 0; ++ } ++ case WM_SETFOCUS: ++ { ++ if (_interopWindowHandle != nullptr) ++ { ++ // send focus to the child window ++ SetFocus(_interopWindowHandle); ++ return 0; // eat the message ++ } ++ } ++ ++ case WM_NCLBUTTONDOWN: ++ case WM_NCLBUTTONUP: ++ case WM_NCMBUTTONDOWN: ++ case WM_NCMBUTTONUP: ++ case WM_NCRBUTTONDOWN: ++ case WM_NCRBUTTONUP: ++ case WM_NCXBUTTONDOWN: ++ case WM_NCXBUTTONUP: ++ { ++ // If we clicked in the titlebar, raise an event so the app host can ++ // dispatch an appropriate event. ++ _DragRegionClickedHandlers(); ++ break; ++ } ++ case WM_MENUCHAR: ++ { ++ // GH#891: return this LRESULT here to prevent the app from making a ++ // bell when alt+key is pressed. A menu is active and the user presses a ++ // key that does not correspond to any mnemonic or accelerator key, ++ return MAKELRESULT(0, MNC_CLOSE); ++ } ++ case WM_SIZING: ++ { ++ return _OnSizing(wparam, lparam); ++ } ++ case WM_CLOSE: ++ { ++ // If the user wants to close the app by clicking 'X' button, ++ // we hand off the close experience to the app layer. If all the tabs ++ // are closed, the window will be closed as well. ++ _windowCloseButtonClickedHandler(); ++ return 0; ++ } ++ case WM_MOUSEWHEEL: ++ try ++ { ++ // This whole handler is a hack for GH#979. ++ // ++ // On some laptops, their trackpads won't scroll inactive windows ++ // _ever_. With our entire window just being one giant XAML Island, the ++ // touchpad driver thinks our entire window is inactive, and won't ++ // scroll the XAML island. On those types of laptops, we'll get a ++ // WM_MOUSEWHEEL here, in our root window, when the trackpad scrolls. ++ // We're going to take that message and manually plumb it through to our ++ // TermControl's, or anything else that implements IMouseWheelListener. ++ ++ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx ++ // Important! Do not use the LOWORD or HIWORD macros to extract the x- ++ // and y- coordinates of the cursor position because these macros return ++ // incorrect results on systems with multiple monitors. Systems with ++ // multiple monitors can have negative x- and y- coordinates, and LOWORD ++ // and HIWORD treat the coordinates as unsigned quantities. ++ const til::point eventPoint{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; ++ // This mouse event is relative to the display origin, not the window. Convert here. ++ const til::rectangle windowRect{ GetWindowRect() }; ++ const auto origin = windowRect.origin(); ++ const auto relative = eventPoint - origin; ++ // Convert to logical scaling before raising the event. ++ const auto real = relative / GetCurrentDpiScale(); ++ ++ const short wheelDelta = static_cast(HIWORD(wparam)); ++ ++ // Raise an event, so any listeners can handle the mouse wheel event manually. ++ _MouseScrolledHandlers(real, wheelDelta); ++ return 0; ++ } ++ CATCH_LOG(); ++ } ++ ++ // TODO: handle messages here... ++ return base_type::MessageHandler(message, wparam, lparam); ++} ++ ++// Method Description: ++// - Called when the window has been resized (or maximized) ++// Arguments: ++// - width: the new width of the window _in pixels_ ++// - height: the new height of the window _in pixels_ ++void IslandWindow::OnResize(const UINT width, const UINT height) ++{ ++ if (_interopWindowHandle) ++ { ++ OnSize(width, height); ++ } ++} ++ ++// Method Description: ++// - Called when the window is minimized to the taskbar. ++void IslandWindow::OnMinimize() ++{ ++ // TODO GH#1989 Stop rendering island content when the app is minimized. ++} ++ ++// Method Description: ++// - Called when the window is restored from having been minimized. ++void IslandWindow::OnRestore() ++{ ++ // TODO GH#1989 Stop rendering island content when the app is minimized. ++} ++ ++void IslandWindow::SetContent(winrt::Windows::UI::Xaml::UIElement content) ++{ ++ _rootGrid.Children().Clear(); ++ _rootGrid.Children().Append(content); ++} ++ ++// Method Description: ++// - Gets the difference between window and client area size. ++// Arguments: ++// - dpi: dpi of a monitor on which the window is placed ++// Return Value ++// - The size difference ++SIZE IslandWindow::GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept ++{ ++ const auto windowStyle = static_cast(GetWindowLong(_window.get(), GWL_STYLE)); ++ RECT islandFrame{}; ++ ++ // If we failed to get the correct window size for whatever reason, log ++ // the error and go on. We'll use whatever the control proposed as the ++ // size of our window, which will be at least close. ++ LOG_IF_WIN32_BOOL_FALSE(AdjustWindowRectExForDpi(&islandFrame, windowStyle, false, 0, dpi)); ++ ++ return { ++ islandFrame.right - islandFrame.left, ++ islandFrame.bottom - islandFrame.top ++ }; ++} ++ ++void IslandWindow::OnAppInitialized() ++{ ++ // Do a quick resize to force the island to paint ++ const auto size = GetPhysicalSize(); ++ OnSize(size.cx, size.cy); ++} ++ ++// Method Description: ++// - Called when the app wants to change its theme. We'll update the root UI ++// element of the entire XAML tree, so that all UI elements get the theme ++// applied. ++// Arguments: ++// - arg: the ElementTheme to use as the new theme for the UI ++// Return Value: ++// - ++void IslandWindow::OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) ++{ ++ _rootGrid.RequestedTheme(requestedTheme); ++ // Invalidate the window rect, so that we'll repaint any elements we're ++ // drawing ourselves to match the new theme ++ ::InvalidateRect(_window.get(), nullptr, false); ++} ++ ++// Method Description: ++// - Updates our focus mode state. See _SetIsBorderless for more details. ++// Arguments: ++// - ++// Return Value: ++// - ++void IslandWindow::FocusModeChanged(const bool focusMode) ++{ ++ // Do nothing if the value was unchanged. ++ if (focusMode == _borderless) ++ { ++ return; ++ } ++ ++ _SetIsBorderless(focusMode); ++} ++ ++// Method Description: ++// - Updates our fullscreen state. See _SetIsFullscreen for more details. ++// Arguments: ++// - ++// Return Value: ++// - ++void IslandWindow::FullscreenChanged(const bool fullscreen) ++{ ++ // Do nothing if the value was unchanged. ++ if (fullscreen == _fullscreen) ++ { ++ return; ++ } ++ ++ _SetIsFullscreen(fullscreen); ++} ++ ++// Method Description: ++// - Enter or exit the "always on top" state. Before the window is created, this ++// value will later be used when we create the window to create the window on ++// top of all others. After the window is created, it will either enter the ++// group of topmost windows, or exit the group of topmost windows. ++// Arguments: ++// - alwaysOnTop: whether we should be entering or exiting always on top mode. ++// Return Value: ++// - ++void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop) ++{ ++ _alwaysOnTop = alwaysOnTop; ++ ++ const auto hwnd = GetHandle(); ++ if (hwnd) ++ { ++ SetWindowPos(hwnd, ++ _alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, ++ 0, // the window dimensions are unused, because we're passing SWP_NOSIZE ++ 0, ++ 0, ++ 0, ++ SWP_NOMOVE | SWP_NOSIZE); ++ } ++} ++ ++// From GdiEngine::s_SetWindowLongWHelper ++void _SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept ++{ ++ // SetWindowLong has strange error handling. On success, it returns the ++ // previous Window Long value and doesn't modify the Last Error state. To ++ // deal with this, we set the last error to 0/S_OK first, call it, and if ++ // the previous long was 0, we check if the error was non-zero before ++ // reporting. Otherwise, we'll get an "Error: The operation has completed ++ // successfully." and there will be another screenshot on the internet ++ // making fun of Windows. See: ++ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx ++ SetLastError(0); ++ LONG const lResult = SetWindowLongW(hWnd, nIndex, dwNewLong); ++ if (0 == lResult) ++ { ++ LOG_LAST_ERROR_IF(0 != GetLastError()); ++ } ++} ++ ++// Method Description: ++// - This is a helper to figure out what the window styles should be, given the ++// current state of flags like borderless mode and fullscreen mode. ++// Arguments: ++// - ++// Return Value: ++// - a LONG with the appropriate flags set for our current window mode, to be used with GWL_STYLE ++LONG IslandWindow::_getDesiredWindowStyle() const ++{ ++ auto windowStyle = GetWindowLongW(GetHandle(), GWL_STYLE); ++ ++ // If we're both fullscreen and borderless, fullscreen mode takes precedence. ++ ++ if (_fullscreen) ++ { ++ // When moving to fullscreen, remove WS_OVERLAPPEDWINDOW, which specifies ++ // styles for non-fullscreen windows (e.g. caption bar), and add the ++ // WS_POPUP style to allow us to size ourselves to the monitor size. ++ // Do the reverse when restoring from fullscreen. ++ // Doing these modifications to that window will cause a vista-style ++ // window frame to briefly appear when entering and exiting fullscreen. ++ WI_ClearFlag(windowStyle, WS_BORDER); ++ WI_ClearFlag(windowStyle, WS_SIZEBOX); ++ WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW); ++ ++ WI_SetFlag(windowStyle, WS_POPUP); ++ return windowStyle; ++ } ++ else if (_borderless) ++ { ++ // When moving to borderless, remove WS_OVERLAPPEDWINDOW, which ++ // specifies styles for non-fullscreen windows (e.g. caption bar), and ++ // add the WS_BORDER and WS_SIZEBOX styles. This allows us to still have ++ // a small resizing frame, but without a full titlebar, nor caption ++ // buttons. ++ ++ WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW); ++ WI_ClearFlag(windowStyle, WS_POPUP); ++ ++ WI_SetFlag(windowStyle, WS_BORDER); ++ WI_SetFlag(windowStyle, WS_SIZEBOX); ++ return windowStyle; ++ } ++ ++ // Here, we're not in either fullscreen or borderless mode. Return to ++ // WS_OVERLAPPEDWINDOW. ++ WI_ClearFlag(windowStyle, WS_POPUP); ++ WI_ClearFlag(windowStyle, WS_BORDER); ++ WI_ClearFlag(windowStyle, WS_SIZEBOX); ++ ++ WI_SetAllFlags(windowStyle, WS_OVERLAPPEDWINDOW); ++ ++ return windowStyle; ++} ++ ++// Method Description: ++// - Enable or disable focus mode. When entering focus mode, we'll ++// need to manually hide the entire titlebar. ++// - When we're entering focus we need to do some additional modification ++// of our window styles. However, the NonClientIslandWindow very explicitly ++// _doesn't_ need to do these steps. ++// Arguments: ++// - borderlessEnabled: If true, we're entering focus mode. If false, we're leaving. ++// Return Value: ++// - ++void IslandWindow::_SetIsBorderless(const bool borderlessEnabled) ++{ ++ _borderless = borderlessEnabled; ++ ++ HWND const hWnd = GetHandle(); ++ ++ // First, modify regular window styles as appropriate ++ auto windowStyle = _getDesiredWindowStyle(); ++ _SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle); ++ ++ // Now modify extended window styles as appropriate ++ // When moving to fullscreen, remove the window edge style to avoid an ++ // ugly border when not focused. ++ auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE); ++ WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen); ++ _SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle); ++ ++ // Resize the window, with SWP_FRAMECHANGED, to trigger user32 to ++ // recalculate the non/client areas ++ const til::rectangle windowPos{ GetWindowRect() }; ++ SetWindowPos(GetHandle(), ++ HWND_TOP, ++ windowPos.left(), ++ windowPos.top(), ++ windowPos.width(), ++ windowPos.height(), ++ SWP_SHOWWINDOW | SWP_FRAMECHANGED); ++} ++ ++// Method Description: ++// - Controls setting us into or out of fullscreen mode. Largely taken from ++// Window::SetIsFullscreen in conhost. ++// - When entering fullscreen mode, we'll save the current window size and ++// location, and expand to take the entire monitor size. When leaving, we'll ++// use that saved size to restore back to. ++// Arguments: ++// - fullscreenEnabled true if we should enable fullscreen mode, false to disable. ++// Return Value: ++// - ++void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) ++{ ++ // It is possible to enter _SetIsFullscreen even if we're already in full ++ // screen. Use the old is in fullscreen flag to gate checks that rely on the ++ // current state. ++ const auto oldIsInFullscreen = _fullscreen; ++ _fullscreen = fullscreenEnabled; ++ ++ HWND const hWnd = GetHandle(); ++ ++ // First, modify regular window styles as appropriate ++ auto windowStyle = _getDesiredWindowStyle(); ++ _SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle); ++ ++ // Now modify extended window styles as appropriate ++ // When moving to fullscreen, remove the window edge style to avoid an ++ // ugly border when not focused. ++ auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE); ++ WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen); ++ _SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle); ++ ++ // When entering/exiting fullscreen mode, we also need to backup/restore the ++ // current window size, and resize the window to match the new state. ++ _BackupWindowSizes(oldIsInFullscreen); ++ _ApplyWindowSize(); ++} ++ ++// Method Description: ++// - Used in entering/exiting fullscreen mode. Saves the current window size, ++// and the full size of the monitor, for use in _ApplyWindowSize. ++// - Taken from conhost's Window::_BackupWindowSizes ++// Arguments: ++// - fCurrentIsInFullscreen: true if we're currently in fullscreen mode. ++// Return Value: ++// - ++void IslandWindow::_BackupWindowSizes(const bool fCurrentIsInFullscreen) ++{ ++ if (_fullscreen) ++ { ++ // Note: the current window size depends on the current state of the ++ // window. So don't back it up if we're already in full screen. ++ if (!fCurrentIsInFullscreen) ++ { ++ _nonFullscreenWindowSize = GetWindowRect(); ++ } ++ ++ // get and back up the current monitor's size ++ HMONITOR const hCurrentMonitor = MonitorFromWindow(GetHandle(), MONITOR_DEFAULTTONEAREST); ++ MONITORINFO currMonitorInfo; ++ currMonitorInfo.cbSize = sizeof(currMonitorInfo); ++ if (GetMonitorInfo(hCurrentMonitor, &currMonitorInfo)) ++ { ++ _fullscreenWindowSize = currMonitorInfo.rcMonitor; ++ } ++ } ++} ++ ++// Method Description: ++// - Applys the appropriate window size for transitioning to/from fullscreen mode. ++// - Taken from conhost's Window::_ApplyWindowSize ++// Arguments: ++// - ++// Return Value: ++// - ++void IslandWindow::_ApplyWindowSize() ++{ ++ const auto newSize = _fullscreen ? _fullscreenWindowSize : _nonFullscreenWindowSize; ++ LOG_IF_WIN32_BOOL_FALSE(SetWindowPos(GetHandle(), ++ HWND_TOP, ++ newSize.left, ++ newSize.top, ++ newSize.right - newSize.left, ++ newSize.bottom - newSize.top, ++ SWP_FRAMECHANGED)); ++} ++ ++DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); ++DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); +diff --git a/src/tools/ScratchIsland/IslandWindow.h b/src/tools/ScratchIsland/IslandWindow.h +new file mode 100644 +index 000000000..109f7c1ec +--- /dev/null ++++ b/src/tools/ScratchIsland/IslandWindow.h +@@ -0,0 +1,77 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT license. ++ ++#include "pch.h" ++#include "BaseWindow.h" ++#include "../../cascadia/inc/cppwinrt_utils.h" ++ ++class IslandWindow : ++ public BaseWindow ++{ ++public: ++ IslandWindow() noexcept; ++ virtual ~IslandWindow() override; ++ ++ virtual void MakeWindow() noexcept; ++ void Close(); ++ virtual void OnSize(const UINT width, const UINT height); ++ ++ [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; ++ void OnResize(const UINT width, const UINT height) override; ++ void OnMinimize() override; ++ void OnRestore() override; ++ virtual void OnAppInitialized(); ++ virtual void SetContent(winrt::Windows::UI::Xaml::UIElement content); ++ virtual void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); ++ virtual SIZE GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept; ++ ++ virtual void Initialize(); ++ ++ void SetCreateCallback(std::function pfn) noexcept; ++ ++ void FocusModeChanged(const bool focusMode); ++ void FullscreenChanged(const bool fullscreen); ++ void SetAlwaysOnTop(const bool alwaysOnTop); ++ ++#pragma endregion ++ ++ DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); ++ DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); ++ WINRT_CALLBACK(MouseScrolled, winrt::delegate); ++ ++protected: ++ void ForceResize() ++ { ++ // Do a quick resize to force the island to paint ++ const auto size = GetPhysicalSize(); ++ OnSize(size.cx, size.cy); ++ } ++ ++ HWND _interopWindowHandle; ++ ++ winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source; ++ ++ winrt::Windows::UI::Xaml::Controls::Grid _rootGrid; ++ ++ std::function _pfnCreateCallback; ++ ++ void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept; ++ [[nodiscard]] LRESULT _OnSizing(const WPARAM wParam, const LPARAM lParam); ++ ++ bool _borderless{ false }; ++ bool _fullscreen{ false }; ++ bool _alwaysOnTop{ false }; ++ RECT _fullscreenWindowSize; ++ RECT _nonFullscreenWindowSize; ++ ++ virtual void _SetIsBorderless(const bool borderlessEnabled); ++ virtual void _SetIsFullscreen(const bool fullscreenEnabled); ++ void _BackupWindowSizes(const bool currentIsInFullscreen); ++ void _ApplyWindowSize(); ++ ++ LONG _getDesiredWindowStyle() const; ++ ++private: ++ // This minimum width allows for width the tabs fit ++ static constexpr long minimumWidth = 460L; ++}; +diff --git a/src/tools/ScratchIsland/ScratchIsland.def b/src/tools/ScratchIsland/ScratchIsland.def +new file mode 100644 +index 000000000..5f282702b +--- /dev/null ++++ b/src/tools/ScratchIsland/ScratchIsland.def +@@ -0,0 +1 @@ ++ +\ No newline at end of file +diff --git a/src/tools/ScratchIsland/ScratchIsland.manifest b/src/tools/ScratchIsland/ScratchIsland.manifest +new file mode 100644 +index 000000000..a447bc8fd +--- /dev/null ++++ b/src/tools/ScratchIsland/ScratchIsland.manifest +@@ -0,0 +1,24 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ PerMonitorV2 ++ true ++ ++ ++ +diff --git a/src/tools/ScratchIsland/ScratchIsland.rc b/src/tools/ScratchIsland/ScratchIsland.rc +new file mode 100644 +index 000000000..78a784cf2 +--- /dev/null ++++ b/src/tools/ScratchIsland/ScratchIsland.rc +@@ -0,0 +1,94 @@ ++// Microsoft Visual C++ generated resource script. ++// ++#include "resource.h" ++ ++#define APSTUDIO_READONLY_SYMBOLS ++///////////////////////////////////////////////////////////////////////////// ++// ++// Generated from the TEXTINCLUDE 2 resource. ++// ++#include "winres.h" ++ ++///////////////////////////////////////////////////////////////////////////// ++#undef APSTUDIO_READONLY_SYMBOLS ++ ++///////////////////////////////////////////////////////////////////////////// ++// English (United States) resources ++ ++#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) ++LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US ++#pragma code_page(1252) ++ ++#ifdef APSTUDIO_INVOKED ++///////////////////////////////////////////////////////////////////////////// ++// ++// TEXTINCLUDE ++// ++ ++1 TEXTINCLUDE ++BEGIN ++ "resource.h\0" ++END ++ ++2 TEXTINCLUDE ++BEGIN ++ "#include ""winres.h""\r\n" ++ "\0" ++END ++ ++3 TEXTINCLUDE ++BEGIN ++ "\r\n" ++ "\0" ++END ++ ++#endif // APSTUDIO_INVOKED ++ ++ ++///////////////////////////////////////////////////////////////////////////// ++// ++// Icon ++// ++ ++// Icon with lowest ID value placed first to ensure application icon ++// remains consistent on all systems. ++IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico" ++ ++ ++///////////////////////////////////////////////////////////////////////////// ++// ++// String Table ++// ++ ++STRINGTABLE ++BEGIN ++ IDS_ERROR_DIALOG_TITLE "Error" ++ IDS_HELP_DIALOG_TITLE "Help" ++ IDS_ERROR_ARCHITECTURE_FORMAT ++ "Windows Terminal is designed to run on your system's native architecture (%s).\nYou are currently using the %s version.\n\nPlease use the version of Windows Terminal that matches your system's native architecture." ++ IDS_X86_ARCHITECTURE "i386" ++END ++ ++STRINGTABLE ++BEGIN ++ IDS_AMD64_ARCHITECTURE "AMD64" ++ IDS_ARM64_ARCHITECTURE "ARM64" ++ IDS_ARM_ARCHITECTURE "ARM" ++ IDS_UNKNOWN_ARCHITECTURE "Unknown" ++END ++ ++#endif // English (United States) resources ++///////////////////////////////////////////////////////////////////////////// ++ ++ ++ ++#ifndef APSTUDIO_INVOKED ++///////////////////////////////////////////////////////////////////////////// ++// ++// Generated from the TEXTINCLUDE 3 resource. ++// ++ ++ ++///////////////////////////////////////////////////////////////////////////// ++#endif // not APSTUDIO_INVOKED ++ +diff --git a/src/tools/ScratchIsland/ScratchIsland.vcxproj b/src/tools/ScratchIsland/ScratchIsland.vcxproj +new file mode 100644 +index 000000000..d155853ab +--- /dev/null ++++ b/src/tools/ScratchIsland/ScratchIsland.vcxproj +@@ -0,0 +1,164 @@ ++ ++ ++ ++ ++ ++ {23a1f736-cd19-4196-980f-84bcd50cf783} ++ Win32Proj ++ ScratchIsland ++ ScratchIsland ++ ScratchIsland ++ Application ++ false ++ Windows Store ++ true ++ false ++ ++ ++ 10.0.18362.0 ++ ++ true ++ ++ ++ ++ ++ ++ ++ ++ true ++ ++ ++ ++ ++ ++ $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;%(AdditionalIncludeDirectories); ++ ++ ++ ++ "$(OpenConsoleDir)src\cascadia\TerminalCore\lib\Generated Files";%(AdditionalIncludeDirectories); ++ ++ ++ gdi32.lib;dwmapi.lib;Shcore.lib;UxTheme.lib;%(AdditionalDependencies) ++ ++ ++ ++ true ++ true ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Create ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ WindowsLocalDebugger ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ <_ContinueOnError Condition="'$(BuildingProject)' == 'true'">true ++ <_ContinueOnError Condition="'$(BuildingProject)' != 'true'">false ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ x86 ++ $(Platform) ++ ++ ++ ++ ++ <_OpenConsoleVCLibToCopy Include="$(VCToolsRedistInstallDir)\$(ReasonablePlatform)\Microsoft.VC142.CRT\*.dll" /> ++ ++ ++ $(ProjectName) ++ BuiltProjectOutputGroup ++ %(Filename)%(Extension) ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/tools/ScratchIsland/main.cpp b/src/tools/ScratchIsland/main.cpp +new file mode 100644 +index 000000000..777e3ee9e +--- /dev/null ++++ b/src/tools/ScratchIsland/main.cpp +@@ -0,0 +1,54 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT license. ++ ++#include "pch.h" ++#include "AppHost.h" ++#include "resource.h" ++ ++using namespace winrt; ++using namespace winrt::Windows::UI; ++using namespace winrt::Windows::UI::Composition; ++using namespace winrt::Windows::UI::Xaml::Hosting; ++using namespace winrt::Windows::Foundation::Numerics; ++ ++int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) ++{ ++ // If Terminal is spawned by a shortcut that requests that it run in a new process group ++ // while attached to a console session, that request is nonsense. That request will, however, ++ // cause WT to start with Ctrl-C disabled. This wouldn't matter, because it's a Windows-subsystem ++ // application. Unfortunately, that state is heritable. In short, if you start WT using cmd in ++ // a weird way, ^C stops working _inside_ the terminal. Mad. ++ SetConsoleCtrlHandler(NULL, FALSE); ++ ++ // Make sure to call this so we get WM_POINTER messages. ++ EnableMouseInPointer(true); ++ ++ // !!! LOAD BEARING !!! ++ // We must initialize the main thread as a single-threaded apartment before ++ // constructing any Xaml objects. Failing to do so will cause some issues ++ // in accessibility somewhere down the line when a UIAutomation object will ++ // be queried on the wrong thread at the wrong time. ++ // We used to initialize as STA only _after_ initializing the application ++ // host, which loaded the settings. The settings needed to be loaded in MTA ++ // because we were using the Windows.Storage APIs. Since we're no longer ++ // 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; ++ ++ // Initialize the xaml content. This must be called AFTER the ++ // WindowsXamlManager is initialized. ++ host.Initialize(); ++ ++ MSG message; ++ ++ while (GetMessage(&message, nullptr, 0, 0)) ++ { ++ TranslateMessage(&message); ++ DispatchMessage(&message); ++ } ++ return 0; ++} +diff --git a/src/tools/ScratchIsland/packages.config b/src/tools/ScratchIsland/packages.config +new file mode 100644 +index 000000000..166f8861f +--- /dev/null ++++ b/src/tools/ScratchIsland/packages.config +@@ -0,0 +1,8 @@ ++ ++ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/src/tools/ScratchIsland/pch.cpp b/src/tools/ScratchIsland/pch.cpp +new file mode 100644 +index 000000000..398a99f66 +--- /dev/null ++++ b/src/tools/ScratchIsland/pch.cpp +@@ -0,0 +1,4 @@ ++// Copyright (c) Microsoft Corporation. ++// Licensed under the MIT license. ++ ++#include "pch.h" +diff --git a/src/tools/ScratchIsland/pch.h b/src/tools/ScratchIsland/pch.h +new file mode 100644 +index 000000000..40be55842 +--- /dev/null ++++ b/src/tools/ScratchIsland/pch.h +@@ -0,0 +1,74 @@ ++/*++ ++Copyright (c) Microsoft Corporation ++Licensed under the MIT license. ++ ++Module Name: ++- pch.h ++ ++Abstract: ++- Contains external headers to include in the precompile phase of console build process. ++- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building). ++--*/ ++ ++#pragma once ++ ++// Ignore checked iterators warning from VC compiler. ++#define _SCL_SECURE_NO_WARNINGS ++ ++// Block minwindef.h min/max macros to prevent conflict ++#define NOMINMAX ++ ++#define WIN32_LEAN_AND_MEAN ++#include ++ ++#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// Manually include til after we include Windows.Foundation to give it winrt superpowers ++#define BLOCK_TIL ++#include "../inc/LibraryIncludes.h" ++ ++// This is inexplicable, but for whatever reason, cppwinrt conflicts with the ++// SDK definition of this function, so the only fix is to undef it. ++// from WinBase.h ++// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime ++#ifdef GetCurrentTime ++#undef GetCurrentTime ++#endif ++ ++#include ++ ++// Needed just for XamlIslands to work at all: ++#include ++#include ++#include ++#include ++ ++// Additional headers for various xaml features. We need: ++// * Controls for grid ++// * Media for ScaleTransform ++#include ++#include ++#include ++ ++#include ++#include ++ ++// Including TraceLogging essentials for the binary ++#include ++#include ++TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider); ++#include ++#include ++ ++// For commandline argument processing ++#include ++#include ++ ++#include "til.h" +diff --git a/src/tools/ScratchIsland/resource.h b/src/tools/ScratchIsland/resource.h +new file mode 100644 +index 000000000..011413eae +--- /dev/null ++++ b/src/tools/ScratchIsland/resource.h +@@ -0,0 +1,24 @@ ++//{{NO_DEPENDENCIES}} ++// Microsoft Visual C++ generated include file. ++// Used by WindowsTerminal.rc ++// ++#define IDI_APPICON 101 ++#define IDS_ERROR_DIALOG_TITLE 105 ++#define IDS_HELP_DIALOG_TITLE 106 ++#define IDS_ERROR_ARCHITECTURE_FORMAT 110 ++#define IDS_X86_ARCHITECTURE 111 ++#define IDS_AMD64_ARCHITECTURE 112 ++#define IDS_ARM64_ARCHITECTURE 113 ++#define IDS_ARM_ARCHITECTURE 114 ++#define IDS_UNKNOWN_ARCHITECTURE 115 ++ ++// Next default values for new objects ++// ++#ifdef APSTUDIO_INVOKED ++#ifndef APSTUDIO_READONLY_SYMBOLS ++#define _APS_NEXT_RESOURCE_VALUE 104 ++#define _APS_NEXT_COMMAND_VALUE 40001 ++#define _APS_NEXT_CONTROL_VALUE 1001 ++#define _APS_NEXT_SYMED_VALUE 101 ++#endif ++#endif +-- +2.20.1.windows.1 + diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index e7b196cd511..e2400891090 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -562,4 +562,9 @@ More options + + Window: {} + {} will be replaced with a number identifying this window + + diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index c2031d7b03d..71e05cd03e3 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -78,6 +78,10 @@ MinMaxCloseControl.xaml + + TerminalPage.idl + Code + PaletteItemTemplateSelector.idl Code @@ -155,6 +159,10 @@ MinMaxCloseControl.xaml + + TerminalPage.idl + Code + PaletteItemTemplateSelector.idl Code @@ -393,4 +401,4 @@ - \ No newline at end of file + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 5f0d836dccc..02df92f4c3e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -31,6 +31,7 @@ using namespace winrt::Microsoft::Terminal::TerminalConnection; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace ::TerminalApp; using namespace ::Microsoft::Console; +using namespace std::chrono_literals; #define HOOKUP_ACTION(action) _actionDispatch->action({ this, &TerminalPage::_Handle##action }); @@ -3293,20 +3294,21 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget TerminalPage::IdentifyWindow() { auto weakThis{ get_weak() }; + // co_await 3s; co_await winrt::resume_foreground(Dispatcher()); if (auto page{ weakThis.get() }) { page->WindowIdToast().IsOpen(true); } } - winrt::hstring TerminalPage::WindowName() - { - return L""; - } - uint64_t TerminalPage::WindowId() - { - return 42; - } + // winrt::hstring TerminalPage::WindowName() + // { + // return L""; + // } + // uint64_t TerminalPage::WindowId() + // { + // return 42; + // } // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index feb8d629f56..84c0293c5ff 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -84,13 +84,18 @@ namespace winrt::TerminalApp::implementation winrt::hstring KeyboardServiceDisabledText(); winrt::fire_and_forget IdentifyWindow(); - winrt::hstring WindowName(); - uint64_t WindowId(); + + // winrt::hstring WindowName(); + // uint64_t WindowId(); winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial, const winrt::hstring cwd = L""); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + OBSERVABLE_GETSET_PROPERTY(winrt::hstring, WindowName, _PropertyChangedHandlers, L"foo"); + OBSERVABLE_GETSET_PROPERTY(uint64_t, WindowId, _PropertyChangedHandlers, 42); + // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index a025595b7a2..413e9d70ce2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -10,7 +10,19 @@ namespace TerminalApp Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); }; - [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page + runtimeclass WindowIdConverter : [default] Windows.UI.Xaml.Data.IValueConverter + { + WindowIdConverter(); + }; + + + runtimeclass WindowNameConverter : [default] Windows.UI.Xaml.Data.IValueConverter + { + WindowNameConverter(); + }; + + + [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged { TerminalPage(); @@ -23,8 +35,8 @@ namespace TerminalApp Boolean AlwaysOnTop { get; }; void IdentifyWindow(); - String WindowName { get; }; - UInt64 WindowId { get; }; + String WindowName; + UInt64 WindowId; // 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 d6915d12874..efa4b994a21 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -10,6 +10,13 @@ the MIT License. See LICENSE in the project root for license information. --> xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> + + + + + + + @@ -134,14 +141,12 @@ the MIT License. See LICENSE in the project root for license information. --> Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"/> + dismiss itself if the window is unfocused (In Xaml Islands). This is + tracked by MUX#4382--> - - - + IsLightDismissEnabled="True" + Title="{x:Bind WindowId, Mode=OneWay, Converter={StaticResource WindowIdConverter}}" + Subtitle="{x:Bind WindowName, Mode=OneWay, Converter={StaticResource WindowNameConverter}}"> diff --git a/src/cascadia/TerminalApp/WindowNameConverter.cpp b/src/cascadia/TerminalApp/WindowNameConverter.cpp new file mode 100644 index 00000000000..c2e31b2cbfb --- /dev/null +++ b/src/cascadia/TerminalApp/WindowNameConverter.cpp @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "WindowNameConverter.h" + +#include "WindowIdConverter.g.cpp" +#include "WindowNameConverter.g.cpp" +#include + +using namespace winrt::Windows; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Text; + +namespace winrt::TerminalApp::implementation +{ + Foundation::IInspectable WindowIdConverter::Convert(Foundation::IInspectable const& value, + Windows::UI::Xaml::Interop::TypeName const& /* targetType */, + Foundation::IInspectable const& /* parameter */, + hstring const& /* language */) + { + // Returns Visible if the string is _not_ "desktopWallpaper", else returns Collapsed + const auto& id = winrt::unbox_value_or(value, 0); + return winrt::box_value(fmt::format(std::wstring_view(RS_(L"WindowIdPrefix")), id)); + } + + Foundation::IInspectable WindowIdConverter::ConvertBack(Foundation::IInspectable const& /*value*/, + Windows::UI::Xaml::Interop::TypeName const& /* targetType */, + Foundation::IInspectable const& /*parameter*/, + hstring const& /* language */) + { + throw hresult_not_implemented(); + } + + Foundation::IInspectable WindowNameConverter::Convert(Foundation::IInspectable const& value, + Windows::UI::Xaml::Interop::TypeName const& /* targetType */, + Foundation::IInspectable const& /* parameter */, + hstring const& /* language */) + { + const auto& name = winrt::unbox_value_or(value, L""); + return winrt::box_value(name.empty() ? L"" : name); + } + + Foundation::IInspectable WindowNameConverter::ConvertBack(Foundation::IInspectable const& /*value*/, + Windows::UI::Xaml::Interop::TypeName const& /* targetType */, + Foundation::IInspectable const& /*parameter*/, + hstring const& /* language */) + { + throw hresult_not_implemented(); + } +} diff --git a/src/cascadia/TerminalApp/WindowNameConverter.h b/src/cascadia/TerminalApp/WindowNameConverter.h new file mode 100644 index 00000000000..2f2cb229941 --- /dev/null +++ b/src/cascadia/TerminalApp/WindowNameConverter.h @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "WindowIdConverter.g.h" +#include "WindowNameConverter.g.h" +#include "../inc/cppwinrt_utils.h" + +DECLARE_CONVERTER(winrt::TerminalApp, WindowIdConverter); +DECLARE_CONVERTER(winrt::TerminalApp, WindowNameConverter); From 9270e0fce3ecd03b124445ac8b7a37991b28f719 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 1 Mar 2021 12:06:23 -0600 Subject: [PATCH 10/25] bind the name, id down to the actual page --- src/cascadia/TerminalApp/AppLogic.cpp | 22 ++++++++++++++++++++++ src/cascadia/TerminalApp/AppLogic.h | 5 +++++ src/cascadia/TerminalApp/AppLogic.idl | 3 +++ src/cascadia/TerminalApp/TerminalPage.h | 3 --- src/cascadia/WindowsTerminal/AppHost.cpp | 3 +++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 0675d28d91c..2d4f0259eca 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1376,6 +1376,28 @@ namespace winrt::TerminalApp::implementation } } + 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); + } + } // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 21e41690a82..135de8bed72 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -56,7 +56,12 @@ namespace winrt::TerminalApp::implementation bool FocusMode() const; bool Fullscreen() const; bool AlwaysOnTop() const; + void IdentifyWindow(); + winrt::hstring WindowName(); + void WindowName(const winrt::hstring& name); + uint64_t WindowId(); + void WindowId(const uint64_t& id); Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi); bool CenterOnLaunch(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 2c0f56f3890..35fa8f713ea 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -47,7 +47,10 @@ namespace TerminalApp Boolean FocusMode { get; }; Boolean Fullscreen { get; }; Boolean AlwaysOnTop { get; }; + void IdentifyWindow(); + String WindowName; + UInt64 WindowId; Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); Boolean CenterOnLaunch { get; }; diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 84c0293c5ff..d147609b5fe 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -85,9 +85,6 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget IdentifyWindow(); - // winrt::hstring WindowName(); - // uint64_t WindowId(); - winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial, const winrt::hstring cwd = L""); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 7c133cd68be..a0fb590c01c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -196,6 +196,9 @@ void AppHost::_HandleCommandlineArgs() peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId }); + + _logic.WindowName(peasant.WindowName()); + _logic.WindowId(peasant.GetID()); } } From 1f0dceb342bc83d80d3709e4384224e6b970a4ee Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 1 Mar 2021 16:59:19 -0600 Subject: [PATCH 11/25] add an action for just identifying one single window --- .../TerminalApp/AppActionHandlers.cpp | 15 +++++++++++++ .../TerminalApp/ShortcutActionDispatch.cpp | 1 + .../TerminalApp/ShortcutActionDispatch.h | 1 + .../TerminalApp/ShortcutActionDispatch.idl | 1 + src/cascadia/TerminalApp/TerminalPage.cpp | 21 +++++++++++-------- src/cascadia/TerminalApp/TerminalPage.h | 1 + .../TerminalSettingsModel/ActionAndArgs.cpp | 3 +++ .../TerminalSettingsModel/KeyMapping.idl | 1 + .../Resources/en-US/Resources.resw | 3 +++ .../TerminalSettingsModel/defaults.json | 2 ++ tools/GenerateHeaderForJson.ps1 | 8 ++++--- 11 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 301169b4393..79205ba9f76 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -671,7 +671,22 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleIdentifyWindows(const IInspectable& /*sender*/, const ActionEventArgs& args) { + // Raise a IdentifyWindowsRequested event. This will bubble up to the + // AppLogic, to the AppHost, to the Peasant, to the Monarch, then get + // distributed down to _all_ the Peasants, as to display info about the + // window in _every_ Peasant window. + // + // This action is also buggy right now, because TeachingTips behave + // weird in XAML Islands. See microsoft-ui-xaml#4382 _IdentifyWindowsRequestedHandlers(*this, nullptr); args.Handled(true); } + void TerminalPage::_HandleIdentifyWindow(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + // Unlike _HandleIdentifyWindow**s**, this event just displays the + // window ID and name in the current window. + IdentifyWindow(); + args.Handled(true); + } } diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp index 98b4f68139f..245f30a7e09 100644 --- a/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp +++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.cpp @@ -89,6 +89,7 @@ namespace winrt::TerminalApp::implementation ACTION_CASE(FindMatch); ACTION_CASE(TogglePaneReadOnly); ACTION_CASE(NewWindow); + ACTION_CASE(IdentifyWindow); ACTION_CASE(IdentifyWindows); default: return false; diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.h b/src/cascadia/TerminalApp/ShortcutActionDispatch.h index 92312495d84..f4494e2c3c4 100644 --- a/src/cascadia/TerminalApp/ShortcutActionDispatch.h +++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.h @@ -69,6 +69,7 @@ namespace winrt::TerminalApp::implementation DECLARE_ACTION(FindMatch); DECLARE_ACTION(TogglePaneReadOnly); DECLARE_ACTION(NewWindow); + DECLARE_ACTION(IdentifyWindow); DECLARE_ACTION(IdentifyWindows); // clang-format on diff --git a/src/cascadia/TerminalApp/ShortcutActionDispatch.idl b/src/cascadia/TerminalApp/ShortcutActionDispatch.idl index 870a2175aa9..577c7097efc 100644 --- a/src/cascadia/TerminalApp/ShortcutActionDispatch.idl +++ b/src/cascadia/TerminalApp/ShortcutActionDispatch.idl @@ -55,6 +55,7 @@ namespace TerminalApp ACTION_EVENT(FindMatch); ACTION_EVENT(TogglePaneReadOnly); ACTION_EVENT(NewWindow); + ACTION_EVENT(IdentifyWindow); ACTION_EVENT(IdentifyWindows); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 02df92f4c3e..aa6a1b9bdf7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1161,6 +1161,7 @@ namespace winrt::TerminalApp::implementation HOOKUP_ACTION(FindMatch); HOOKUP_ACTION(TogglePaneReadOnly); HOOKUP_ACTION(NewWindow); + HOOKUP_ACTION(IdentifyWindow); HOOKUP_ACTION(IdentifyWindows); } @@ -3291,24 +3292,26 @@ namespace winrt::TerminalApp::implementation } } + // Method Description: + // - Display the name and ID of this window in a TeachingTip. If the window + // has no name, the name will be presented as "". + // - This can be invoked by either: + // * An identifyWindow action, that displays the info only for the current + // window + // * An identifyWindows action, that displays the info for all windows. + // Arguments: + // - + // Return Value: + // - winrt::fire_and_forget TerminalPage::IdentifyWindow() { auto weakThis{ get_weak() }; - // co_await 3s; co_await winrt::resume_foreground(Dispatcher()); if (auto page{ weakThis.get() }) { page->WindowIdToast().IsOpen(true); } } - // winrt::hstring TerminalPage::WindowName() - // { - // return L""; - // } - // uint64_t TerminalPage::WindowId() - // { - // return 42; - // } // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index d147609b5fe..8d3cd74758e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -329,6 +329,7 @@ namespace winrt::TerminalApp::implementation DECLARE_ACTION_HANDLER(FindMatch); DECLARE_ACTION_HANDLER(TogglePaneReadOnly); DECLARE_ACTION_HANDLER(NewWindow); + DECLARE_ACTION_HANDLER(IdentifyWindow); DECLARE_ACTION_HANDLER(IdentifyWindows); // Make sure to hook new actions up in _RegisterActionCallbacks! #pragma endregion diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index bd9daf8f9dc..5fdaa3faf75 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -53,6 +53,7 @@ static constexpr std::string_view BreakIntoDebuggerKey{ "breakIntoDebugger" }; static constexpr std::string_view FindMatchKey{ "findMatch" }; static constexpr std::string_view TogglePaneReadOnlyKey{ "toggleReadOnlyMode" }; static constexpr std::string_view NewWindowKey{ "newWindow" }; +static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" }; static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" }; static constexpr std::string_view ActionKey{ "action" }; @@ -120,6 +121,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { FindMatchKey, ShortcutAction::FindMatch }, { TogglePaneReadOnlyKey, ShortcutAction::TogglePaneReadOnly }, { NewWindowKey, ShortcutAction::NewWindow }, + { IdentifyWindowKey, ShortcutAction::IdentifyWindow }, { IdentifyWindowsKey, ShortcutAction::IdentifyWindows }, }; @@ -324,6 +326,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::FindMatch, L"" }, // Intentionally omitted, must be generated by GenerateName { ShortcutAction::TogglePaneReadOnly, RS_(L"TogglePaneReadOnlyCommandKey") }, { ShortcutAction::NewWindow, RS_(L"NewWindowCommandKey") }, + { ShortcutAction::IdentifyWindow, RS_(L"IdentifyWindowCommandKey") }, { ShortcutAction::IdentifyWindows, RS_(L"IdentifyWindowsCommandKey") }, }; }(); diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.idl b/src/cascadia/TerminalSettingsModel/KeyMapping.idl index 56aff56fed5..2727954ead1 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.idl +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.idl @@ -55,6 +55,7 @@ namespace Microsoft.Terminal.Settings.Model TogglePaneReadOnly, FindMatch, NewWindow, + IdentifyWindow, IdentifyWindows }; diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index f9fe7361b29..65ca12e9dca 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -244,6 +244,9 @@ New window + + Identify window + Identify windows diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 3bc56c560be..be855b333ef 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -296,6 +296,8 @@ { "command": "openTabRenamer" }, { "command": "commandPalette", "keys":"ctrl+shift+p" }, + { "command": "identifyWindow" }, + // Tab Management // "command": "closeTab" is unbound by default. // The closeTab command closes a tab without confirmation, even if it has multiple panes. diff --git a/tools/GenerateHeaderForJson.ps1 b/tools/GenerateHeaderForJson.ps1 index 2510951f1e2..f655ba16026 100644 --- a/tools/GenerateHeaderForJson.ps1 +++ b/tools/GenerateHeaderForJson.ps1 @@ -22,7 +22,9 @@ Write-Output "// THIS IS AN AUTO-GENERATED FILE" | Out-File -FilePath $OutPath - Write-Output "// Generated from " | Out-File -FilePath $OutPath -Encoding ASCII -Append -NoNewline $fullPath = Resolve-Path -Path $JsonFile Write-Output $fullPath.Path | Out-File -FilePath $OutPath -Encoding ASCII -Append -Write-Output "constexpr std::string_view $($VariableName){ R`"(" | Out-File -FilePath $OutPath -Encoding ASCII -Append -NoNewline -Write-Output $jsonData | Out-File -FilePath $OutPath -Encoding ASCII -Append -Write-Output ")`" };" | Out-File -FilePath $OutPath -Encoding ASCII -Append +Write-Output "constexpr std::string_view $($VariableName){ " | Out-File -FilePath $OutPath -Encoding ASCII -Append -NoNewline +$jsonData | foreach { + Write-Output "R`"($_`n)`"" | Out-File -FilePath $OutPath -Encoding ASCII -Append +} +Write-Output "};" | Out-File -FilePath $OutPath -Encoding ASCII -Append From e238daa0fcec9b9005f5029ca4797c6ce206c141 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Mar 2021 15:59:39 -0600 Subject: [PATCH 12/25] THIS SHOULD GO TO THE PARENT BRANCH (cherry picked from commit aa51c07bca3d06dda34d0c4ccef52a75a3e6e920) --- src/cascadia/TerminalApp/TerminalPage.cpp | 14 +++++++++++--- src/cascadia/TerminalApp/TerminalPage.h | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index aa6a1b9bdf7..f83298cc764 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -243,7 +243,7 @@ namespace winrt::TerminalApp::implementation CommandPalette().RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) { if (CommandPalette().Visibility() == Visibility::Collapsed) { - _CommandPaletteClosed(nullptr, nullptr); + _FocusActiveControl(nullptr, nullptr); } }); CommandPalette().DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested }); @@ -264,6 +264,14 @@ namespace winrt::TerminalApp::implementation _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); + auto safeRefocus = [weakThis{ get_weak() }](auto&&, auto&&) { + if (auto page{ weakThis.get() }) + { + page->_FocusActiveControl(nullptr, nullptr); + } + }; + + WindowIdToast().Closed(safeRefocus); // Setup mouse vanish attributes SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &_shouldMouseVanish, false); @@ -2959,8 +2967,8 @@ namespace winrt::TerminalApp::implementation return {}; } - void TerminalPage::_CommandPaletteClosed(const IInspectable& /*sender*/, - const RoutedEventArgs& /*eventArgs*/) + void TerminalPage::_FocusActiveControl(const IInspectable& /*sender*/, + const RoutedEventArgs& /*eventArgs*/) { // We don't want to set focus on the tab if fly-out is open as it will be closed // TODO GH#5400: consider checking we are not in the opening state, by hooking both Opening and Open events diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 8d3cd74758e..bb27b6bd068 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -263,7 +263,7 @@ namespace winrt::TerminalApp::implementation void _CompleteInitialization(); - void _CommandPaletteClosed(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + void _FocusActiveControl(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void _UnZoomIfNeeded(); From 27d12a2a923611e0f9a80f5dbacef20395afcdd3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 26 Feb 2021 12:29:52 -0600 Subject: [PATCH 13/25] THIS NEEDS TO GO TO THE PARENT (cherry picked from commit fa26f7f569ad843997cff960590d4abfcf38f1b3) --- src/cascadia/Remoting/WindowManager.cpp | 27 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index b5b77b19411..cbf36830f77 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -127,18 +127,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation 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)); } 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(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)); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline_AsMonarch", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline_AsMonarch", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } } if (_shouldCreateWindow) From 9d6f47f0652ca9dfedc0e1e1fd64bc27ea7128cd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Mar 2021 16:59:17 -0600 Subject: [PATCH 14/25] yea you better believe the Toast just worked --- .../TerminalApp/TerminalAppLib.vcxproj | 3 ++ src/cascadia/TerminalApp/TerminalPage.cpp | 5 ++- src/cascadia/TerminalApp/TerminalPage.h | 3 ++ src/cascadia/TerminalApp/Toast.cpp | 33 +++++++++++++++++++ src/cascadia/TerminalApp/Toast.h | 21 ++++++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/cascadia/TerminalApp/Toast.cpp create mode 100644 src/cascadia/TerminalApp/Toast.h diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 71e05cd03e3..8730948814f 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -147,6 +147,7 @@ AppLogic.idl + @@ -235,6 +236,8 @@ AppLogic.idl + + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f83298cc764..c1fc3c4c0ed 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -271,7 +271,9 @@ namespace winrt::TerminalApp::implementation } }; + _windowIdToast = std::make_unique(WindowIdToast()); WindowIdToast().Closed(safeRefocus); + // Setup mouse vanish attributes SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &_shouldMouseVanish, false); @@ -3317,7 +3319,8 @@ namespace winrt::TerminalApp::implementation co_await winrt::resume_foreground(Dispatcher()); if (auto page{ weakThis.get() }) { - page->WindowIdToast().IsOpen(true); + // page->WindowIdToast().IsOpen(true); + page->_windowIdToast->Open(); } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index bb27b6bd068..b6e3ea214bf 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -11,6 +11,7 @@ #include #include "AppCommandlineArgs.h" +#include "Toast.h" #define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args); @@ -152,6 +153,8 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IVector _startupActions; + std::shared_ptr _windowIdToast{ nullptr }; + void _ShowAboutDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseWarningDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseReadOnlyDialog(); diff --git a/src/cascadia/TerminalApp/Toast.cpp b/src/cascadia/TerminalApp/Toast.cpp new file mode 100644 index 00000000000..76ef703fe9d --- /dev/null +++ b/src/cascadia/TerminalApp/Toast.cpp @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "Toast.h" + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::Xaml; + +constexpr const auto ToastDuration = std::chrono::milliseconds(3000); + +Toast::Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip) : + _tip{ tip } +{ + _timer.Interval(ToastDuration); +} + +void Toast::Open() +{ + _tip.IsOpen(true); + + std::weak_ptr weakThis{ shared_from_this() }; + _timer.Tick([weakThis](auto&&...) { + if (auto self{ weakThis.lock() }) + { + self->_timer.Stop(); + self->_tip.IsOpen(false); + } + }); + _timer.Start(); +} diff --git a/src/cascadia/TerminalApp/Toast.h b/src/cascadia/TerminalApp/Toast.h new file mode 100644 index 00000000000..86f1cee9429 --- /dev/null +++ b/src/cascadia/TerminalApp/Toast.h @@ -0,0 +1,21 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- Toast.h +--*/ + +#pragma once +#include "pch.h" + +class Toast : public std::enable_shared_from_this +{ +public: + Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip); + void Open(); + +private: + winrt::Microsoft::UI::Xaml::Controls::TeachingTip _tip; + winrt::Windows::UI::Xaml::DispatcherTimer _timer; +}; From c2022573abeaa48f8488fc3fb48b9371758343ba Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 4 Mar 2021 09:19:08 -0600 Subject: [PATCH 15/25] feedback from review --- src/cascadia/Remoting/WindowManager.cpp | 4 ++-- src/cascadia/TerminalApp/AppCommandlineArgs.cpp | 6 ------ src/cascadia/TerminalApp/AppLogic.cpp | 12 ++++++------ src/cascadia/TerminalApp/AppLogic.h | 3 +++ 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index cbf36830f77..480d0a33c55 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -71,7 +71,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Otherwise, the King will tell us if we should make a new window _shouldCreateWindow = _isKing; std::optional givenID; - winrt::hstring givenName = L""; // TODO:MG If we're the king, we might STILL WANT TO GET THE NAME. How do we get the name? + winrt::hstring givenName = L""; if (!_isKing) { // The monarch may respond back "you should be a new @@ -123,7 +123,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto findWindowArgs{ winrt::make_self(args) }; _raiseFindTargetWindowRequested(nullptr, *findWindowArgs); - auto responseId = findWindowArgs->ResultTargetWindow(); + const auto responseId = findWindowArgs->ResultTargetWindow(); if (responseId > 0) { givenID = ::base::saturated_cast(responseId); diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 030fe2780cc..900d763d9f6 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -859,11 +859,5 @@ void AppCommandlineArgs::FullResetState() std::string_view AppCommandlineArgs::GetTargetWindow() const noexcept { - // // If the user provides _any_ negative number, then treat it as -1, for "use a new window". - // if (_windowTarget.has_value() && *_windowTarget < 0) - // { - // return { -1 }; - // } - return _windowTarget; } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 56f1964291b..bffafa71fd6 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1240,7 +1240,7 @@ namespace winrt::TerminalApp::implementation { if (!appArgs.GetExitMessage().empty()) { - return winrt::make(WindowingBehaviorUseNew, L""); + return winrt::make(WindowingBehaviorUseNew); } const std::string parsedTarget{ appArgs.GetTargetWindow() }; @@ -1263,7 +1263,7 @@ namespace winrt::TerminalApp::implementation windowId = WindowingBehaviorUseAnyExisting; break; } - return winrt::make(windowId, L""); + return winrt::make(windowId); } // Here, the user _has_ provided a window-id on the commandline. @@ -1282,7 +1282,7 @@ namespace winrt::TerminalApp::implementation // Hooray! This is a valid integer. The set of possible values // here is {-1, 0, ℤ+}. Let's return that window ID. - return winrt::make(windowId, L""); + return winrt::make(windowId); } catch (...) { @@ -1292,11 +1292,11 @@ namespace winrt::TerminalApp::implementation // First, check the reserved keywords: if (parsedTarget == "new") { - return winrt::make(WindowingBehaviorUseNew, L""); + return winrt::make(WindowingBehaviorUseNew); } else if (parsedTarget == "last") { - return winrt::make(WindowingBehaviorUseExisting, L""); + return winrt::make(WindowingBehaviorUseExisting); } else { @@ -1319,7 +1319,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, L""); + return winrt::make(WindowingBehaviorUseNew); } // Method Description: diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 0c83d0afe95..28543a6faab 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -27,6 +27,9 @@ namespace winrt::TerminalApp::implementation public: FindTargetWindowResult(const int32_t id, const winrt::hstring& name) : _WindowId{ id }, _WindowName{ name } {}; + + FindTargetWindowResult(const int32_t id) : + FindTargetWindowResult(id, L""){}; }; struct AppLogic : AppLogicT From eaaa2044c4dfd3999f00564036a53b162b01dc23 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 16 Mar 2021 16:03:48 -0500 Subject: [PATCH 16/25] initial review feedback --- src/cascadia/Remoting/FindTargetWindowArgs.h | 2 +- src/cascadia/Remoting/Monarch.cpp | 19 +++++++++++-------- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Peasant.h | 2 +- .../Remoting/ProposeCommandlineResult.h | 2 +- src/cascadia/Remoting/WindowManager.cpp | 2 +- .../TerminalApp/AppCommandlineArgs.cpp | 2 +- src/cascadia/TerminalApp/AppCommandlineArgs.h | 2 +- .../UnitTests_Remoting/RemotingTests.cpp | 11 +++++++++++ 9 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.h b/src/cascadia/Remoting/FindTargetWindowArgs.h index 309793afd4e..25da4645b9b 100644 --- a/src/cascadia/Remoting/FindTargetWindowArgs.h +++ b/src/cascadia/Remoting/FindTargetWindowArgs.h @@ -27,7 +27,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr); GETSET_PROPERTY(int, ResultTargetWindow, -1); - GETSET_PROPERTY(winrt::hstring, ResultTargetWindowName, L""); + GETSET_PROPERTY(winrt::hstring, ResultTargetWindowName); public: FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) : diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cf98bdb1d4b..ad908fe3d0c 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -147,7 +147,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } - uint64_t Monarch::_lookupPeasantIdForName(const winrt::hstring& name) + // Method Description: + // - Find the ID of the peasant with the given name. If no such peasant + // exists, then we'll return 0. If we encounter any peasants who have died + // during this process, then we'll remove them from the set of _peasants + // Arguments: + // - name: The window name to look for + // Return Value: + // - 0 if we didn't find the given peasant, otherwise a positive number for + // the window's ID. + uint64_t Monarch::_lookupPeasantIdForName(std::wstring_view name) { if (name.empty()) { @@ -175,12 +184,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Instead, pull a good ole Java and collect this id for removal // later. peasantsToErase.push_back(id); - - // // Remove the peasant from the list of peasants - // _peasants.erase(id); - // // Remove the peasant from the list of MRU windows. They're dead. - // // They can't be the MRU anymore. - // _clearOldMruEntries(id); } } @@ -450,7 +453,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // After the event was handled, ResultTargetWindow() will be filled with // the parsed result. const auto targetWindow = findWindowArgs->ResultTargetWindow(); - const auto& targetWindowName = findWindowArgs->ResultTargetWindowName(); + const auto targetWindowName = findWindowArgs->ResultTargetWindowName(); TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline", diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 52399ad5971..8b9ddfa6e00 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -67,7 +67,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop); - uint64_t _lookupPeasantIdForName(const winrt::hstring& name); + uint64_t _lookupPeasantIdForName(std::wstring_view name); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 3b680d15ccf..582dd715df8 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -26,7 +26,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); - GETSET_PROPERTY(winrt::hstring, WindowName, L""); + GETSET_PROPERTY(winrt::hstring, WindowName); TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.h b/src/cascadia/Remoting/ProposeCommandlineResult.h index b095be75b8b..8363326c20a 100644 --- a/src/cascadia/Remoting/ProposeCommandlineResult.h +++ b/src/cascadia/Remoting/ProposeCommandlineResult.h @@ -27,7 +27,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { public: GETSET_PROPERTY(Windows::Foundation::IReference, Id); - GETSET_PROPERTY(winrt::hstring, WindowName, L""); + GETSET_PROPERTY(winrt::hstring, WindowName); GETSET_PROPERTY(bool, ShouldCreateWindow, true); public: diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 480d0a33c55..d5bb3fe40d3 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -71,7 +71,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Otherwise, the King will tell us if we should make a new window _shouldCreateWindow = _isKing; std::optional givenID; - winrt::hstring givenName = L""; + winrt::hstring givenName{}; if (!_isKing) { // The monarch may respond back "you should be a new diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 900d763d9f6..31fc51af2df 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -854,7 +854,7 @@ void AppCommandlineArgs::FullResetState() _exitMessage = ""; _shouldExitEarly = false; - _windowTarget = ""; + _windowTarget = {}; } std::string_view AppCommandlineArgs::GetTargetWindow() const noexcept diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index 9c8662293e1..1585bca634b 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -106,7 +106,7 @@ class TerminalApp::AppCommandlineArgs final std::string _exitMessage; bool _shouldExitEarly{ false }; - std::string _windowTarget{ "" }; + std::string _windowTarget{}; // Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand); diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 6cea9c533dd..886442f1b35 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -987,6 +987,8 @@ namespace RemotingUnitTests void RemotingTests::GetPeasantsByName() { + Log::Comment(L"Test that looking up a peasant by name finds the window we expect"); + const auto monarch0PID = 12345u; const auto peasant1PID = 23456u; const auto peasant2PID = 34567u; @@ -1033,6 +1035,8 @@ namespace RemotingUnitTests void RemotingTests::AddNamedPeasantsToNewMonarch() { + Log::Comment(L"Test that moving peasants to a new monarch persists their original names"); + const auto monarch0PID = 12345u; const auto peasant1PID = 23456u; const auto peasant2PID = 34567u; @@ -1087,6 +1091,10 @@ namespace RemotingUnitTests void RemotingTests::LookupNamedPeasantWhenOthersDied() { + Log::Comment(L"Test that looking for a peasant by name when a different" + L" peasant has died cleans up the corpses of any peasants " + L"we may have tripped over."); + const auto monarch0PID = 12345u; const auto peasant1PID = 23456u; const auto peasant2PID = 34567u; @@ -1136,6 +1144,9 @@ namespace RemotingUnitTests void RemotingTests::LookupNamedPeasantWhenItDied() { + Log::Comment(L"Test that looking up a dead peasant by name returns 0, " + L"indicating there's no peasant with that name."); + const auto monarch0PID = 12345u; const auto peasant1PID = 23456u; const auto peasant2PID = 34567u; From bba09e3f8b9db79bc40f685dd962168dd0678d7d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 16 Mar 2021 17:02:07 -0500 Subject: [PATCH 17/25] Address PR concerns add a test, and add liberal comments. --- .../CommandlineTest.cpp | 25 +++++ src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/Remoting/WindowManager.cpp | 10 ++ .../UnitTests_Remoting/RemotingTests.cpp | 98 +++++++++++++++++++ 4 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp index a593aed03ce..0afeb245e8b 100644 --- a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp +++ b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp @@ -1594,6 +1594,9 @@ namespace TerminalAppLocalTests void CommandlineTest::TestFindTargetWindow() { { + Log::Comment(L"wt.exe with no args should always use the value from" + L" the settings (passed as the second argument)."); + std::vector args{ L"wt.exe" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); @@ -1608,6 +1611,8 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"-w -1 should always result in a new window"); + std::vector args{ L"wt.exe", L"-w", L"-1" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); @@ -1622,6 +1627,8 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"\"new\" should always result in a new window"); + std::vector args{ L"wt.exe", L"-w", L"new" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); @@ -1636,6 +1643,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"-w with a negative number should always result in a " + L"new window"); + std::vector args{ L"wt.exe", L"-w", L"-12345" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); @@ -1650,6 +1660,10 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"-w with a positive number should result in us trying" + L" to either make a new one or find an existing one " + L"with that ID, depending on the provided argument"); + std::vector args{ L"wt.exe", L"-w", L"12345" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(12345, result.WindowId()); @@ -1664,6 +1678,8 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"-w 0 should always use the \"current\" window"); + std::vector args{ L"wt.exe", L"-w", L"0" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); @@ -1678,6 +1694,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"-w last should always use the most recent window on " + L"this desktop"); + std::vector args{ L"wt.exe", L"-w", L"last" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); @@ -1692,6 +1711,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"Make sure we follow the provided argument when a " + L"--window-id wasn't explicitly provided"); + std::vector args{ L"wt.exe", L"new-tab" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); @@ -1706,6 +1728,9 @@ namespace TerminalAppLocalTests VERIFY_ARE_EQUAL(L"", result.WindowName()); } { + Log::Comment(L"Even if someone uses a subcommand as a window name, " + L"that should work"); + std::vector args{ L"wt.exe", L"-w", L"new-tab" }; auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId()); diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ad908fe3d0c..f7aa8bee85c 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -507,7 +507,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (auto targetPeasant{ _getPeasant(windowID) }) { auto result{ winrt::make_self(false) }; - result->WindowName(targetWindowName); try { // This will raise the peasant's ExecuteCommandlineRequested @@ -521,6 +520,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // If we fail to propose the commandline to the peasant (it // died?) then just tell this process to become a new window // instead. + result->WindowName(targetWindowName); result->ShouldCreateWindow(true); // If this fails, it'll be logged in the following diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index d5bb3fe40d3..f76bfd47821 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -120,6 +120,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // 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); @@ -142,6 +149,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation 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)); } @@ -150,6 +158,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation 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)); } } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 886442f1b35..e5388ab5a25 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -6,6 +6,7 @@ #include "../Remoting/CommandlineArgs.h" #include "../Remoting/FindTargetWindowArgs.h" #include "../Remoting/ProposeCommandlineResult.h" +#include "../inc/WindowingBehavior.h" using namespace Microsoft::Console; using namespace WEX::Logging; @@ -92,6 +93,8 @@ namespace RemotingUnitTests TEST_METHOD(LookupNamedPeasantWhenItDied); TEST_METHOD(GetMruPeasantAfterNameLookupForDeadPeasant); + TEST_METHOD(ProposeCommandlineForDeadNamedWindow); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -102,6 +105,9 @@ namespace RemotingUnitTests static void _findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + + static void _findTargetWindowByNameHelper(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; // Helper to replace the specified peasant in a monarch with a @@ -133,6 +139,19 @@ namespace RemotingUnitTests } } + // Helper to get the first argument out of the commandline, and return it as + // a name to use. + void RemotingTests::_findTargetWindowByNameHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + { + const auto arguments = args.Args().Commandline(); + if (arguments.size() > 0) + { + args.ResultTargetWindow(WindowingBehaviorUseName); + args.ResultTargetWindowName(arguments.at(0)); + } + } + void RemotingTests::CreateMonarch() { auto m1 = winrt::make_self(); @@ -1254,4 +1273,83 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(true)); } + void RemotingTests::ProposeCommandlineForDeadNamedWindow() + { + Log::Comment(L"Test proposing a commandline for a named window that's " + L"currently dead. This should result in a new window with " + L"the given name."); + + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowByNameHelper); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + p1->WindowName(L"one"); + p2->WindowName(L"two"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p1"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); + VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1)); + }); + p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p2"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); + VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1)); + }); + + p1->WindowName(L"one"); + p2->WindowName(L"two"); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + std::vector p1Args{ L"one", L"arg[1]" }; + std::vector p2Args{ L"two", L"this is for p2" }; + + { + Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } }; + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value + VERIFY_ARE_EQUAL(L"", result.WindowName()); + } + + { + Log::Comment(L"Send a commandline to \"two\", which should be p2"); + Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } }; + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value + VERIFY_ARE_EQUAL(L"", result.WindowName()); + } + + Log::Comment(L"Kill peasant 2."); + RemotingTests::_killPeasant(m0, p2->GetID()); + + { + Log::Comment(L"Send a commandline to \"two\", who is now dead."); + Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } }; + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value + VERIFY_ARE_EQUAL(L"two", result.WindowName()); + } + } } From 1105328258c33b1f535073dcb4b2a453b6b7fd4b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 17 Mar 2021 09:29:29 -0500 Subject: [PATCH 18/25] That's better --- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index e5388ab5a25..5523d64b18b 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -93,7 +93,7 @@ namespace RemotingUnitTests TEST_METHOD(LookupNamedPeasantWhenItDied); TEST_METHOD(GetMruPeasantAfterNameLookupForDeadPeasant); - TEST_METHOD(ProposeCommandlineForDeadNamedWindow); + TEST_METHOD(ProposeCommandlineForNamedDeadWindow); TEST_CLASS_SETUP(ClassSetup) { @@ -1273,7 +1273,7 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(true)); } - void RemotingTests::ProposeCommandlineForDeadNamedWindow() + void RemotingTests::ProposeCommandlineForNamedDeadWindow() { Log::Comment(L"Test proposing a commandline for a named window that's " L"currently dead. This should result in a new window with " From 97b49357b0f80fcac61457a1c3c1e2337e51a75b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 17 Mar 2021 11:00:31 -0500 Subject: [PATCH 19/25] Cleanup for review --- patch.diff | 1745 ----------------- src/cascadia/Remoting/Monarch.cpp | 12 +- src/cascadia/Remoting/Peasant.cpp | 16 + .../TerminalApp/AppActionHandlers.cpp | 30 +- src/cascadia/TerminalApp/TerminalPage.cpp | 3 +- src/cascadia/TerminalApp/Toast.cpp | 7 + src/cascadia/TerminalApp/Toast.h | 17 + .../TerminalApp/WindowNameConverter.cpp | 3 +- src/cascadia/WindowsTerminal/AppHost.cpp | 17 + tools/GenerateHeaderForJson.ps1 | 5 + 10 files changed, 98 insertions(+), 1757 deletions(-) delete mode 100644 patch.diff diff --git a/patch.diff b/patch.diff deleted file mode 100644 index 4fda47e4210..00000000000 --- a/patch.diff +++ /dev/null @@ -1,1745 +0,0 @@ -From f33c69d8b4171ed43adc09113f2bd9975fdf4422 Mon Sep 17 00:00:00 2001 -From: Mike Griese -Date: Fri, 17 Jul 2020 13:01:07 -0500 -Subject: [PATCH] Add a scratch island project for testing - ---- - OpenConsole.sln | 38 ++ - src/tools/ScratchIsland/AppHost.cpp | 176 +++++ - src/tools/ScratchIsland/AppHost.h | 24 + - src/tools/ScratchIsland/BaseWindow.h | 228 +++++++ - src/tools/ScratchIsland/IslandWindow.cpp | 604 ++++++++++++++++++ - src/tools/ScratchIsland/IslandWindow.h | 77 +++ - src/tools/ScratchIsland/ScratchIsland.def | 1 + - .../ScratchIsland/ScratchIsland.manifest | 24 + - src/tools/ScratchIsland/ScratchIsland.rc | 94 +++ - src/tools/ScratchIsland/ScratchIsland.vcxproj | 164 +++++ - src/tools/ScratchIsland/main.cpp | 54 ++ - src/tools/ScratchIsland/packages.config | 8 + - src/tools/ScratchIsland/pch.cpp | 4 + - src/tools/ScratchIsland/pch.h | 74 +++ - src/tools/ScratchIsland/resource.h | 24 + - 15 files changed, 1594 insertions(+) - create mode 100644 src/tools/ScratchIsland/AppHost.cpp - create mode 100644 src/tools/ScratchIsland/AppHost.h - create mode 100644 src/tools/ScratchIsland/BaseWindow.h - create mode 100644 src/tools/ScratchIsland/IslandWindow.cpp - create mode 100644 src/tools/ScratchIsland/IslandWindow.h - create mode 100644 src/tools/ScratchIsland/ScratchIsland.def - create mode 100644 src/tools/ScratchIsland/ScratchIsland.manifest - create mode 100644 src/tools/ScratchIsland/ScratchIsland.rc - create mode 100644 src/tools/ScratchIsland/ScratchIsland.vcxproj - create mode 100644 src/tools/ScratchIsland/main.cpp - create mode 100644 src/tools/ScratchIsland/packages.config - create mode 100644 src/tools/ScratchIsland/pch.cpp - create mode 100644 src/tools/ScratchIsland/pch.h - create mode 100644 src/tools/ScratchIsland/resource.h - -diff --git a/OpenConsole.sln b/OpenConsole.sln -index dcae33515..5099f457d 100644 ---- a/OpenConsole.sln -+++ b/OpenConsole.sln -@@ -321,6 +321,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchWinRTClient", "src\t - {D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4} - EndProjectSection - EndProject -+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchIsland", "src\tools\ScratchIsland\ScratchIsland.vcxproj", "{23a1f736-cd19-4196-980f-84bcd50cf783}" -+ ProjectSection(ProjectDependencies) = postProject -+ {06382349-D62A-4C7D-A7D3-9CA817EAE092} = {06382349-D62A-4C7D-A7D3-9CA817EAE092} -+ {D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4} -+ EndProjectSection -+EndProject - Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}" - EndProject - Global -@@ -2087,6 +2093,37 @@ Global - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32 - {506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32 -+ -+ -+ -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.ActiveCfg = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.Build.0 = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.ActiveCfg = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.Build.0 = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.Build.0 = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.Build.0 = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.ActiveCfg = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.Build.0 = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.ActiveCfg = Release|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.Build.0 = Release|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|Any CPU.ActiveCfg = Debug|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|ARM64.ActiveCfg = Debug|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.ActiveCfg = Debug|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.Build.0 = Debug|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.ActiveCfg = Debug|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.Build.0 = Debug|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|Any CPU.ActiveCfg = Release|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|ARM64.ActiveCfg = Release|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.ActiveCfg = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.Build.0 = Release|x64 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.ActiveCfg = Release|Win32 -+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE -@@ -2169,6 +2206,7 @@ Global - {1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202} - {D46D9547-F085-4645-B8F7-E8CD21559AB4} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {06382349-D62A-4C7D-A7D3-9CA817EAE092} = {A10C4720-DCA4-4640-9749-67F4314F527C} -+ {23a1f736-cd19-4196-980f-84bcd50cf783} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution -diff --git a/src/tools/ScratchIsland/AppHost.cpp b/src/tools/ScratchIsland/AppHost.cpp -new file mode 100644 -index 000000000..d1ec7b004 ---- /dev/null -+++ b/src/tools/ScratchIsland/AppHost.cpp -@@ -0,0 +1,176 @@ -+// Copyright (c) Microsoft Corporation. -+// Licensed under the MIT license. -+ -+#include "pch.h" -+#include "AppHost.h" -+#include "../types/inc/Viewport.hpp" -+#include "../types/inc/utils.hpp" -+#include "../types/inc/User32Utils.hpp" -+ -+#include "resource.h" -+ -+using namespace winrt::Windows::UI; -+using namespace winrt::Windows::UI::Composition; -+using namespace winrt::Windows::UI::Xaml; -+using namespace winrt::Windows::UI::Xaml::Hosting; -+using namespace winrt::Windows::Foundation::Numerics; -+using namespace ::Microsoft::Console; -+using namespace ::Microsoft::Console::Types; -+ -+AppHost::AppHost() noexcept : -+ _window{ nullptr } -+{ -+ _window = std::make_unique(); -+ -+ // Tell the window to callback to us when it's about to handle a WM_CREATE -+ auto pfn = std::bind(&AppHost::_HandleCreateWindow, -+ this, -+ std::placeholders::_1, -+ std::placeholders::_2); -+ _window->SetCreateCallback(pfn); -+ -+ _window->MakeWindow(); -+} -+ -+AppHost::~AppHost() -+{ -+ // destruction order is important for proper teardown here -+ _window = nullptr; -+} -+ -+// Method Description: -+// - Initializes the XAML island, creates the terminal app, and sets the -+// island's content to that of the terminal app's content. Also registers some -+// callbacks with TermApp. -+// !!! IMPORTANT!!! -+// This must be called *AFTER* WindowsXamlManager::InitializeForCurrentThread. -+// If it isn't, then we won't be able to create the XAML island. -+// Arguments: -+// - -+// Return Value: -+// - -+void AppHost::Initialize() -+{ -+ _window->Initialize(); -+ -+ // Set up the content of the application. If the app has a custom titlebar, -+ // set that content as well. -+ winrt::Windows::UI::Xaml::Controls::Grid g; -+ // TODO: INITIALIZE THIS UI HERE -+ _window->SetContent(g); -+ -+ _window->OnAppInitialized(); -+} -+ -+// Method Description: -+// - Resize the window we're about to create to the appropriate dimensions, as -+// specified in the settings. This will be called during the handling of -+// WM_CREATE. We'll load the settings for the app, then get the proposed size -+// of the terminal from the app. Using that proposed size, we'll resize the -+// window we're creating, so that it'll match the values in the settings. -+// Arguments: -+// - hwnd: The HWND of the window we're about to create. -+// - proposedRect: The location and size of the window that we're about to -+// create. We'll use this rect to determine which monitor the window is about -+// to appear on. -+// - launchMode: A LaunchMode enum reference that indicates the launch mode -+// Return Value: -+// - None -+void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect) -+{ -+ // Acquire the actual initial position -+ winrt::Windows::Foundation::Point initialPosition{ (float)proposedRect.left, (float)proposedRect.top }; -+ proposedRect.left = gsl::narrow_cast(initialPosition.X); -+ proposedRect.top = gsl::narrow_cast(initialPosition.Y); -+ -+ long adjustedHeight = 0; -+ long adjustedWidth = 0; -+ -+ // Find nearest monitor. -+ HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST); -+ -+ // Get nearest monitor information -+ MONITORINFO monitorInfo; -+ monitorInfo.cbSize = sizeof(MONITORINFO); -+ GetMonitorInfo(hmon, &monitorInfo); -+ -+ // This API guarantees that dpix and dpiy will be equal, but neither is an -+ // optional parameter so give two UINTs. -+ UINT dpix = USER_DEFAULT_SCREEN_DPI; -+ UINT dpiy = USER_DEFAULT_SCREEN_DPI; -+ // If this fails, we'll use the default of 96. -+ GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy); -+ -+ // We need to check if the top left point of the titlebar of the window is within any screen -+ RECT offScreenTestRect; -+ offScreenTestRect.left = proposedRect.left; -+ offScreenTestRect.top = proposedRect.top; -+ offScreenTestRect.right = offScreenTestRect.left + 1; -+ offScreenTestRect.bottom = offScreenTestRect.top + 1; -+ -+ bool isTitlebarIntersectWithMonitors = false; -+ EnumDisplayMonitors( -+ nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL { -+ auto intersectWithMonitor = reinterpret_cast(lParam); -+ *intersectWithMonitor = true; -+ // Continue the enumeration -+ return FALSE; -+ }, -+ reinterpret_cast(&isTitlebarIntersectWithMonitors)); -+ -+ if (!isTitlebarIntersectWithMonitors) -+ { -+ // If the title bar is out-of-screen, we set the initial position to -+ // the top left corner of the nearest monitor -+ proposedRect.left = monitorInfo.rcWork.left; -+ proposedRect.top = monitorInfo.rcWork.top; -+ } -+ -+ winrt::Windows::Foundation::Size initialSize{ 800, 600 }; -+ -+ const short islandWidth = Utils::ClampToShortMax( -+ static_cast(ceil(initialSize.Width)), 1); -+ const short 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 auto nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix); -+ adjustedWidth = islandWidth + nonClientSize.cx; -+ adjustedHeight = islandHeight + nonClientSize.cy; -+ -+ const COORD origin{ gsl::narrow(proposedRect.left), -+ gsl::narrow(proposedRect.top) }; -+ const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1), -+ Utils::ClampToShortMax(adjustedHeight, 1) }; -+ -+ const auto newPos = Viewport::FromDimensions(origin, dimensions); -+ bool succeeded = SetWindowPos(hwnd, -+ nullptr, -+ newPos.Left(), -+ newPos.Top(), -+ newPos.Width(), -+ newPos.Height(), -+ SWP_NOACTIVATE | SWP_NOZORDER); -+ -+ // Refresh the dpi of HWND because the dpi where the window will launch may be different -+ // at this time -+ _window->RefreshCurrentDPI(); -+ -+ // If we can't resize the window, that's really okay. We can just go on with -+ // the originally proposed window size. -+ LOG_LAST_ERROR_IF(!succeeded); -+} -+ -+// Method Description: -+// - Called when the app wants to change its theme. We'll forward this to the -+// IslandWindow, so it can update the root UI element of the entire XAML tree. -+// Arguments: -+// - sender: unused -+// - arg: the ElementTheme to use as the new theme for the UI -+// Return Value: -+// - -+void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& arg) -+{ -+ _window->OnApplicationThemeChanged(arg); -+} -diff --git a/src/tools/ScratchIsland/AppHost.h b/src/tools/ScratchIsland/AppHost.h -new file mode 100644 -index 000000000..e4a957cd8 ---- /dev/null -+++ b/src/tools/ScratchIsland/AppHost.h -@@ -0,0 +1,24 @@ -+// Copyright (c) Microsoft Corporation. -+// Licensed under the MIT license. -+ -+#include "pch.h" -+ -+#include "IslandWindow.h" -+ -+class AppHost -+{ -+public: -+ AppHost() noexcept; -+ virtual ~AppHost(); -+ -+ void Initialize(); -+ -+private: -+ bool _useNonClientArea{ false }; -+ -+ std::unique_ptr _window; -+ -+ void _HandleCreateWindow(const HWND hwnd, RECT proposedRect); -+ void _UpdateTheme(const winrt::Windows::Foundation::IInspectable&, -+ const winrt::Windows::UI::Xaml::ElementTheme& arg); -+}; -diff --git a/src/tools/ScratchIsland/BaseWindow.h b/src/tools/ScratchIsland/BaseWindow.h -new file mode 100644 -index 000000000..d1a8b86d7 ---- /dev/null -+++ b/src/tools/ScratchIsland/BaseWindow.h -@@ -0,0 +1,228 @@ -+// Copyright (c) Microsoft Corporation. -+// Licensed under the MIT license. -+ -+#pragma once -+ -+// Custom window messages -+#define CM_UPDATE_TITLE (WM_USER) -+ -+#include -+ -+template -+class BaseWindow -+{ -+public: -+ virtual ~BaseWindow() = 0; -+ static T* GetThisFromHandle(HWND const window) noexcept -+ { -+ return reinterpret_cast(GetWindowLongPtr(window, GWLP_USERDATA)); -+ } -+ -+ [[nodiscard]] static LRESULT __stdcall WndProc(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); -+ T* that = static_cast(cs->lpCreateParams); -+ WINRT_ASSERT(that); -+ WINRT_ASSERT(!that->_window); -+ that->_window = wil::unique_hwnd(window); -+ -+ return that->_OnNcCreate(wparam, lparam); -+ } -+ else if (T* that = GetThisFromHandle(window)) -+ { -+ return that->MessageHandler(message, wparam, lparam); -+ } -+ -+ return DefWindowProc(window, message, wparam, lparam); -+ } -+ -+ [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept -+ { -+ switch (message) -+ { -+ case WM_DPICHANGED: -+ { -+ return HandleDpiChange(_window.get(), wparam, lparam); -+ } -+ -+ case WM_DESTROY: -+ { -+ PostQuitMessage(0); -+ return 0; -+ } -+ -+ case WM_SIZE: -+ { -+ UINT width = LOWORD(lparam); -+ UINT height = HIWORD(lparam); -+ -+ switch (wparam) -+ { -+ case SIZE_MAXIMIZED: -+ [[fallthrough]]; -+ case SIZE_RESTORED: -+ if (_minimized) -+ { -+ _minimized = false; -+ OnRestore(); -+ } -+ -+ // We always need to fire the resize event, even when we're transitioning from minimized. -+ // We might be transitioning directly from minimized to maximized, and we'll need -+ // to trigger any size-related content changes. -+ OnResize(width, height); -+ break; -+ case SIZE_MINIMIZED: -+ if (!_minimized) -+ { -+ _minimized = true; -+ OnMinimize(); -+ } -+ break; -+ default: -+ // do nothing. -+ break; -+ } -+ break; -+ } -+ case CM_UPDATE_TITLE: -+ { -+ SetWindowTextW(_window.get(), _title.c_str()); -+ break; -+ } -+ } -+ -+ return DefWindowProc(_window.get(), message, wparam, lparam); -+ } -+ -+ // DPI Change handler. on WM_DPICHANGE resize the window -+ [[nodiscard]] LRESULT HandleDpiChange(const HWND hWnd, const WPARAM wParam, const LPARAM lParam) -+ { -+ _inDpiChange = true; -+ const HWND hWndStatic = GetWindow(hWnd, GW_CHILD); -+ if (hWndStatic != nullptr) -+ { -+ const UINT uDpi = HIWORD(wParam); -+ -+ // Resize the window -+ auto lprcNewScale = reinterpret_cast(lParam); -+ -+ SetWindowPos(hWnd, nullptr, lprcNewScale->left, lprcNewScale->top, lprcNewScale->right - lprcNewScale->left, lprcNewScale->bottom - lprcNewScale->top, SWP_NOZORDER | SWP_NOACTIVATE); -+ -+ _currentDpi = uDpi; -+ } -+ _inDpiChange = false; -+ return 0; -+ } -+ -+ virtual void OnResize(const UINT width, const UINT height) = 0; -+ virtual void OnMinimize() = 0; -+ virtual void OnRestore() = 0; -+ -+ RECT GetWindowRect() const noexcept -+ { -+ RECT rc = { 0 }; -+ ::GetWindowRect(_window.get(), &rc); -+ return rc; -+ } -+ -+ HWND GetHandle() const noexcept -+ { -+ return _window.get(); -+ } -+ -+ float GetCurrentDpiScale() const noexcept -+ { -+ const auto dpi = ::GetDpiForWindow(_window.get()); -+ const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); -+ return scale; -+ } -+ -+ //// Gets the physical size of the client area of the HWND in _window -+ SIZE GetPhysicalSize() const noexcept -+ { -+ RECT rect = {}; -+ GetClientRect(_window.get(), &rect); -+ const auto windowsWidth = rect.right - rect.left; -+ const auto windowsHeight = rect.bottom - rect.top; -+ return SIZE{ windowsWidth, windowsHeight }; -+ } -+ -+ //// Gets the logical (in DIPs) size of a physical size specified by the parameter physicalSize -+ //// Remarks: -+ //// XAML coordinate system is always in Display Independent Pixels (a.k.a DIPs or Logical). However Win32 GDI (because of legacy reasons) -+ //// in DPI mode "Per-Monitor and Per-Monitor (V2) DPI Awareness" is always in physical pixels. -+ //// The formula to transform is: -+ //// logical = (physical / dpi) + 0.5 // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75 -+ //// See also: -+ //// https://docs.microsoft.com/en-us/windows/desktop/LearnWin32/dpi-and-device-independent-pixels -+ //// https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows#per-monitor-and-per-monitor-v2-dpi-awareness -+ winrt::Windows::Foundation::Size GetLogicalSize(const SIZE physicalSize) const noexcept -+ { -+ const auto scale = GetCurrentDpiScale(); -+ // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75 -+ const auto logicalWidth = (physicalSize.cx / scale) + 0.5f; -+ const auto logicalHeight = (physicalSize.cy / scale) + 0.5f; -+ return winrt::Windows::Foundation::Size(logicalWidth, logicalHeight); -+ } -+ -+ winrt::Windows::Foundation::Size GetLogicalSize() const noexcept -+ { -+ return GetLogicalSize(GetPhysicalSize()); -+ } -+ -+ // Method Description: -+ // - Sends a message to our message loop to update the title of the window. -+ // Arguments: -+ // - newTitle: a string to use as the new title of the window. -+ // Return Value: -+ // - -+ void UpdateTitle(std::wstring_view newTitle) -+ { -+ _title = newTitle; -+ PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast(nullptr)); -+ } -+ -+ // Method Description: -+ // Reset the current dpi of the window. This method is only called after we change the -+ // initial launch position. This makes sure the dpi is consistent with the monitor on which -+ // the window will launch -+ void RefreshCurrentDPI() -+ { -+ _currentDpi = GetDpiForWindow(_window.get()); -+ } -+ -+protected: -+ using base_type = BaseWindow; -+ wil::unique_hwnd _window; -+ -+ unsigned int _currentDpi = 0; -+ bool _inDpiChange = false; -+ -+ std::wstring _title = L""; -+ -+ bool _minimized = false; -+ -+ // Method Description: -+ // - This method is called when the window receives the WM_NCCREATE message. -+ // Return Value: -+ // - The value returned from the window proc. -+ virtual [[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept -+ { -+ SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast(this)); -+ -+ EnableNonClientDpiScaling(_window.get()); -+ _currentDpi = GetDpiForWindow(_window.get()); -+ -+ return DefWindowProc(_window.get(), WM_NCCREATE, wParam, lParam); -+ }; -+}; -+ -+template -+inline BaseWindow::~BaseWindow() -+{ -+} -diff --git a/src/tools/ScratchIsland/IslandWindow.cpp b/src/tools/ScratchIsland/IslandWindow.cpp -new file mode 100644 -index 000000000..3813c6e70 ---- /dev/null -+++ b/src/tools/ScratchIsland/IslandWindow.cpp -@@ -0,0 +1,604 @@ -+// Copyright (c) Microsoft Corporation. -+// Licensed under the MIT license. -+ -+#include "pch.h" -+#include "IslandWindow.h" -+#include "../types/inc/Viewport.hpp" -+#include "resource.h" -+ -+extern "C" IMAGE_DOS_HEADER __ImageBase; -+ -+using namespace winrt::Windows::UI; -+using namespace winrt::Windows::UI::Composition; -+using namespace winrt::Windows::UI::Xaml; -+using namespace winrt::Windows::UI::Xaml::Hosting; -+using namespace winrt::Windows::Foundation::Numerics; -+using namespace ::Microsoft::Console::Types; -+ -+#define XAML_HOSTING_WINDOW_CLASS_NAME L"SCRATCH_ISLAND_CLASS" -+ -+IslandWindow::IslandWindow() noexcept : -+ _interopWindowHandle{ nullptr }, -+ _rootGrid{ nullptr }, -+ _source{ nullptr }, -+ _pfnCreateCallback{ nullptr } -+{ -+} -+ -+IslandWindow::~IslandWindow() -+{ -+ _source.Close(); -+} -+ -+// Method Description: -+// - Create the actual window that we'll use for the application. -+// Arguments: -+// - -+// Return Value: -+// - -+void IslandWindow::MakeWindow() noexcept -+{ -+ WNDCLASS wc{}; -+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW); -+ wc.hInstance = reinterpret_cast(&__ImageBase); -+ wc.lpszClassName = XAML_HOSTING_WINDOW_CLASS_NAME; -+ wc.style = CS_HREDRAW | CS_VREDRAW; -+ wc.lpfnWndProc = WndProc; -+ wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON)); -+ RegisterClass(&wc); -+ WINRT_ASSERT(!_window); -+ -+ // Create the window with the default size here - During the creation of the -+ // window, the system will give us a chance to set its size in WM_CREATE. -+ // WM_CREATE will be handled synchronously, before CreateWindow returns. -+ WINRT_VERIFY(CreateWindowEx(_alwaysOnTop ? WS_EX_TOPMOST : 0, -+ wc.lpszClassName, -+ L"Scratch Island", -+ WS_OVERLAPPEDWINDOW, -+ CW_USEDEFAULT, -+ CW_USEDEFAULT, -+ CW_USEDEFAULT, -+ CW_USEDEFAULT, -+ nullptr, -+ nullptr, -+ wc.hInstance, -+ this)); -+ -+ WINRT_ASSERT(_window); -+} -+ -+// Method Description: -+// - Called when no tab is remaining to close the window. -+// Arguments: -+// - -+// Return Value: -+// - -+void IslandWindow::Close() -+{ -+ PostQuitMessage(0); -+} -+ -+// Method Description: -+// - Set a callback to be called when we process a WM_CREATE message. This gives -+// the AppHost a chance to resize the window to the proper size. -+// Arguments: -+// - pfn: a function to be called during the handling of WM_CREATE. It takes two -+// parameters: -+// * HWND: the HWND of the window that's being created. -+// * RECT: The position on the screen that the system has proposed for our -+// window. -+// Return Value: -+// - -+void IslandWindow::SetCreateCallback(std::function pfn) noexcept -+{ -+ _pfnCreateCallback = pfn; -+} -+ -+// Method Description: -+// - Handles a WM_CREATE message. Calls our create callback, if one's been set. -+// Arguments: -+// - wParam: unused -+// - lParam: the lParam of a WM_CREATE, which is a pointer to a CREATESTRUCTW -+// Return Value: -+// - -+void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexcept -+{ -+ // Get proposed window rect from create structure -+ CREATESTRUCTW* pcs = reinterpret_cast(lParam); -+ RECT rc; -+ rc.left = pcs->x; -+ rc.top = pcs->y; -+ rc.right = rc.left + pcs->cx; -+ rc.bottom = rc.top + pcs->cy; -+ -+ if (_pfnCreateCallback) -+ { -+ _pfnCreateCallback(_window.get(), rc); -+ } -+ -+ int nCmdShow = SW_SHOW; -+ -+ ShowWindow(_window.get(), nCmdShow); -+ -+ UpdateWindow(_window.get()); -+} -+ -+// Method Description: -+// - Handles a WM_SIZING message, which occurs when user drags a window border -+// or corner. It intercepts this resize action and applies 'snapping' i.e. -+// aligns the terminal's size to its cell grid. We're given the window size, -+// which we then adjust based on the terminal's properties (like font size). -+// Arguments: -+// - wParam: Specifies which edge of the window is being dragged. -+// - lParam: Pointer to the requested window rectangle (this is, the one that -+// originates from current drag action). It also acts as the return value -+// (it's a ref parameter). -+// Return Value: -+// - -+LRESULT IslandWindow::_OnSizing(const WPARAM /*wParam*/, const LPARAM /*lParam*/) -+{ -+ // If we haven't been given the callback that would adjust the dimension, -+ // then we can't do anything, so just bail out. -+ return FALSE; -+} -+ -+void IslandWindow::Initialize() -+{ -+ const bool initialized = (_interopWindowHandle != nullptr); -+ -+ _source = DesktopWindowXamlSource{}; -+ -+ auto interop = _source.as(); -+ winrt::check_hresult(interop->AttachToWindow(_window.get())); -+ -+ // stash the child interop handle so we can resize it when the main hwnd is resized -+ interop->get_WindowHandle(&_interopWindowHandle); -+ -+ _rootGrid = winrt::Windows::UI::Xaml::Controls::Grid(); -+ _source.Content(_rootGrid); -+} -+ -+void IslandWindow::OnSize(const UINT width, const UINT height) -+{ -+ // update the interop window size -+ SetWindowPos(_interopWindowHandle, nullptr, 0, 0, width, height, SWP_SHOWWINDOW); -+ -+ if (_rootGrid) -+ { -+ const auto size = GetLogicalSize(); -+ _rootGrid.Width(size.Width); -+ _rootGrid.Height(size.Height); -+ } -+} -+ -+[[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept -+{ -+ switch (message) -+ { -+ case WM_CREATE: -+ { -+ _HandleCreateWindow(wparam, lparam); -+ return 0; -+ } -+ case WM_SETFOCUS: -+ { -+ if (_interopWindowHandle != nullptr) -+ { -+ // send focus to the child window -+ SetFocus(_interopWindowHandle); -+ return 0; // eat the message -+ } -+ } -+ -+ case WM_NCLBUTTONDOWN: -+ case WM_NCLBUTTONUP: -+ case WM_NCMBUTTONDOWN: -+ case WM_NCMBUTTONUP: -+ case WM_NCRBUTTONDOWN: -+ case WM_NCRBUTTONUP: -+ case WM_NCXBUTTONDOWN: -+ case WM_NCXBUTTONUP: -+ { -+ // If we clicked in the titlebar, raise an event so the app host can -+ // dispatch an appropriate event. -+ _DragRegionClickedHandlers(); -+ break; -+ } -+ case WM_MENUCHAR: -+ { -+ // GH#891: return this LRESULT here to prevent the app from making a -+ // bell when alt+key is pressed. A menu is active and the user presses a -+ // key that does not correspond to any mnemonic or accelerator key, -+ return MAKELRESULT(0, MNC_CLOSE); -+ } -+ case WM_SIZING: -+ { -+ return _OnSizing(wparam, lparam); -+ } -+ case WM_CLOSE: -+ { -+ // If the user wants to close the app by clicking 'X' button, -+ // we hand off the close experience to the app layer. If all the tabs -+ // are closed, the window will be closed as well. -+ _windowCloseButtonClickedHandler(); -+ return 0; -+ } -+ case WM_MOUSEWHEEL: -+ try -+ { -+ // This whole handler is a hack for GH#979. -+ // -+ // On some laptops, their trackpads won't scroll inactive windows -+ // _ever_. With our entire window just being one giant XAML Island, the -+ // touchpad driver thinks our entire window is inactive, and won't -+ // scroll the XAML island. On those types of laptops, we'll get a -+ // WM_MOUSEWHEEL here, in our root window, when the trackpad scrolls. -+ // We're going to take that message and manually plumb it through to our -+ // TermControl's, or anything else that implements IMouseWheelListener. -+ -+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx -+ // Important! Do not use the LOWORD or HIWORD macros to extract the x- -+ // and y- coordinates of the cursor position because these macros return -+ // incorrect results on systems with multiple monitors. Systems with -+ // multiple monitors can have negative x- and y- coordinates, and LOWORD -+ // and HIWORD treat the coordinates as unsigned quantities. -+ const til::point eventPoint{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; -+ // This mouse event is relative to the display origin, not the window. Convert here. -+ const til::rectangle windowRect{ GetWindowRect() }; -+ const auto origin = windowRect.origin(); -+ const auto relative = eventPoint - origin; -+ // Convert to logical scaling before raising the event. -+ const auto real = relative / GetCurrentDpiScale(); -+ -+ const short wheelDelta = static_cast(HIWORD(wparam)); -+ -+ // Raise an event, so any listeners can handle the mouse wheel event manually. -+ _MouseScrolledHandlers(real, wheelDelta); -+ return 0; -+ } -+ CATCH_LOG(); -+ } -+ -+ // TODO: handle messages here... -+ return base_type::MessageHandler(message, wparam, lparam); -+} -+ -+// Method Description: -+// - Called when the window has been resized (or maximized) -+// Arguments: -+// - width: the new width of the window _in pixels_ -+// - height: the new height of the window _in pixels_ -+void IslandWindow::OnResize(const UINT width, const UINT height) -+{ -+ if (_interopWindowHandle) -+ { -+ OnSize(width, height); -+ } -+} -+ -+// Method Description: -+// - Called when the window is minimized to the taskbar. -+void IslandWindow::OnMinimize() -+{ -+ // TODO GH#1989 Stop rendering island content when the app is minimized. -+} -+ -+// Method Description: -+// - Called when the window is restored from having been minimized. -+void IslandWindow::OnRestore() -+{ -+ // TODO GH#1989 Stop rendering island content when the app is minimized. -+} -+ -+void IslandWindow::SetContent(winrt::Windows::UI::Xaml::UIElement content) -+{ -+ _rootGrid.Children().Clear(); -+ _rootGrid.Children().Append(content); -+} -+ -+// Method Description: -+// - Gets the difference between window and client area size. -+// Arguments: -+// - dpi: dpi of a monitor on which the window is placed -+// Return Value -+// - The size difference -+SIZE IslandWindow::GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept -+{ -+ const auto windowStyle = static_cast(GetWindowLong(_window.get(), GWL_STYLE)); -+ RECT islandFrame{}; -+ -+ // If we failed to get the correct window size for whatever reason, log -+ // the error and go on. We'll use whatever the control proposed as the -+ // size of our window, which will be at least close. -+ LOG_IF_WIN32_BOOL_FALSE(AdjustWindowRectExForDpi(&islandFrame, windowStyle, false, 0, dpi)); -+ -+ return { -+ islandFrame.right - islandFrame.left, -+ islandFrame.bottom - islandFrame.top -+ }; -+} -+ -+void IslandWindow::OnAppInitialized() -+{ -+ // Do a quick resize to force the island to paint -+ const auto size = GetPhysicalSize(); -+ OnSize(size.cx, size.cy); -+} -+ -+// Method Description: -+// - Called when the app wants to change its theme. We'll update the root UI -+// element of the entire XAML tree, so that all UI elements get the theme -+// applied. -+// Arguments: -+// - arg: the ElementTheme to use as the new theme for the UI -+// Return Value: -+// - -+void IslandWindow::OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) -+{ -+ _rootGrid.RequestedTheme(requestedTheme); -+ // Invalidate the window rect, so that we'll repaint any elements we're -+ // drawing ourselves to match the new theme -+ ::InvalidateRect(_window.get(), nullptr, false); -+} -+ -+// Method Description: -+// - Updates our focus mode state. See _SetIsBorderless for more details. -+// Arguments: -+// - -+// Return Value: -+// - -+void IslandWindow::FocusModeChanged(const bool focusMode) -+{ -+ // Do nothing if the value was unchanged. -+ if (focusMode == _borderless) -+ { -+ return; -+ } -+ -+ _SetIsBorderless(focusMode); -+} -+ -+// Method Description: -+// - Updates our fullscreen state. See _SetIsFullscreen for more details. -+// Arguments: -+// - -+// Return Value: -+// - -+void IslandWindow::FullscreenChanged(const bool fullscreen) -+{ -+ // Do nothing if the value was unchanged. -+ if (fullscreen == _fullscreen) -+ { -+ return; -+ } -+ -+ _SetIsFullscreen(fullscreen); -+} -+ -+// Method Description: -+// - Enter or exit the "always on top" state. Before the window is created, this -+// value will later be used when we create the window to create the window on -+// top of all others. After the window is created, it will either enter the -+// group of topmost windows, or exit the group of topmost windows. -+// Arguments: -+// - alwaysOnTop: whether we should be entering or exiting always on top mode. -+// Return Value: -+// - -+void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop) -+{ -+ _alwaysOnTop = alwaysOnTop; -+ -+ const auto hwnd = GetHandle(); -+ if (hwnd) -+ { -+ SetWindowPos(hwnd, -+ _alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST, -+ 0, // the window dimensions are unused, because we're passing SWP_NOSIZE -+ 0, -+ 0, -+ 0, -+ SWP_NOMOVE | SWP_NOSIZE); -+ } -+} -+ -+// From GdiEngine::s_SetWindowLongWHelper -+void _SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept -+{ -+ // SetWindowLong has strange error handling. On success, it returns the -+ // previous Window Long value and doesn't modify the Last Error state. To -+ // deal with this, we set the last error to 0/S_OK first, call it, and if -+ // the previous long was 0, we check if the error was non-zero before -+ // reporting. Otherwise, we'll get an "Error: The operation has completed -+ // successfully." and there will be another screenshot on the internet -+ // making fun of Windows. See: -+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx -+ SetLastError(0); -+ LONG const lResult = SetWindowLongW(hWnd, nIndex, dwNewLong); -+ if (0 == lResult) -+ { -+ LOG_LAST_ERROR_IF(0 != GetLastError()); -+ } -+} -+ -+// Method Description: -+// - This is a helper to figure out what the window styles should be, given the -+// current state of flags like borderless mode and fullscreen mode. -+// Arguments: -+// - -+// Return Value: -+// - a LONG with the appropriate flags set for our current window mode, to be used with GWL_STYLE -+LONG IslandWindow::_getDesiredWindowStyle() const -+{ -+ auto windowStyle = GetWindowLongW(GetHandle(), GWL_STYLE); -+ -+ // If we're both fullscreen and borderless, fullscreen mode takes precedence. -+ -+ if (_fullscreen) -+ { -+ // When moving to fullscreen, remove WS_OVERLAPPEDWINDOW, which specifies -+ // styles for non-fullscreen windows (e.g. caption bar), and add the -+ // WS_POPUP style to allow us to size ourselves to the monitor size. -+ // Do the reverse when restoring from fullscreen. -+ // Doing these modifications to that window will cause a vista-style -+ // window frame to briefly appear when entering and exiting fullscreen. -+ WI_ClearFlag(windowStyle, WS_BORDER); -+ WI_ClearFlag(windowStyle, WS_SIZEBOX); -+ WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW); -+ -+ WI_SetFlag(windowStyle, WS_POPUP); -+ return windowStyle; -+ } -+ else if (_borderless) -+ { -+ // When moving to borderless, remove WS_OVERLAPPEDWINDOW, which -+ // specifies styles for non-fullscreen windows (e.g. caption bar), and -+ // add the WS_BORDER and WS_SIZEBOX styles. This allows us to still have -+ // a small resizing frame, but without a full titlebar, nor caption -+ // buttons. -+ -+ WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW); -+ WI_ClearFlag(windowStyle, WS_POPUP); -+ -+ WI_SetFlag(windowStyle, WS_BORDER); -+ WI_SetFlag(windowStyle, WS_SIZEBOX); -+ return windowStyle; -+ } -+ -+ // Here, we're not in either fullscreen or borderless mode. Return to -+ // WS_OVERLAPPEDWINDOW. -+ WI_ClearFlag(windowStyle, WS_POPUP); -+ WI_ClearFlag(windowStyle, WS_BORDER); -+ WI_ClearFlag(windowStyle, WS_SIZEBOX); -+ -+ WI_SetAllFlags(windowStyle, WS_OVERLAPPEDWINDOW); -+ -+ return windowStyle; -+} -+ -+// Method Description: -+// - Enable or disable focus mode. When entering focus mode, we'll -+// need to manually hide the entire titlebar. -+// - When we're entering focus we need to do some additional modification -+// of our window styles. However, the NonClientIslandWindow very explicitly -+// _doesn't_ need to do these steps. -+// Arguments: -+// - borderlessEnabled: If true, we're entering focus mode. If false, we're leaving. -+// Return Value: -+// - -+void IslandWindow::_SetIsBorderless(const bool borderlessEnabled) -+{ -+ _borderless = borderlessEnabled; -+ -+ HWND const hWnd = GetHandle(); -+ -+ // First, modify regular window styles as appropriate -+ auto windowStyle = _getDesiredWindowStyle(); -+ _SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle); -+ -+ // Now modify extended window styles as appropriate -+ // When moving to fullscreen, remove the window edge style to avoid an -+ // ugly border when not focused. -+ auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE); -+ WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen); -+ _SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle); -+ -+ // Resize the window, with SWP_FRAMECHANGED, to trigger user32 to -+ // recalculate the non/client areas -+ const til::rectangle windowPos{ GetWindowRect() }; -+ SetWindowPos(GetHandle(), -+ HWND_TOP, -+ windowPos.left(), -+ windowPos.top(), -+ windowPos.width(), -+ windowPos.height(), -+ SWP_SHOWWINDOW | SWP_FRAMECHANGED); -+} -+ -+// Method Description: -+// - Controls setting us into or out of fullscreen mode. Largely taken from -+// Window::SetIsFullscreen in conhost. -+// - When entering fullscreen mode, we'll save the current window size and -+// location, and expand to take the entire monitor size. When leaving, we'll -+// use that saved size to restore back to. -+// Arguments: -+// - fullscreenEnabled true if we should enable fullscreen mode, false to disable. -+// Return Value: -+// - -+void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) -+{ -+ // It is possible to enter _SetIsFullscreen even if we're already in full -+ // screen. Use the old is in fullscreen flag to gate checks that rely on the -+ // current state. -+ const auto oldIsInFullscreen = _fullscreen; -+ _fullscreen = fullscreenEnabled; -+ -+ HWND const hWnd = GetHandle(); -+ -+ // First, modify regular window styles as appropriate -+ auto windowStyle = _getDesiredWindowStyle(); -+ _SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle); -+ -+ // Now modify extended window styles as appropriate -+ // When moving to fullscreen, remove the window edge style to avoid an -+ // ugly border when not focused. -+ auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE); -+ WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen); -+ _SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle); -+ -+ // When entering/exiting fullscreen mode, we also need to backup/restore the -+ // current window size, and resize the window to match the new state. -+ _BackupWindowSizes(oldIsInFullscreen); -+ _ApplyWindowSize(); -+} -+ -+// Method Description: -+// - Used in entering/exiting fullscreen mode. Saves the current window size, -+// and the full size of the monitor, for use in _ApplyWindowSize. -+// - Taken from conhost's Window::_BackupWindowSizes -+// Arguments: -+// - fCurrentIsInFullscreen: true if we're currently in fullscreen mode. -+// Return Value: -+// - -+void IslandWindow::_BackupWindowSizes(const bool fCurrentIsInFullscreen) -+{ -+ if (_fullscreen) -+ { -+ // Note: the current window size depends on the current state of the -+ // window. So don't back it up if we're already in full screen. -+ if (!fCurrentIsInFullscreen) -+ { -+ _nonFullscreenWindowSize = GetWindowRect(); -+ } -+ -+ // get and back up the current monitor's size -+ HMONITOR const hCurrentMonitor = MonitorFromWindow(GetHandle(), MONITOR_DEFAULTTONEAREST); -+ MONITORINFO currMonitorInfo; -+ currMonitorInfo.cbSize = sizeof(currMonitorInfo); -+ if (GetMonitorInfo(hCurrentMonitor, &currMonitorInfo)) -+ { -+ _fullscreenWindowSize = currMonitorInfo.rcMonitor; -+ } -+ } -+} -+ -+// Method Description: -+// - Applys the appropriate window size for transitioning to/from fullscreen mode. -+// - Taken from conhost's Window::_ApplyWindowSize -+// Arguments: -+// - -+// Return Value: -+// - -+void IslandWindow::_ApplyWindowSize() -+{ -+ const auto newSize = _fullscreen ? _fullscreenWindowSize : _nonFullscreenWindowSize; -+ LOG_IF_WIN32_BOOL_FALSE(SetWindowPos(GetHandle(), -+ HWND_TOP, -+ newSize.left, -+ newSize.top, -+ newSize.right - newSize.left, -+ newSize.bottom - newSize.top, -+ SWP_FRAMECHANGED)); -+} -+ -+DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); -+DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); -diff --git a/src/tools/ScratchIsland/IslandWindow.h b/src/tools/ScratchIsland/IslandWindow.h -new file mode 100644 -index 000000000..109f7c1ec ---- /dev/null -+++ b/src/tools/ScratchIsland/IslandWindow.h -@@ -0,0 +1,77 @@ -+// Copyright (c) Microsoft Corporation. -+// Licensed under the MIT license. -+ -+#include "pch.h" -+#include "BaseWindow.h" -+#include "../../cascadia/inc/cppwinrt_utils.h" -+ -+class IslandWindow : -+ public BaseWindow -+{ -+public: -+ IslandWindow() noexcept; -+ virtual ~IslandWindow() override; -+ -+ virtual void MakeWindow() noexcept; -+ void Close(); -+ virtual void OnSize(const UINT width, const UINT height); -+ -+ [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; -+ void OnResize(const UINT width, const UINT height) override; -+ void OnMinimize() override; -+ void OnRestore() override; -+ virtual void OnAppInitialized(); -+ virtual void SetContent(winrt::Windows::UI::Xaml::UIElement content); -+ virtual void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); -+ virtual SIZE GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept; -+ -+ virtual void Initialize(); -+ -+ void SetCreateCallback(std::function pfn) noexcept; -+ -+ void FocusModeChanged(const bool focusMode); -+ void FullscreenChanged(const bool fullscreen); -+ void SetAlwaysOnTop(const bool alwaysOnTop); -+ -+#pragma endregion -+ -+ DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); -+ DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); -+ WINRT_CALLBACK(MouseScrolled, winrt::delegate); -+ -+protected: -+ void ForceResize() -+ { -+ // Do a quick resize to force the island to paint -+ const auto size = GetPhysicalSize(); -+ OnSize(size.cx, size.cy); -+ } -+ -+ HWND _interopWindowHandle; -+ -+ winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source; -+ -+ winrt::Windows::UI::Xaml::Controls::Grid _rootGrid; -+ -+ std::function _pfnCreateCallback; -+ -+ void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept; -+ [[nodiscard]] LRESULT _OnSizing(const WPARAM wParam, const LPARAM lParam); -+ -+ bool _borderless{ false }; -+ bool _fullscreen{ false }; -+ bool _alwaysOnTop{ false }; -+ RECT _fullscreenWindowSize; -+ RECT _nonFullscreenWindowSize; -+ -+ virtual void _SetIsBorderless(const bool borderlessEnabled); -+ virtual void _SetIsFullscreen(const bool fullscreenEnabled); -+ void _BackupWindowSizes(const bool currentIsInFullscreen); -+ void _ApplyWindowSize(); -+ -+ LONG _getDesiredWindowStyle() const; -+ -+private: -+ // This minimum width allows for width the tabs fit -+ static constexpr long minimumWidth = 460L; -+}; -diff --git a/src/tools/ScratchIsland/ScratchIsland.def b/src/tools/ScratchIsland/ScratchIsland.def -new file mode 100644 -index 000000000..5f282702b ---- /dev/null -+++ b/src/tools/ScratchIsland/ScratchIsland.def -@@ -0,0 +1 @@ -+ -\ No newline at end of file -diff --git a/src/tools/ScratchIsland/ScratchIsland.manifest b/src/tools/ScratchIsland/ScratchIsland.manifest -new file mode 100644 -index 000000000..a447bc8fd ---- /dev/null -+++ b/src/tools/ScratchIsland/ScratchIsland.manifest -@@ -0,0 +1,24 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ PerMonitorV2 -+ true -+ -+ -+ -diff --git a/src/tools/ScratchIsland/ScratchIsland.rc b/src/tools/ScratchIsland/ScratchIsland.rc -new file mode 100644 -index 000000000..78a784cf2 ---- /dev/null -+++ b/src/tools/ScratchIsland/ScratchIsland.rc -@@ -0,0 +1,94 @@ -+// Microsoft Visual C++ generated resource script. -+// -+#include "resource.h" -+ -+#define APSTUDIO_READONLY_SYMBOLS -+///////////////////////////////////////////////////////////////////////////// -+// -+// Generated from the TEXTINCLUDE 2 resource. -+// -+#include "winres.h" -+ -+///////////////////////////////////////////////////////////////////////////// -+#undef APSTUDIO_READONLY_SYMBOLS -+ -+///////////////////////////////////////////////////////////////////////////// -+// English (United States) resources -+ -+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -+#pragma code_page(1252) -+ -+#ifdef APSTUDIO_INVOKED -+///////////////////////////////////////////////////////////////////////////// -+// -+// TEXTINCLUDE -+// -+ -+1 TEXTINCLUDE -+BEGIN -+ "resource.h\0" -+END -+ -+2 TEXTINCLUDE -+BEGIN -+ "#include ""winres.h""\r\n" -+ "\0" -+END -+ -+3 TEXTINCLUDE -+BEGIN -+ "\r\n" -+ "\0" -+END -+ -+#endif // APSTUDIO_INVOKED -+ -+ -+///////////////////////////////////////////////////////////////////////////// -+// -+// Icon -+// -+ -+// Icon with lowest ID value placed first to ensure application icon -+// remains consistent on all systems. -+IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico" -+ -+ -+///////////////////////////////////////////////////////////////////////////// -+// -+// String Table -+// -+ -+STRINGTABLE -+BEGIN -+ IDS_ERROR_DIALOG_TITLE "Error" -+ IDS_HELP_DIALOG_TITLE "Help" -+ IDS_ERROR_ARCHITECTURE_FORMAT -+ "Windows Terminal is designed to run on your system's native architecture (%s).\nYou are currently using the %s version.\n\nPlease use the version of Windows Terminal that matches your system's native architecture." -+ IDS_X86_ARCHITECTURE "i386" -+END -+ -+STRINGTABLE -+BEGIN -+ IDS_AMD64_ARCHITECTURE "AMD64" -+ IDS_ARM64_ARCHITECTURE "ARM64" -+ IDS_ARM_ARCHITECTURE "ARM" -+ IDS_UNKNOWN_ARCHITECTURE "Unknown" -+END -+ -+#endif // English (United States) resources -+///////////////////////////////////////////////////////////////////////////// -+ -+ -+ -+#ifndef APSTUDIO_INVOKED -+///////////////////////////////////////////////////////////////////////////// -+// -+// Generated from the TEXTINCLUDE 3 resource. -+// -+ -+ -+///////////////////////////////////////////////////////////////////////////// -+#endif // not APSTUDIO_INVOKED -+ -diff --git a/src/tools/ScratchIsland/ScratchIsland.vcxproj b/src/tools/ScratchIsland/ScratchIsland.vcxproj -new file mode 100644 -index 000000000..d155853ab ---- /dev/null -+++ b/src/tools/ScratchIsland/ScratchIsland.vcxproj -@@ -0,0 +1,164 @@ -+ -+ -+ -+ -+ -+ {23a1f736-cd19-4196-980f-84bcd50cf783} -+ Win32Proj -+ ScratchIsland -+ ScratchIsland -+ ScratchIsland -+ Application -+ false -+ Windows Store -+ true -+ false -+ -+ -+ 10.0.18362.0 -+ -+ true -+ -+ -+ -+ -+ -+ -+ -+ true -+ -+ -+ -+ -+ -+ $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;%(AdditionalIncludeDirectories); -+ -+ -+ -+ "$(OpenConsoleDir)src\cascadia\TerminalCore\lib\Generated Files";%(AdditionalIncludeDirectories); -+ -+ -+ gdi32.lib;dwmapi.lib;Shcore.lib;UxTheme.lib;%(AdditionalDependencies) -+ -+ -+ -+ true -+ true -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Create -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ WindowsLocalDebugger -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ <_ContinueOnError Condition="'$(BuildingProject)' == 'true'">true -+ <_ContinueOnError Condition="'$(BuildingProject)' != 'true'">false -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ x86 -+ $(Platform) -+ -+ -+ -+ -+ <_OpenConsoleVCLibToCopy Include="$(VCToolsRedistInstallDir)\$(ReasonablePlatform)\Microsoft.VC142.CRT\*.dll" /> -+ -+ -+ $(ProjectName) -+ BuiltProjectOutputGroup -+ %(Filename)%(Extension) -+ -+ -+ -+ -+ -+ -+ -+ -+ -diff --git a/src/tools/ScratchIsland/main.cpp b/src/tools/ScratchIsland/main.cpp -new file mode 100644 -index 000000000..777e3ee9e ---- /dev/null -+++ b/src/tools/ScratchIsland/main.cpp -@@ -0,0 +1,54 @@ -+// Copyright (c) Microsoft Corporation. -+// Licensed under the MIT license. -+ -+#include "pch.h" -+#include "AppHost.h" -+#include "resource.h" -+ -+using namespace winrt; -+using namespace winrt::Windows::UI; -+using namespace winrt::Windows::UI::Composition; -+using namespace winrt::Windows::UI::Xaml::Hosting; -+using namespace winrt::Windows::Foundation::Numerics; -+ -+int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) -+{ -+ // If Terminal is spawned by a shortcut that requests that it run in a new process group -+ // while attached to a console session, that request is nonsense. That request will, however, -+ // cause WT to start with Ctrl-C disabled. This wouldn't matter, because it's a Windows-subsystem -+ // application. Unfortunately, that state is heritable. In short, if you start WT using cmd in -+ // a weird way, ^C stops working _inside_ the terminal. Mad. -+ SetConsoleCtrlHandler(NULL, FALSE); -+ -+ // Make sure to call this so we get WM_POINTER messages. -+ EnableMouseInPointer(true); -+ -+ // !!! LOAD BEARING !!! -+ // We must initialize the main thread as a single-threaded apartment before -+ // constructing any Xaml objects. Failing to do so will cause some issues -+ // in accessibility somewhere down the line when a UIAutomation object will -+ // be queried on the wrong thread at the wrong time. -+ // We used to initialize as STA only _after_ initializing the application -+ // host, which loaded the settings. The settings needed to be loaded in MTA -+ // because we were using the Windows.Storage APIs. Since we're no longer -+ // 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; -+ -+ // Initialize the xaml content. This must be called AFTER the -+ // WindowsXamlManager is initialized. -+ host.Initialize(); -+ -+ MSG message; -+ -+ while (GetMessage(&message, nullptr, 0, 0)) -+ { -+ TranslateMessage(&message); -+ DispatchMessage(&message); -+ } -+ return 0; -+} -diff --git a/src/tools/ScratchIsland/packages.config b/src/tools/ScratchIsland/packages.config -new file mode 100644 -index 000000000..166f8861f ---- /dev/null -+++ b/src/tools/ScratchIsland/packages.config -@@ -0,0 +1,8 @@ -+ -+ -+ -+ -+ -+ -+ -+ -\ No newline at end of file -diff --git a/src/tools/ScratchIsland/pch.cpp b/src/tools/ScratchIsland/pch.cpp -new file mode 100644 -index 000000000..398a99f66 ---- /dev/null -+++ b/src/tools/ScratchIsland/pch.cpp -@@ -0,0 +1,4 @@ -+// Copyright (c) Microsoft Corporation. -+// Licensed under the MIT license. -+ -+#include "pch.h" -diff --git a/src/tools/ScratchIsland/pch.h b/src/tools/ScratchIsland/pch.h -new file mode 100644 -index 000000000..40be55842 ---- /dev/null -+++ b/src/tools/ScratchIsland/pch.h -@@ -0,0 +1,74 @@ -+/*++ -+Copyright (c) Microsoft Corporation -+Licensed under the MIT license. -+ -+Module Name: -+- pch.h -+ -+Abstract: -+- Contains external headers to include in the precompile phase of console build process. -+- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building). -+--*/ -+ -+#pragma once -+ -+// Ignore checked iterators warning from VC compiler. -+#define _SCL_SECURE_NO_WARNINGS -+ -+// Block minwindef.h min/max macros to prevent conflict -+#define NOMINMAX -+ -+#define WIN32_LEAN_AND_MEAN -+#include -+ -+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+// Manually include til after we include Windows.Foundation to give it winrt superpowers -+#define BLOCK_TIL -+#include "../inc/LibraryIncludes.h" -+ -+// This is inexplicable, but for whatever reason, cppwinrt conflicts with the -+// SDK definition of this function, so the only fix is to undef it. -+// from WinBase.h -+// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime -+#ifdef GetCurrentTime -+#undef GetCurrentTime -+#endif -+ -+#include -+ -+// Needed just for XamlIslands to work at all: -+#include -+#include -+#include -+#include -+ -+// Additional headers for various xaml features. We need: -+// * Controls for grid -+// * Media for ScaleTransform -+#include -+#include -+#include -+ -+#include -+#include -+ -+// Including TraceLogging essentials for the binary -+#include -+#include -+TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider); -+#include -+#include -+ -+// For commandline argument processing -+#include -+#include -+ -+#include "til.h" -diff --git a/src/tools/ScratchIsland/resource.h b/src/tools/ScratchIsland/resource.h -new file mode 100644 -index 000000000..011413eae ---- /dev/null -+++ b/src/tools/ScratchIsland/resource.h -@@ -0,0 +1,24 @@ -+//{{NO_DEPENDENCIES}} -+// Microsoft Visual C++ generated include file. -+// Used by WindowsTerminal.rc -+// -+#define IDI_APPICON 101 -+#define IDS_ERROR_DIALOG_TITLE 105 -+#define IDS_HELP_DIALOG_TITLE 106 -+#define IDS_ERROR_ARCHITECTURE_FORMAT 110 -+#define IDS_X86_ARCHITECTURE 111 -+#define IDS_AMD64_ARCHITECTURE 112 -+#define IDS_ARM64_ARCHITECTURE 113 -+#define IDS_ARM_ARCHITECTURE 114 -+#define IDS_UNKNOWN_ARCHITECTURE 115 -+ -+// Next default values for new objects -+// -+#ifdef APSTUDIO_INVOKED -+#ifndef APSTUDIO_READONLY_SYMBOLS -+#define _APS_NEXT_RESOURCE_VALUE 104 -+#define _APS_NEXT_COMMAND_VALUE 40001 -+#define _APS_NEXT_CONTROL_VALUE 1001 -+#define _APS_NEXT_SYMED_VALUE 101 -+#endif -+#endif --- -2.20.1.windows.1 - diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 79b90c5a7d1..ebf6703ef3a 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -573,6 +573,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *result; } + // Method Description: + // - This is an event handler for the IdentifyWindowsRequested event. A + // Peasant may raise that event if they want _all_ windows to identify + // themselves. + // - This will tell each and every peasant to identify themselves. This will + // eventually propagate down to TerminalPage::IdentifyWindow. + // Arguments: + // - + // Return Value: + // - void Monarch::_identifyWindows(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { @@ -586,7 +596,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation catch (...) { LOG_CAUGHT_EXCEPTION(); - // If this fails, we don't _really_ care. Just movoe on to the + // If this fails, we don't _really_ care. Just move on to the // next one. Someone else will clean up the dead peasant. TraceLoggingWrite(g_hRemotingProvider, "Monarch_identifyWindows_Failed", diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 01d7c5dfcbc..8eb9ffe5231 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -117,6 +117,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _lastActivatedArgs; } + // Method Description: + // - Tell this window to display it's window ID. We'll raise a + // DisplayWindowIdRequested event, which will get handled in the AppHost, + // and used to tell the app to display the ID toast. + // Arguments: + // - + // Return Value: + // - void Peasant::DisplayWindowId() { // Not worried about try/catching this. The handler is in AppHost, which @@ -124,6 +132,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _DisplayWindowIdRequestedHandlers(*this, nullptr); } + // Method Description: + // - Raises an event to ask that all windows be identified. This will come + // back to us when the Monarch handles the event and calls our + // DisplayWindowId method. + // Arguments: + // - + // Return Value: + // - void Peasant::RequestIdentifyWindows() { bool successfullyNotified = false; diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 55ab1ea82d2..32467210583 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -667,24 +667,36 @@ namespace winrt::TerminalApp::implementation actionArgs.Handled(true); } + // Method Description: + // - Raise a IdentifyWindowsRequested event. This will bubble up to the + // AppLogic, to the AppHost, to the Peasant, to the Monarch, then get + // distributed down to _all_ the Peasants, as to display info about the + // window in _every_ Peasant window. + // - This action is also buggy right now, because TeachingTips behave + // weird in XAML Islands. See microsoft-ui-xaml#4382 + // Arguments: + // - + // Return Value: + // - void TerminalPage::_HandleIdentifyWindows(const IInspectable& /*sender*/, const ActionEventArgs& args) { - // Raise a IdentifyWindowsRequested event. This will bubble up to the - // AppLogic, to the AppHost, to the Peasant, to the Monarch, then get - // distributed down to _all_ the Peasants, as to display info about the - // window in _every_ Peasant window. - // - // This action is also buggy right now, because TeachingTips behave - // weird in XAML Islands. See microsoft-ui-xaml#4382 _IdentifyWindowsRequestedHandlers(*this, nullptr); args.Handled(true); } + + // Method Description: + // - Display the "Toast" with the name and ID of this window. + // - Unlike _HandleIdentifyWindow**s**, this event just displays the window + // ID and name in the current window. It does not involve any bubbling + // up/down the page/logic/host/manager/peasant/monarch. + // Arguments: + // - + // Return Value: + // - void TerminalPage::_HandleIdentifyWindow(const IInspectable& /*sender*/, const ActionEventArgs& args) { - // Unlike _HandleIdentifyWindow**s**, this event just displays the - // window ID and name in the current window. IdentifyWindow(); args.Handled(true); } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2f6966df45d..8e846d4d0e6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -275,6 +275,8 @@ namespace winrt::TerminalApp::implementation _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); + // This lambda is a local because there will be more toasts in the + // future that will need the same logic. auto safeRefocus = [weakThis{ get_weak() }](auto&&, auto&&) { if (auto page{ weakThis.get() }) { @@ -3374,7 +3376,6 @@ namespace winrt::TerminalApp::implementation co_await winrt::resume_foreground(Dispatcher()); if (auto page{ weakThis.get() }) { - // page->WindowIdToast().IsOpen(true); page->_windowIdToast->Open(); } } diff --git a/src/cascadia/TerminalApp/Toast.cpp b/src/cascadia/TerminalApp/Toast.cpp index 76ef703fe9d..d9ea845cadf 100644 --- a/src/cascadia/TerminalApp/Toast.cpp +++ b/src/cascadia/TerminalApp/Toast.cpp @@ -17,6 +17,13 @@ Toast::Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip) : _timer.Interval(ToastDuration); } +// Method Description: +// - Open() the TeachingTip, and start our timer. When the timer expires, the +// tip will be closed. +// Arguments: +// - +// Return Value: +// - void Toast::Open() { _tip.IsOpen(true); diff --git a/src/cascadia/TerminalApp/Toast.h b/src/cascadia/TerminalApp/Toast.h index 86f1cee9429..40d3df7cf55 100644 --- a/src/cascadia/TerminalApp/Toast.h +++ b/src/cascadia/TerminalApp/Toast.h @@ -4,6 +4,23 @@ Licensed under the MIT license. Module Name: - Toast.h + +Module Description: +- This is a helper class for wrapping a TeachingTip with a timer to + automatically dismiss it. Callers should add the TeachingTip wherever they'd + like in the UI tree, then wrap that TeachingTip in a toast like so: + +``` +std::unique_ptr myToast = std::make_unique(MyTeachingTip()); +``` +- Then, you can show the TeachingTip with + +``` +myToast->Open(); +``` + + which will open the tip and close it after a brief timeout. + --*/ #pragma once diff --git a/src/cascadia/TerminalApp/WindowNameConverter.cpp b/src/cascadia/TerminalApp/WindowNameConverter.cpp index c2e31b2cbfb..b782a918735 100644 --- a/src/cascadia/TerminalApp/WindowNameConverter.cpp +++ b/src/cascadia/TerminalApp/WindowNameConverter.cpp @@ -19,7 +19,7 @@ namespace winrt::TerminalApp::implementation Foundation::IInspectable const& /* parameter */, hstring const& /* language */) { - // Returns Visible if the string is _not_ "desktopWallpaper", else returns Collapsed + // Returns a string like "Window: 12345" for the window with ID 12345 const auto& id = winrt::unbox_value_or(value, 0); return winrt::box_value(fmt::format(std::wstring_view(RS_(L"WindowIdPrefix")), id)); } @@ -37,6 +37,7 @@ namespace winrt::TerminalApp::implementation Foundation::IInspectable const& /* parameter */, hstring const& /* language */) { + // Returns the window's name, or "" if a window has no name const auto& name = winrt::unbox_value_or(value, L""); return winrt::box_value(name.empty() ? L"" : name); } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a0fb590c01c..c604b539cba 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -610,9 +610,19 @@ GUID AppHost::_CurrentDesktopGuid() return currentDesktopGuid; } +// Method Description: +// - Called when this window wants _all_ windows to display their +// identification. We'll hop to the BG thread, and raise an event (eventually +// handled by the monarch) to bubble this request to all the Terminal windows. +// Arguments: +// - +// Return Value: +// - winrt::fire_and_forget AppHost::_IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { + // We'll be raising an event that may result in a RPC call to the monarch - + // make sure we're on the background thread, or this will silently fail co_await winrt::resume_background(); if (auto peasant{ _windowManager.CurrentWindow() }) @@ -621,6 +631,13 @@ winrt::fire_and_forget AppHost::_IdentifyWindowsRequested(const winrt::Windows:: } } +// Method Description: +// - Called when the monarch wants us to display our window ID. We'll call down +// to the app layer to display the toast. +// Arguments: +// - +// Return Value: +// - void AppHost::_DisplayWindowId(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { diff --git a/tools/GenerateHeaderForJson.ps1 b/tools/GenerateHeaderForJson.ps1 index f655ba16026..9daff3ae822 100644 --- a/tools/GenerateHeaderForJson.ps1 +++ b/tools/GenerateHeaderForJson.ps1 @@ -23,6 +23,11 @@ Write-Output "// Generated from " | Out-File -FilePath $OutPath -Encoding ASCII $fullPath = Resolve-Path -Path $JsonFile Write-Output $fullPath.Path | Out-File -FilePath $OutPath -Encoding ASCII -Append Write-Output "constexpr std::string_view $($VariableName){ " | Out-File -FilePath $OutPath -Encoding ASCII -Append -NoNewline + +# Write each line escaped on its own, as it's own literal. This file is _very +# big_, so big that it cannot fit in a single string literal :O The compiler is, +# however, smart enough to just concatenate all these literals into one big +# string. $jsonData | foreach { Write-Output "R`"($_`n)`"" | Out-File -FilePath $OutPath -Encoding ASCII -Append } From 8b6e0bca879270947110be73bd8c07ef0e487083 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Mar 2021 13:58:47 -0500 Subject: [PATCH 20/25] I'm overcomplicating this --- src/cascadia/TerminalApp/Toast.cpp | 17 +++++++++++++++++ src/cascadia/TerminalApp/Toast.h | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/src/cascadia/TerminalApp/Toast.cpp b/src/cascadia/TerminalApp/Toast.cpp index d9ea845cadf..fbbac8f12d8 100644 --- a/src/cascadia/TerminalApp/Toast.cpp +++ b/src/cascadia/TerminalApp/Toast.cpp @@ -17,6 +17,15 @@ Toast::Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip) : _timer.Interval(ToastDuration); } +Toast::Toast(const winrt::hstring tipName, + const winrt::Windows::Foundation::TypedEventHandler& onCloseHandler) : + _tipName{ tipName }, + _onCloseHandler{ onCloseHandler }, + _tip{ nullptr } +{ + _timer.Interval(ToastDuration); +} + // Method Description: // - Open() the TeachingTip, and start our timer. When the timer expires, the // tip will be closed. @@ -26,6 +35,14 @@ Toast::Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip) : // - void Toast::Open() { + if (_tip == nullptr) + { + if (!_tipName.empty()) + { + presenter.ShowDialog(FindName(L"AboutDialog").try_as()); + } + } + _tip.IsOpen(true); std::weak_ptr weakThis{ shared_from_this() }; diff --git a/src/cascadia/TerminalApp/Toast.h b/src/cascadia/TerminalApp/Toast.h index 40d3df7cf55..66b9fc756f2 100644 --- a/src/cascadia/TerminalApp/Toast.h +++ b/src/cascadia/TerminalApp/Toast.h @@ -30,9 +30,13 @@ class Toast : public std::enable_shared_from_this { public: Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip); + Toast(const winrt::hstring& tipName, + const winrt::Windows::Foundation::TypedEventHandler& onCloseHandler); void Open(); private: + winrt::hstring _tipName; + winrt::Windows::Foundation::TypedEventHandler _onCloseHandler; winrt::Microsoft::UI::Xaml::Controls::TeachingTip _tip; winrt::Windows::UI::Xaml::DispatcherTimer _timer; }; From 7c775d5fa39dbb1a1212a0b1a934c8e6e7ee3f6b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Mar 2021 13:59:01 -0500 Subject: [PATCH 21/25] Revert "I'm overcomplicating this" This reverts commit 8b6e0bca879270947110be73bd8c07ef0e487083. --- src/cascadia/TerminalApp/Toast.cpp | 17 ----------------- src/cascadia/TerminalApp/Toast.h | 4 ---- 2 files changed, 21 deletions(-) diff --git a/src/cascadia/TerminalApp/Toast.cpp b/src/cascadia/TerminalApp/Toast.cpp index fbbac8f12d8..d9ea845cadf 100644 --- a/src/cascadia/TerminalApp/Toast.cpp +++ b/src/cascadia/TerminalApp/Toast.cpp @@ -17,15 +17,6 @@ Toast::Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip) : _timer.Interval(ToastDuration); } -Toast::Toast(const winrt::hstring tipName, - const winrt::Windows::Foundation::TypedEventHandler& onCloseHandler) : - _tipName{ tipName }, - _onCloseHandler{ onCloseHandler }, - _tip{ nullptr } -{ - _timer.Interval(ToastDuration); -} - // Method Description: // - Open() the TeachingTip, and start our timer. When the timer expires, the // tip will be closed. @@ -35,14 +26,6 @@ Toast::Toast(const winrt::hstring tipName, // - void Toast::Open() { - if (_tip == nullptr) - { - if (!_tipName.empty()) - { - presenter.ShowDialog(FindName(L"AboutDialog").try_as()); - } - } - _tip.IsOpen(true); std::weak_ptr weakThis{ shared_from_this() }; diff --git a/src/cascadia/TerminalApp/Toast.h b/src/cascadia/TerminalApp/Toast.h index 66b9fc756f2..40d3df7cf55 100644 --- a/src/cascadia/TerminalApp/Toast.h +++ b/src/cascadia/TerminalApp/Toast.h @@ -30,13 +30,9 @@ class Toast : public std::enable_shared_from_this { public: Toast(const winrt::Microsoft::UI::Xaml::Controls::TeachingTip& tip); - Toast(const winrt::hstring& tipName, - const winrt::Windows::Foundation::TypedEventHandler& onCloseHandler); void Open(); private: - winrt::hstring _tipName; - winrt::Windows::Foundation::TypedEventHandler _onCloseHandler; winrt::Microsoft::UI::Xaml::Controls::TeachingTip _tip; winrt::Windows::UI::Xaml::DispatcherTimer _timer; }; From b1b94f63b065e691477a7effba7a8e5ed5c7c592 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Mar 2021 16:23:08 -0500 Subject: [PATCH 22/25] Mainly: delayload the TeachingTip --- src/cascadia/TerminalApp/TerminalPage.cpp | 36 +++++++++++++--------- src/cascadia/TerminalApp/TerminalPage.h | 2 +- src/cascadia/TerminalApp/TerminalPage.xaml | 1 + tools/GenerateHeaderForJson.ps1 | 2 +- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index fd74fa369e5..41ed47a6148 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -274,17 +274,9 @@ namespace winrt::TerminalApp::implementation _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); - // This lambda is a local because there will be more toasts in the - // future that will need the same logic. - auto safeRefocus = [weakThis{ get_weak() }](auto&&, auto&&) { - if (auto page{ weakThis.get() }) - { - page->_FocusActiveControl(nullptr, nullptr); - } - }; - - _windowIdToast = std::make_unique(WindowIdToast()); - WindowIdToast().Closed(safeRefocus); + // DON'T set up Toasts/TeachingTips here. They should be loaded and + // initialized the first time they're opened, in whatever method opens + // them. // Setup mouse vanish attributes SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &_shouldMouseVanish, false); @@ -3025,8 +3017,8 @@ namespace winrt::TerminalApp::implementation return {}; } - void TerminalPage::_FocusActiveControl(const IInspectable& /*sender*/, - const RoutedEventArgs& /*eventArgs*/) + void TerminalPage::_FocusActiveControl(IInspectable /*sender*/, + IInspectable /*eventArgs*/) { // We don't want to set focus on the tab if fly-out is open as it will be closed // TODO GH#5400: consider checking we are not in the opening state, by hooking both Opening and Open events @@ -3375,7 +3367,23 @@ namespace winrt::TerminalApp::implementation co_await winrt::resume_foreground(Dispatcher()); if (auto page{ weakThis.get() }) { - page->_windowIdToast->Open(); + // If we haven't ever loaded the TeachingTip, then do so now and + // create the toast for it. + if (page->_windowIdToast == nullptr) + { + if (MUX::Controls::TeachingTip tip{ page->FindName(L"WindowIdToast").try_as() }) + { + page->_windowIdToast = std::make_shared(tip); + // Make sure to use the weak ref when setting up this + // callback. + tip.Closed({ page->get_weak(), &TerminalPage::_FocusActiveControl }); + } + } + + if (page->_windowIdToast != nullptr) + { + page->_windowIdToast->Open(); + } } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 2c2246d2059..cb4325dd096 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -268,7 +268,7 @@ namespace winrt::TerminalApp::implementation void _CompleteInitialization(); - void _FocusActiveControl(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + void _FocusActiveControl(IInspectable sender, IInspectable eventArgs); void _UnZoomIfNeeded(); diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 36528c070ea..48bcedc92a0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -144,6 +144,7 @@ the MIT License. See LICENSE in the project root for license information. --> dismiss itself if the window is unfocused (In Xaml Islands). This is tracked by MUX#4382--> diff --git a/tools/GenerateHeaderForJson.ps1 b/tools/GenerateHeaderForJson.ps1 index 9daff3ae822..1116275e097 100644 --- a/tools/GenerateHeaderForJson.ps1 +++ b/tools/GenerateHeaderForJson.ps1 @@ -22,7 +22,7 @@ Write-Output "// THIS IS AN AUTO-GENERATED FILE" | Out-File -FilePath $OutPath - Write-Output "// Generated from " | Out-File -FilePath $OutPath -Encoding ASCII -Append -NoNewline $fullPath = Resolve-Path -Path $JsonFile Write-Output $fullPath.Path | Out-File -FilePath $OutPath -Encoding ASCII -Append -Write-Output "constexpr std::string_view $($VariableName){ " | Out-File -FilePath $OutPath -Encoding ASCII -Append -NoNewline +Write-Output "constexpr std::string_view $($VariableName){ " | Out-File -FilePath $OutPath -Encoding ASCII -Append # Write each line escaped on its own, as it's own literal. This file is _very # big_, so big that it cannot fit in a single string literal :O The compiler is, From 31cb1b7a294967e03a8b97a7cc240d6f5cdce510 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 24 Mar 2021 17:02:33 -0500 Subject: [PATCH 23/25] This is the thing dustin suggested, and I like it --- src/cascadia/Remoting/Monarch.cpp | 54 ++++++++++++++----- src/cascadia/Remoting/Monarch.h | 2 + .../UnitTests_Remoting/RemotingTests.cpp | 4 ++ 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ebf6703ef3a..5fd86a251d7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -574,35 +574,61 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } // Method Description: - // - This is an event handler for the IdentifyWindowsRequested event. A - // Peasant may raise that event if they want _all_ windows to identify - // themselves. - // - This will tell each and every peasant to identify themselves. This will - // eventually propagate down to TerminalPage::IdentifyWindow. + // - Helper for doing something on each and every peasant, with no regard + // for if the peasant is living or dead. + // - We'll try calling callback on every peasant. + // - If any single peasant is dead, then we'll call errorCallback, and move on. + // - We're taking an errorCallback here, because the thing we usually want + // to do is TraceLog a message, but TraceLoggingWrite is actually a macro + // that _requires_ the second arg to be a string literal. It can't just be + // a variable. // Arguments: - // - + // - callback: The function to call on each peasant + // - errorCallback: The function to call if a peasant is dead. // Return Value: // - - void Monarch::_identifyWindows(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) + void Monarch::_forAllPeasantsIgnoringTheDead(std::function callback, + std::function errorCallback) { - // Notify all the peasants to display their ID. for (const auto& [id, p] : _peasants) { try { - p.DisplayWindowId(); + callback(p, id); } catch (...) { LOG_CAUGHT_EXCEPTION(); // If this fails, we don't _really_ care. Just move on to the // next one. Someone else will clean up the dead peasant. - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_identifyWindows_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + errorCallback(id); } } } + + // Method Description: + // - This is an event handler for the IdentifyWindowsRequested event. A + // Peasant may raise that event if they want _all_ windows to identify + // themselves. + // - This will tell each and every peasant to identify themselves. This will + // eventually propagate down to TerminalPage::IdentifyWindow. + // Arguments: + // - + // Return Value: + // - + void Monarch::_identifyWindows(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + // Notify all the peasants to display their ID. + auto callback = [](auto&& p, auto&& /*id*/) { + p.DisplayWindowId(); + }; + auto onError = [](auto&& id) { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_identifyWindows_Failed", + TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + }; + _forAllPeasantsIgnoringTheDead(callback, onError); + } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 51c59f363ac..796c6aa07d9 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -77,6 +77,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _identifyWindows(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _forAllPeasantsIgnoringTheDead(std::function callback, + std::function errorCallback); friend class RemotingUnitTests::RemotingTests; }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 5523d64b18b..d8ea2921b0a 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -55,10 +55,14 @@ namespace RemotingUnitTests uint64_t GetPID() { throw winrt::hresult_error{}; }; bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { throw winrt::hresult_error{}; } + void RequestIdentifyWindows() { throw winrt::hresult_error{}; }; + void DisplayWindowId() { throw winrt::hresult_error{}; }; Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; } 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); + TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); }; class RemotingTests From 5904c58c1a3acce1c9e8217a0a5b26c3868c3847 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 26 Mar 2021 16:32:58 -0500 Subject: [PATCH 24/25] This will make @dhowett happy --- .../Resources/en-US/Resources.resw | 10 +++- .../TerminalApp/TerminalAppLib.vcxproj | 8 --- src/cascadia/TerminalApp/TerminalPage.cpp | 57 +++++++++++++++++++ src/cascadia/TerminalApp/TerminalPage.h | 14 ++++- src/cascadia/TerminalApp/TerminalPage.idl | 14 +---- src/cascadia/TerminalApp/TerminalPage.xaml | 11 +--- .../TerminalApp/WindowNameConverter.cpp | 52 ----------------- .../TerminalApp/WindowNameConverter.h | 11 ---- 8 files changed, 80 insertions(+), 97 deletions(-) delete mode 100644 src/cascadia/TerminalApp/WindowNameConverter.cpp delete mode 100644 src/cascadia/TerminalApp/WindowNameConverter.h diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 473690af98a..51a69c4ba0f 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -588,9 +588,13 @@ More options - - Window: {} - {} will be replaced with a number identifying this window + + Window + This is displayed as a label for a number, like "Window: 10" + + + unnamed window + text used to identify when a window hasn't been assigned a name by the user Maximize diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 665058a58df..f368382268b 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -78,10 +78,6 @@ MinMaxCloseControl.xaml - - TerminalPage.idl - Code - PaletteItemTemplateSelector.idl Code @@ -157,10 +153,6 @@ MinMaxCloseControl.xaml - - TerminalPage.idl - Code - PaletteItemTemplateSelector.idl Code diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 41ed47a6148..89f9214134c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3387,4 +3387,61 @@ 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; + } + void TerminalPage::WindowName(const winrt::hstring& value) + { + if (_WindowName != value) + { + _WindowName = value; + _PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"WindowNameForDisplay" }); + } + } + + // 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, 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 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; + } } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index cb4325dd096..209a0cfd32d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -86,9 +86,17 @@ 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; + 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; + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - WINRT_OBSERVABLE_PROPERTY(winrt::hstring, WindowName, _PropertyChangedHandlers); - WINRT_OBSERVABLE_PROPERTY(uint64_t, WindowId, _PropertyChangedHandlers); // -------------------------------- WinRT Events --------------------------------- TYPED_EVENT(TitleChanged, IInspectable, winrt::hstring); @@ -130,6 +138,8 @@ namespace winrt::TerminalApp::implementation bool _isInFocusMode{ false }; bool _isFullscreen{ false }; bool _isAlwaysOnTop{ false }; + winrt::hstring _WindowName{}; + uint64_t _WindowId{ 0 }; bool _rearranging; std::optional _rearrangeFrom; diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 413e9d70ce2..d725d67ca73 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -10,18 +10,6 @@ namespace TerminalApp Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); }; - runtimeclass WindowIdConverter : [default] Windows.UI.Xaml.Data.IValueConverter - { - WindowIdConverter(); - }; - - - runtimeclass WindowNameConverter : [default] Windows.UI.Xaml.Data.IValueConverter - { - WindowNameConverter(); - }; - - [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged { TerminalPage(); @@ -37,6 +25,8 @@ namespace TerminalApp void IdentifyWindow(); String WindowName; UInt64 WindowId; + String WindowNameForDisplay { get; }; + String WindowIdForDisplay { 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 48bcedc92a0..ff2397eaf78 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -10,13 +10,6 @@ the MIT License. See LICENSE in the project root for license information. --> xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - - - - - - @@ -146,8 +139,8 @@ the MIT License. See LICENSE in the project root for license information. --> + Title="{x:Bind WindowIdForDisplay}" + Subtitle="{x:Bind WindowNameForDisplay}"> diff --git a/src/cascadia/TerminalApp/WindowNameConverter.cpp b/src/cascadia/TerminalApp/WindowNameConverter.cpp deleted file mode 100644 index b782a918735..00000000000 --- a/src/cascadia/TerminalApp/WindowNameConverter.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "WindowNameConverter.h" - -#include "WindowIdConverter.g.cpp" -#include "WindowNameConverter.g.cpp" -#include - -using namespace winrt::Windows; -using namespace winrt::Windows::UI::Xaml; -using namespace winrt::Windows::UI::Text; - -namespace winrt::TerminalApp::implementation -{ - Foundation::IInspectable WindowIdConverter::Convert(Foundation::IInspectable const& value, - Windows::UI::Xaml::Interop::TypeName const& /* targetType */, - Foundation::IInspectable const& /* parameter */, - hstring const& /* language */) - { - // Returns a string like "Window: 12345" for the window with ID 12345 - const auto& id = winrt::unbox_value_or(value, 0); - return winrt::box_value(fmt::format(std::wstring_view(RS_(L"WindowIdPrefix")), id)); - } - - Foundation::IInspectable WindowIdConverter::ConvertBack(Foundation::IInspectable const& /*value*/, - Windows::UI::Xaml::Interop::TypeName const& /* targetType */, - Foundation::IInspectable const& /*parameter*/, - hstring const& /* language */) - { - throw hresult_not_implemented(); - } - - Foundation::IInspectable WindowNameConverter::Convert(Foundation::IInspectable const& value, - Windows::UI::Xaml::Interop::TypeName const& /* targetType */, - Foundation::IInspectable const& /* parameter */, - hstring const& /* language */) - { - // Returns the window's name, or "" if a window has no name - const auto& name = winrt::unbox_value_or(value, L""); - return winrt::box_value(name.empty() ? L"" : name); - } - - Foundation::IInspectable WindowNameConverter::ConvertBack(Foundation::IInspectable const& /*value*/, - Windows::UI::Xaml::Interop::TypeName const& /* targetType */, - Foundation::IInspectable const& /*parameter*/, - hstring const& /* language */) - { - throw hresult_not_implemented(); - } -} diff --git a/src/cascadia/TerminalApp/WindowNameConverter.h b/src/cascadia/TerminalApp/WindowNameConverter.h deleted file mode 100644 index 2f2cb229941..00000000000 --- a/src/cascadia/TerminalApp/WindowNameConverter.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -#include "WindowIdConverter.g.h" -#include "WindowNameConverter.g.h" -#include "../inc/cppwinrt_utils.h" - -DECLARE_CONVERTER(winrt::TerminalApp, WindowIdConverter); -DECLARE_CONVERTER(winrt::TerminalApp, WindowNameConverter); From ff2ce4bd38788d0efe63920c9253739020146517 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 30 Mar 2021 06:41:14 -0500 Subject: [PATCH 25/25] runformat --- src/cascadia/TerminalApp/TerminalPage.xaml | 262 ++++++++++----------- 1 file changed, 131 insertions(+), 131 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index 011dd06cf83..20bed6717ff 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -1,131 +1,131 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +