From 3bef7bbb389f7b048983bac8dda32af0e662a9fb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 07:59:52 -0600 Subject: [PATCH 01/88] Get all the projects created and hooked up to the sln --- OpenConsole.sln | 105 +++++++ .../LocalTests_Remoting.def | 3 + .../Remoting.LocalTests.vcxproj | 78 +++++ .../LocalTests_Remoting/RemotingTests.cpp | 295 ++++++++++++++++++ src/cascadia/LocalTests_Remoting/pch.cpp | 4 + src/cascadia/LocalTests_Remoting/pch.h | 40 +++ .../Microsoft.Terminal.RemotingLib.vcxproj | 87 ++++++ .../Remoting/Resources/en-US/Resources.resw | 120 +++++++ .../dll/Microsoft.Terminal.Remoting.def | 3 + .../dll/Microsoft.Terminal.Remoting.vcxproj | 74 +++++ src/cascadia/Remoting/packages.config | 4 + src/cascadia/Remoting/pch.cpp | 4 + src/cascadia/Remoting/pch.h | 48 +++ .../WindowsTerminal/WindowsTerminal.vcxproj | 5 +- 14 files changed, 868 insertions(+), 2 deletions(-) create mode 100644 src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def create mode 100644 src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj create mode 100644 src/cascadia/LocalTests_Remoting/RemotingTests.cpp create mode 100644 src/cascadia/LocalTests_Remoting/pch.cpp create mode 100644 src/cascadia/LocalTests_Remoting/pch.h create mode 100644 src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj create mode 100644 src/cascadia/Remoting/Resources/en-US/Resources.resw create mode 100644 src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def create mode 100644 src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj create mode 100644 src/cascadia/Remoting/packages.config create mode 100644 src/cascadia/Remoting/pch.cpp create mode 100644 src/cascadia/Remoting/pch.h diff --git a/OpenConsole.sln b/OpenConsole.sln index 364216a7cbf..2d5c1deb4f5 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -323,6 +323,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} EndProjectSection EndProject + + Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} @@ -341,6 +343,25 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} EndProjectSection EndProject + + +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting.Lib", "src\cascadia\Remoting\Microsoft.Terminal.RemotingLib.vcxproj", "{43ce4ce5-0010-4b99-9569-672670d26e26}" +EndProject + +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting", "src\cascadia\Remoting\dll\Microsoft.Terminal.Remoting.vcxproj", "{27b5aaeb-a548-44cf-9777-f8baa32af7ae}" + ProjectSection(ProjectDependencies) = postProject + {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + EndProjectSection +EndProject + +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68a10cd3-aa64-465b-af5f-ed4e9700543c}" + ProjectSection(ProjectDependencies) = postProject + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {27b5aaeb-a548-44cf-9777-f8baa32af7ae} + {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + EndProjectSection +EndProject + + Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2137,6 +2158,87 @@ Global {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.Build.0 = Release|x64 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.Build.0 = Release|Win32 + + + + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x64.ActiveCfg = Release|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.Build.0 = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.Build.0 = Debug|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.ActiveCfg = Debug|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.Build.0 = Debug|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.Build.0 = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|Any CPU.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.ActiveCfg = Release|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.Build.0 = Release|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.ActiveCfg = Release|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.Build.0 = Release|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.Build.0 = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x64.ActiveCfg = Release|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.Build.0 = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.Build.0 = Debug|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.ActiveCfg = Debug|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.Build.0 = Debug|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.Build.0 = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|Any CPU.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.ActiveCfg = Release|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.Build.0 = Release|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.ActiveCfg = Release|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.Build.0 = Release|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.Build.0 = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.Build.0 = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.Build.0 = Debug|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.ActiveCfg = Debug|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.Build.0 = Debug|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.Build.0 = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|Any CPU.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.ActiveCfg = Release|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.Build.0 = Release|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.ActiveCfg = Release|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.Build.0 = Release|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2221,6 +2323,9 @@ Global {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {43ce4ce5-0010-4b99-9569-672670d26e26} = {59840756-302F-44DF-AA47-441A9D673202} + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {59840756-302F-44DF-AA47-441A9D673202} + {68a10cd3-aa64-465b-af5f-ed4e9700543c} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def b/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def new file mode 100644 index 00000000000..8c1a02932d0 --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj b/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj new file mode 100644 index 00000000000..c91a5d003e8 --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj @@ -0,0 +1,78 @@ + + + + + + + {68a10cd3-aa64-465b-af5f-ed4e9700543c} + Win32Proj + RemotingLocalTests + LocalTests_Remoting + Remoting.LocalTests + DynamicLibrary + 10.0.18362.0 + 10.0.18362.0 + true + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + ..;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\jsoncpp\json;$(OpenConsoleDir)src\inc;$(OpenConsoleDir)src\inc\test;$(WinRT_IncludePath)\..\cppwinrt\winrt;"$(OpenConsoleDir)\src\cascadia\Remoting\Generated Files";%(AdditionalIncludeDirectories) + pch.h + + + 4702;%(DisableSpecificWarnings) + + + onecoreuap.lib;%(AdditionalDependencies) + + + + + true + true + + + + + + + + diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp new file mode 100644 index 00000000000..60491de9cbf --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "../TerminalSettingsModel/ColorScheme.h" +#include "../TerminalSettingsModel/CascadiaSettings.h" +#include "JsonTestClass.h" +#include "TestUtils.h" +#include +#include "../ut_app/TestDynamicProfileGenerator.h" + +using namespace Microsoft::Console; +using namespace WEX::Logging; +using namespace WEX::TestExecution; +using namespace WEX::Common; +using namespace winrt::Microsoft::Terminal::Settings::Model; +using namespace winrt::Microsoft::Terminal::TerminalControl; + +namespace SettingsModelLocalTests +{ + // TODO:microsoft/terminal#3838: + // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for + // an updated TAEF that will let us install framework packages when the test + // package is deployed. Until then, these tests won't deploy in CI. + + class SerializationTests : public JsonTestClass + { + // Use a custom AppxManifest to ensure that we can activate winrt types + // from our test. This property will tell taef to manually use this as + // the AppxManifest for this test class. + // This does not yet work for anything XAML-y. See TabTests.cpp for more + // details on that. + BEGIN_TEST_CLASS(SerializationTests) + TEST_CLASS_PROPERTY(L"RunAs", L"UAP") + TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") + END_TEST_CLASS() + + TEST_METHOD(GlobalSettings); + TEST_METHOD(Profile); + TEST_METHOD(ColorScheme); + TEST_METHOD(CascadiaSettings); + + TEST_CLASS_SETUP(ClassSetup) + { + InitializeJsonReader(); + InitializeJsonWriter(); + return true; + } + + private: + // Method Description: + // - deserializes and reserializes a json string representing a settings object model of type T + // - verifies that the generated json string matches the provided one + // Template Types: + // - : The type of Settings Model object to generate (must be impl type) + // Arguments: + // - jsonString - JSON string we're performing the test on + // Return Value: + // - the JsonObject representing this instance + template + void RoundtripTest(const std::string& jsonString) + { + const auto json{ VerifyParseSucceeded(jsonString) }; + const auto settings{ T::FromJson(json) }; + const auto result{ settings->ToJson() }; + + // Compare toString(json) instead of jsonString here. + // The toString writes the json out alphabetically. + // This trick allows jsonString to _not_ have to be + // written alphabetically. + VERIFY_ARE_EQUAL(toString(json), toString(result)); + } + }; + + void SerializationTests::GlobalSettings() + { + const std::string globalsString{ R"( + { + "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", + + "initialRows": 30, + "initialCols": 120, + "initialPosition": ",", + "launchMode": "default", + "alwaysOnTop": false, + + "copyOnSelect": false, + "copyFormatting": "all", + "wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502", + + "alwaysShowTabs": true, + "showTabsInTitlebar": true, + "showTerminalTitleInTitlebar": true, + "tabWidthMode": "equal", + "tabSwitcherMode": "mru", + + "startOnUserLogin": false, + "theme": "system", + "snapToGridOnResize": true, + "disableAnimations": false, + + "confirmCloseAllTabs": true, + "largePasteWarning": true, + "multiLinePasteWarning": true, + + "experimental.input.forceVT": false, + "experimental.rendering.forceFullRepaint": false, + "experimental.rendering.software": false + })" }; + + const std::string smallGlobalsString{ R"( + { + "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" + })" }; + + RoundtripTest(globalsString); + RoundtripTest(smallGlobalsString); + } + + void SerializationTests::Profile() + { + const std::string profileString{ R"( + { + "name": "Windows PowerShell", + "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", + + "commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "startingDirectory": "%USERPROFILE%", + + "icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png", + "hidden": false, + + "tabTitle": "Cool Tab", + "suppressApplicationTitle": false, + + "fontFace": "Cascadia Mono", + "fontSize": 12, + "fontWeight": "normal", + "padding": "8, 8, 8, 8", + "antialiasingMode": "grayscale", + + "cursorShape": "bar", + "cursorColor": "#CCBBAA", + "cursorHeight": 10, + + "altGrAliasing": true, + + "colorScheme": "Campbell", + "tabColor": "#0C0C0C", + "foreground": "#AABBCC", + "background": "#BBCCAA", + "selectionBackground": "#CCAABB", + + "useAcrylic": false, + "acrylicOpacity": 0.5, + + "backgroundImage": "made_you_look.jpeg", + "backgroundImageStretchMode": "uniformToFill", + "backgroundImageAlignment": "center", + "backgroundImageOpacity": 1.0, + + "scrollbarState": "visible", + "snapOnInput": true, + "historySize": 9001, + + "closeOnExit": "graceful", + "experimental.retroTerminalEffect": false + })" }; + + const std::string smallProfileString{ R"( + { + "name": "Custom Profile" + })" }; + + // Setting "tabColor" to null tests two things: + // - null should count as an explicit user-set value, not falling back to the parent's value + // - null should be acceptable even though we're working with colors + const std::string weirdProfileString{ R"( + { + "name": "Weird Profile", + "tabColor": null, + "foreground": null, + "source": "local" + })" }; + + RoundtripTest(profileString); + RoundtripTest(smallProfileString); + RoundtripTest(weirdProfileString); + } + + void SerializationTests::ColorScheme() + { + const std::string schemeString{ R"({ + "name": "Campbell", + + "cursorColor": "#FFFFFF", + "selectionBackground": "#131313", + + "background": "#0C0C0C", + "foreground": "#F2F2F2", + + "black": "#0C0C0C", + "blue": "#0037DA", + "cyan": "#3A96DD", + "green": "#13A10E", + "purple": "#881798", + "red": "#C50F1F", + "white": "#CCCCCC", + "yellow": "#C19C00", + "brightBlack": "#767676", + "brightBlue": "#3B78FF", + "brightCyan": "#61D6D6", + "brightGreen": "#16C60C", + "brightPurple": "#B4009E", + "brightRed": "#E74856", + "brightWhite": "#F2F2F2", + "brightYellow": "#F9F1A5" + })" }; + + RoundtripTest(schemeString); + } + + void SerializationTests::CascadiaSettings() + { + const std::string settingsString{ R"({ + "$schema": "https://aka.ms/terminal-profiles-schema", + "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", + + "profiles": { + "defaults": { + "fontFace": "Zamora Code" + }, + "list": [ + { + "fontFace": "Cascadia Code", + "guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", + "name": "HowettShell" + }, + { + "hidden": true, + "name": "BhojwaniShell" + }, + { + "antialiasingMode": "aliased", + "name": "NiksaShell" + } + ] + }, + "schemes": [ + { + "name": "Cinnamon Roll", + + "cursorColor": "#FFFFFD", + "selectionBackground": "#FFFFFF", + + "background": "#3C0315", + "foreground": "#FFFFFD", + + "black": "#282A2E", + "blue": "#0170C5", + "cyan": "#3F8D83", + "green": "#76AB23", + "purple": "#7D498F", + "red": "#BD0940", + "white": "#FFFFFD", + "yellow": "#E0DE48", + "brightBlack": "#676E7A", + "brightBlue": "#5C98C5", + "brightCyan": "#8ABEB7", + "brightGreen": "#B5D680", + "brightPurple": "#AC79BB", + "brightRed": "#BD6D85", + "brightWhite": "#FFFFFD", + "brightYellow": "#FFFD76" + } + ], + "actions": [ + {"command": { "action": "renameTab","input": "Liang Tab" },"keys": "ctrl+t" } + ], + "keybindings": [ + { "command": { "action": "sendInput","input": "VT Griese Mode" },"keys": "ctrl+k" } + ] + })" }; + + auto settings{ winrt::make_self(false) }; + settings->_ParseJsonString(settingsString, false); + settings->_ApplyDefaultsFromUserSettings(); + settings->LayerJson(settings->_userSettings); + settings->_ValidateSettings(); + + const auto result{ settings->ToJson() }; + VERIFY_ARE_EQUAL(toString(settings->_userSettings), toString(result)); + } +} diff --git a/src/cascadia/LocalTests_Remoting/pch.cpp b/src/cascadia/LocalTests_Remoting/pch.cpp new file mode 100644 index 00000000000..3c27d44d570 --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/src/cascadia/LocalTests_Remoting/pch.h b/src/cascadia/LocalTests_Remoting/pch.h new file mode 100644 index 00000000000..a88cde20a9c --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/pch.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ + +#pragma once + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#define BLOCK_TIL +// This includes support libraries from the CRT, STL, WIL, and GSL +#include "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 +#include +#include + +#include +#include +#include "consoletaeftemplates.hpp" + +#include +#include +#include +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" + +// Common includes for most tests: +#include "../../inc/argb.h" +#include "../../inc/conattrs.hpp" +#include "../../types/inc/utils.hpp" +#include "../../inc/DefaultSettings.h" diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj new file mode 100644 index 00000000000..bc034434b95 --- /dev/null +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -0,0 +1,87 @@ + + + + {43ce4ce5-0010-4b99-9569-672670d26e26} + Win32Proj + Microsoft.Terminal.Remoting.Lib + Microsoft.Terminal.Remoting + Microsoft.Terminal.Remoting.Lib + 10.0.17763.0 + StaticLibrary + Console + true + + + + + + + + Monarch.idl + + + + + Peasant.idl + + + + + + Monarch.idl + + + Create + + + Peasant.idl + + + + + + + + + + + + + + + + + + + + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} + false + + + + + + + pch.h + ..;$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); + + 4702;%(DisableSpecificWarnings) + + + WindowsApp.lib;user32.lib;shell32.lib;%(AdditionalDependencies) + + + false + + + + + + + diff --git a/src/cascadia/Remoting/Resources/en-US/Resources.resw b/src/cascadia/Remoting/Resources/en-US/Resources.resw new file mode 100644 index 00000000000..f4af46df557 --- /dev/null +++ b/src/cascadia/Remoting/Resources/en-US/Resources.resw @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def new file mode 100644 index 00000000000..8c1a02932d0 --- /dev/null +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj new file mode 100644 index 00000000000..e5077043835 --- /dev/null +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -0,0 +1,74 @@ + + + + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} + Microsoft.Terminal.Remoting + Microsoft.Terminal.Remoting + + + DynamicLibrary + Console + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {18D09A24-8240-42D6-8CB6-236EEE820263} + + + + true + true + + + + + + + $(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); + + + User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) + + /INCLUDE:_DllMain@12 + /INCLUDE:DllMain + + + + false + + + + diff --git a/src/cascadia/Remoting/packages.config b/src/cascadia/Remoting/packages.config new file mode 100644 index 00000000000..8a013cf32b2 --- /dev/null +++ b/src/cascadia/Remoting/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/src/cascadia/Remoting/pch.cpp b/src/cascadia/Remoting/pch.cpp new file mode 100644 index 00000000000..3c27d44d570 --- /dev/null +++ b/src/cascadia/Remoting/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/src/cascadia/Remoting/pch.h b/src/cascadia/Remoting/pch.h new file mode 100644 index 00000000000..165aefe02d8 --- /dev/null +++ b/src/cascadia/Remoting/pch.h @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// pch.h +// Header for platform projection include files +// + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMCX +#define NOHELP +#define NOCOMM + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#define BLOCK_TIL +#include +// 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 + +#include + +#include + +#include +#include +#include + +#include + +// Including TraceLogging essentials for the binary +#include +#include +TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider); +#include +#include + +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index c66d6c80333..aa45f987aba 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -77,6 +77,7 @@ + @@ -98,7 +99,7 @@ - + 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}. @@ -106,7 +107,7 @@ - + From 1f52d3583390e300b8253d605b5f79adac5e921b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 08:08:55 -0600 Subject: [PATCH 02/88] Yank all the M/P files, this builds?! --- src/cascadia/Remoting/Monarch.cpp | 186 ++++++++++++++++++ src/cascadia/Remoting/Monarch.h | 55 ++++++ src/cascadia/Remoting/Monarch.idl | 12 ++ src/cascadia/Remoting/MonarchFactory.h | 49 +++++ src/cascadia/Remoting/Peasant.cpp | 51 +++++ src/cascadia/Remoting/Peasant.h | 31 +++ src/cascadia/Remoting/Peasant.idl | 18 ++ .../dll/Microsoft.Terminal.Remoting.vcxproj | 2 +- 8 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 src/cascadia/Remoting/Monarch.cpp create mode 100644 src/cascadia/Remoting/Monarch.h create mode 100644 src/cascadia/Remoting/Monarch.idl create mode 100644 src/cascadia/Remoting/MonarchFactory.h create mode 100644 src/cascadia/Remoting/Peasant.cpp create mode 100644 src/cascadia/Remoting/Peasant.h create mode 100644 src/cascadia/Remoting/Peasant.idl diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp new file mode 100644 index 00000000000..6a74171004f --- /dev/null +++ b/src/cascadia/Remoting/Monarch.cpp @@ -0,0 +1,186 @@ +#include "pch.h" +#include "Monarch.h" + +#include "Monarch.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + Monarch::Monarch() + { + printf("Instantiated a Monarch\n"); + } + + Monarch::~Monarch() + { + printf("~Monarch()\n"); + } + + uint64_t Monarch::GetPID() + { + return GetCurrentProcessId(); + } + + uint64_t Monarch::AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant) + { + // TODO: This whole algorithm is terrible. There's gotta be a better way + // of finding the first opening in a non-consecutive map of int->object + auto providedID = peasant.GetID(); + + if (providedID == 0) + { + peasant.AssignID(_nextPeasantID++); + printf("Assigned the peasant the ID %lld\n", peasant.GetID()); + } + else + { + printf("Peasant already had an ID, %lld\n", peasant.GetID()); + _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; + } + auto newPeasantsId = peasant.GetID(); + _peasants[newPeasantsId] = peasant; + _setMostRecentPeasant(newPeasantsId); + printf("(the next new peasant will get the ID %lld)\n", _nextPeasantID); + + peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + + return newPeasantsId; + } + + void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + if (auto peasant{ sender.try_as() }) + { + auto theirID = peasant.GetID(); + _setMostRecentPeasant(theirID); + } + } + + winrt::Microsoft::Terminal::Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) + { + auto peasantSearch = _peasants.find(peasantID); + return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + } + + void Monarch::_setMostRecentPeasant(const uint64_t peasantID) + { + _mostRecentPeasant = peasantID; + printf("\x1b[90mThe most recent peasant is now \x1b[m#%llu\n", _mostRecentPeasant); + } + + void Monarch::SetSelfID(const uint64_t selfID) + { + this->_thisPeasantID = selfID; + // Right now, the monarch assumes the role of the most recent + // window. If the monarch dies, and a new monarch takes over, then the + // entire stack of MRU windows will go with it. That's not what you + // want! + // + // In the real app, we'll have each window also track the timestamp it + // was activated at, and the monarch will cache these. So a new monarch + // could re-query these last activated timestamps, and reconstruct the + // MRU stack. + // + // This is a sample though, and we're not too worried about complete + // correctness here. + _setMostRecentPeasant(_thisPeasantID); + } + + bool Monarch::ProposeCommandline(array_view args, winrt::hstring cwd) + { + auto argsProcessed = 0; + std::wstring fullCmdline; + for (const auto& arg : args) + { + fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + fullCmdline += L" "; + } + wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); + wprintf(fullCmdline.c_str()); + wprintf(L"\"\n"); + + bool createNewWindow = true; + + if (args.size() >= 3) + { + // We'll need three args at least - [WindowsTerminal.exe, -s, + // id] to be able to have a session ID passed on the commandline. + + if (args[1] == L"-s" || args[1] == L"--session") + { + auto sessionId = std::stoi({ args[2].data(), args[2].size() }); + printf("Found a commandline intended for session %d\n", sessionId); + if (sessionId < 0) + { + printf("That certainly isn't a valid ID, they should make a new window.\n"); + createNewWindow = true; + } + else if (sessionId == 0) + { + printf("Session 0 is actually #%llu\n", _mostRecentPeasant); + if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) + { + mruPeasant.ExecuteCommandline(args, cwd); + createNewWindow = false; + } + } + else + { + if (auto otherPeasant = _getPeasant(sessionId)) + { + otherPeasant.ExecuteCommandline(args, cwd); + createNewWindow = false; + } + else + { + printf("I couldn't find a peasant for that ID, they should make a new window.\n"); + } + } + } + } + else if (_windowingBehavior == WindowingBehavior::UseExisting) + { + if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) + { + mruPeasant.ExecuteCommandline(args, cwd); + createNewWindow = false; + } + } + else + { + printf("They definitely weren't an existing process. They should make a new window.\n"); + } + + return createNewWindow; + } + void Monarch::ToggleWindowingBehavior() + { + switch (_windowingBehavior) + { + case WindowingBehavior::UseNew: + _windowingBehavior = WindowingBehavior::UseExisting; + break; + case WindowingBehavior::UseExisting: + _windowingBehavior = WindowingBehavior::UseNew; + break; + } + + printf("windowingBehavior: "); + switch (_windowingBehavior) + { + case WindowingBehavior::UseNew: + printf("useNew"); + break; + case WindowingBehavior::UseExisting: + printf("useExisting"); + break; + } + printf("\n"); + } + +} diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h new file mode 100644 index 00000000000..653ae51976f --- /dev/null +++ b/src/cascadia/Remoting/Monarch.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Monarch.g.h" +#include "Peasant.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +// {06171993-7eb1-4f3e-85f5-8bdd7386cce3} +constexpr GUID Monarch_clsid{ + 0x06171993, + 0x7eb1, + 0x4f3e, + { 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 } +}; + +enum class WindowingBehavior : uint64_t +{ + UseNew = 0, + UseExisting = 1, +}; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct Monarch : public MonarchT + { + Monarch(); + ~Monarch(); + + uint64_t GetPID(); + + uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); + + void SetSelfID(const uint64_t selfID); + + bool ProposeCommandline(array_view args, winrt::hstring cwd); + void ToggleWindowingBehavior(); + + private: + uint64_t _nextPeasantID{ 1 }; + uint64_t _thisPeasantID{ 0 }; + uint64_t _mostRecentPeasant{ 0 }; + WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew }; + std::unordered_map _peasants; + + winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); + void _setMostRecentPeasant(const uint64_t peasantID); + + void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(Monarch); +} diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl new file mode 100644 index 00000000000..8b228480caa --- /dev/null +++ b/src/cascadia/Remoting/Monarch.idl @@ -0,0 +1,12 @@ +import "Peasant.idl"; + +namespace Microsoft.Terminal.Remoting +{ + [default_interface] runtimeclass Monarch { + Monarch(); + + UInt64 GetPID(); + UInt64 AddPeasant(IPeasant peasant); + Boolean ProposeCommandline(String[] args, String cwd); + }; +} diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h new file mode 100644 index 00000000000..9b912995681 --- /dev/null +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -0,0 +1,49 @@ +#include "pch.h" + +#include "Monarch.h" + +// This seems like a hack, but it works. +// +// This class factory works so that there's only ever one instance of a Monarch +// per-process. Once the first monarch is created, we'll stash it in g_weak. +// Future callers who try to instantiate a Monarch will get the one that's +// already been made. +// +// I'm sure there's a better way to do this with WRL, but I'm not familiar +// enough with WRL to know for sure. + +winrt::weak_ref g_weak{ nullptr }; + +struct MonarchFactory : winrt::implements +{ + MonarchFactory() = default; + + HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept + { + *result = nullptr; + if (outer) + { + return CLASS_E_NOAGGREGATION; + } + + if (!g_weak) + { + // Create a new Monarch instance + auto strong = winrt::make_self(); + + g_weak = (*strong).get_weak(); + return strong.as(iid, result); + } + else + { + // We already instantiated one Monarch, let's just return that one! + auto strong = g_weak.get(); + return strong.as(iid, result); + } + } + + HRESULT __stdcall LockServer(BOOL) noexcept + { + return S_OK; + } +}; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp new file mode 100644 index 00000000000..30b10b5f8f9 --- /dev/null +++ b/src/cascadia/Remoting/Peasant.cpp @@ -0,0 +1,51 @@ +#include "pch.h" +#include "Peasant.h" + +#include "Peasant.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + Peasant::Peasant() + { + } + + void Peasant::AssignID(uint64_t id) + { + _id = id; + } + uint64_t Peasant::GetID() + { + return _id; + } + + uint64_t Peasant::GetPID() + { + return GetCurrentProcessId(); + } + + bool Peasant::ExecuteCommandline(winrt::array_view args, winrt::hstring currentDirectory) + { + auto argsProcessed = 0; + std::wstring fullCmdline; + for (const auto& arg : args) + { + fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + fullCmdline += L" "; + } + wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); + wprintf(fullCmdline.c_str()); + wprintf(L"\"\n"); + return true; + } + + void Peasant::raiseActivatedEvent() + { + _WindowActivatedHandlers(*this, nullptr); + } + +} diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h new file mode 100644 index 00000000000..31da53ff5c5 --- /dev/null +++ b/src/cascadia/Remoting/Peasant.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Peasant.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct Peasant : public PeasantT + { + Peasant(); + + void AssignID(uint64_t id); + uint64_t GetID(); + uint64_t GetPID(); + + bool ExecuteCommandline(winrt::array_view args, + winrt::hstring currentDirectory); + + void raiseActivatedEvent(); + + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + + private: + uint64_t _id{ 0 }; + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(Peasant); +} diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl new file mode 100644 index 00000000000..b351448183a --- /dev/null +++ b/src/cascadia/Remoting/Peasant.idl @@ -0,0 +1,18 @@ + + +namespace Microsoft.Terminal.Remoting +{ + interface IPeasant + { + void AssignID(UInt64 id); + UInt64 GetID(); + UInt64 GetPID(); + Boolean ExecuteCommandline(String[] args, String currentDirectory); + event Windows.Foundation.TypedEventHandler WindowActivated; + }; + + [default_interface] runtimeclass Peasant : IPeasant + { + Peasant(); + }; +} diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj index e5077043835..51c6f62ad18 100644 --- a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -48,7 +48,7 @@ - + true true From 5a9cdc8b0bfda790a70368d8381559628eba554f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 09:51:24 -0600 Subject: [PATCH 03/88] Shockingly, this works, it works elevated, and it works unpackaged --- OpenConsole.sln | 192 +++++++++--------- .../Microsoft.Terminal.RemotingLib.vcxproj | 7 + src/cascadia/Remoting/WindowManager.cpp | 67 ++++++ src/cascadia/Remoting/WindowManager.h | 31 +++ src/cascadia/Remoting/WindowManager.idl | 12 ++ src/cascadia/WindowsTerminal/AppHost.cpp | 41 +++- src/cascadia/WindowsTerminal/AppHost.h | 12 +- src/cascadia/WindowsTerminal/main.cpp | 4 + src/cascadia/WindowsTerminal/pch.h | 5 + 9 files changed, 263 insertions(+), 108 deletions(-) create mode 100644 src/cascadia/Remoting/WindowManager.cpp create mode 100644 src/cascadia/Remoting/WindowManager.h create mode 100644 src/cascadia/Remoting/WindowManager.idl diff --git a/OpenConsole.sln b/OpenConsole.sln index 2d5c1deb4f5..e8a479e2876 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -171,18 +171,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControl", "src\casc EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj", "{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}" ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} + {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} {CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia\TerminalApp\dll\TerminalApp.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} + {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} EndProjectSection @@ -234,8 +235,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\cascadia\TerminalApp\TerminalAppLib.vcxproj", "{CA5CAD1A-9A12-429C-B551-8562EC954746}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} + {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}" @@ -323,8 +324,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} EndProjectSection EndProject - - Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} @@ -343,25 +342,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} EndProjectSection EndProject - - -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting.Lib", "src\cascadia\Remoting\Microsoft.Terminal.RemotingLib.vcxproj", "{43ce4ce5-0010-4b99-9569-672670d26e26}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting.Lib", "src\cascadia\Remoting\Microsoft.Terminal.RemotingLib.vcxproj", "{43CE4CE5-0010-4B99-9569-672670D26E26}" EndProject - -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting", "src\cascadia\Remoting\dll\Microsoft.Terminal.Remoting.vcxproj", "{27b5aaeb-a548-44cf-9777-f8baa32af7ae}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting", "src\cascadia\Remoting\dll\Microsoft.Terminal.Remoting.vcxproj", "{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}" ProjectSection(ProjectDependencies) = postProject - {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} EndProjectSection EndProject - -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68a10cd3-aa64-465b-af5f-ed4e9700543c}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68A10CD3-AA64-465B-AF5F-ED4E9700543C}" ProjectSection(ProjectDependencies) = postProject - {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {27b5aaeb-a548-44cf-9777-f8baa32af7ae} - {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} EndProjectSection EndProject - - Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2158,87 +2151,84 @@ Global {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.Build.0 = Release|x64 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.Build.0 = Release|Win32 - - - - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x64.ActiveCfg = Release|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.Build.0 = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.Build.0 = Debug|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.ActiveCfg = Debug|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.Build.0 = Debug|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.Build.0 = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|Any CPU.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.ActiveCfg = Release|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.Build.0 = Release|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.ActiveCfg = Release|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.Build.0 = Release|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.Build.0 = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x64.ActiveCfg = Release|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.Build.0 = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.Build.0 = Debug|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.ActiveCfg = Debug|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.Build.0 = Debug|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.Build.0 = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|Any CPU.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.ActiveCfg = Release|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.Build.0 = Release|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.ActiveCfg = Release|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.Build.0 = Release|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.Build.0 = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.Build.0 = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.Build.0 = Debug|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.ActiveCfg = Debug|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.Build.0 = Debug|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.Build.0 = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|Any CPU.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.ActiveCfg = Release|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.Build.0 = Release|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.ActiveCfg = Release|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.Build.0 = Release|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.Build.0 = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.Build.0 = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|ARM64.Build.0 = Debug|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x64.ActiveCfg = Debug|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x64.Build.0 = Debug|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x86.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x86.Build.0 = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|Any CPU.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|ARM64.ActiveCfg = Release|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|ARM64.Build.0 = Release|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x64.Build.0 = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x86.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x86.Build.0 = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x64.ActiveCfg = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x86.Build.0 = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|ARM64.Build.0 = Debug|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x64.ActiveCfg = Debug|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x64.Build.0 = Debug|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x86.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x86.Build.0 = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|Any CPU.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|ARM64.ActiveCfg = Release|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|ARM64.Build.0 = Release|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x64.ActiveCfg = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x64.Build.0 = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x86.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x86.Build.0 = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x86.Build.0 = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|ARM64.Build.0 = Debug|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x64.ActiveCfg = Debug|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x64.Build.0 = Debug|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x86.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x86.Build.0 = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|Any CPU.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|ARM64.ActiveCfg = Release|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|ARM64.Build.0 = Release|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x64.ActiveCfg = Release|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x64.Build.0 = Release|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2323,9 +2313,9 @@ Global {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {43ce4ce5-0010-4b99-9569-672670d26e26} = {59840756-302F-44DF-AA47-441A9D673202} - {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {59840756-302F-44DF-AA47-441A9D673202} - {68a10cd3-aa64-465b-af5f-ed4e9700543c} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {43CE4CE5-0010-4B99-9569-672670D26E26} = {59840756-302F-44DF-AA47-441A9D673202} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {59840756-302F-44DF-AA47-441A9D673202} + {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index bc034434b95..ad736bd6d32 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -24,6 +24,9 @@ Peasant.idl + + WindowManager .idl + @@ -36,12 +39,16 @@ Peasant.idl + + WindowManager.idl + + diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp new file mode 100644 index 00000000000..87e2b482b3e --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -0,0 +1,67 @@ +#include "pch.h" +#include "WindowManager.h" +#include "MonarchFactory.h" + +#include "WindowManager.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + WindowManager::WindowManager() + { + _RegisterAsMonarch(); + _CreateMonarch(); + } + WindowManager::~WindowManager() + { + // IMPORTANT! Tear down the registration as soon as we exit. If we're not a + // real peasant window (the monarch passed our commandline to someone else), + // then the monarch dies, we don't want our registration becoming the active + // monarch! + CoRevokeClassObject(_registrationHostClass); + _registrationHostClass = 0; + } + + void WindowManager::ProposeCommandline() + { + _shouldCreateWindow = true; + } + + bool WindowManager::ShouldCreateWindow() + { + return _shouldCreateWindow; + } + + void WindowManager::_RegisterAsMonarch() + { + winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, + winrt::make<::MonarchFactory>().get(), + CLSCTX_LOCAL_SERVER, + REGCLS_MULTIPLEUSE, + &_registrationHostClass)); + } + + void WindowManager::_CreateMonarch() + { + // Heads up! This only works because we're using + // "metadata-based-marshalling" for our WinRT types. THat means the OS is + // using the .winmd file we generate to figure out the proxy/stub + // definitions for our types automatically. This only works in the following + // cases: + // + // * If we're running unpackaged: the .winmd but be a sibling of the .exe + // * If we're running packaged: the .winmd must be in the package root + _monarch = create_instance(Monarch_clsid, + CLSCTX_LOCAL_SERVER); + } + + // bool AppHost::_ProposeCommandlineToMonarch() + // { + // // returns true if we should create a new window + // return true; + // } +} diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h new file mode 100644 index 00000000000..14e4fd33069 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.h @@ -0,0 +1,31 @@ +#pragma once + +#include "WindowManager.g.h" +#include "Peasant.h" +#include "Monarch.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct WindowManager : public WindowManagerT + { + WindowManager(); + ~WindowManager(); + + void ProposeCommandline(); + bool ShouldCreateWindow(); + + private: + bool _shouldCreateWindow{ false }; + DWORD _registrationHostClass{ 0 }; + winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; + + void _RegisterAsMonarch(); + void _CreateMonarch(); + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(WindowManager); +} diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl new file mode 100644 index 00000000000..033602e0e37 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.idl @@ -0,0 +1,12 @@ + + +namespace Microsoft.Terminal.Remoting +{ + + [default_interface] runtimeclass WindowManager + { + WindowManager(); + void ProposeCommandline(); + Boolean ShouldCreateWindow { get; }; + }; +} diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a97ba3f46be..40a3784793f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -3,13 +3,12 @@ #include "pch.h" #include "AppHost.h" +// #include "MonarchFactory.h" #include "../types/inc/Viewport.hpp" #include "../types/inc/utils.hpp" #include "../types/inc/User32Utils.hpp" #include "resource.h" -#include - using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Composition; using namespace winrt::Windows::UI::Xaml; @@ -25,17 +24,26 @@ static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; AppHost::AppHost() noexcept : _app{}, + _windowManager{}, _logic{ nullptr }, // don't make one, we're going to take a ref on app's _window{ nullptr } { _logic = _app.Logic(); // get a ref to app's logic - _useNonClientArea = _logic.GetShowTabsInTitlebar(); + _windowManager.ProposeCommandline(); + // _RegisterAsMonarch(); + // _CreateMonarch(); + _shouldCreateWindow = _windowManager.ShouldCreateWindow(); + if (!_shouldCreateWindow) + { + return; + } // If there were commandline args to our process, try and process them here. // Do this before AppLogic::Create, otherwise this will have no effect - _HandleCommandlineArgs(); + _HandleCommandlineArgs(); // TODO:MG <-- This probably needs to move into _ProposeCommandlineToMonarch + _useNonClientArea = _logic.GetShowTabsInTitlebar(); if (_useNonClientArea) { _window = std::make_unique(_logic.GetRequestedTheme()); @@ -65,6 +73,7 @@ AppHost::AppHost() noexcept : AppHost::~AppHost() { // destruction order is important for proper teardown here + _window = nullptr; _app.Close(); _app = nullptr; @@ -462,3 +471,27 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta) } } } + +bool AppHost::HasWindow() +{ + return _shouldCreateWindow; +} + +// void AppHost::_RegisterAsMonarch() +// { +// winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, +// winrt::make<::MonarchFactory>().get(), +// CLSCTX_LOCAL_SERVER, +// REGCLS_MULTIPLEUSE, +// &_registrationHostClass)); +// } + +// void AppHost::_CreateMonarch() +// { +// } + +// bool AppHost::_ProposeCommandlineToMonarch() +// { +// // returns true if we should create a new window +// return true; +// } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 25de688131b..e83601be6c5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -3,9 +3,6 @@ #include "pch.h" -#include -#include - #include "NonClientIslandWindow.h" class AppHost @@ -20,12 +17,17 @@ class AppHost bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + bool HasWindow(); + private: bool _useNonClientArea; std::unique_ptr _window; winrt::TerminalApp::App _app; winrt::TerminalApp::AppLogic _logic; + bool _shouldCreateWindow{ false }; + // DWORD _registrationHostClass{ 0 }; + winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; void _HandleCommandlineArgs(); @@ -43,4 +45,8 @@ class AppHost void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const til::point coord, const int32_t delta); + + // void _RegisterAsMonarch(); + // void _CreateMonarch(); + // bool _ProposeCommandlineToMonarch(); }; diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index 726ded30444..91188aaca5d 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -124,6 +124,10 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) // Terminal App. This MUST BE constructed before the Xaml manager as TermApp // provides an implementation of Windows.UI.Xaml.Application. AppHost host; + if (!host.HasWindow()) + { + return 0; + } // Initialize the xaml content. This must be called AFTER the // WindowsXamlManager is initialized. diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index e91aeada6e6..b416ccccc4f 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -61,6 +61,11 @@ Module Name: #include #include +#include +#include +#include +#include + #include #include From 36539cfa47d1b5f3a3b0bf44f7bdcb057aa08e95 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 16:03:23 -0600 Subject: [PATCH 04/88] This won't work, but I'm committing this becaus I finally got it to compile a String[] --- src/cascadia/Remoting/CommandlineArgs.cpp | 20 ++++++ src/cascadia/Remoting/CommandlineArgs.h | 39 +++++++++++ .../Microsoft.Terminal.RemotingLib.vcxproj | 8 ++- src/cascadia/Remoting/Monarch.cpp | 13 ++-- src/cascadia/Remoting/Peasant.cpp | 33 ++++++--- src/cascadia/Remoting/Peasant.h | 11 ++- src/cascadia/Remoting/Peasant.idl | 15 +++- src/cascadia/Remoting/WindowManager.cpp | 70 +++++++++++++++---- src/cascadia/Remoting/WindowManager.h | 11 ++- src/cascadia/Remoting/WindowManager.idl | 4 +- src/cascadia/WindowsTerminal/AppHost.cpp | 34 +++++++-- 11 files changed, 217 insertions(+), 41 deletions(-) create mode 100644 src/cascadia/Remoting/CommandlineArgs.cpp create mode 100644 src/cascadia/Remoting/CommandlineArgs.h diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp new file mode 100644 index 00000000000..e3c31cd2a31 --- /dev/null +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -0,0 +1,20 @@ +#include "pch.h" + +#include "CommandlineArgs.h" +#include "CommandlineArgs.g.cpp" +using namespace winrt; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Windows::Foundation; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + void CommandlineArgs::Args(winrt::array_view const& value) + { + _args = { value.begin(), value.end() }; + } + + winrt::com_array CommandlineArgs::Args() + { + return winrt::com_array{ _args.begin(), _args.end() }; + } +} diff --git a/src/cascadia/Remoting/CommandlineArgs.h b/src/cascadia/Remoting/CommandlineArgs.h new file mode 100644 index 00000000000..159071854f2 --- /dev/null +++ b/src/cascadia/Remoting/CommandlineArgs.h @@ -0,0 +1,39 @@ +#pragma once + +#include "CommandlineArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct CommandlineArgs : public CommandlineArgsT + { + public: + CommandlineArgs() : + _args{}, + _cwd{ L"" } + { + } + + CommandlineArgs(const winrt::array_view& args, + winrt::hstring currentDirectory) : + _args{ args.begin(), args.end() }, + _cwd{ currentDirectory } + { + } + + winrt::hstring CurrentDirectory() { return _cwd; }; + + void Args(winrt::array_view const& value); + winrt::com_array Args(); + + private: + winrt::com_array _args; + winrt::hstring _cwd; + }; + +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(CommandlineArgs); +} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index ad736bd6d32..b5d5355e414 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -25,7 +25,10 @@ Peasant.idl - WindowManager .idl + WindowManager.idl + + + Peasant.idl @@ -42,6 +45,9 @@ WindowManager.idl + + Peasant.idl + diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 6a74171004f..888dbade666 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "Monarch.h" +#include "CommandlineArgs.h" #include "Monarch.g.cpp" #include "../../types/inc/utils.hpp" @@ -91,7 +92,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _setMostRecentPeasant(_thisPeasantID); } - bool Monarch::ProposeCommandline(array_view args, winrt::hstring cwd) + bool Monarch::ProposeCommandline(array_view args, + winrt::hstring cwd) { auto argsProcessed = 0; std::wstring fullCmdline; @@ -125,7 +127,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation printf("Session 0 is actually #%llu\n", _mostRecentPeasant); if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) { - mruPeasant.ExecuteCommandline(args, cwd); + auto eventArgs = winrt::make_self(args, cwd); + mruPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } } @@ -133,7 +136,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { if (auto otherPeasant = _getPeasant(sessionId)) { - otherPeasant.ExecuteCommandline(args, cwd); + auto eventArgs = winrt::make_self(args, cwd); + otherPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } else @@ -147,7 +151,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) { - mruPeasant.ExecuteCommandline(args, cwd); + auto eventArgs = winrt::make_self(args, cwd); + mruPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 30b10b5f8f9..cd8b98fe55a 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -1,10 +1,11 @@ #include "pch.h" #include "Peasant.h" - +#include "CommandlineArgs.h" #include "Peasant.g.cpp" #include "../../types/inc/utils.hpp" using namespace winrt; +using namespace winrt::Microsoft::Terminal; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; @@ -28,18 +29,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return GetCurrentProcessId(); } - bool Peasant::ExecuteCommandline(winrt::array_view args, winrt::hstring currentDirectory) + bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args) { - auto argsProcessed = 0; - std::wstring fullCmdline; - for (const auto& arg : args) + if (_initialArgs == nullptr) { - fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - fullCmdline += L" "; + _initialArgs = args; } - wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); - wprintf(fullCmdline.c_str()); - wprintf(L"\"\n"); + + _ExecuteCommandlineRequestedHandlers(*this, args); + + // auto argsProcessed = 0; + // std::wstring fullCmdline; + // for (const auto& arg : args) + // { + // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + // fullCmdline += L" "; + // } + // wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); + // wprintf(fullCmdline.c_str()); + // wprintf(L"\"\n"); return true; } @@ -48,4 +56,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _WindowActivatedHandlers(*this, nullptr); } + Remoting::CommandlineArgs Peasant::InitialArgs() + { + return _initialArgs; + } + } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 31da53ff5c5..df4db13b529 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -13,15 +13,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t GetID(); uint64_t GetPID(); - bool ExecuteCommandline(winrt::array_view args, - winrt::hstring currentDirectory); + bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void raiseActivatedEvent(); - + winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); private: uint64_t _id{ 0 }; + + winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs; //{ nullptr }; + + // array_view _args; + // winrt::hstring _cwd; }; } diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index b351448183a..7ffb9512170 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -2,13 +2,26 @@ namespace Microsoft.Terminal.Remoting { + + runtimeclass CommandlineArgs + { + CommandlineArgs(); + CommandlineArgs(String[] args, String cwd); + + String[] Args { get; set; }; + String CurrentDirectory(); + }; + interface IPeasant { + CommandlineArgs InitialArgs { get; }; + void AssignID(UInt64 id); UInt64 GetID(); UInt64 GetPID(); - Boolean ExecuteCommandline(String[] args, String currentDirectory); + Boolean ExecuteCommandline(CommandlineArgs args); event Windows.Foundation.TypedEventHandler WindowActivated; + event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 87e2b482b3e..ddc0d85989a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -1,11 +1,13 @@ #include "pch.h" #include "WindowManager.h" #include "MonarchFactory.h" +#include "CommandlineArgs.h" #include "WindowManager.g.cpp" #include "../../types/inc/utils.hpp" using namespace winrt; +using namespace winrt::Microsoft::Terminal; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; @@ -13,8 +15,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowManager::WindowManager() { - _RegisterAsMonarch(); - _CreateMonarch(); + _registerAsMonarch(); + _createMonarch(); } WindowManager::~WindowManager() { @@ -26,8 +28,29 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; } - void WindowManager::ProposeCommandline() + void WindowManager::ProposeCommandline(array_view args, const winrt::hstring cwd) { + const bool isKing = _areWeTheKing(); + // If we're the king, we _definitely_ want to process the arguments, we were + // launched with them! + // + // Otherwise, the King will tell us if we should make a new window + const bool createNewWindow = isKing || + _monarch.ProposeCommandline(args, cwd); + + if (createNewWindow) + { + _createOurPeasant(); + + auto eventArgs = winrt::make_self(args, cwd); + _peasant.ExecuteCommandline(*eventArgs); + _shouldCreateWindow = false; + } + else + { + // printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n"); + } + _shouldCreateWindow = true; } @@ -36,7 +59,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _shouldCreateWindow; } - void WindowManager::_RegisterAsMonarch() + void WindowManager::_registerAsMonarch() { winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, winrt::make<::MonarchFactory>().get(), @@ -45,7 +68,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation &_registrationHostClass)); } - void WindowManager::_CreateMonarch() + void WindowManager::_createMonarch() { // Heads up! This only works because we're using // "metadata-based-marshalling" for our WinRT types. THat means the OS is @@ -55,13 +78,36 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // * If we're running unpackaged: the .winmd but be a sibling of the .exe // * If we're running packaged: the .winmd must be in the package root - _monarch = create_instance(Monarch_clsid, - CLSCTX_LOCAL_SERVER); + _monarch = create_instance(Monarch_clsid, + CLSCTX_LOCAL_SERVER); + } + + bool WindowManager::_areWeTheKing() + { + auto kingPID = _monarch.GetPID(); + auto ourPID = GetCurrentProcessId(); + return (ourPID == kingPID); + } + + Remoting::IPeasant WindowManager::_createOurPeasant() + { + auto p = winrt::make_self(); + _peasant = *p; + auto ourID = _monarch.AddPeasant(_peasant); + ourID; + // printf("The monarch assigned us the ID %llu\n", ourID); + + // if (areWeTheKing()) + // { + // remindKingWhoTheyAre(*peasant); + // } + + return _peasant; + } + + Remoting::Peasant WindowManager::CurrentWindow() + { + return _peasant; } - // bool AppHost::_ProposeCommandlineToMonarch() - // { - // // returns true if we should create a new window - // return true; - // } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 14e4fd33069..1d36ec15bf5 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -12,16 +12,21 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager(); ~WindowManager(); - void ProposeCommandline(); + void ProposeCommandline(array_view args, const winrt::hstring cwd); bool ShouldCreateWindow(); + winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); + private: bool _shouldCreateWindow{ false }; DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; + winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; - void _RegisterAsMonarch(); - void _CreateMonarch(); + void _registerAsMonarch(); + void _createMonarch(); + bool _areWeTheKing(); + winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); }; } diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 033602e0e37..55785aac718 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -1,3 +1,4 @@ +import "Peasant.idl"; namespace Microsoft.Terminal.Remoting @@ -6,7 +7,8 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass WindowManager { WindowManager(); - void ProposeCommandline(); + void ProposeCommandline(String[] commands, String cwd); Boolean ShouldCreateWindow { get; }; + Peasant CurrentWindow(); }; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 40a3784793f..928931ada43 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -30,13 +30,35 @@ AppHost::AppHost() noexcept : { _logic = _app.Logic(); // get a ref to app's logic - _windowManager.ProposeCommandline(); - // _RegisterAsMonarch(); - // _CreateMonarch(); - _shouldCreateWindow = _windowManager.ShouldCreateWindow(); - if (!_shouldCreateWindow) { - return; + std::vector args; + if (auto commandline{ GetCommandLineW() }) + { + int argc = 0; + + // Get the argv, and turn them into a hstring array to pass to the app. + wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + if (argv) + { + for (auto& elem : wil::make_range(argv.get(), argc)) + { + args.emplace_back(elem); + } + } + } + if (args.empty()) + { + args.emplace_back(L"wt.exe"); + } + + _windowManager.ProposeCommandline({ args }, L"placeholder/cwd"); + // _RegisterAsMonarch(); + // _CreateMonarch(); + _shouldCreateWindow = _windowManager.ShouldCreateWindow(); + if (!_shouldCreateWindow) + { + return; + } } // If there were commandline args to our process, try and process them here. From 27ace166529367171fc81cef12aa4812e6ff8e13 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 16:31:51 -0600 Subject: [PATCH 05/88] whoop, we pass the commandline from the peasant, to the monarch, and then back! --- src/cascadia/Remoting/CommandlineArgs.cpp | 4 ++ src/cascadia/Remoting/Monarch.cpp | 22 +++++----- src/cascadia/Remoting/Peasant.h | 2 +- src/cascadia/Remoting/WindowManager.cpp | 3 +- src/cascadia/WindowsTerminal/AppHost.cpp | 51 +++++++++++++++++------ 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index e3c31cd2a31..bbe31961c6e 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -8,6 +8,10 @@ using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Terminal::Remoting::implementation { + // If you try to move this into the header, you will experience P A I N + // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler + // will give you just the most impossible tmplate errors to try and + // decipher. void CommandlineArgs::Args(winrt::array_view const& value) { _args = { value.begin(), value.end() }; diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 888dbade666..9c304b951b7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -95,16 +95,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool Monarch::ProposeCommandline(array_view args, winrt::hstring cwd) { - auto argsProcessed = 0; - std::wstring fullCmdline; - for (const auto& arg : args) - { - fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - fullCmdline += L" "; - } - wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); - wprintf(fullCmdline.c_str()); - wprintf(L"\"\n"); + // auto argsProcessed = 0; + // std::wstring fullCmdline; + // for (const auto& arg : args) + // { + // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + // fullCmdline += L" "; + // } + // wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); + // wprintf(fullCmdline.c_str()); + // wprintf(L"\"\n"); bool createNewWindow = true; @@ -113,7 +113,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // We'll need three args at least - [WindowsTerminal.exe, -s, // id] to be able to have a session ID passed on the commandline. - if (args[1] == L"-s" || args[1] == L"--session") + if (args[1] == L"-w" || args[1] == L"--window") { auto sessionId = std::stoi({ args[2].data(), args[2].size() }); printf("Found a commandline intended for session %d\n", sessionId); diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index df4db13b529..e2181690e2b 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -23,7 +23,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation private: uint64_t _id{ 0 }; - winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs; //{ nullptr }; + winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; // array_view _args; // winrt::hstring _cwd; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index ddc0d85989a..b5e60723bd2 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -28,7 +28,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; } - void WindowManager::ProposeCommandline(array_view args, const winrt::hstring cwd) + void WindowManager::ProposeCommandline(array_view args, + const winrt::hstring cwd) { const bool isKing = _areWeTheKing(); // If we're the king, we _definitely_ want to process the arguments, we were diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 928931ada43..c37a74e8149 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -142,21 +142,11 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& // - void AppHost::_HandleCommandlineArgs() { - if (auto commandline{ GetCommandLineW() }) + if (auto peasant{ _windowManager.CurrentWindow() }) { - int argc = 0; - - // Get the argv, and turn them into a hstring array to pass to the app. - wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - if (argv) + if (auto args{ peasant.InitialArgs() }) { - std::vector args; - for (auto& elem : wil::make_range(argv.get(), argc)) - { - args.emplace_back(elem); - } - - const auto result = _logic.SetStartupCommandline({ args }); + const auto result = _logic.SetStartupCommandline(args.Args()); const auto message = _logic.ParseCommandlineMessage(); if (!message.empty()) { @@ -177,6 +167,41 @@ void AppHost::_HandleCommandlineArgs() } } } + // if (auto commandline{ GetCommandLineW() }) + // { + // int argc = 0; + + // // Get the argv, and turn them into a hstring array to pass to the app. + // wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + // if (argv) + // { + // std::vector args; + // for (auto& elem : wil::make_range(argv.get(), argc)) + // { + // args.emplace_back(elem); + // } + + // const auto result = _logic.SetStartupCommandline({ args }); + // const auto message = _logic.ParseCommandlineMessage(); + // if (!message.empty()) + // { + // const auto displayHelp = result == 0; + // const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE; + // const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR; + // // TODO:GH#4134: polish this dialog more, to make the text more + // // like msiexec /? + // MessageBoxW(nullptr, + // message.data(), + // GetStringResource(messageTitle).data(), + // MB_OK | messageIcon); + + // if (_logic.ShouldExitEarly()) + // { + // ExitProcess(result); + // } + // } + // } + // } } // Method Description: From 9a41647ffe00d973f11478fb937221239d6ac81a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 07:24:16 -0600 Subject: [PATCH 06/88] HOLY SHIT I GOT THE COMANDLINE TO EXECUTE IN THE CURRENT WINDOW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --- src/cascadia/Remoting/Monarch.cpp | 15 +++++++++++++-- src/cascadia/Remoting/WindowManager.cpp | 5 ++--- src/cascadia/TerminalApp/AppActionHandlers.cpp | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 13 +++++++++++++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 1 + src/cascadia/TerminalApp/TerminalPage.cpp | 6 +++--- src/cascadia/TerminalApp/TerminalPage.h | 3 ++- src/cascadia/WindowsTerminal/AppHost.cpp | 10 ++++++++++ src/cascadia/WindowsTerminal/AppHost.h | 3 +++ 10 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 9c304b951b7..af6e50e53b9 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -117,6 +117,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto sessionId = std::stoi({ args[2].data(), args[2].size() }); printf("Found a commandline intended for session %d\n", sessionId); + + // TODO:MG + // HACK: do an args[2:] to slice off the `-w window` args. + array_view argsNoWindow{ args.begin() + 2, args.end() }; if (sessionId < 0) { printf("That certainly isn't a valid ID, they should make a new window.\n"); @@ -127,7 +131,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation printf("Session 0 is actually #%llu\n", _mostRecentPeasant); if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) { - auto eventArgs = winrt::make_self(args, cwd); + // TODO In the morning: + // Right now, this commandline includes the "-w window" param, and CLI11 is biting it when parsing that. + // Either: + // * hack yank it for the time being (args[2:]) + // * actually have an AppCommandlineArgs do the parsing. + + auto eventArgs = winrt::make_self(argsNoWindow, cwd); mruPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } @@ -136,7 +146,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { if (auto otherPeasant = _getPeasant(sessionId)) { - auto eventArgs = winrt::make_self(args, cwd); + auto eventArgs = winrt::make_self(argsNoWindow, cwd); otherPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } @@ -163,6 +173,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return createNewWindow; } + void Monarch::ToggleWindowingBehavior() { switch (_windowingBehavior) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index b5e60723bd2..5ba26eada7a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -45,14 +45,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto eventArgs = winrt::make_self(args, cwd); _peasant.ExecuteCommandline(*eventArgs); - _shouldCreateWindow = false; + _shouldCreateWindow = true; } else { // printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n"); + _shouldCreateWindow = false; } - - _shouldCreateWindow = true; } bool WindowManager::ShouldCreateWindow() diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index f51ea8c2ae2..c7c4d028a42 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -453,7 +453,7 @@ namespace winrt::TerminalApp::implementation if (_startupActions.Size() != 0) { actionArgs.Handled(true); - _ProcessStartupActions(actions, false); + ProcessStartupActions(actions, false); } } } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index f052341b53f..e581d1f987c 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1111,6 +1111,19 @@ namespace winrt::TerminalApp::implementation return result; } + int32_t AppLogic::ExecuteCommandline(array_view args) + { + ::TerminalApp::AppCommandlineArgs appArgs; + auto result = appArgs.ParseArgs(args); + if (result == 0) + { + auto actions = winrt::single_threaded_vector(std::move(appArgs.GetStartupActions())); + _root->ProcessStartupActions(actions, false); + } + + return result; // TODO:MG does a return value make sense + } + // Method Description: // - If there were any errors parsing the commandline that was used to // initialize the terminal, this will return a string containing that diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 3d2051a1200..1a93abcf509 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -29,6 +29,7 @@ namespace winrt::TerminalApp::implementation [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; int32_t SetStartupCommandline(array_view actions); + int32_t ExecuteCommandline(array_view actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index d1bc606d4b8..3abf62eb12a 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -29,6 +29,7 @@ namespace TerminalApp Boolean IsElevated(); Int32 SetStartupCommandline(String[] commands); + Int32 ExecuteCommandline(String[] commands); String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index a21779e263c..adfbb3a46c1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -331,7 +331,7 @@ namespace winrt::TerminalApp::implementation } else { - _ProcessStartupActions(_startupActions, true); + ProcessStartupActions(_startupActions, true); } } } @@ -347,8 +347,8 @@ namespace winrt::TerminalApp::implementation // should fire an Initialized event. // Return Value: // - - winrt::fire_and_forget TerminalPage::_ProcessStartupActions(Windows::Foundation::Collections::IVector actions, - const bool initial) + winrt::fire_and_forget TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector actions, + const bool initial) { // If there are no actions left, do nothing. if (actions.Size() == 0) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 3edc7ac40ed..407dd46958a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -81,6 +81,8 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); + winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); + // -------------------------------- 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); @@ -137,7 +139,6 @@ namespace winrt::TerminalApp::implementation StartupState _startupState{ StartupState::NotInitialized }; Windows::Foundation::Collections::IVector _startupActions; - winrt::fire_and_forget _ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); void _ShowAboutDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseWarningDialog(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index c37a74e8149..346f5d127ee 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -59,6 +59,10 @@ AppHost::AppHost() noexcept : { return; } + if (auto peasant{ _windowManager.CurrentWindow() }) + { + peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); + } } // If there were commandline args to our process, try and process them here. @@ -542,3 +546,9 @@ bool AppHost::HasWindow() // // returns true if we should create a new window // return true; // } + +void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, + winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) +{ + _logic.ExecuteCommandline(args.Args()); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index e83601be6c5..22dbbc2c113 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -49,4 +49,7 @@ class AppHost // void _RegisterAsMonarch(); // void _CreateMonarch(); // bool _ProposeCommandlineToMonarch(); + + void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, + winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); }; From 5cabcfb45229f686d424c6d5a2fdfca7dd446639 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 07:43:46 -0600 Subject: [PATCH 07/88] add a note to future me --- src/cascadia/WindowsTerminal/AppHost.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 346f5d127ee..0eb9418cd09 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -63,6 +63,12 @@ AppHost::AppHost() noexcept : { peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); } + + // TODO:MG if we end up not creating a new window, we crash. I'm + // thinking this is because the XAML host is not happy about being torn + // down before it has a chance to do really anything. Is there some way + // to get the app logic without instantiating the entire app? or at + // least the parts we'll need for remoting? } // If there were commandline args to our process, try and process them here. From 03bfc6e8a93c901ef00b34310c4e1c313019e720 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 10:50:48 -0600 Subject: [PATCH 08/88] This works as a unittest, but not a local test. That's batty --- OpenConsole.sln | 1 + build/Helix/runtests.cmd | 6 +- .../templates/helix-runtests-job.yml | 42 ++- .../LocalTests_Remoting/RemotingTests.cpp | 289 ++---------------- .../TestHostApp/TestHostApp.vcxproj | 4 + src/cascadia/Remoting/Monarch.cpp | 10 +- src/cascadia/Remoting/Monarch.h | 10 + tools/OpenConsole.psm1 | 2 +- tools/runut.cmd | 4 + tools/tests.xml | 2 + 10 files changed, 90 insertions(+), 280 deletions(-) diff --git a/OpenConsole.sln b/OpenConsole.sln index e8a479e2876..e94b662f4ac 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -262,6 +262,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {CA5CAD1A-9B68-456A-B13E-C8218070DC42} + {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {68A10CD3-AA64-465B-AF5F-ED4E9700543C} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDB237B6-1D1D-400F-84CC-40A58FA59C8E}" diff --git a/build/Helix/runtests.cmd b/build/Helix/runtests.cmd index ddf8c2d3d11..3b155e279a9 100644 --- a/build/Helix/runtests.cmd +++ b/build/Helix/runtests.cmd @@ -18,7 +18,7 @@ FOR %%A IN (TestHostApp.exe,te.exe,te.processhost.exe,conhost.exe,OpenConsole.ex echo %TIME% -:: kill dhandler, which is a tool designed to handle unexpected windows appearing. But since our tests are +:: kill dhandler, which is a tool designed to handle unexpected windows appearing. But since our tests are :: expected to show UI we don't want it running. taskkill -f -im dhandler.exe @@ -28,7 +28,7 @@ echo %TIME% powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1 echo %TIME% -set testBinaryCandidates=TerminalApp.LocalTests.dll Conhost.UIA.Tests.dll +set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Remoting.LocalTests.dll Conhost.UIA.Tests.dll set testBinaries= for %%B in (%testBinaryCandidates%) do ( if exist %%B ( @@ -103,4 +103,4 @@ copy /y *_subresults.json %HELIX_WORKITEM_UPLOAD_ROOT% type testResults.xml -echo %TIME% \ No newline at end of file +echo %TIME% diff --git a/build/pipelines/templates/helix-runtests-job.yml b/build/pipelines/templates/helix-runtests-job.yml index 81125f3d988..736bfc5338e 100644 --- a/build/pipelines/templates/helix-runtests-job.yml +++ b/build/pipelines/templates/helix-runtests-job.yml @@ -5,14 +5,14 @@ parameters: testSuite: '' # If a Pipeline runs this template more than once, this parameter should be unique per build flavor to differentiate the # the different test runs: - helixType: 'test/devtest' + helixType: 'test/devtest' artifactName: 'drop' maxParallel: 4 rerunPassesRequiredToAvoidFailure: 5 taefQuery: '' # if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline: useBuildOutputFromPipeline: $(System.DefinitionId) - matrix: + matrix: # Release_x86: # buildPlatform: 'x86' # buildConfiguration: 'release' @@ -39,13 +39,13 @@ jobs: taefPath: $(Build.SourcesDirectory)\build\Helix\packages\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$(buildPlatform) helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}' - + steps: - task: CmdLine@1 displayName: 'Display build machine environment variables' inputs: filename: 'set' - + - task: NuGetToolInstaller@0 displayName: 'Use NuGet 5.2.0' inputs: @@ -59,23 +59,23 @@ jobs: nugetConfigPath: nuget.config restoreDirectory: packages - - task: DownloadBuildArtifacts@0 + - task: DownloadBuildArtifacts@0 condition: and(succeeded(),eq(variables['useBuildOutputFromBuildId'],'')) - inputs: - artifactName: ${{ parameters.artifactName }} + inputs: + artifactName: ${{ parameters.artifactName }} downloadPath: '$(artifactsDir)' - - task: DownloadBuildArtifacts@0 + - task: DownloadBuildArtifacts@0 condition: and(succeeded(),ne(variables['useBuildOutputFromBuildId'],'')) - inputs: + inputs: buildType: specific buildVersionToDownload: specific project: $(System.TeamProjectId) pipeline: ${{ parameters.useBuildOutputFromPipeline }} buildId: $(useBuildOutputFromBuildId) - artifactName: ${{ parameters.artifactName }} + artifactName: ${{ parameters.artifactName }} downloadPath: '$(artifactsDir)' - task: CmdLine@1 @@ -90,7 +90,7 @@ jobs: targetType: filePath filePath: build\Helix\PrepareHelixPayload.ps1 arguments: -Platform '$(buildPlatform)' -Configuration '$(buildConfiguration)' -ArtifactName '${{ parameters.artifactName }}' - + - task: CmdLine@1 displayName: 'Display Helix payload contents' inputs: @@ -104,7 +104,23 @@ jobs: outputProjFileName: 'RunTestsInHelix-TerminalAppLocalTests.proj' testSuite: '${{ parameters.testSuite }}' taefQuery: ${{ parameters.taefQuery }} - + + - template: helix-createprojfile-steps.yml + parameters: + condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) + testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\SettingsModel.LocalTests.dll' + outputProjFileName: 'RunTestsInHelix-SettingsModelLocalTests.proj' + testSuite: '${{ parameters.testSuite }}' + taefQuery: ${{ parameters.taefQuery }} + + - template: helix-createprojfile-steps.yml + parameters: + condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) + testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\Remoting.LocalTests.dll' + outputProjFileName: 'RunTestsInHelix-RemotingLocalTests.proj' + testSuite: '${{ parameters.testSuite }}' + taefQuery: ${{ parameters.taefQuery }} + - template: helix-createprojfile-steps.yml parameters: condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) @@ -118,7 +134,7 @@ jobs: inputs: PathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: ${{ parameters.artifactName }} - + - task: DotNetCoreCLI@2 displayName: 'Run tests in Helix (open queues)' env: diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp index 60491de9cbf..008cc62579d 100644 --- a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp @@ -2,294 +2,63 @@ // Licensed under the MIT license. #include "pch.h" - -#include "../TerminalSettingsModel/ColorScheme.h" -#include "../TerminalSettingsModel/CascadiaSettings.h" -#include "JsonTestClass.h" -#include "TestUtils.h" -#include -#include "../ut_app/TestDynamicProfileGenerator.h" +#include "../Remoting/Monarch.h" using namespace Microsoft::Console; using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; -using namespace winrt::Microsoft::Terminal::Settings::Model; -using namespace winrt::Microsoft::Terminal::TerminalControl; -namespace SettingsModelLocalTests +using namespace winrt; +using namespace winrt::Microsoft::Terminal; + +namespace RemotingLocalTests { // TODO:microsoft/terminal#3838: // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for // an updated TAEF that will let us install framework packages when the test // package is deployed. Until then, these tests won't deploy in CI. - class SerializationTests : public JsonTestClass + class RemotingTests { // Use a custom AppxManifest to ensure that we can activate winrt types // from our test. This property will tell taef to manually use this as // the AppxManifest for this test class. // This does not yet work for anything XAML-y. See TabTests.cpp for more // details on that. - BEGIN_TEST_CLASS(SerializationTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") + BEGIN_TEST_CLASS(RemotingTests) + // TEST_CLASS_PROPERTY(L"RunAs", L"UAP") + // TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") END_TEST_CLASS() - TEST_METHOD(GlobalSettings); - TEST_METHOD(Profile); - TEST_METHOD(ColorScheme); - TEST_METHOD(CascadiaSettings); + TEST_METHOD(CreateMonarch); TEST_CLASS_SETUP(ClassSetup) { - InitializeJsonReader(); - InitializeJsonWriter(); return true; } - - private: - // Method Description: - // - deserializes and reserializes a json string representing a settings object model of type T - // - verifies that the generated json string matches the provided one - // Template Types: - // - : The type of Settings Model object to generate (must be impl type) - // Arguments: - // - jsonString - JSON string we're performing the test on - // Return Value: - // - the JsonObject representing this instance - template - void RoundtripTest(const std::string& jsonString) - { - const auto json{ VerifyParseSucceeded(jsonString) }; - const auto settings{ T::FromJson(json) }; - const auto result{ settings->ToJson() }; - - // Compare toString(json) instead of jsonString here. - // The toString writes the json out alphabetically. - // This trick allows jsonString to _not_ have to be - // written alphabetically. - VERIFY_ARE_EQUAL(toString(json), toString(result)); - } }; - void SerializationTests::GlobalSettings() - { - const std::string globalsString{ R"( - { - "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", - - "initialRows": 30, - "initialCols": 120, - "initialPosition": ",", - "launchMode": "default", - "alwaysOnTop": false, - - "copyOnSelect": false, - "copyFormatting": "all", - "wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502", - - "alwaysShowTabs": true, - "showTabsInTitlebar": true, - "showTerminalTitleInTitlebar": true, - "tabWidthMode": "equal", - "tabSwitcherMode": "mru", - - "startOnUserLogin": false, - "theme": "system", - "snapToGridOnResize": true, - "disableAnimations": false, - - "confirmCloseAllTabs": true, - "largePasteWarning": true, - "multiLinePasteWarning": true, - - "experimental.input.forceVT": false, - "experimental.rendering.forceFullRepaint": false, - "experimental.rendering.software": false - })" }; - - const std::string smallGlobalsString{ R"( - { - "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" - })" }; - - RoundtripTest(globalsString); - RoundtripTest(smallGlobalsString); - } - - void SerializationTests::Profile() - { - const std::string profileString{ R"( - { - "name": "Windows PowerShell", - "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", - - "commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", - "startingDirectory": "%USERPROFILE%", - - "icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png", - "hidden": false, - - "tabTitle": "Cool Tab", - "suppressApplicationTitle": false, - - "fontFace": "Cascadia Mono", - "fontSize": 12, - "fontWeight": "normal", - "padding": "8, 8, 8, 8", - "antialiasingMode": "grayscale", - - "cursorShape": "bar", - "cursorColor": "#CCBBAA", - "cursorHeight": 10, - - "altGrAliasing": true, - - "colorScheme": "Campbell", - "tabColor": "#0C0C0C", - "foreground": "#AABBCC", - "background": "#BBCCAA", - "selectionBackground": "#CCAABB", - - "useAcrylic": false, - "acrylicOpacity": 0.5, - - "backgroundImage": "made_you_look.jpeg", - "backgroundImageStretchMode": "uniformToFill", - "backgroundImageAlignment": "center", - "backgroundImageOpacity": 1.0, - - "scrollbarState": "visible", - "snapOnInput": true, - "historySize": 9001, - - "closeOnExit": "graceful", - "experimental.retroTerminalEffect": false - })" }; - - const std::string smallProfileString{ R"( - { - "name": "Custom Profile" - })" }; - - // Setting "tabColor" to null tests two things: - // - null should count as an explicit user-set value, not falling back to the parent's value - // - null should be acceptable even though we're working with colors - const std::string weirdProfileString{ R"( - { - "name": "Weird Profile", - "tabColor": null, - "foreground": null, - "source": "local" - })" }; - - RoundtripTest(profileString); - RoundtripTest(smallProfileString); - RoundtripTest(weirdProfileString); - } - - void SerializationTests::ColorScheme() + void RemotingTests::CreateMonarch() { - const std::string schemeString{ R"({ - "name": "Campbell", - - "cursorColor": "#FFFFFF", - "selectionBackground": "#131313", - - "background": "#0C0C0C", - "foreground": "#F2F2F2", - - "black": "#0C0C0C", - "blue": "#0037DA", - "cyan": "#3A96DD", - "green": "#13A10E", - "purple": "#881798", - "red": "#C50F1F", - "white": "#CCCCCC", - "yellow": "#C19C00", - "brightBlack": "#767676", - "brightBlue": "#3B78FF", - "brightCyan": "#61D6D6", - "brightGreen": "#16C60C", - "brightPurple": "#B4009E", - "brightRed": "#E74856", - "brightWhite": "#F2F2F2", - "brightYellow": "#F9F1A5" - })" }; - - RoundtripTest(schemeString); + auto m1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(m1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + m1->GetPID(), + L"A Monarch without an explicit PID should use the current PID"); + + Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); + + auto expectedFakePID = 1234u; + Remoting::implementation::Monarch foo{ expectedFakePID }; + com_ptr m2; + m2.attach(&foo); + auto cleanup = wil::scope_exit([&]() { m2.detach(); }); + // auto m2 = winrt::make_self(expectedFakePID); + VERIFY_IS_NOT_NULL(m2); + VERIFY_ARE_EQUAL(expectedFakePID, + m2->GetPID(), + L"A Monarch with an explicit PID should use the one we provided"); } - void SerializationTests::CascadiaSettings() - { - const std::string settingsString{ R"({ - "$schema": "https://aka.ms/terminal-profiles-schema", - "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", - - "profiles": { - "defaults": { - "fontFace": "Zamora Code" - }, - "list": [ - { - "fontFace": "Cascadia Code", - "guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", - "name": "HowettShell" - }, - { - "hidden": true, - "name": "BhojwaniShell" - }, - { - "antialiasingMode": "aliased", - "name": "NiksaShell" - } - ] - }, - "schemes": [ - { - "name": "Cinnamon Roll", - - "cursorColor": "#FFFFFD", - "selectionBackground": "#FFFFFF", - - "background": "#3C0315", - "foreground": "#FFFFFD", - - "black": "#282A2E", - "blue": "#0170C5", - "cyan": "#3F8D83", - "green": "#76AB23", - "purple": "#7D498F", - "red": "#BD0940", - "white": "#FFFFFD", - "yellow": "#E0DE48", - "brightBlack": "#676E7A", - "brightBlue": "#5C98C5", - "brightCyan": "#8ABEB7", - "brightGreen": "#B5D680", - "brightPurple": "#AC79BB", - "brightRed": "#BD6D85", - "brightWhite": "#FFFFFD", - "brightYellow": "#FFFD76" - } - ], - "actions": [ - {"command": { "action": "renameTab","input": "Liang Tab" },"keys": "ctrl+t" } - ], - "keybindings": [ - { "command": { "action": "sendInput","input": "VT Griese Mode" },"keys": "ctrl+k" } - ] - })" }; - - auto settings{ winrt::make_self(false) }; - settings->_ParseJsonString(settingsString, false); - settings->_ApplyDefaultsFromUserSettings(); - settings->LayerJson(settings->_userSettings); - settings->_ValidateSettings(); - - const auto result{ settings->ToJson() }; - VERIFY_ARE_EQUAL(toString(settings->_userSettings), toString(result)); - } } diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 384356df6f5..99194c82db8 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -107,6 +107,9 @@ {CA5CAD1A-082C-4476-9F33-94B339494076} + + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} + @@ -132,6 +135,7 @@ + diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index af6e50e53b9..6b124dbd0b9 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -11,9 +11,13 @@ using namespace ::Microsoft::Console; namespace winrt::Microsoft::Terminal::Remoting::implementation { - Monarch::Monarch() + Monarch::Monarch() : + _ourPID{ GetCurrentProcessId() } + { + } + Monarch::Monarch(const uint64_t testPID) : + _ourPID{ testPID } { - printf("Instantiated a Monarch\n"); } Monarch::~Monarch() @@ -23,7 +27,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Monarch::GetPID() { - return GetCurrentProcessId(); + return _ourPID; } uint64_t Monarch::AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant) diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 653ae51976f..1069bb0f3af 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -18,6 +18,11 @@ enum class WindowingBehavior : uint64_t UseExisting = 1, }; +namespace RemotingLocalTests +{ + class RemotingTests; +}; + namespace winrt::Microsoft::Terminal::Remoting::implementation { struct Monarch : public MonarchT @@ -35,6 +40,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void ToggleWindowingBehavior(); private: + Monarch(const uint64_t testPID); + uint64_t _ourPID; + uint64_t _nextPeasantID{ 1 }; uint64_t _thisPeasantID{ 0 }; uint64_t _mostRecentPeasant{ 0 }; @@ -46,6 +54,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + + friend class RemotingLocalTests::RemotingTests; }; } diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index 3f628e4235c..fda169365c4 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -159,7 +159,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'localRemoting')] [string]$Test, [parameter(Mandatory=$false)] diff --git a/tools/runut.cmd b/tools/runut.cmd index 9df937cf1a9..c6cec1fd12b 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -23,6 +23,10 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Types.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\til.unit.tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^ + %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\LocalTests_Remoting\Remoting.LocalTests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ + %_TestHostAppPath%\SettingsModel.LocalTests.dll ^ %* + + rem %_TestHostAppPath%\Remoting.LocalTests.dll ^ diff --git a/tools/tests.xml b/tools/tests.xml index 437145b0517..74beb621f15 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -5,6 +5,8 @@ + + From 590b9ff0c73487720a110213c4b19a2f4a00be8e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 11:01:52 -0600 Subject: [PATCH 09/88] this macro makes me feel dirty --- src/cascadia/LocalTests_Remoting/RemotingTests.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp index 008cc62579d..b8169289950 100644 --- a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp @@ -12,6 +12,12 @@ using namespace WEX::Common; using namespace winrt; using namespace winrt::Microsoft::Terminal; +#define MAKE_MONARCH(name, pid) \ + Remoting::implementation::Monarch _local_##name##{ pid }; \ + com_ptr name; \ + name.attach(&_local_##name##); \ + auto cleanup = wil::scope_exit([&]() { name.detach(); }); + namespace RemotingLocalTests { // TODO:microsoft/terminal#3838: @@ -50,11 +56,8 @@ namespace RemotingLocalTests Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); auto expectedFakePID = 1234u; - Remoting::implementation::Monarch foo{ expectedFakePID }; - com_ptr m2; - m2.attach(&foo); - auto cleanup = wil::scope_exit([&]() { m2.detach(); }); - // auto m2 = winrt::make_self(expectedFakePID); + MAKE_MONARCH(m2, expectedFakePID); + VERIFY_IS_NOT_NULL(m2); VERIFY_ARE_EQUAL(expectedFakePID, m2->GetPID(), From 0579b2417bcdd74814ea05d7eb64723542584764 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 11:10:09 -0600 Subject: [PATCH 10/88] LocalTests_Remoting -> UnitTests_Remoting --- .../TestHostApp/TestHostApp.vcxproj | 4 --- src/cascadia/Remoting/Monarch.cpp | 25 ------------------- src/cascadia/Remoting/Monarch.h | 5 ++-- .../LocalTests_Remoting.def | 0 .../Remoting.LocalTests.vcxproj | 0 .../RemotingTests.cpp | 14 +---------- .../pch.cpp | 0 .../pch.h | 0 tools/runut.cmd | 4 +-- 9 files changed, 4 insertions(+), 48 deletions(-) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/LocalTests_Remoting.def (100%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/Remoting.LocalTests.vcxproj (100%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/RemotingTests.cpp (66%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/pch.cpp (100%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/pch.h (100%) diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 99194c82db8..384356df6f5 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -107,9 +107,6 @@ {CA5CAD1A-082C-4476-9F33-94B339494076} - - {27b5aaeb-a548-44cf-9777-f8baa32af7ae} - @@ -135,7 +132,6 @@ - diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 6b124dbd0b9..70b02d60191 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -178,29 +178,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return createNewWindow; } - void Monarch::ToggleWindowingBehavior() - { - switch (_windowingBehavior) - { - case WindowingBehavior::UseNew: - _windowingBehavior = WindowingBehavior::UseExisting; - break; - case WindowingBehavior::UseExisting: - _windowingBehavior = WindowingBehavior::UseNew; - break; - } - - printf("windowingBehavior: "); - switch (_windowingBehavior) - { - case WindowingBehavior::UseNew: - printf("useNew"); - break; - case WindowingBehavior::UseExisting: - printf("useExisting"); - break; - } - printf("\n"); - } - } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 1069bb0f3af..a79cd7754fd 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -18,7 +18,7 @@ enum class WindowingBehavior : uint64_t UseExisting = 1, }; -namespace RemotingLocalTests +namespace RemotingUnitTests { class RemotingTests; }; @@ -37,7 +37,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void SetSelfID(const uint64_t selfID); bool ProposeCommandline(array_view args, winrt::hstring cwd); - void ToggleWindowingBehavior(); private: Monarch(const uint64_t testPID); @@ -55,7 +54,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); - friend class RemotingLocalTests::RemotingTests; + friend class RemotingUnitTests::RemotingTests; }; } diff --git a/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def b/src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def similarity index 100% rename from src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def rename to src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def diff --git a/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj b/src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj similarity index 100% rename from src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj rename to src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp similarity index 66% rename from src/cascadia/LocalTests_Remoting/RemotingTests.cpp rename to src/cascadia/UnitTests_Remoting/RemotingTests.cpp index b8169289950..ac96debf541 100644 --- a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -18,23 +18,11 @@ using namespace winrt::Microsoft::Terminal; name.attach(&_local_##name##); \ auto cleanup = wil::scope_exit([&]() { name.detach(); }); -namespace RemotingLocalTests +namespace RemotingUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class RemotingTests { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. BEGIN_TEST_CLASS(RemotingTests) - // TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - // TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") END_TEST_CLASS() TEST_METHOD(CreateMonarch); diff --git a/src/cascadia/LocalTests_Remoting/pch.cpp b/src/cascadia/UnitTests_Remoting/pch.cpp similarity index 100% rename from src/cascadia/LocalTests_Remoting/pch.cpp rename to src/cascadia/UnitTests_Remoting/pch.cpp diff --git a/src/cascadia/LocalTests_Remoting/pch.h b/src/cascadia/UnitTests_Remoting/pch.h similarity index 100% rename from src/cascadia/LocalTests_Remoting/pch.h rename to src/cascadia/UnitTests_Remoting/pch.h diff --git a/tools/runut.cmd b/tools/runut.cmd index c6cec1fd12b..7b7571c678b 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -23,10 +23,8 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Types.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\til.unit.tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^ - %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\LocalTests_Remoting\Remoting.LocalTests.dll ^ + %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_Remoting\Remoting.UnitTests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ %_TestHostAppPath%\SettingsModel.LocalTests.dll ^ %* - - rem %_TestHostAppPath%\Remoting.LocalTests.dll ^ From a3faed6b7d82049b7f43162bfd88bd8534d7de15 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 11:15:57 -0600 Subject: [PATCH 11/88] finish renaming this test --- OpenConsole.sln | 3 +-- build/pipelines/templates/helix-runtests-job.yml | 7 ------- ...oting.LocalTests.vcxproj => Remoting.UnitTests.vcxproj} | 6 +++--- .../{LocalTests_Remoting.def => UnitTests_Remoting.def} | 0 src/cascadia/UnitTests_Remoting/pch.h | 1 - tools/OpenConsole.psm1 | 2 +- tools/tests.xml | 2 +- 7 files changed, 6 insertions(+), 15 deletions(-) rename src/cascadia/UnitTests_Remoting/{Remoting.LocalTests.vcxproj => Remoting.UnitTests.vcxproj} (93%) rename src/cascadia/UnitTests_Remoting/{LocalTests_Remoting.def => UnitTests_Remoting.def} (100%) diff --git a/OpenConsole.sln b/OpenConsole.sln index e94b662f4ac..06bfcb492b7 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -262,7 +262,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {CA5CAD1A-9B68-456A-B13E-C8218070DC42} - {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {68A10CD3-AA64-465B-AF5F-ED4E9700543C} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDB237B6-1D1D-400F-84CC-40A58FA59C8E}" @@ -350,7 +349,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68A10CD3-AA64-465B-AF5F-ED4E9700543C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Remoting", "src\cascadia\UnitTests_Remoting\Remoting.UnitTests.vcxproj", "{68A10CD3-AA64-465B-AF5F-ED4E9700543C}" ProjectSection(ProjectDependencies) = postProject {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} diff --git a/build/pipelines/templates/helix-runtests-job.yml b/build/pipelines/templates/helix-runtests-job.yml index 736bfc5338e..04169e342aa 100644 --- a/build/pipelines/templates/helix-runtests-job.yml +++ b/build/pipelines/templates/helix-runtests-job.yml @@ -113,13 +113,6 @@ jobs: testSuite: '${{ parameters.testSuite }}' taefQuery: ${{ parameters.taefQuery }} - - template: helix-createprojfile-steps.yml - parameters: - condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) - testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\Remoting.LocalTests.dll' - outputProjFileName: 'RunTestsInHelix-RemotingLocalTests.proj' - testSuite: '${{ parameters.testSuite }}' - taefQuery: ${{ parameters.taefQuery }} - template: helix-createprojfile-steps.yml parameters: diff --git a/src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj similarity index 93% rename from src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj rename to src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj index c91a5d003e8..a151a594027 100644 --- a/src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj +++ b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj @@ -14,9 +14,9 @@ {68a10cd3-aa64-465b-af5f-ed4e9700543c} Win32Proj - RemotingLocalTests - LocalTests_Remoting - Remoting.LocalTests + RemotingUnitTests + UnitTests_Remoting + Remoting.UnitTests DynamicLibrary 10.0.18362.0 10.0.18362.0 diff --git a/src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def b/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def similarity index 100% rename from src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def rename to src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def diff --git a/src/cascadia/UnitTests_Remoting/pch.h b/src/cascadia/UnitTests_Remoting/pch.h index a88cde20a9c..da79b2d4fd8 100644 --- a/src/cascadia/UnitTests_Remoting/pch.h +++ b/src/cascadia/UnitTests_Remoting/pch.h @@ -22,7 +22,6 @@ Licensed under the MIT license. #include #include -#include #include "consoletaeftemplates.hpp" #include diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index fda169365c4..fd205c2ba51 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -159,7 +159,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'localRemoting')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'unitRemoting')] [string]$Test, [parameter(Mandatory=$false)] diff --git a/tools/tests.xml b/tools/tests.xml index 74beb621f15..0a296e21f6d 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -6,7 +6,7 @@ - + From 9c6eac4e86e387d19e3d1073d16eedde014eadbb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 12:39:59 -0600 Subject: [PATCH 12/88] Clean up a lot for review --- src/cascadia/Remoting/Monarch.cpp | 121 ++++++-------------- src/cascadia/Remoting/WindowManager.cpp | 29 ++--- src/cascadia/WindowsTerminal/AppHost.cpp | 136 ++++++++++------------- 3 files changed, 102 insertions(+), 184 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 70b02d60191..cfac14f3011 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -6,6 +6,7 @@ #include "../../types/inc/utils.hpp" using namespace winrt; +using namespace winrt::Microsoft::Terminal; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; @@ -22,7 +23,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Monarch::~Monarch() { - printf("~Monarch()\n"); } uint64_t Monarch::GetPID() @@ -30,27 +30,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _ourPID; } - uint64_t Monarch::AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant) + uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) { - // TODO: This whole algorithm is terrible. There's gotta be a better way + // TODO:projects/5 This is terrible. There's gotta be a better way // of finding the first opening in a non-consecutive map of int->object auto providedID = peasant.GetID(); if (providedID == 0) { + // Peasant doesn't currently have an ID. Assign it a new one. peasant.AssignID(_nextPeasantID++); - printf("Assigned the peasant the ID %lld\n", peasant.GetID()); } else { - printf("Peasant already had an ID, %lld\n", peasant.GetID()); + // Peasant already had an ID (from an older monarch). Leave that one + // be. Make sure that the next peasant's ID is higher than it. _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; } + auto newPeasantsId = peasant.GetID(); _peasants[newPeasantsId] = peasant; - _setMostRecentPeasant(newPeasantsId); - printf("(the next new peasant will get the ID %lld)\n", _nextPeasantID); + // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); return newPeasantsId; @@ -59,14 +60,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/) { - if (auto peasant{ sender.try_as() }) + // TODO:projects/5 Pass the desktop and timestamp of when the window was + // activated in `args`. + + if (auto peasant{ sender.try_as() }) { auto theirID = peasant.GetID(); _setMostRecentPeasant(theirID); } } - winrt::Microsoft::Terminal::Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) + // Method Description: + // - Lookup a peasant by its ID. + // Arguments: + // - peasantID: The ID Of the peasant to find + // Return Value: + // - the peasant if it exists in our map, otherwise null + Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) { auto peasantSearch = _peasants.find(peasantID); return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; @@ -74,8 +84,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Monarch::_setMostRecentPeasant(const uint64_t peasantID) { + // TODO:projects/5 Use a heap/priority queue per-desktop to track which + // peasant was the most recent per-desktop. When we want to get the most + // recent of all desktops (WindowingBehavior::UseExisting), then use the + // most recent of all desktops. _mostRecentPeasant = peasantID; - printf("\x1b[90mThe most recent peasant is now \x1b[m#%llu\n", _mostRecentPeasant); } void Monarch::SetSelfID(const uint64_t selfID) @@ -96,86 +109,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _setMostRecentPeasant(_thisPeasantID); } - bool Monarch::ProposeCommandline(array_view args, - winrt::hstring cwd) + bool Monarch::ProposeCommandline(array_view /*args*/, + winrt::hstring /*cwd*/) { - // auto argsProcessed = 0; - // std::wstring fullCmdline; - // for (const auto& arg : args) - // { - // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - // fullCmdline += L" "; - // } - // wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); - // wprintf(fullCmdline.c_str()); - // wprintf(L"\"\n"); - - bool createNewWindow = true; - - if (args.size() >= 3) - { - // We'll need three args at least - [WindowsTerminal.exe, -s, - // id] to be able to have a session ID passed on the commandline. - - if (args[1] == L"-w" || args[1] == L"--window") - { - auto sessionId = std::stoi({ args[2].data(), args[2].size() }); - printf("Found a commandline intended for session %d\n", sessionId); - - // TODO:MG - // HACK: do an args[2:] to slice off the `-w window` args. - array_view argsNoWindow{ args.begin() + 2, args.end() }; - if (sessionId < 0) - { - printf("That certainly isn't a valid ID, they should make a new window.\n"); - createNewWindow = true; - } - else if (sessionId == 0) - { - printf("Session 0 is actually #%llu\n", _mostRecentPeasant); - if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) - { - // TODO In the morning: - // Right now, this commandline includes the "-w window" param, and CLI11 is biting it when parsing that. - // Either: - // * hack yank it for the time being (args[2:]) - // * actually have an AppCommandlineArgs do the parsing. - - auto eventArgs = winrt::make_self(argsNoWindow, cwd); - mruPeasant.ExecuteCommandline(*eventArgs); - createNewWindow = false; - } - } - else - { - if (auto otherPeasant = _getPeasant(sessionId)) - { - auto eventArgs = winrt::make_self(argsNoWindow, cwd); - otherPeasant.ExecuteCommandline(*eventArgs); - createNewWindow = false; - } - else - { - printf("I couldn't find a peasant for that ID, they should make a new window.\n"); - } - } - } - } - else if (_windowingBehavior == WindowingBehavior::UseExisting) - { - if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) - { - auto eventArgs = winrt::make_self(args, cwd); - mruPeasant.ExecuteCommandline(*eventArgs); - createNewWindow = false; - } - } - else - { - printf("They definitely weren't an existing process. They should make a new window.\n"); - } - - return createNewWindow; + // TODO:projects/5 + // The branch dev/migrie/f/remote-commandlines has a more complete + // version of this function, with a naive implementation. For now, we + // always want to create a new window, so we'll just return true. This + // will tell the caller that we didn't handle the commandline, and they + // should open a new window to deal with it themself. + return true; } } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 5ba26eada7a..884eab6f26a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -15,9 +15,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowManager::WindowManager() { + // Register with COM as a server for the Monarch class _registerAsMonarch(); + // Instantiate an instance of the Monarch. This may or may not be in-proc! _createMonarch(); } + WindowManager::~WindowManager() { // IMPORTANT! Tear down the registration as soon as we exit. If we're not a @@ -36,22 +39,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // launched with them! // // Otherwise, the King will tell us if we should make a new window - const bool createNewWindow = isKing || - _monarch.ProposeCommandline(args, cwd); + _shouldCreateWindow = isKing || + _monarch.ProposeCommandline(args, cwd); - if (createNewWindow) + if (_shouldCreateWindow) { + // If we should create a new window, then instantiate our Peasant + // instance, and tell that peasant to handle that commandline. _createOurPeasant(); auto eventArgs = winrt::make_self(args, cwd); _peasant.ExecuteCommandline(*eventArgs); - _shouldCreateWindow = true; - } - else - { - // printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n"); - _shouldCreateWindow = false; } + // Othersize, we'll do _nothing_. } bool WindowManager::ShouldCreateWindow() @@ -93,14 +93,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto p = winrt::make_self(); _peasant = *p; - auto ourID = _monarch.AddPeasant(_peasant); - ourID; - // printf("The monarch assigned us the ID %llu\n", ourID); - - // if (areWeTheKing()) - // { - // remindKingWhoTheyAre(*peasant); - // } + _monarch.AddPeasant(_peasant); + + // TODO:MG Spawn a thread to wait on the monarch, and handle the election return _peasant; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0eb9418cd09..aeb5cc739df 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -30,51 +30,17 @@ AppHost::AppHost() noexcept : { _logic = _app.Logic(); // get a ref to app's logic + // If there were commandline args to our process, try and process them here. + // Do this before AppLogic::Create, otherwise this will have no effect. + // + // This will send our commandline to the Monarch, to ask if we should make a + // new window or not. If not, exit immediately. + _HandleCommandlineArgs(); + if (!_shouldCreateWindow) { - std::vector args; - if (auto commandline{ GetCommandLineW() }) - { - int argc = 0; - - // Get the argv, and turn them into a hstring array to pass to the app. - wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - if (argv) - { - for (auto& elem : wil::make_range(argv.get(), argc)) - { - args.emplace_back(elem); - } - } - } - if (args.empty()) - { - args.emplace_back(L"wt.exe"); - } - - _windowManager.ProposeCommandline({ args }, L"placeholder/cwd"); - // _RegisterAsMonarch(); - // _CreateMonarch(); - _shouldCreateWindow = _windowManager.ShouldCreateWindow(); - if (!_shouldCreateWindow) - { - return; - } - if (auto peasant{ _windowManager.CurrentWindow() }) - { - peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); - } - - // TODO:MG if we end up not creating a new window, we crash. I'm - // thinking this is because the XAML host is not happy about being torn - // down before it has a chance to do really anything. Is there some way - // to get the app logic without instantiating the entire app? or at - // least the parts we'll need for remoting? + return; } - // If there were commandline args to our process, try and process them here. - // Do this before AppLogic::Create, otherwise this will have no effect - _HandleCommandlineArgs(); // TODO:MG <-- This probably needs to move into _ProposeCommandlineToMonarch - _useNonClientArea = _logic.GetShowTabsInTitlebar(); if (_useNonClientArea) { @@ -137,9 +103,35 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& } } +void _buildArgsFromCommandline(std::vector& args) +{ + if (auto commandline{ GetCommandLineW() }) + { + int argc = 0; + + // Get the argv, and turn them into a hstring array to pass to the app. + wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + if (argv) + { + for (auto& elem : wil::make_range(argv.get(), argc)) + { + args.emplace_back(elem); + } + } + } + if (args.empty()) + { + args.emplace_back(L"wt.exe"); + } +} + // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to -// the app logic for processing. +// the WindowManager, to ask if we should become a window process. +// - If we should create a window, then pass the arguements to the app logic for +// processing. +// - If we shouldn't become a window, set _shouldCreateWindow to false and exit +// immediately. // - If the logic determined there's an error while processing that commandline, // display a message box to the user with the text of the error, and exit. // * We display a message box because we're a Win32 application (not a @@ -152,6 +144,17 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& // - void AppHost::_HandleCommandlineArgs() { + std::vector args; + _buildArgsFromCommandline(args); + std::wstring cwd{ wil::GetCurrentDirectoryW() }; + _windowManager.ProposeCommandline({ args }, { cwd }); + + _shouldCreateWindow = _windowManager.ShouldCreateWindow(); + if (!_shouldCreateWindow) + { + return; + } + if (auto peasant{ _windowManager.CurrentWindow() }) { if (auto args{ peasant.InitialArgs() }) @@ -176,42 +179,19 @@ void AppHost::_HandleCommandlineArgs() } } } + + // After handling the initial args, hookup the callback for handling + // future commandline invocations. When our peasant is told to execute a + // commandline (in the future), it'll trigger this callback, that we'll + // use to send the actions to the app. + peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); } - // if (auto commandline{ GetCommandLineW() }) - // { - // int argc = 0; - - // // Get the argv, and turn them into a hstring array to pass to the app. - // wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - // if (argv) - // { - // std::vector args; - // for (auto& elem : wil::make_range(argv.get(), argc)) - // { - // args.emplace_back(elem); - // } - - // const auto result = _logic.SetStartupCommandline({ args }); - // const auto message = _logic.ParseCommandlineMessage(); - // if (!message.empty()) - // { - // const auto displayHelp = result == 0; - // const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE; - // const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR; - // // TODO:GH#4134: polish this dialog more, to make the text more - // // like msiexec /? - // MessageBoxW(nullptr, - // message.data(), - // GetStringResource(messageTitle).data(), - // MB_OK | messageIcon); - - // if (_logic.ShouldExitEarly()) - // { - // ExitProcess(result); - // } - // } - // } - // } + + // TODO:projects/5 if we end up not creating a new window, we crash. I'm + // thinking this is because the XAML host is not happy about being torn + // down before it has a chance to do really anything. Is there some way + // to get the app logic without instantiating the entire app? or at + // least the parts we'll need for remoting? } // Method Description: From 010333198725f21791c4d9ad28a7dc7f990ba816 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 12:49:27 -0600 Subject: [PATCH 13/88] cleanup Peasant for review --- src/cascadia/Remoting/Monarch.cpp | 21 +++------------ src/cascadia/Remoting/Monarch.h | 2 -- src/cascadia/Remoting/Peasant.cpp | 24 +++++------------ src/cascadia/Remoting/Peasant.h | 11 +++++--- .../UnitTests_Remoting/RemotingTests.cpp | 26 +++++++++++++++++++ 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cfac14f3011..5aa35ca425e 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -54,6 +54,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + // TODO:projects/5 Wait on the peasant's PID, and remove them from the + // map if they die. + return newPeasantsId; } @@ -91,24 +94,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mostRecentPeasant = peasantID; } - void Monarch::SetSelfID(const uint64_t selfID) - { - this->_thisPeasantID = selfID; - // Right now, the monarch assumes the role of the most recent - // window. If the monarch dies, and a new monarch takes over, then the - // entire stack of MRU windows will go with it. That's not what you - // want! - // - // In the real app, we'll have each window also track the timestamp it - // was activated at, and the monarch will cache these. So a new monarch - // could re-query these last activated timestamps, and reconstruct the - // MRU stack. - // - // This is a sample though, and we're not too worried about complete - // correctness here. - _setMostRecentPeasant(_thisPeasantID); - } - bool Monarch::ProposeCommandline(array_view /*args*/, winrt::hstring /*cwd*/) { diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index a79cd7754fd..bacea9731fd 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -34,8 +34,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); - void SetSelfID(const uint64_t selfID); - bool ProposeCommandline(array_view args, winrt::hstring cwd); private: diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index cd8b98fe55a..3dbb6e36081 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -11,7 +11,12 @@ using namespace ::Microsoft::Console; namespace winrt::Microsoft::Terminal::Remoting::implementation { - Peasant::Peasant() + Peasant::Peasant() : + _ourPID{ GetCurrentProcessId() } + { + } + Peasant::Peasant(const uint64_t testPID) : + _ourPID{ testPID } { } @@ -26,7 +31,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Peasant::GetPID() { - return GetCurrentProcessId(); + return _ourPID; } bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args) @@ -38,24 +43,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _ExecuteCommandlineRequestedHandlers(*this, args); - // auto argsProcessed = 0; - // std::wstring fullCmdline; - // for (const auto& arg : args) - // { - // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - // fullCmdline += L" "; - // } - // wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); - // wprintf(fullCmdline.c_str()); - // wprintf(L"\"\n"); return true; } - void Peasant::raiseActivatedEvent() - { - _WindowActivatedHandlers(*this, nullptr); - } - Remoting::CommandlineArgs Peasant::InitialArgs() { return _initialArgs; diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index e2181690e2b..d24d6c11656 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -3,6 +3,10 @@ #include "Peasant.g.h" #include "../cascadia/inc/cppwinrt_utils.h" +namespace RemotingUnitTests +{ + class RemotingTests; +}; namespace winrt::Microsoft::Terminal::Remoting::implementation { struct Peasant : public PeasantT @@ -15,18 +19,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - void raiseActivatedEvent(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); private: + Peasant(const uint64_t testPID); + uint64_t _ourPID; + uint64_t _id{ 0 }; winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; - // array_view _args; - // winrt::hstring _cwd; + friend class RemotingUnitTests::RemotingTests; }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index ac96debf541..3cf4f8d92f8 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -18,6 +18,12 @@ using namespace winrt::Microsoft::Terminal; name.attach(&_local_##name##); \ auto cleanup = wil::scope_exit([&]() { name.detach(); }); +#define MAKE_PEASANT(name, pid) \ + Remoting::implementation::Peasant _local_##name##{ pid }; \ + com_ptr name; \ + name.attach(&_local_##name##); \ + auto cleanup = wil::scope_exit([&]() { name.detach(); }); + namespace RemotingUnitTests { class RemotingTests @@ -26,6 +32,7 @@ namespace RemotingUnitTests END_TEST_CLASS() TEST_METHOD(CreateMonarch); + TEST_METHOD(CreatePeasant); TEST_CLASS_SETUP(ClassSetup) { @@ -52,4 +59,23 @@ namespace RemotingUnitTests L"A Monarch with an explicit PID should use the one we provided"); } + void RemotingTests::CreatePeasant() + { + auto p1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(p1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + p1->GetPID(), + L"A Peasant without an explicit PID should use the current PID"); + + Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); + + auto expectedFakePID = 2345u; + MAKE_PEASANT(p2, expectedFakePID); + + VERIFY_IS_NOT_NULL(p2); + VERIFY_ARE_EQUAL(expectedFakePID, + p2->GetPID(), + L"A Peasant with an explicit PID should use the one we provided"); + } + } From b4fe1bffbfed2fecad8f8c61d6a1ee0c3f01efbc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 14:26:06 -0600 Subject: [PATCH 14/88] Final cleanup for review --- src/cascadia/Remoting/Monarch.cpp | 31 +++++++++++++++++-- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/Remoting/Peasant.cpp | 9 ++++++ src/cascadia/Remoting/WindowManager.cpp | 8 ++--- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/Remoting/WindowManager.idl | 5 ++- .../UnitTests_Remoting/RemotingTests.cpp | 10 ++++-- src/cascadia/WindowsTerminal/AppHost.cpp | 5 ++- 9 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 5aa35ca425e..58d29135671 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -16,6 +16,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _ourPID{ GetCurrentProcessId() } { } + + // This is a private constructor to be used in unit tests, where we don't + // want each Monarch to necessarily use the current PID. Monarch::Monarch(const uint64_t testPID) : _ourPID{ testPID } { @@ -30,6 +33,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _ourPID; } + // Method Description: + // - Add the given peasant to the list of peasants we're tracking. This Peasant may have already been assigned an ID. If it hasn't, then give it an ID. + // Arguments: + // - peasant: the new Peasant to track. + // Return Value: + // - the ID assigned to the peasant. uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) { // TODO:projects/5 This is terrible. There's gotta be a better way @@ -55,11 +64,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); // TODO:projects/5 Wait on the peasant's PID, and remove them from the - // map if they die. + // map if they die. This won't work great in tests though, with fake + // PIDs. return newPeasantsId; } + // Method Description: + // - Event handler for the Peasant::WindowActivated event. Used as an + // opportunity for us to update our internal stack of the "most recent + // window". + // Arguments: + // - sender: the Peasant that raised this event. This might be out-of-proc! + // Return Value: + // - void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/) { @@ -94,8 +112,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mostRecentPeasant = peasantID; } - bool Monarch::ProposeCommandline(array_view /*args*/, - winrt::hstring /*cwd*/) + // Method Description: + // - Try to handle a commandline from a new WT invocation. We might need to + // hand the commandline to an existing window, or we might need to tell + // the caller that they need to become a new window to handle it themself. + // Arguments: + // - + // Return Value: + // - + bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& /*args*/) { // TODO:projects/5 // The branch dev/migrie/f/remote-commandlines has a more complete diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index bacea9731fd..5331f753f45 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -34,7 +34,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); - bool ProposeCommandline(array_view args, winrt::hstring cwd); + bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); private: Monarch(const uint64_t testPID); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 8b228480caa..bf89bcccef1 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -7,6 +7,6 @@ namespace Microsoft.Terminal.Remoting UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); - Boolean ProposeCommandline(String[] args, String cwd); + Boolean ProposeCommandline(CommandlineArgs args); }; } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 3dbb6e36081..a0b8d8b372d 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -15,6 +15,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _ourPID{ GetCurrentProcessId() } { } + + // This is a private constructor to be used in unit tests, where we don't + // want each Peasant to necessarily use the current PID. Peasant::Peasant(const uint64_t testPID) : _ourPID{ testPID } { @@ -36,11 +39,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args) { + // If this is the first set of args we were ever told about, stash them + // away. We'll need to get at them later, when we setup the startup + // actions for the window. if (_initialArgs == nullptr) { _initialArgs = args; } + // Raise an event with these args. The AppHost will listen for this + // event to know when to take these args and dispatch them to a + // currently-running window. _ExecuteCommandlineRequestedHandlers(*this, args); return true; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 884eab6f26a..6f50eadebed 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -31,8 +31,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; } - void WindowManager::ProposeCommandline(array_view args, - const winrt::hstring cwd) + void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) { const bool isKing = _areWeTheKing(); // If we're the king, we _definitely_ want to process the arguments, we were @@ -40,7 +39,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // Otherwise, the King will tell us if we should make a new window _shouldCreateWindow = isKing || - _monarch.ProposeCommandline(args, cwd); + _monarch.ProposeCommandline(args); if (_shouldCreateWindow) { @@ -48,8 +47,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // instance, and tell that peasant to handle that commandline. _createOurPeasant(); - auto eventArgs = winrt::make_self(args, cwd); - _peasant.ExecuteCommandline(*eventArgs); + _peasant.ExecuteCommandline(args); } // Othersize, we'll do _nothing_. } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 1d36ec15bf5..8f60530b455 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -12,7 +12,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager(); ~WindowManager(); - void ProposeCommandline(array_view args, const winrt::hstring cwd); + void ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); bool ShouldCreateWindow(); winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 55785aac718..e9ef1ee345c 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -3,12 +3,11 @@ import "Peasant.idl"; namespace Microsoft.Terminal.Remoting { - [default_interface] runtimeclass WindowManager { WindowManager(); - void ProposeCommandline(String[] commands, String cwd); + void ProposeCommandline(CommandlineArgs args); Boolean ShouldCreateWindow { get; }; - Peasant CurrentWindow(); + IPeasant CurrentWindow(); }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 3cf4f8d92f8..396482ecc03 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -12,17 +12,23 @@ using namespace WEX::Common; using namespace winrt; using namespace winrt::Microsoft::Terminal; +// These are some gross macros that let us call a private ctor for +// Monarch/Peasant. We can't just use make_self, because that doesn't let us +// call a private ctor. We can use com_ptr::attach, but since we're allocating +// the thing on the stack, we need to make sure to call detach before the object +// is destructed. + #define MAKE_MONARCH(name, pid) \ Remoting::implementation::Monarch _local_##name##{ pid }; \ com_ptr name; \ name.attach(&_local_##name##); \ - auto cleanup = wil::scope_exit([&]() { name.detach(); }); + auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); }); #define MAKE_PEASANT(name, pid) \ Remoting::implementation::Peasant _local_##name##{ pid }; \ com_ptr name; \ name.attach(&_local_##name##); \ - auto cleanup = wil::scope_exit([&]() { name.detach(); }); + auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); }); namespace RemotingUnitTests { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index aeb5cc739df..38c478b40d7 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -14,6 +14,7 @@ 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 winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace ::Microsoft::Console; using namespace ::Microsoft::Console::Types; @@ -147,7 +148,9 @@ void AppHost::_HandleCommandlineArgs() std::vector args; _buildArgsFromCommandline(args); std::wstring cwd{ wil::GetCurrentDirectoryW() }; - _windowManager.ProposeCommandline({ args }, { cwd }); + + Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; + _windowManager.ProposeCommandline(eventArgs); _shouldCreateWindow = _windowManager.ShouldCreateWindow(); if (!_shouldCreateWindow) From d08e65ce032fba14c24665907336986f8ec1ac7a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 14:33:50 -0600 Subject: [PATCH 15/88] _final_ final cleanup for review --- .../actions/spell-check/dictionary/apis.txt | 1 + build/Helix/runtests.cmd | 2 +- src/cascadia/Remoting/CommandlineArgs.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 21 +------------------ src/cascadia/WindowsTerminal/AppHost.h | 5 ----- 5 files changed, 4 insertions(+), 27 deletions(-) diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index 627ddbf0875..73fe69d0835 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -34,6 +34,7 @@ IExplorer IInheritable IMap IObject +IPeasant IStorage ITab ITaskbar diff --git a/build/Helix/runtests.cmd b/build/Helix/runtests.cmd index 3b155e279a9..b55ee2cf3c8 100644 --- a/build/Helix/runtests.cmd +++ b/build/Helix/runtests.cmd @@ -28,7 +28,7 @@ echo %TIME% powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1 echo %TIME% -set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Remoting.LocalTests.dll Conhost.UIA.Tests.dll +set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll set testBinaries= for %%B in (%testBinaryCandidates%) do ( if exist %%B ( diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index bbe31961c6e..b9830d3d499 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -10,7 +10,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // If you try to move this into the header, you will experience P A I N // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler - // will give you just the most impossible tmplate errors to try and + // will give you just the most impossible template errors to try and // decipher. void CommandlineArgs::Args(winrt::array_view const& value) { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 38c478b40d7..985ea6b8a81 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -517,26 +517,7 @@ bool AppHost::HasWindow() return _shouldCreateWindow; } -// void AppHost::_RegisterAsMonarch() -// { -// winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, -// winrt::make<::MonarchFactory>().get(), -// CLSCTX_LOCAL_SERVER, -// REGCLS_MULTIPLEUSE, -// &_registrationHostClass)); -// } - -// void AppHost::_CreateMonarch() -// { -// } - -// bool AppHost::_ProposeCommandlineToMonarch() -// { -// // returns true if we should create a new window -// return true; -// } - -void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, +void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) { _logic.ExecuteCommandline(args.Args()); diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 22dbbc2c113..18c175d1757 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -26,7 +26,6 @@ class AppHost winrt::TerminalApp::App _app; winrt::TerminalApp::AppLogic _logic; bool _shouldCreateWindow{ false }; - // DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; void _HandleCommandlineArgs(); @@ -46,10 +45,6 @@ class AppHost const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const til::point coord, const int32_t delta); - // void _RegisterAsMonarch(); - // void _CreateMonarch(); - // bool _ProposeCommandlineToMonarch(); - void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); }; From e101efd11dba93208bba9c4191c937eabbbb4ed3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 07:12:12 -0600 Subject: [PATCH 16/88] pr nits --- src/cascadia/Remoting/CommandlineArgs.cpp | 1 + src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj | 3 --- src/cascadia/Remoting/Monarch.cpp | 4 ++-- src/cascadia/Remoting/WindowManager.cpp | 4 ++-- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 3 +-- tools/tests.xml | 2 +- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index b9830d3d499..db90f3381fd 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -8,6 +8,7 @@ using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Terminal::Remoting::implementation { + // LOAD BEARING CODE // If you try to move this into the header, you will experience P A I N // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler // will give you just the most impossible template errors to try and diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index b5d5355e414..d3f1bf6c7f4 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -82,9 +82,6 @@ pch.h - ..;$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); - - 4702;%(DisableSpecificWarnings) WindowsApp.lib;user32.lib;shell32.lib;%(AdditionalDependencies) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 58d29135671..46e18dab3d4 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -115,7 +115,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Method Description: // - Try to handle a commandline from a new WT invocation. We might need to // hand the commandline to an existing window, or we might need to tell - // the caller that they need to become a new window to handle it themself. + // the caller that they need to become a new window to handle it themselves. // Arguments: // - // Return Value: @@ -127,7 +127,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // version of this function, with a naive implementation. For now, we // always want to create a new window, so we'll just return true. This // will tell the caller that we didn't handle the commandline, and they - // should open a new window to deal with it themself. + // should open a new window to deal with it themselves. return true; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 6f50eadebed..c0a2b87f2fe 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant.ExecuteCommandline(args); } - // Othersize, we'll do _nothing_. + // Otherwise, we'll do _nothing_. } bool WindowManager::ShouldCreateWindow() @@ -74,7 +74,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // definitions for our types automatically. This only works in the following // cases: // - // * If we're running unpackaged: the .winmd but be a sibling of the .exe + // * If we're running unpackaged: the .winmd must be a sibling of the .exe // * If we're running packaged: the .winmd must be in the package root _monarch = create_instance(Monarch_clsid, CLSCTX_LOCAL_SERVER); diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 8f60530b455..f755d0c700a 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -7,7 +7,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { - struct WindowManager : public WindowManagerT + struct WindowManager final : public WindowManagerT { WindowManager(); ~WindowManager(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 985ea6b8a81..57043b0ed13 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "AppHost.h" -// #include "MonarchFactory.h" #include "../types/inc/Viewport.hpp" #include "../types/inc/utils.hpp" #include "../types/inc/User32Utils.hpp" @@ -129,7 +128,7 @@ void _buildArgsFromCommandline(std::vector& args) // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to // the WindowManager, to ask if we should become a window process. -// - If we should create a window, then pass the arguements to the app logic for +// - If we should create a window, then pass the arguments to the app logic for // processing. // - If we shouldn't become a window, set _shouldCreateWindow to false and exit // immediately. diff --git a/tools/tests.xml b/tools/tests.xml index 0a296e21f6d..d1831acfad8 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -6,7 +6,7 @@ - + From e1402d834f2dfb6055989b93f4976743c8b34c76 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 09:29:45 -0600 Subject: [PATCH 17/88] This seems to work to create a thread listening for the monarch, and safely tear it down. It _seems_ like it. --- src/cascadia/Remoting/WindowManager.cpp | 81 ++++++++++++++++++++++++- src/cascadia/Remoting/WindowManager.h | 8 ++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index c0a2b87f2fe..50de554bf30 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -15,6 +15,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowManager::WindowManager() { + _monarchWaitInterrupt.create(); + // Register with COM as a server for the Monarch class _registerAsMonarch(); // Instantiate an instance of the Monarch. This may or may not be in-proc! @@ -29,6 +31,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // monarch! CoRevokeClassObject(_registrationHostClass); _registrationHostClass = 0; + _monarchWaitInterrupt.SetEvent(); + if (_electionThread.joinable()) + { + _electionThread.join(); + } } void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) @@ -47,6 +54,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // instance, and tell that peasant to handle that commandline. _createOurPeasant(); + // TODO:MG Spawn a thread to wait on the monarch, and handle the election + if (!isKing) + { + _createPeasantThread(); + } + _peasant.ExecuteCommandline(args); } // Otherwise, we'll do _nothing_. @@ -93,11 +106,75 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant = *p; _monarch.AddPeasant(_peasant); - // TODO:MG Spawn a thread to wait on the monarch, and handle the election - return _peasant; } + bool WindowManager::_electionNight2020() + { + _createMonarch(); + if (_areWeTheKing()) + { + return true; + } + return false; + } + + void WindowManager::_createPeasantThread() + { + // If we catch an exception trying to get at the monarch ever, we can + // set the _monarchWaitInterrupt, and use that to trigger a new + // election. Though, we wouldn't be able to retry the function that + // caused the exception in the first place... + + _electionThread = std::thread([this] { + HANDLE waits[2]; + waits[1] = _monarchWaitInterrupt.get(); + + bool exitRequested = false; + while (!exitRequested) + { + wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast(_monarch.GetPID())) }; + // TODO:MG + // if (hMonarch.get() == nullptr) + // { + // const auto gle = GetLastError(); + // return false; + // } + waits[0] = hMonarch.get(); + auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); + + switch (waitResult) + { + case WAIT_OBJECT_0 + 0: // waits[0] was signaled + // printf("THE KING IS \x1b[31mDEAD\x1b[m\n"); + // // Return false here - this will trigger us to find the new monarch + // return false; + // + // TODO:MG Connect to the new monarch, which might be us! + exitRequested = _electionNight2020(); + break; + case WAIT_OBJECT_0 + 1: // waits[1] was signaled + exitRequested = true; + break; + + case WAIT_TIMEOUT: + printf("Wait timed out. This should be impossible.\n"); + exitRequested = true; + break; + + // Return value is invalid. + default: + { + auto gle = GetLastError(); + printf("WaitForMultipleObjects returned: %d\n", waitResult); + printf("Wait error: %d\n", gle); + ExitProcess(0); + } + } + } + }); + } + Remoting::Peasant WindowManager::CurrentWindow() { return _peasant; diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index f755d0c700a..e51fc79b07b 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -7,7 +7,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { - struct WindowManager final : public WindowManagerT + struct WindowManager final : public WindowManagerT { WindowManager(); ~WindowManager(); @@ -23,10 +23,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; + wil::unique_event _monarchWaitInterrupt; + std::thread _electionThread; + void _registerAsMonarch(); void _createMonarch(); bool _areWeTheKing(); winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); + + bool _electionNight2020(); + void _createPeasantThread(); }; } From f978a9c52cb2b5ca935521e70773fa652d2fb331 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 12:45:29 -0600 Subject: [PATCH 18/88] it's hard to believe he's gone / were it so easy --- src/cascadia/Remoting/Monarch.cpp | 19 +- src/cascadia/Remoting/WindowManager.cpp | 54 ++++- src/cascadia/Remoting/WindowManager.h | 7 + .../UnitTests_Remoting/RemotingTests.cpp | 208 ++++++++++++++++++ 4 files changed, 278 insertions(+), 10 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 46e18dab3d4..d8543360cc7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -66,6 +66,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // TODO:projects/5 Wait on the peasant's PID, and remove them from the // map if they die. This won't work great in tests though, with fake // PIDs. + // + // We should trigger a callback. The manager will use this callback as + // an opportunity to start waiting on the new peasant. return newPeasantsId; } @@ -99,8 +102,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - the peasant if it exists in our map, otherwise null Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) { - auto peasantSearch = _peasants.find(peasantID); - return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + try + { + auto peasantSearch = _peasants.find(peasantID); + auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + maybeThePeasant.GetPID(); + return maybeThePeasant; + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + // TODO: Remove the peasant from the list of peasants + + return nullptr; + } } void Monarch::_setMostRecentPeasant(const uint64_t peasantID) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 50de554bf30..10bf4831465 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -16,11 +16,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager::WindowManager() { _monarchWaitInterrupt.create(); + // _peasantListenerInterrupt.create(); + + // wil::unique_event peasantListenerInterrupt; + // peasantListenerInterrupt.create(); + // _peasantHandles.emplace_back(std::move(peasantListenerInterrupt)); // Register with COM as a server for the Monarch class _registerAsMonarch(); // Instantiate an instance of the Monarch. This may or may not be in-proc! - _createMonarch(); + _createMonarchAndCallbacks(); } WindowManager::~WindowManager() @@ -32,10 +37,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CoRevokeClassObject(_registrationHostClass); _registrationHostClass = 0; _monarchWaitInterrupt.SetEvent(); + // _peasantListenerInterrupt.SetEvent(); if (_electionThread.joinable()) { _electionThread.join(); } + // if (_peasantListenerThread.joinable()) + // { + // _peasantListenerThread.join(); + // } } void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) @@ -93,6 +103,35 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CLSCTX_LOCAL_SERVER); } + void WindowManager::_createMonarchAndCallbacks() + { + _createMonarch(); + const auto isKing = _areWeTheKing(); + if (!isKing) + { + return; + } + // Here, we're the king! + + // TODO:MG Add an even handler to the monarch's PeasantAdded event. + // We'll use that callback as a chance to start waiting on the peasant's + // PID. If they die, we'll tell the monarch to remove them from the + // list. + // _peasantHandles.emplace_back(_peasantListenerInterrupt.get()); + + // _peasantListenerThread = std::thread([this]() { + + // bool exitRequested = false; + // while (!exitRequested) + // { + // } + // }); + + // Wait, don't. Let's just have the monarch try/catch any accesses to + // peasants. If the peasant dies, then it can't get the peasant's + // anything. In that case, _remove it_. + } + bool WindowManager::_areWeTheKing() { auto kingPID = _monarch.GetPID(); @@ -111,7 +150,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_electionNight2020() { - _createMonarch(); + _createMonarchAndCallbacks(); if (_areWeTheKing()) { return true; @@ -134,7 +173,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation while (!exitRequested) { wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast(_monarch.GetPID())) }; - // TODO:MG + // TODO:MG If we fail to open the monarch, then they don't exist + // anymore! Go straight to an election. + // // if (hMonarch.get() == nullptr) // { // const auto gle = GetLastError(); @@ -146,11 +187,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation switch (waitResult) { case WAIT_OBJECT_0 + 0: // waits[0] was signaled - // printf("THE KING IS \x1b[31mDEAD\x1b[m\n"); - // // Return false here - this will trigger us to find the new monarch - // return false; - // - // TODO:MG Connect to the new monarch, which might be us! + // Connect to the new monarch, which might be us! + // If we become the monarch, then we'll return true and exit this thread. exitRequested = _electionNight2020(); break; case WAIT_OBJECT_0 + 1: // waits[1] was signaled diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index e51fc79b07b..acbca463588 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -24,10 +24,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; wil::unique_event _monarchWaitInterrupt; + // wil::unique_event _peasantListenerInterrupt; + std::thread _electionThread; + // std::thread _peasantListenerThread; + + // // NON-OWNING HANDLES + // std::vector _peasantHandles{}; void _registerAsMonarch(); void _createMonarch(); + void _createMonarchAndCallbacks(); bool _areWeTheKing(); winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 396482ecc03..c23e2219555 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -32,6 +32,22 @@ using namespace winrt::Microsoft::Terminal; namespace RemotingUnitTests { + // This is a silly helper struct. + // It will always throw an hresult_error on any of it's methods. + // In the tests, it's hard to emulate a peasant process being totally dead once the Monarch has captured a reference to it. Since everything's in-proc in the tests, we can't decrement the refcount in such a way that the monarch's reference will throw a catchable exception. + // Instead, this class can be used to replace a peasant inside a Monarch, to emulate that peasant process dying. Any time the monarch tries to do something to this peasant, it'll throw an exception. + struct DeadPeasant : implements + { + DeadPeasant() = default; + void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; }; + uint64_t GetID() { throw winrt::hresult_error{}; }; + uint64_t GetPID() { throw winrt::hresult_error{}; }; + bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } + winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); + }; + class RemotingTests { BEGIN_TEST_CLASS(RemotingTests) @@ -39,13 +55,34 @@ namespace RemotingUnitTests TEST_METHOD(CreateMonarch); TEST_METHOD(CreatePeasant); + TEST_METHOD(CreatePeasantWithNew); + TEST_METHOD(AddPeasants); + TEST_METHOD(GetPeasantsByID); + TEST_METHOD(AddPeasantsToNewMonarch); + TEST_METHOD(RemovePeasantFromMonarchWhenFreed); TEST_CLASS_SETUP(ClassSetup) { return true; } + + static void _killPeasant(const com_ptr& m, + const uint64_t peasantID); }; + void RemotingTests::_killPeasant(const com_ptr& m, + const uint64_t peasantID) + { + if (peasantID <= 0) + { + return; + } + + com_ptr tombstone; + tombstone.attach(new DeadPeasant()); + m->_peasants[peasantID] = *tombstone; + } + void RemotingTests::CreateMonarch() { auto m1 = winrt::make_self(); @@ -84,4 +121,175 @@ namespace RemotingUnitTests L"A Peasant with an explicit PID should use the one we provided"); } + void RemotingTests::CreatePeasantWithNew() + { + Log::Comment(L"The same thing as the above test, but with `new` instead of insanity on the stack"); + + auto p1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(p1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + p1->GetPID(), + L"A Peasant without an explicit PID should use the current PID"); + + auto expectedFakePID = 2345u; + + com_ptr p2; + VERIFY_IS_NULL(p2); + p2.attach(new Remoting::implementation::Peasant(expectedFakePID)); + + VERIFY_IS_NOT_NULL(p2); + VERIFY_ARE_EQUAL(expectedFakePID, + p2->GetPID(), + L"A Peasant with an explicit PID should use the one we provided"); + } + + void RemotingTests::AddPeasants() + { + 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); + + 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()); + } + + void RemotingTests::GetPeasantsByID() + { + 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); + + 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()); + + auto maybeP1 = m0->_getPeasant(1); + VERIFY_IS_NOT_NULL(maybeP1); + VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID()); + + auto maybeP2 = m0->_getPeasant(2); + VERIFY_IS_NOT_NULL(maybeP2); + VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID()); + } + + void RemotingTests::AddPeasantsToNewMonarch() + { + 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); + + 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()); + + m3->AddPeasant(*p1); + m3->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + } + + void RemotingTests::RemovePeasantFromMonarchWhenFreed() + { + 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); + + 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()); + + RemotingTests::_killPeasant(m0, p1->GetID()); + + auto maybeP2 = m0->_getPeasant(2); + VERIFY_IS_NOT_NULL(maybeP2); + VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID()); + + auto maybeP1 = m0->_getPeasant(1); + // VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID()); + VERIFY_IS_NULL(maybeP1); + + VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); + } + } From 5939636182e1d433d18c767e42af8045d4b0490b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 12:48:23 -0600 Subject: [PATCH 19/88] Oh yea actually remove the thing --- src/cascadia/Remoting/Monarch.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index d8543360cc7..9792f5f1ec6 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -106,14 +106,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto peasantSearch = _peasants.find(peasantID); auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; - maybeThePeasant.GetPID(); + if (maybeThePeasant) + { + maybeThePeasant.GetPID(); + } return maybeThePeasant; } catch (...) { LOG_CAUGHT_EXCEPTION(); // TODO: Remove the peasant from the list of peasants - + _peasants.erase(peasantID); return nullptr; } } From c08889585bf3fdb05093b2713ef00402835fb51d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 15:21:53 -0600 Subject: [PATCH 20/88] last commit before the holidays --- src/cascadia/Remoting/Monarch.cpp | 9 +- src/cascadia/Remoting/WindowManager.cpp | 124 ++++++++---------- src/cascadia/Remoting/WindowManager.h | 6 +- .../UnitTests_Remoting/RemotingTests.cpp | 2 +- 4 files changed, 61 insertions(+), 80 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 9792f5f1ec6..591b5d177c4 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -63,13 +63,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); - // TODO:projects/5 Wait on the peasant's PID, and remove them from the - // map if they die. This won't work great in tests though, with fake - // PIDs. - // - // We should trigger a callback. The manager will use this callback as - // an opportunity to start waiting on the new peasant. - return newPeasantsId; } @@ -115,7 +108,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation catch (...) { LOG_CAUGHT_EXCEPTION(); - // TODO: Remove the peasant from the list of peasants + // Remove the peasant from the list of peasants _peasants.erase(peasantID); return nullptr; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 10bf4831465..cb8c124b52a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -16,11 +16,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager::WindowManager() { _monarchWaitInterrupt.create(); - // _peasantListenerInterrupt.create(); - - // wil::unique_event peasantListenerInterrupt; - // peasantListenerInterrupt.create(); - // _peasantHandles.emplace_back(std::move(peasantListenerInterrupt)); // Register with COM as a server for the Monarch class _registerAsMonarch(); @@ -37,15 +32,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CoRevokeClassObject(_registrationHostClass); _registrationHostClass = 0; _monarchWaitInterrupt.SetEvent(); - // _peasantListenerInterrupt.SetEvent(); + if (_electionThread.joinable()) { _electionThread.join(); } - // if (_peasantListenerThread.joinable()) - // { - // _peasantListenerThread.join(); - // } } void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) @@ -113,20 +104,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } // Here, we're the king! - // TODO:MG Add an even handler to the monarch's PeasantAdded event. - // We'll use that callback as a chance to start waiting on the peasant's - // PID. If they die, we'll tell the monarch to remove them from the - // list. - // _peasantHandles.emplace_back(_peasantListenerInterrupt.get()); - - // _peasantListenerThread = std::thread([this]() { - - // bool exitRequested = false; - // while (!exitRequested) - // { - // } - // }); - // Wait, don't. Let's just have the monarch try/catch any accesses to // peasants. If the peasant dies, then it can't get the peasant's // anything. In that case, _remove it_. @@ -151,6 +128,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_electionNight2020() { _createMonarchAndCallbacks(); + + // Tell the new monarch who we are. We might be that monarch! + _monarch.AddPeasant(_peasant); + if (_areWeTheKing()) { return true; @@ -166,51 +147,62 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // caused the exception in the first place... _electionThread = std::thread([this] { - HANDLE waits[2]; - waits[1] = _monarchWaitInterrupt.get(); + _waitOnMonarchThread(); + }); + } + + void WindowManager::_waitOnMonarchThread() + { + HANDLE waits[2]; + waits[1] = _monarchWaitInterrupt.get(); - bool exitRequested = false; - while (!exitRequested) + bool exitRequested = false; + while (!exitRequested) + { + wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, + FALSE, + static_cast(_monarch.GetPID())) }; + // TODO:MG If we fail to open the monarch, then they don't exist + // anymore! Go straight to an election. + // + // TODO:MG At any point in all this, the current monarch might die. + // We go straight to a new election, right? Worst case, eventually, + // we'll become the new monarch. + // + // if (hMonarch.get() == nullptr) + // { + // const auto gle = GetLastError(); + // return false; + // } + waits[0] = hMonarch.get(); + auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); + + switch (waitResult) { - wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast(_monarch.GetPID())) }; - // TODO:MG If we fail to open the monarch, then they don't exist - // anymore! Go straight to an election. - // - // if (hMonarch.get() == nullptr) - // { - // const auto gle = GetLastError(); - // return false; - // } - waits[0] = hMonarch.get(); - auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); - - switch (waitResult) - { - case WAIT_OBJECT_0 + 0: // waits[0] was signaled - // Connect to the new monarch, which might be us! - // If we become the monarch, then we'll return true and exit this thread. - exitRequested = _electionNight2020(); - break; - case WAIT_OBJECT_0 + 1: // waits[1] was signaled - exitRequested = true; - break; - - case WAIT_TIMEOUT: - printf("Wait timed out. This should be impossible.\n"); - exitRequested = true; - break; - - // Return value is invalid. - default: - { - auto gle = GetLastError(); - printf("WaitForMultipleObjects returned: %d\n", waitResult); - printf("Wait error: %d\n", gle); - ExitProcess(0); - } - } + case WAIT_OBJECT_0 + 0: // waits[0] was signaled + // Connect to the new monarch, which might be us! + // If we become the monarch, then we'll return true and exit this thread. + exitRequested = _electionNight2020(); + break; + case WAIT_OBJECT_0 + 1: // waits[1] was signaled + exitRequested = true; + break; + + case WAIT_TIMEOUT: + printf("Wait timed out. This should be impossible.\n"); + exitRequested = true; + break; + + // Return value is invalid. + default: + { + auto gle = GetLastError(); + printf("WaitForMultipleObjects returned: %d\n", waitResult); + printf("Wait error: %d\n", gle); + ExitProcess(0); } - }); + } + } } Remoting::Peasant WindowManager::CurrentWindow() diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index acbca463588..69c26175902 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -24,13 +24,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; wil::unique_event _monarchWaitInterrupt; - // wil::unique_event _peasantListenerInterrupt; std::thread _electionThread; - // std::thread _peasantListenerThread; - - // // NON-OWNING HANDLES - // std::vector _peasantHandles{}; void _registerAsMonarch(); void _createMonarch(); @@ -40,6 +35,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _electionNight2020(); void _createPeasantThread(); + void _waitOnMonarchThread(); }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index c23e2219555..dd3b5ad123d 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -279,6 +279,7 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); + Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch."); RemotingTests::_killPeasant(m0, p1->GetID()); auto maybeP2 = m0->_getPeasant(2); @@ -286,7 +287,6 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID()); auto maybeP1 = m0->_getPeasant(1); - // VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID()); VERIFY_IS_NULL(maybeP1); VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); From 0f5c24f661b356ad754b2700bd55a9fa9cb8b09a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 5 Jan 2021 11:22:23 -0600 Subject: [PATCH 21/88] Enable audit mode --- OpenConsole.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenConsole.sln b/OpenConsole.sln index 06bfcb492b7..eaf51110a64 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -2157,6 +2157,7 @@ Global {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.Build.0 = AuditMode|x64 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.Build.0 = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|Any CPU.ActiveCfg = Debug|Win32 From 921d91582b24ec4afedea45ee715a1aa6fb99ed1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 5 Jan 2021 11:28:10 -0600 Subject: [PATCH 22/88] nits --- .github/actions/spell-check/dictionary/apis.txt | 2 ++ src/cascadia/Remoting/Monarch.cpp | 5 ++++- src/cascadia/Remoting/Monarch.h | 3 +++ src/cascadia/Remoting/Monarch.idl | 3 +++ src/cascadia/Remoting/MonarchFactory.h | 6 +++--- src/cascadia/Remoting/Peasant.cpp | 3 +++ src/cascadia/Remoting/Peasant.h | 3 +++ src/cascadia/Remoting/Peasant.idl | 3 ++- src/cascadia/Remoting/WindowManager.cpp | 11 +++++++---- src/cascadia/Remoting/WindowManager.h | 5 ++++- .../Remoting/dll/Microsoft.Terminal.Remoting.vcxproj | 2 +- 11 files changed, 35 insertions(+), 11 deletions(-) diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index 73fe69d0835..4bb2d3daf84 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -44,6 +44,7 @@ localtime lround LSHIFT msappx +MULTIPLEUSE NCHITTEST NCLBUTTONDBLCLK NCRBUTTONDBLCLK @@ -57,6 +58,7 @@ otms OUTLINETEXTMETRICW overridable PAGESCROLL +REGCLS RETURNCMD rfind roundf diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 46e18dab3d4..ab32df78283 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "Monarch.h" #include "CommandlineArgs.h" @@ -43,7 +46,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // TODO:projects/5 This is terrible. There's gotta be a better way // of finding the first opening in a non-consecutive map of int->object - auto providedID = peasant.GetID(); + const auto providedID = peasant.GetID(); if (providedID == 0) { diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 5331f753f45..374b30ec391 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #pragma once #include "Monarch.g.h" diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index bf89bcccef1..8dcb7a66a7b 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + import "Peasant.idl"; namespace Microsoft.Terminal.Remoting diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h index 9b912995681..5e9add11a3c 100644 --- a/src/cascadia/Remoting/MonarchFactory.h +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "Monarch.h" @@ -8,9 +11,6 @@ // per-process. Once the first monarch is created, we'll stash it in g_weak. // Future callers who try to instantiate a Monarch will get the one that's // already been made. -// -// I'm sure there's a better way to do this with WRL, but I'm not familiar -// enough with WRL to know for sure. winrt::weak_ref g_weak{ nullptr }; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index a0b8d8b372d..ce3c4e96e97 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "Peasant.h" #include "CommandlineArgs.h" diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index d24d6c11656..b83011e8673 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #pragma once #include "Peasant.g.h" diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 7ffb9512170..8597b2489dd 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -1,4 +1,5 @@ - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. namespace Microsoft.Terminal.Remoting { diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index c0a2b87f2fe..f80f8e84cf9 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "WindowManager.h" #include "MonarchFactory.h" @@ -69,7 +72,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void WindowManager::_createMonarch() { // Heads up! This only works because we're using - // "metadata-based-marshalling" for our WinRT types. THat means the OS is + // "metadata-based-marshalling" for our WinRT types. That means the OS is // using the .winmd file we generate to figure out the proxy/stub // definitions for our types automatically. This only works in the following // cases: @@ -82,8 +85,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_areWeTheKing() { - auto kingPID = _monarch.GetPID(); - auto ourPID = GetCurrentProcessId(); + const auto kingPID{ _monarch.GetPID() }; + const auto ourPID{ GetCurrentProcessId() }; return (ourPID == kingPID); } @@ -93,7 +96,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant = *p; _monarch.AddPeasant(_peasant); - // TODO:MG Spawn a thread to wait on the monarch, and handle the election + // TODO:projects/5 Spawn a thread to wait on the monarch, and handle the election return _peasant; } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index f755d0c700a..0d25a4dfb96 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #pragma once #include "WindowManager.g.h" @@ -7,7 +10,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { - struct WindowManager final : public WindowManagerT + struct WindowManager final : public WindowManagerT { WindowManager(); ~WindowManager(); diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj index 51c6f62ad18..8b58ce05673 100644 --- a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -61,7 +61,7 @@ User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) - + /INCLUDE:_DllMain@12 /INCLUDE:DllMain From 658db6b568374c068161e17b8db10679c0ee2084 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 6 Jan 2021 07:10:54 -0600 Subject: [PATCH 23/88] Ask the TerminalApp to parse the commandline, and tell us what the window should be. It just always says 0 for now, but in the future it could actually give us useful info. --- .../Remoting/FindTargetWindowArgs.cpp | 5 ++ src/cascadia/Remoting/FindTargetWindowArgs.h | 17 ++++++ .../Microsoft.Terminal.RemotingLib.vcxproj | 6 ++ src/cascadia/Remoting/Monarch.cpp | 56 ++++++++++++++++++- src/cascadia/Remoting/Monarch.h | 3 + src/cascadia/Remoting/Monarch.idl | 8 +++ src/cascadia/Remoting/WindowManager.cpp | 21 +++++++ src/cascadia/Remoting/WindowManager.h | 6 ++ src/cascadia/Remoting/WindowManager.idl | 2 + src/cascadia/TerminalApp/AppLogic.cpp | 20 +++++++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 2 + src/cascadia/WindowsTerminal/AppHost.cpp | 13 +++++ src/cascadia/WindowsTerminal/AppHost.h | 3 + 14 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 src/cascadia/Remoting/FindTargetWindowArgs.cpp create mode 100644 src/cascadia/Remoting/FindTargetWindowArgs.h diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.cpp b/src/cascadia/Remoting/FindTargetWindowArgs.cpp new file mode 100644 index 00000000000..1781c1c9c12 --- /dev/null +++ b/src/cascadia/Remoting/FindTargetWindowArgs.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "FindTargetWindowArgs.h" +#include "FindTargetWindowArgs.g.cpp" diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.h b/src/cascadia/Remoting/FindTargetWindowArgs.h new file mode 100644 index 00000000000..7a30b7b1815 --- /dev/null +++ b/src/cascadia/Remoting/FindTargetWindowArgs.h @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "FindTargetWindowArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct FindTargetWindowArgs : public FindTargetWindowArgsT + { + public: + GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr); + GETSET_PROPERTY(int, ResultTargetWindow, -1); + }; +} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index d3f1bf6c7f4..0d977767f27 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -19,6 +19,9 @@ Monarch.idl + + Monarch.idl + @@ -36,6 +39,9 @@ Monarch.idl + + Monarch.idl + Create diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 50b6c24f11b..4f31609ebbb 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "Monarch.h" #include "CommandlineArgs.h" +#include "FindTargetWindowArgs.h" #include "Monarch.g.cpp" #include "../../types/inc/utils.hpp" @@ -117,6 +118,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } + // TODO:MG This probably shouldn't be a public function. I'm making it + // public so the WindowManager can use it to manually tell the monarch it's + // own ID to use as the MRU, when the monarch is first instantiated. THat's + // dumb, but it's a hack to get something working. + // + // That was stupid. I knew it would be but yea it didn't work. + // THe Window manager doesn't have a peasant yet when it first creates the monarch. void Monarch::_setMostRecentPeasant(const uint64_t peasantID) { // TODO:projects/5 Use a heap/priority queue per-desktop to track which @@ -126,6 +134,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mostRecentPeasant = peasantID; } + uint64_t Monarch::_getMostRecentPeasantID() + { + if (_mostRecentPeasant == 0) + { + // We haven't yet been told the MRU peasant. Just use the first one. + // TODO: GOD this is just gonna be a random one. Hacks on hacks on hacks + if (_peasants.size() > 0) + { + return _peasants.begin()->second.GetID(); + } + return 0; + } + else + { + return _mostRecentPeasant; + } + } + // Method Description: // - Try to handle a commandline from a new WT invocation. We might need to // hand the commandline to an existing window, or we might need to tell @@ -133,8 +159,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Arguments: // - // Return Value: - // - - bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& /*args*/) + // - true if the caller should create a new window for this commandline. + // False otherwise - the monarch should have dispatched this commandline + // to another window in this case. + bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& args) { // TODO:projects/5 // The branch dev/migrie/f/remote-commandlines has a more complete @@ -142,6 +170,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // always want to create a new window, so we'll just return true. This // will tell the caller that we didn't handle the commandline, and they // should open a new window to deal with it themselves. + auto findWindowArgs = winrt::make_self(); + findWindowArgs->Args(args); + _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); + const auto targetWindow = findWindowArgs->ResultTargetWindow(); + + // TODO:projects/5 targetWindow==0 -> use the currently active window + if (targetWindow >= 0) + { + uint64_t windowID = ::base::saturated_cast(targetWindow); + + if (windowID == 0) + { + windowID = _getMostRecentPeasantID(); + } + + if (auto targetPeasant{ _getPeasant(windowID) }) + { + targetPeasant.ExecuteCommandline(args); + return false; + } + } + // TEMPORARY: if the target window is -1, then we want a new window. All + // other cases, just do it in this window (for now). + // return targetWindow == -1; return true; } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 374b30ec391..3f58f6916d6 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -39,6 +39,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + private: Monarch(const uint64_t testPID); uint64_t _ourPID; @@ -51,6 +53,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); void _setMostRecentPeasant(const uint64_t peasantID); + uint64_t _getMostRecentPeasantID(); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 8dcb7a66a7b..946b3f63c36 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -5,11 +5,19 @@ import "Peasant.idl"; namespace Microsoft.Terminal.Remoting { + + [default_interface] runtimeclass FindTargetWindowArgs { + CommandlineArgs Args; + Int32 ResultTargetWindow; + } + [default_interface] runtimeclass Monarch { Monarch(); UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); Boolean ProposeCommandline(CommandlineArgs args); + + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index d6d2d07af55..2780c6f03a1 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -106,10 +106,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return; } // Here, we're the king! + // + // This is where you should do any aditional setup that might need to be + // done when we become the king. THis will be called both for the first + // window, and when the current monarch diesd. // Wait, don't. Let's just have the monarch try/catch any accesses to // peasants. If the peasant dies, then it can't get the peasant's // anything. In that case, _remove it_. + + _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); + + // winrt::com_ptr monarchImpl; + // monarchImpl.copy_from(winrt::get_self(_monarch)); + // if (monarchImpl) + // { + // monarchImpl->SetMostRecentPeasant(_peasant.GetID()); + // } } bool WindowManager::_areWeTheKing() @@ -137,6 +150,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_areWeTheKing()) { + // This is only called when a _new_ monarch is elected. We need to + // do this _always_, even on the first instance, which won't have an + // election return true; } return false; @@ -213,4 +229,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _peasant; } + void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + { + _FindTargetWindowRequestedHandlers(sender, args); + } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 35f38b51624..b87ce94d646 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -20,6 +20,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); + // Don't do this, the monarch can and will change over time + // FORWARDED_TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs, _monarch, FindTargetWindowRequested); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + private: bool _shouldCreateWindow{ false }; DWORD _registrationHostClass{ 0 }; @@ -39,6 +43,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _electionNight2020(); void _createPeasantThread(); void _waitOnMonarchThread(); + void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; } diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index e9ef1ee345c..3099f1128b5 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -1,4 +1,5 @@ import "Peasant.idl"; +import "Monarch.idl"; namespace Microsoft.Terminal.Remoting @@ -9,5 +10,6 @@ namespace Microsoft.Terminal.Remoting void ProposeCommandline(CommandlineArgs args); Boolean ShouldCreateWindow { get; }; IPeasant CurrentWindow(); + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 12a6223dce1..551fcf0b1a4 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1125,6 +1125,26 @@ namespace winrt::TerminalApp::implementation return result; // TODO:MG does a return value make sense } + int32_t AppLogic::FindTargetWindow(array_view args) + { + ::TerminalApp::AppCommandlineArgs appArgs; + auto result = appArgs.ParseArgs(args); + if (result == 0) + { + // TODO:MG Right now, any successful parse will end up in the same window + return 0; + // TODO:projects/5 We'll want to use the windowingBehavior setting to determine + // well + // Maybe that'd be a special return value out of here, to tell the monarch to do something special + // -1 -> create a new window + // -2 -> find the mru, this desktop + // -3 -> MRU, any desktop + } + + // Any unsuccessful parse will be a new window. + return -1; + } + // Method Description: // - If there were any errors parsing the commandline that was used to // initialize the terminal, this will return a string containing that diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 1a93abcf509..d0b39a73f57 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -30,6 +30,7 @@ namespace winrt::TerminalApp::implementation int32_t SetStartupCommandline(array_view actions); int32_t ExecuteCommandline(array_view actions); + int32_t FindTargetWindow(array_view actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 3abf62eb12a..db50878a225 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -56,6 +56,8 @@ namespace TerminalApp UInt64 GetLastActiveControlTaskbarState(); UInt64 GetLastActiveControlTaskbarProgress(); + Int32 FindTargetWindow(String[] args); + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 57043b0ed13..bd409c3c52f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -30,6 +30,12 @@ AppHost::AppHost() noexcept : { _logic = _app.Logic(); // get a ref to app's logic + // Inform the WindowManager that it can use us to find the target window for + // a set of commandline args. This needs to be done before + // _HandleCommandlineArgs, because WE might end up being the monarch. That + // would mean we'd need to be responsible for looking that up. + _windowManager.FindTargetWindowRequested({ this, &AppHost::_FindTargetWindow }); + // If there were commandline args to our process, try and process them here. // Do this before AppLogic::Create, otherwise this will have no effect. // @@ -521,3 +527,10 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se { _logic.ExecuteCommandline(args.Args()); } + +void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) +{ + const auto targetWindow = _logic.FindTargetWindow(args.Args().Args()); + args.ResultTargetWindow(targetWindow); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 18c175d1757..4a8dfa5d8e8 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -47,4 +47,7 @@ class AppHost void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); + + void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; From 977db464fabd8d7d4d5cf5afc19ad9bea1e36e10 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 6 Jan 2021 09:50:00 -0600 Subject: [PATCH 24/88] Holy bajesus, this works like a charm --- .../CascadiaPackage/CascadiaPackage.wapproj | 1 + src/cascadia/TerminalApp/AppCommandlineArgs.cpp | 17 +++++++++++++++++ src/cascadia/TerminalApp/AppCommandlineArgs.h | 4 ++++ src/cascadia/TerminalApp/AppLogic.cpp | 4 +++- .../TerminalApp/Resources/en-US/Resources.resw | 3 +++ src/cascadia/WindowsTerminal/main.cpp | 3 ++- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 3d918935add..dd88eb674f7 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -5,6 +5,7 @@ Native + win 10.0.18362.0 10.0.18362.0 diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f45247b2696..cf6392604ff 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -132,6 +132,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // recent of all desktops (WindowingBehavior::UseExisting), then use the // most recent of all desktops. _mostRecentPeasant = peasantID; + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_MostRecentPeasantSet", + TraceLoggingUInt64(peasantID, "peasantID", "the ID of the activated peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } uint64_t Monarch::_getMostRecentPeasantID() @@ -194,6 +199,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // TODO:MG if the targeted peasant fails to execute the // commandline, we should create our own window to display the // message box. + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline_Existing", + TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return false; } else @@ -205,9 +217,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // `Monarch::ProposeCommandline` needs to return a structure of // `{ shouldCreateWindow: bool, givenID: optional }` // + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline_Existing", + TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } } + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline_NewWindow", + TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // TODO:MG in this case, no usable ID was provided. Return { true, nullopt } return true; } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index de5a484357b..a64373808c1 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -69,6 +69,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // desktop into this method. The Peasant shouldn't need to be able to // figure it out, but it will need to report it to the monarch. + bool successfullyNotified = false; // Raise our WindowActivated event, to let the monarch know we've been // activated. try @@ -78,6 +79,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // will throw and exception. Just eat it, the election thread will // handle hooking up the new one. _WindowActivatedHandlers(*this, nullptr); + successfullyNotified = true; } catch (...) { @@ -85,6 +87,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation LOG_CAUGHT_EXCEPTION(); } + TraceLoggingWrite(g_hRemotingProvider, + "Peasant_ActivateWindow", + TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), + TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // TODO:MG Open three windows, close the first (the monarch). The focus // should automatically move to the third, from the windows shell. In // that window, `wt -w 0` does not work right. diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index f0b34d04e75..d7428782d07 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -110,6 +110,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { _createMonarch(); const auto isKing = _areWeTheKing(); + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ConnectedToMonarch", + TraceLoggingUInt64(_monarch.GetPID(), "monarchPID", "The PID of the new Monarch"), + TraceLoggingBoolean(isKing, "isKing", "true if we are the new monarch"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + if (!isKing) { return; @@ -147,6 +154,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant = *p; _monarch.AddPeasant(_peasant); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_CreateOurPeasant", + TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return _peasant; } @@ -209,11 +221,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation switch (waitResult) { case WAIT_OBJECT_0 + 0: // waits[0] was signaled + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchDied", + TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // Connect to the new monarch, which might be us! // If we become the monarch, then we'll return true and exit this thread. exitRequested = _electionNight2020(); break; case WAIT_OBJECT_0 + 1: // waits[1] was signaled + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchWaitInterrupted", + TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + exitRequested = true; break; diff --git a/src/cascadia/Remoting/init.cpp b/src/cascadia/Remoting/init.cpp new file mode 100644 index 00000000000..51496a8f71c --- /dev/null +++ b/src/cascadia/Remoting/init.cpp @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// Licensed under the MIT license. + +#include "pch.h" +#include +#include + +// Note: Generate GUID using TlgGuid.exe tool +#pragma warning(suppress : 26477) // One of the macros uses 0/NULL. We don't have control to make it nullptr. +TRACELOGGING_DEFINE_PROVIDER( + g_hRemotingProvider, + "Microsoft.Windows.Terminal.Remoting", + // {d6f04aad-629f-539a-77c1-73f5c3e4aa7b} + (0xd6f04aad, 0x629f, 0x539a, 0x77, 0xc1, 0x73, 0xf5, 0xc3, 0xe4, 0xaa, 0x7b), + TraceLoggingOptionMicrosoftTelemetry()); + +BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDll); + TraceLoggingRegister(g_hRemotingProvider); + Microsoft::Console::ErrorReporting::EnableFallbackFailureReporting(g_hRemotingProvider); + break; + case DLL_PROCESS_DETACH: + if (g_hRemotingProvider) + { + TraceLoggingUnregister(g_hRemotingProvider); + } + break; + } + + return TRUE; +} + +UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"Microsoft.Terminal.Remoting/Resources"); diff --git a/src/cascadia/Remoting/pch.h b/src/cascadia/Remoting/pch.h index 165aefe02d8..0fef7307ae5 100644 --- a/src/cascadia/Remoting/pch.h +++ b/src/cascadia/Remoting/pch.h @@ -38,7 +38,7 @@ // Including TraceLogging essentials for the binary #include #include -TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider); +TRACELOGGING_DECLARE_PROVIDER(g_hRemotingProvider); #include #include From 65376865b53969939cedcf23596229cf915177e6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 05:37:25 -0600 Subject: [PATCH 29/88] The peasant will now correctly inform the monarch when it re-connects --- .../Microsoft.Terminal.RemotingLib.vcxproj | 6 ++++ src/cascadia/Remoting/Monarch.cpp | 31 ++++++++++++------- src/cascadia/Remoting/Monarch.h | 4 +-- src/cascadia/Remoting/Monarch.idl | 1 + src/cascadia/Remoting/Peasant.cpp | 11 +++++-- src/cascadia/Remoting/Peasant.h | 7 +++-- src/cascadia/Remoting/Peasant.idl | 13 ++++++-- src/cascadia/Remoting/WindowActivatedArgs.cpp | 5 +++ src/cascadia/Remoting/WindowActivatedArgs.h | 28 +++++++++++++++++ src/cascadia/Remoting/WindowManager.cpp | 16 ++++++++-- src/cascadia/WindowsTerminal/AppHost.cpp | 8 ++++- 11 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 src/cascadia/Remoting/WindowActivatedArgs.cpp create mode 100644 src/cascadia/Remoting/WindowActivatedArgs.h diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index 3e0b801b29c..0763a84f039 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -22,6 +22,9 @@ Monarch.idl + + Peasant.idl + @@ -42,6 +45,9 @@ Monarch.idl + + Peasant.idl + Create diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cf6392604ff..ec96333bdf2 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -67,6 +67,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_AddPeasant", + TraceLoggingUInt64(providedID, "providedID", "the provided ID for the peasant"), + TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return newPeasantsId; } @@ -78,17 +84,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - sender: the Peasant that raised this event. This might be out-of-proc! // Return Value: // - - void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& /*args*/) + void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) { // TODO:projects/5 Pass the desktop and timestamp of when the window was // activated in `args`. - if (auto peasant{ sender.try_as() }) - { - auto theirID = peasant.GetID(); - _setMostRecentPeasant(theirID); - } + HandleActivatePeasant(args); + // if (auto peasant{ sender.try_as() }) + // { + // auto theirID = peasant.GetID(); + // } } // Method Description: @@ -125,17 +131,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // That was stupid. I knew it would be but yea it didn't work. // THe Window manager doesn't have a peasant yet when it first creates the monarch. - void Monarch::_setMostRecentPeasant(const uint64_t peasantID) + // + // TODO:MG Make this HandleActivatePeasant(PeasantActivatedArgs args). The + // WindowManager can pass in an args for _peasant.GetLastActivatedArgs() + void Monarch::HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) { // TODO:projects/5 Use a heap/priority queue per-desktop to track which // peasant was the most recent per-desktop. When we want to get the most // recent of all desktops (WindowingBehavior::UseExisting), then use the // most recent of all desktops. - _mostRecentPeasant = peasantID; + _mostRecentPeasant = args.PeasantID(); TraceLoggingWrite(g_hRemotingProvider, - "Monarch_MostRecentPeasantSet", - TraceLoggingUInt64(peasantID, "peasantID", "the ID of the activated peasant"), + "Monarch_SetMostRecentPeasant", + TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 3f58f6916d6..ca114c17502 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); @@ -52,11 +53,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation std::unordered_map _peasants; winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); - void _setMostRecentPeasant(const uint64_t peasantID); uint64_t _getMostRecentPeasantID(); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); friend class RemotingUnitTests::RemotingTests; }; diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 946b3f63c36..299ef9088a3 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -17,6 +17,7 @@ namespace Microsoft.Terminal.Remoting UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); Boolean ProposeCommandline(CommandlineArgs args); + void HandleActivatePeasant(WindowActivatedArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index a64373808c1..47af55da293 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -63,12 +63,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _initialArgs; } - void Peasant::ActivateWindow() + void Peasant::ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) { // TODO: projects/5 - somehow, pass an identifier for the current // desktop into this method. The Peasant shouldn't need to be able to // figure it out, but it will need to report it to the monarch. + _lastActivatedArgs = args; + bool successfullyNotified = false; // Raise our WindowActivated event, to let the monarch know we've been // activated. @@ -78,7 +80,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // by the monarch. The monarch might have died. If they have, this // will throw and exception. Just eat it, the election thread will // handle hooking up the new one. - _WindowActivatedHandlers(*this, nullptr); + _WindowActivatedHandlers(*this, args); successfullyNotified = true; } catch (...) @@ -97,4 +99,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // that window, `wt -w 0` does not work right. } + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs() + { + return _lastActivatedArgs; + } + } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 305c36e310d..4d2bb24a01b 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -21,10 +21,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t GetPID(); bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - void ActivateWindow(); + void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); + + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); - TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); private: @@ -34,6 +36,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t _id{ 0 }; winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs _lastActivatedArgs{ nullptr }; friend class RemotingUnitTests::RemotingTests; }; diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index b1c5c2acc2d..e3fb5af1b86 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -13,6 +13,14 @@ namespace Microsoft.Terminal.Remoting String CurrentDirectory(); }; + runtimeclass WindowActivatedArgs + { + WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime); + UInt64 PeasantID { get; }; + Guid DesktopID { get; }; + Windows.Foundation.DateTime ActivatedTime { get; }; + }; + interface IPeasant { CommandlineArgs InitialArgs { get; }; @@ -21,9 +29,10 @@ namespace Microsoft.Terminal.Remoting UInt64 GetID(); UInt64 GetPID(); Boolean ExecuteCommandline(CommandlineArgs args); - void ActivateWindow(); + void ActivateWindow(WindowActivatedArgs args); + WindowActivatedArgs GetLastActivatedArgs(); - event Windows.Foundation.TypedEventHandler WindowActivated; + event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; }; diff --git a/src/cascadia/Remoting/WindowActivatedArgs.cpp b/src/cascadia/Remoting/WindowActivatedArgs.cpp new file mode 100644 index 00000000000..4466383c2c7 --- /dev/null +++ b/src/cascadia/Remoting/WindowActivatedArgs.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "WindowActivatedArgs.h" +#include "WindowActivatedArgs.g.cpp" diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h new file mode 100644 index 00000000000..465dd6f8f2b --- /dev/null +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "WindowActivatedArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct WindowActivatedArgs : public WindowActivatedArgsT + { + GETSET_PROPERTY(uint64_t, PeasantID, 0); + GETSET_PROPERTY(winrt::guid, DesktopID, {}); + GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {}); + + public: + WindowActivatedArgs(uint64_t peasantID, winrt::guid desktopID, winrt::Windows::Foundation::DateTime timestamp) : + _PeasantID{ peasantID }, + _DesktopID{ desktopID }, + _ActivatedTime{ timestamp } {}; + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(WindowActivatedArgs); +} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index d7428782d07..e76af3d9a00 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -117,6 +117,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingBoolean(isKing, "isKing", "true if we are the new monarch"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + if (_peasant) + { + // Oh, don't do this, we do this in the election night + // // Tell the monarch about our peasant + // _monarch.AddPeasant(_peasant); + + // Inform the monarch of the time we were last activated + _monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs()); + } + if (!isKing) { return; @@ -125,7 +135,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // This is where you should do any aditional setup that might need to be // done when we become the king. THis will be called both for the first - // window, and when the current monarch diesd. + // window, and when the current monarch dies. // Wait, don't. Let's just have the monarch try/catch any accesses to // peasants. If the peasant dies, then it can't get the peasant's @@ -135,9 +145,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // winrt::com_ptr monarchImpl; // monarchImpl.copy_from(winrt::get_self(_monarch)); - // if (monarchImpl) + // if (monarchImpl && _peasant) // { - // monarchImpl->SetMostRecentPeasant(_peasant.GetID()); + // monarchImpl->HandleActivatePeasant(_peasant.GetLastActivatedArgs()); // } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 11e71414eb9..af0c6a7e334 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -534,6 +534,12 @@ void AppHost::_WindowActivated() { if (auto peasant{ _windowManager.CurrentWindow() }) { - peasant.ActivateWindow(); + // TODO: projects.5 - in the future, we'll want to actually get the + // desktip GUID in IslandWindow, and bubble that up here, then down to + // the Peasant. For now, we're just leaving space for it. + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs args{ peasant.GetID(), + winrt::guid{}, + winrt::clock().now() }; + peasant.ActivateWindow(args); } } From a75da0a1045291182bffd37d66561f9effa11263 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 06:33:44 -0600 Subject: [PATCH 30/88] mostly just notes --- src/cascadia/Remoting/Monarch.cpp | 42 ++++++++++++------------- src/cascadia/Remoting/Monarch.h | 2 ++ src/cascadia/Remoting/WindowManager.cpp | 15 --------- src/cascadia/Remoting/WindowManager.h | 2 -- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ec96333bdf2..ddf00470dc9 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -82,19 +82,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // window". // Arguments: // - sender: the Peasant that raised this event. This might be out-of-proc! + // - args: a bundle of the peasant ID, timestamp, and desktop ID, for the activated peasant // Return Value: // - void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) + const Remoting::WindowActivatedArgs& args) { - // TODO:projects/5 Pass the desktop and timestamp of when the window was - // activated in `args`. - HandleActivatePeasant(args); - // if (auto peasant{ sender.try_as() }) - // { - // auto theirID = peasant.GetID(); - // } } // Method Description: @@ -124,27 +118,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } - // TODO:MG This probably shouldn't be a public function. I'm making it - // public so the WindowManager can use it to manually tell the monarch it's - // own ID to use as the MRU, when the monarch is first instantiated. THat's - // dumb, but it's a hack to get something working. - // - // That was stupid. I knew it would be but yea it didn't work. - // THe Window manager doesn't have a peasant yet when it first creates the monarch. - // - // TODO:MG Make this HandleActivatePeasant(PeasantActivatedArgs args). The - // WindowManager can pass in an args for _peasant.GetLastActivatedArgs() - void Monarch::HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) + void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args) { // TODO:projects/5 Use a heap/priority queue per-desktop to track which // peasant was the most recent per-desktop. When we want to get the most // recent of all desktops (WindowingBehavior::UseExisting), then use the // most recent of all desktops. - _mostRecentPeasant = args.PeasantID(); + const auto oldLastActiveTime = _lastActivatedTime.time_since_epoch().count(); + const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count(); + + // For now, we'll just pay attention to whoever the most recent peasant + // was. We're not too wooried about the mru peasant dying. Worst case - + // when the user executes a `wt -w 0`, we won't be able to find that + // peasant, and it'll open in a new window instead of the current one. + if (args.ActivatedTime() > _lastActivatedTime) + { + _mostRecentPeasant = args.PeasantID(); + _lastActivatedTime = args.ActivatedTime(); + } TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"), + TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"), + TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided args.ActivatedTime()"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } @@ -153,7 +150,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_mostRecentPeasant == 0) { // We haven't yet been told the MRU peasant. Just use the first one. - // TODO:MG GOD this is just gonna be a random one. Hacks on hacks on hacks + // This is just gonna be a random one, but really shouldn't happen + // in practice. The WindowManager should set the MRU peasant + // immediately as soon as it creates the monarch/peasant for the + // first window. if (_peasants.size() > 0) { return _peasants.begin()->second.GetID(); diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index ca114c17502..70c210e4784 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -49,6 +49,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t _nextPeasantID{ 1 }; uint64_t _thisPeasantID{ 0 }; uint64_t _mostRecentPeasant{ 0 }; + winrt::Windows::Foundation::DateTime _lastActivatedTime{}; + WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew }; std::unordered_map _peasants; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index e76af3d9a00..1e0c0045757 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -119,10 +119,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_peasant) { - // Oh, don't do this, we do this in the election night - // // Tell the monarch about our peasant - // _monarch.AddPeasant(_peasant); - // Inform the monarch of the time we were last activated _monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs()); } @@ -137,18 +133,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // done when we become the king. THis will be called both for the first // window, and when the current monarch dies. - // Wait, don't. Let's just have the monarch try/catch any accesses to - // peasants. If the peasant dies, then it can't get the peasant's - // anything. In that case, _remove it_. - _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); - - // winrt::com_ptr monarchImpl; - // monarchImpl.copy_from(winrt::get_self(_monarch)); - // if (monarchImpl && _peasant) - // { - // monarchImpl->HandleActivatePeasant(_peasant.GetLastActivatedArgs()); - // } } bool WindowManager::_areWeTheKing() diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index b87ce94d646..020446d7da7 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -20,8 +20,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); - // Don't do this, the monarch can and will change over time - // FORWARDED_TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs, _monarch, FindTargetWindowRequested); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); private: From c02f25aba3f0549173c257ff3c5b8a62f6afa507 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 08:00:51 -0600 Subject: [PATCH 31/88] peasants now switch to the cwd that was requested --- OpenConsole.sln | 11 ++++-- src/cascadia/Remoting/Peasant.cpp | 6 +++ src/cascadia/TerminalApp/AppLogic.cpp | 6 ++- src/cascadia/TerminalApp/AppLogic.h | 2 +- src/cascadia/TerminalApp/AppLogic.idl | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 48 ++++++++++++++++++++++- src/cascadia/TerminalApp/TerminalPage.h | 4 +- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 8 files changed, 69 insertions(+), 12 deletions(-) diff --git a/OpenConsole.sln b/OpenConsole.sln index eaf51110a64..a70034cfaab 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -355,6 +355,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Remoting", "src\c {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wpf", "wpf", "{4DAF0299-495E-4CD1-A982-9BAC16A45932}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2157,7 +2159,7 @@ Global {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.ActiveCfg = Release|x64 - {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.Build.0 = AuditMode|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.Build.0 = Release|x64 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.Build.0 = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|Any CPU.ActiveCfg = Debug|Win32 @@ -2287,8 +2289,8 @@ Global {05500DEF-2294-41E3-AF9A-24E580B82836} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} {1E4A062E-293B-4817-B20D-BF16B979E350} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {84848BFA-931D-42CE-9ADF-01EE54DE7890} = {59840756-302F-44DF-AA47-441A9D673202} - {376FE273-6B84-4EB5-8B30-8DE9D21B022C} = {59840756-302F-44DF-AA47-441A9D673202} + {84848BFA-931D-42CE-9ADF-01EE54DE7890} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} + {376FE273-6B84-4EB5-8B30-8DE9D21B022C} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} {CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} {CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} @@ -2308,7 +2310,7 @@ Global {024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} {067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202} {6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87} - {1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202} + {1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} {506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202} @@ -2317,6 +2319,7 @@ Global {43CE4CE5-0010-4B99-9569-672670D26E26} = {59840756-302F-44DF-AA47-441A9D673202} {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {59840756-302F-44DF-AA47-441A9D673202} {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {4DAF0299-495E-4CD1-A982-9BAC16A45932} = {59840756-302F-44DF-AA47-441A9D673202} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 47af55da293..659a2b293f8 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -50,6 +50,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _initialArgs = args; } + TraceLoggingWrite(g_hRemotingProvider, + "Peasant_ExecuteCommandline", + TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), + TraceLoggingWideString(args.CurrentDirectory().c_str(), "directory", "the provided cwd"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // Raise an event with these args. The AppHost will listen for this // event to know when to take these args and dispatch them to a // currently-running window. diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 7393b248a80..2a1c2dde273 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1112,14 +1112,16 @@ namespace winrt::TerminalApp::implementation return result; } - int32_t AppLogic::ExecuteCommandline(array_view args) + int32_t AppLogic::ExecuteCommandline(array_view args, + const winrt::hstring& cwd) { ::TerminalApp::AppCommandlineArgs appArgs; auto result = appArgs.ParseArgs(args); if (result == 0) { auto actions = winrt::single_threaded_vector(std::move(appArgs.GetStartupActions())); - _root->ProcessStartupActions(actions, false); + + _root->ProcessStartupActions(actions, false, cwd); } return result; // TODO:MG does a return value make sense diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index d0b39a73f57..13c7885d611 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -29,7 +29,7 @@ namespace winrt::TerminalApp::implementation [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; int32_t SetStartupCommandline(array_view actions); - int32_t ExecuteCommandline(array_view actions); + int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); int32_t FindTargetWindow(array_view actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index db50878a225..b38e0fe0303 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -29,7 +29,7 @@ namespace TerminalApp Boolean IsElevated(); Int32 SetStartupCommandline(String[] commands); - Int32 ExecuteCommandline(String[] commands); + Int32 ExecuteCommandline(String[] commands, String cwd); String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index cf3c3bb7c74..830e85ea7dc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -349,7 +349,8 @@ namespace winrt::TerminalApp::implementation // Return Value: // - winrt::fire_and_forget TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector actions, - const bool initial) + const bool initial, + const winrt::hstring cwd) { // If there are no actions left, do nothing. if (actions.Size() == 0) @@ -360,6 +361,30 @@ namespace winrt::TerminalApp::implementation // Handle it on a subsequent pass of the UI thread. co_await winrt::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); + + // If the caller provided a CWD, switch to that directory, then switch + // back once we're done. This looks weird though, because we have to set + // up the scope_exit _first_. We'll release the scope_exit if we don't + // actually need it. + std::wstring originalCwd{ wil::GetCurrentDirectoryW() }; + auto restoreCwd = wil::scope_exit([&originalCwd]() { + // ignore errors, we'll just power on through. We'd rather do + // something rather than fail silently if the direcotry doesn't + // actually exist. + LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(originalCwd.c_str())); + }); + if (cwd.empty()) + { + restoreCwd.release(); + } + else + { + // ignore errors, we'll just power on through. We'd rather do + // something rather than fail silently if the direcotry doesn't + // actually exist. + LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(cwd.c_str())); + } + if (auto page{ weakThis.get() }) { for (const auto& action : actions) @@ -861,9 +886,28 @@ namespace winrt::TerminalApp::implementation envMap.Insert(L"WT_PROFILE_ID", guidWString); envMap.Insert(L"WSLENV", L"WT_PROFILE_ID"); + // Update the path to be relative to whatever our CWD is. + // + // Refer to the examples in + // https://en.cppreference.com/w/cpp/filesystem/path/append + // + // We need to do this here, to ensure we tell the ConptyConnection + // the correct starting path. If we're being invoked from another + // terminal instance (e.g. wt -w 0 -d .), then we have switched our + // CWD to the provided path. We should treat the StartingDirectory + // as relative to the current CWD. + // + // The connection must be informed of the current CWD on + // construction, because the connection might not spawn the child + // process until later, on another thread, after we've already + // restored the CWD to it's original value. + std::wstring cwdString{ wil::GetCurrentDirectoryW() }; + std::filesystem::path cwd{ cwdString }; + cwd /= settings.StartingDirectory().c_str(); + auto conhostConn = TerminalConnection::ConptyConnection( settings.Commandline(), - settings.StartingDirectory(), + winrt::hstring{ cwd.c_str() }, settings.StartingTitle(), envMap.GetView(), settings.InitialRows(), diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 58709e86e8d..9b72dbdb590 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -81,7 +81,9 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); - winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); + winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, + const bool initial, + const winrt::hstring cwd = L""); // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index af0c6a7e334..29fb6e691ca 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -520,7 +520,7 @@ bool AppHost::HasWindow() void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) { - _logic.ExecuteCommandline(args.Args()); + _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); } void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, From 3e39ab9e717d62dcfbda110eed5e41077f3c3152 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 08:29:35 -0600 Subject: [PATCH 32/88] more comments --- src/cascadia/TerminalApp/AppLogic.cpp | 27 ++++++++++++++++++++++-- src/cascadia/WindowsTerminal/AppHost.cpp | 24 +++++++++++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 2a1c2dde273..77f9b94c105 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1112,6 +1112,22 @@ namespace winrt::TerminalApp::implementation return result; } + // Method Description: + // - Parse the provided commandline arguments into actions, and try to + // perform them immediately. + // - This function returns 0, unless a there was a non-zero result from + // trying to parse one of the commands provided. In that case, no commands + // after the failing command will be parsed, and the non-zero code + // returned. + // - If a non-empty cwd is provided, the entire terminal exe will switch to + // that CWD while we handle these actions, then return to the original + // CWD. + // Arguments: + // - args: an array of strings to process as a commandline. These args can contain spaces + // - cwd: The directory to use as the CWD while performing thse actions. + // Return Value: + // - the result of the first command who's parsing returned a non-zero code, + // or 0. (see AppLogic::_ParseArgs) int32_t AppLogic::ExecuteCommandline(array_view args, const winrt::hstring& cwd) { @@ -1123,8 +1139,8 @@ namespace winrt::TerminalApp::implementation _root->ProcessStartupActions(actions, false, cwd); } - - return result; // TODO:MG does a return value make sense + // Return the result of parsing with commandline, though it may or may not be used. + return result; } // Method Description: @@ -1166,6 +1182,13 @@ namespace winrt::TerminalApp::implementation // to handle the commandline iteslf, and find that the commandline // failed to parse. When that happens, the new window will display the // message box. + // + // This will also work for the case where the user specifies an invalid + // commandline in conjunction with `-w 0`. This function will determine + // that the commandline hasa parse error, and indicate that we should + // create a new window. Then, in that new window, we'll try to set the + // StartupActions, which will again fail, returning the correct error + // message. return -1; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 29fb6e691ca..2b124497b8f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -517,12 +517,32 @@ bool AppHost::HasWindow() return _shouldCreateWindow; } +// Method Description: +// - Event handler for the Peasant::ExecuteCommandlineRequested event. Take the +// provided commandline args, and attempt to parse them and perform the +// actions immediately. The parsing is performed by AppLogic. +// - This is invoked when another wt.exe instance runs something like `wt -w 1 +// new-tab`, and the Monarch delegates the commandline to this instance. +// Arguments: +// - args: the bundle of a commandline and working directory to use for this invocation. +// Return Value: +// - void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) { _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); } +// Method Description: +// - Event handler for the WindowManager::FindTargetWindowRequested event. The +// manager will ask us how to figure out what the target window is for a set +// of commandline arguments. We'll take those arguments, and ask AppLogic to +// parse them for us. We'll then set ResultTargetWindow in the given args, so +// the sender can use that reuslt. +// Arguments: +// - args: the bundle of a commandline and working directory to find the correct target window for. +// Return Value: +// - void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) { @@ -534,8 +554,8 @@ void AppHost::_WindowActivated() { if (auto peasant{ _windowManager.CurrentWindow() }) { - // TODO: projects.5 - in the future, we'll want to actually get the - // desktip GUID in IslandWindow, and bubble that up here, then down to + // TODO: projects/5 - in the future, we'll want to actually get the + // desktop GUID in IslandWindow, and bubble that up here, then down to // the Peasant. For now, we're just leaving space for it. winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs args{ peasant.GetID(), winrt::guid{}, From 52b2cb6d3f65d371e71b34aa170078585c240464 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 10:39:07 -0600 Subject: [PATCH 33/88] Allow the user to provide ids on the commandline --- .../Microsoft.Terminal.RemotingLib.vcxproj | 6 +++ src/cascadia/Remoting/Monarch.cpp | 18 ++++++-- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Monarch.idl | 8 +++- .../Remoting/ProposeCommandlineResult.cpp | 5 +++ .../Remoting/ProposeCommandlineResult.h | 17 +++++++ src/cascadia/Remoting/WindowManager.cpp | 45 +++++++++++++++++-- src/cascadia/Remoting/WindowManager.h | 2 +- 8 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 src/cascadia/Remoting/ProposeCommandlineResult.cpp create mode 100644 src/cascadia/Remoting/ProposeCommandlineResult.h diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index 0763a84f039..d0e9882845a 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -22,6 +22,9 @@ Monarch.idl + + Monarch.idl + Peasant.idl @@ -45,6 +48,9 @@ Monarch.idl + + Monarch.idl + Peasant.idl diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ddf00470dc9..f03137bc1d8 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -5,6 +5,7 @@ #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" +#include "ProposeCommandlineResult.h" #include "Monarch.g.cpp" #include "../../types/inc/utils.hpp" @@ -176,7 +177,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - true if the caller should create a new window for this commandline. // False otherwise - the monarch should have dispatched this commandline // to another window in this case. - bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& args) + Remoting::ProposeCommandlineResult Monarch::ProposeCommandline(const Remoting::CommandlineArgs& args) { // Raise an event, to ask how to handle this commandline. We can't ask // the app ourselves - we exist isolated from that knowledge (and @@ -215,9 +216,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - return false; + auto result = winrt::make_self(); + result->ShouldCreateWindow(false); + return *result; } - else + else if (windowID > 0) { // TODO:MG in this case, an ID was provided, but there's no // peasant with that ID. Instead, we should tell the caller that @@ -231,6 +234,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + auto result = winrt::make_self(); + result->ShouldCreateWindow(true); + result->Id(windowID); + return *result; } } @@ -239,7 +246,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // TODO:MG in this case, no usable ID was provided. Return { true, nullopt } - return true; + // return true; + auto result = winrt::make_self(); + result->ShouldCreateWindow(true); + return *result; } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 70c210e4784..266b8cc89f1 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -37,7 +37,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); - bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 299ef9088a3..206a48d05db 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -11,12 +11,18 @@ namespace Microsoft.Terminal.Remoting Int32 ResultTargetWindow; } + [default_interface] runtimeclass ProposeCommandlineResult { + Windows.Foundation.IReference Id { get; }; + // TOOD:projects/5 - also return the name here, if the name was set on the commandline + Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode + } + [default_interface] runtimeclass Monarch { Monarch(); UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); - Boolean ProposeCommandline(CommandlineArgs args); + ProposeCommandlineResult ProposeCommandline(CommandlineArgs args); void HandleActivatePeasant(WindowActivatedArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.cpp b/src/cascadia/Remoting/ProposeCommandlineResult.cpp new file mode 100644 index 00000000000..fc9cc8f616b --- /dev/null +++ b/src/cascadia/Remoting/ProposeCommandlineResult.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "ProposeCommandlineResult.h" +#include "ProposeCommandlineResult.g.cpp" diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.h b/src/cascadia/Remoting/ProposeCommandlineResult.h new file mode 100644 index 00000000000..c45f134a1ac --- /dev/null +++ b/src/cascadia/Remoting/ProposeCommandlineResult.h @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "ProposeCommandlineResult.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct ProposeCommandlineResult : public ProposeCommandlineResultT + { + public: + GETSET_PROPERTY(Windows::Foundation::IReference, Id); + GETSET_PROPERTY(bool, ShouldCreateWindow, true); + }; +} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 1e0c0045757..64495bbd8ae 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -49,8 +49,41 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // launched with them! // // Otherwise, the King will tell us if we should make a new window - _shouldCreateWindow = isKing || - _monarch.ProposeCommandline(args); + _shouldCreateWindow = isKing; + std::optional givenID; + + if (!isKing) + { + auto result = _monarch.ProposeCommandline(args); + _shouldCreateWindow = result.ShouldCreateWindow(); + if (result.Id()) + { + givenID = result.Id().Value(); + } + if (givenID) + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingPointer(nullptr, "Id", "No ID provided"), + 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)); + } // TODO:projects/5 The monarch may respond back "you should be a new // window, with ID,name of (id, name)". Really the responses are: @@ -65,7 +98,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(); + _createOurPeasant({ givenID }); // Spawn a thread to wait on the monarch, and handle the election if (!isKing) @@ -143,9 +176,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return (ourPID == kingPID); } - Remoting::IPeasant WindowManager::_createOurPeasant() + Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID) { auto p = winrt::make_self(); + if (givenID) + { + p->AssignID(givenID.value()); + } _peasant = *p; _monarch.AddPeasant(_peasant); diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 020446d7da7..5e52210cb33 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -36,7 +36,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _createMonarch(); void _createMonarchAndCallbacks(); bool _areWeTheKing(); - winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); + winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional givenID); bool _electionNight2020(); void _createPeasantThread(); From bc492f1815f7639735d45f753904ee82599e11c9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 10:45:46 -0600 Subject: [PATCH 34/88] Code cleanup --- src/cascadia/Remoting/Monarch.cpp | 19 +++++++++---------- src/cascadia/Remoting/WindowManager.cpp | 23 ++++++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f03137bc1d8..fa15636a72a 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -205,10 +205,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (auto targetPeasant{ _getPeasant(windowID) }) { + // This will raise the peasant's ExecuteCommandlineRequested + // event, which will then ask the AppHost to handle the + // commandline, which will then pass it to AppLogic for + // handling. targetPeasant.ExecuteCommandline(args); - // TODO:MG if the targeted peasant fails to execute the - // commandline, we should create our own window to display the - // message box. TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", @@ -222,18 +223,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } else if (windowID > 0) { - // TODO:MG in this case, an ID was provided, but there's no + // In this case, an ID was provided, but there's no // peasant with that ID. Instead, we should tell the caller that // they should make a new window, but _with that ID_. - // - // `Monarch::ProposeCommandline` needs to return a structure of - // `{ shouldCreateWindow: bool, givenID: optional }` - // + TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + auto result = winrt::make_self(); result->ShouldCreateWindow(true); result->Id(windowID); @@ -245,8 +244,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "Monarch_ProposeCommandline_NewWindow", TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // TODO:MG in this case, no usable ID was provided. Return { true, nullopt } - // return true; + + // In this case, no usable ID was provided. Return { true, nullopt } auto result = winrt::make_self(); result->ShouldCreateWindow(true); return *result; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 64495bbd8ae..08234b2ba95 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -54,12 +54,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (!isKing) { + // The monarch may respond back "you should be a new + // window, with ID,name of (id, name)". Really the responses are: + // * You should not create a new window + // * Create a new window (but without a given ID or name). The + // Monarch will assign your ID/name later + // * Create a new window, and you'll have this ID or name + // - This is the case where the user provides `wt -w 1`, and + // there's no existing window 1 + auto result = _monarch.ProposeCommandline(args); _shouldCreateWindow = result.ShouldCreateWindow(); if (result.Id()) { givenID = result.Id().Value(); } + + // TraceLogging doesn't have a good solution for logging an + // optional. So we have to repeat the calls here: if (givenID) { TraceLoggingWrite(g_hRemotingProvider, @@ -79,21 +91,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } else { + // We're the monarch, we don't need to propose anything. We're just + // going to do it. TraceLoggingWrite(g_hRemotingProvider, "WindowManager_ProposeCommandline_AsMonarch", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - // TODO:projects/5 The monarch may respond back "you should be a new - // window, with ID,name of (id, name)". Really the responses are: - // * You should not create a new window - // * Create a new window (but without a given ID or name). The Monarch - // will assign your ID/name later - // * Create a new window, and you'll have this ID or name - // - This is the case where the user provides `wt -w 1`, and there's - // no existing window 1 - if (_shouldCreateWindow) { // If we should create a new window, then instantiate our Peasant From be74b2ee2dd1fb23dc8921d7b6b8480d25b6a478 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 15:18:26 -0600 Subject: [PATCH 35/88] putting var in headers is bad, mkay? --- src/cascadia/Remoting/MonarchFactory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h index 5e9add11a3c..07c2e29a857 100644 --- a/src/cascadia/Remoting/MonarchFactory.h +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -12,14 +12,14 @@ // Future callers who try to instantiate a Monarch will get the one that's // already been made. -winrt::weak_ref g_weak{ nullptr }; - struct MonarchFactory : winrt::implements { MonarchFactory() = default; HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept { + static winrt::weak_ref g_weak{ nullptr }; + *result = nullptr; if (outer) { From 2a7bc94a8ffa6e710cc07794446e49d2a7c19e7c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 16:40:49 -0600 Subject: [PATCH 36/88] branding --- src/cascadia/Remoting/Monarch.h | 25 ++++++++++++++++++++----- src/cascadia/Remoting/MonarchFactory.h | 8 ++++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 374b30ec391..eeeb9fe7602 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -7,12 +7,27 @@ #include "Peasant.h" #include "../cascadia/inc/cppwinrt_utils.h" -// {06171993-7eb1-4f3e-85f5-8bdd7386cce3} -constexpr GUID Monarch_clsid{ +// We sure different GUIDs here depending on whether we're running a Release, +// Preview, or Dev build. This ensures that different installs don't +// accidentally talk to one another. +// +// * Release: {06171993-7eb1-4f3e-85f5-8bdd7386cce3} +// * Preview: {04221993-7eb1-4f3e-85f5-8bdd7386cce3} +// * Dev: {08302020-7eb1-4f3e-85f5-8bdd7386cce3} +constexpr GUID Monarch_clsid +{ +#if defined(WT_BRANDING_RELEASE) 0x06171993, - 0x7eb1, - 0x4f3e, - { 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 } +#elif defined(WT_BRANDING_PREVIEW) + 0x04221993, +#else + 0x08302020, +#endif + 0x7eb1, + 0x4f3e, + { + 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 + } }; enum class WindowingBehavior : uint64_t diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h index 07c2e29a857..496a6094686 100644 --- a/src/cascadia/Remoting/MonarchFactory.h +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -26,18 +26,18 @@ struct MonarchFactory : winrt::implements return CLASS_E_NOAGGREGATION; } - if (!g_weak) + // Lock the ref immediately. We don't want it freed from out beneath us + auto strong = g_weak.get(); + if (!strong) { // Create a new Monarch instance - auto strong = winrt::make_self(); - + strong = winrt::make_self(); g_weak = (*strong).get_weak(); return strong.as(iid, result); } else { // We already instantiated one Monarch, let's just return that one! - auto strong = g_weak.get(); return strong.as(iid, result); } } From 5b8ace276b0eb0752090c8ce6e98fa93e7510ca9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 8 Jan 2021 12:58:43 -0600 Subject: [PATCH 37/88] A bunch of tests for Monarch::ProposeCommandline --- .../UnitTests_Remoting/RemotingTests.cpp | 263 +++++++++++++++++- src/cascadia/WindowsTerminal/AppHost.cpp | 10 +- 2 files changed, 262 insertions(+), 11 deletions(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index dd3b5ad123d..84cf1e932db 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -3,6 +3,9 @@ #include "pch.h" #include "../Remoting/Monarch.h" +#include "../Remoting/CommandlineArgs.h" +#include "../Remoting/FindTargetWindowArgs.h" +#include "../Remoting/ProposeCommandlineResult.h" using namespace Microsoft::Console; using namespace WEX::Logging; @@ -34,18 +37,26 @@ namespace RemotingUnitTests { // This is a silly helper struct. // It will always throw an hresult_error on any of it's methods. - // In the tests, it's hard to emulate a peasant process being totally dead once the Monarch has captured a reference to it. Since everything's in-proc in the tests, we can't decrement the refcount in such a way that the monarch's reference will throw a catchable exception. - // Instead, this class can be used to replace a peasant inside a Monarch, to emulate that peasant process dying. Any time the monarch tries to do something to this peasant, it'll throw an exception. + // + // In the tests, it's hard to emulate a peasant process being totally dead + // once the Monarch has captured a reference to it. Since everything's + // in-proc in the tests, we can't decrement the refcount in such a way that + // the monarch's reference will throw a catchable exception. Instead, this + // class can be used to replace a peasant inside a Monarch, to emulate that + // peasant process dying. Any time the monarch tries to do something to this + // peasant, it'll throw an exception. struct DeadPeasant : implements { DeadPeasant() = default; void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; }; uint64_t GetID() { throw winrt::hresult_error{}; }; uint64_t GetPID() { throw winrt::hresult_error{}; }; - bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } - winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } - TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); + bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } + void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { 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); }; class RemotingTests @@ -61,6 +72,13 @@ namespace RemotingUnitTests TEST_METHOD(AddPeasantsToNewMonarch); TEST_METHOD(RemovePeasantFromMonarchWhenFreed); + TEST_METHOD(ProposeCommandlineNoWindow); + TEST_METHOD(ProposeCommandlineGivenWindow); + TEST_METHOD(ProposeCommandlineNegativeWindow); + TEST_METHOD(ProposeCommandlineCurrentWindow); + TEST_METHOD(ProposeCommandlineNonExistentWindow); + TEST_METHOD(ProposeCommandlineDeadWindow); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -68,8 +86,14 @@ namespace RemotingUnitTests static void _killPeasant(const com_ptr& m, const uint64_t peasantID); + + static void _findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; + // Helper to replace the specified peasant in a monarch with a + // "DeadPeasant", which will emulate what happens when the peasant process + // dies. void RemotingTests::_killPeasant(const com_ptr& m, const uint64_t peasantID) { @@ -83,6 +107,17 @@ namespace RemotingUnitTests m->_peasants[peasantID] = *tombstone; } + void RemotingTests::_findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + { + auto arguments = args.Args().Args(); + if (arguments.size() > 0) + { + auto index = std::stoi(arguments.at(0).c_str()); + args.ResultTargetWindow(index); + } + } + void RemotingTests::CreateMonarch() { auto m1 = winrt::make_self(); @@ -292,4 +327,220 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); } + void RemotingTests::ProposeCommandlineNoWindow() + { + Log::Comment(L"Test proposing a commandline that doesn't have a window specified in it"); + + const auto monarch0PID = 12345u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + std::vector args{}; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + Log::Comment(L"Propose the same args again after adding a peasant - we should still return {create new window, no ID}"); + result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + + void RemotingTests::ProposeCommandlineGivenWindow() + { + Log::Comment(L"Test proposing a commandline for a window that currently exists"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p1"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Args().at(1)); + }); + + std::vector args{ L"1", L"arg[1]" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + void RemotingTests::ProposeCommandlineNegativeWindow() + { + Log::Comment(L"Test proposing a commandline for an invalid window ID, like -1"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + { + std::vector args{ L"-1" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + { + std::vector args{ L"-2" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + } + void RemotingTests::ProposeCommandlineCurrentWindow() + { + Log::Comment(L"Test proposing a commandline for the current window (ID=0)"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p1"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Args().at(1)); + }); + + std::vector p1Args{ L"0", L"arg[1]" }; + std::vector p2Args{ L"0", L"this is for p2" }; + + { + Log::Comment(L"Manually activate the first peasant"); + // This would usually happen immediately when the window is created, but + // there's no actual window in these tests. + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + winrt::guid{}, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + + Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p2"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); + }); + + { + Log::Comment(L"Activate the second peasant"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + winrt::guid{}, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + + Log::Comment(L"Send a commandline to the current window, 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()); + } + { + Log::Comment(L"Reactivate the first peasant"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + winrt::guid{}, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + + Log::Comment(L"Send a commandline to the current window, which should be p1 again"); + Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } }; + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + } + void RemotingTests::ProposeCommandlineNonExistentWindow() + { + Log::Comment(L"Test proposing a commandline for an ID that doesn't have a current peasant"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + { + std::vector args{ L"2" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(true, (bool)result.Id()); + VERIFY_ARE_EQUAL(2u, result.Id().Value()); + } + { + std::vector args{ L"10" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(true, (bool)result.Id()); + VERIFY_ARE_EQUAL(10u, result.Id().Value()); + } + } + void RemotingTests::ProposeCommandlineDeadWindow() + { + Log::Comment(L"Test proposing a commandline for a peasant that previously died"); + VERIFY_ARE_EQUAL(true, false, L"TODO: Finish this test"); + } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 2b124497b8f..6fa2794b192 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -528,7 +528,7 @@ bool AppHost::HasWindow() // Return Value: // - void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, - winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) + Remoting::CommandlineArgs args) { _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); } @@ -544,7 +544,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se // Return Value: // - void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + const Remoting::FindTargetWindowArgs& args) { const auto targetWindow = _logic.FindTargetWindow(args.Args().Args()); args.ResultTargetWindow(targetWindow); @@ -557,9 +557,9 @@ void AppHost::_WindowActivated() // TODO: projects/5 - in the future, we'll want to actually get the // desktop GUID in IslandWindow, and bubble that up here, then down to // the Peasant. For now, we're just leaving space for it. - winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs args{ peasant.GetID(), - winrt::guid{}, - winrt::clock().now() }; + Remoting::WindowActivatedArgs args{ peasant.GetID(), + winrt::guid{}, + winrt::clock().now() }; peasant.ActivateWindow(args); } } From 689c38519b7cdce44731f04682809a855631d462 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 8 Jan 2021 13:00:33 -0600 Subject: [PATCH 38/88] sure yea that's a doc comment --- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 84cf1e932db..dd635eecd1f 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -107,6 +107,8 @@ namespace RemotingUnitTests m->_peasants[peasantID] = *tombstone; } + // Helper to get the first argument out of the commandline, and try to + // convert it to an int. void RemotingTests::_findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) { From f02969b70f21d2603f5753458c514babf0d45c25 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Jan 2021 11:07:15 -0600 Subject: [PATCH 39/88] More tests, more redundancy --- src/cascadia/Remoting/Peasant.cpp | 19 +- src/cascadia/Remoting/WindowManager.cpp | 182 ++++++++++++------ src/cascadia/Remoting/WindowManager.h | 2 +- .../UnitTests_Remoting/RemotingTests.cpp | 56 +++++- 4 files changed, 195 insertions(+), 64 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 659a2b293f8..699adf976b2 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -69,12 +69,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _initialArgs; } - void Peasant::ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) + void Peasant::ActivateWindow(const Remoting::WindowActivatedArgs& args) { // TODO: projects/5 - somehow, pass an identifier for the current // desktop into this method. The Peasant shouldn't need to be able to // figure it out, but it will need to report it to the monarch. + // Store these new args as our last activated state. If a new monarch + // comes looking, we can use this info to tell them when we were last + // activated. _lastActivatedArgs = args; bool successfullyNotified = false; @@ -91,7 +94,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } catch (...) { - // TODO:MG Tracelogging LOG_CAUGHT_EXCEPTION(); } @@ -100,12 +102,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // TODO:MG Open three windows, close the first (the monarch). The focus - // should automatically move to the third, from the windows shell. In - // that window, `wt -w 0` does not work right. } - winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs() + // Method Description: + // - Retrieve the WindowActivatedArgs describing the last activation of this + // peasant. New monarchs can use this state to determine when we were last + // activated. + // Arguments: + // - + // Return Value: + // - a WindowActivatedArgs with info about when and where we were last activated. + Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs() { return _lastActivatedArgs; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 08234b2ba95..df7006b3ca4 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -199,7 +199,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _peasant; } - bool WindowManager::_electionNight2020() + // Method Description: + // - Attempt to connect to the monarch process. This might be us! + // - For the new monarch, add us to their list of peasants. + // Arguments: + // - + // Return Value: + // - true iff we're the new monarch process. + bool WindowManager::_performElection() { _createMonarchAndCallbacks(); @@ -208,10 +215,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_areWeTheKing()) { - // This is only called when a _new_ monarch is elected. So don't do - // anything here that needs to be done for all monarch windows. This - // should only be for work that's done when a window _becomes_ a - // monarch, after the death of the previous monarch. + // This method is only called when a _new_ monarch is elected. So + // don't do anything here that needs to be done for all monarch + // windows. This should only be for work that's done when a window + // _becomes_ a monarch, after the death of the previous monarch. return true; } return false; @@ -233,63 +240,126 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { HANDLE waits[2]; waits[1] = _monarchWaitInterrupt.get(); + const auto peasantID = _peasant.GetID(); - bool exitRequested = false; - while (!exitRequested) + bool exitThreadRequested = false; + while (!exitThreadRequested) { - wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, - FALSE, - static_cast(_monarch.GetPID())) }; - // TODO:MG If we fail to open the monarch, then they don't exist - // anymore! Go straight to an election. - // - // TODO:MG At any point in all this, the current monarch might die. - // We go straight to a new election, right? Worst case, eventually, - // we'll become the new monarch. - // - // if (hMonarch.get() == nullptr) - // { - // const auto gle = GetLastError(); - // return false; - // } - waits[0] = hMonarch.get(); - auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); - - switch (waitResult) + // At any point in all this, the current monarch might die. If it + // does, we'll go straight to a new election, in the "jail" + // try/catch below. Worst case, eventually, we'll become the new + // monarch. + try { - case WAIT_OBJECT_0 + 0: // waits[0] was signaled - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_MonarchDied", - TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // Connect to the new monarch, which might be us! - // If we become the monarch, then we'll return true and exit this thread. - exitRequested = _electionNight2020(); - break; - case WAIT_OBJECT_0 + 1: // waits[1] was signaled + // This might fail to even ask the monarch for it's PID. + wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, + FALSE, + static_cast(_monarch.GetPID())) }; + + // If we fail to open the monarch, then they don't exist + // anymore! Go straight to an election. + if (hMonarch.get() == nullptr) + { + const auto gle = GetLastError(); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_FailedToOpenMonarch", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + exitThreadRequested = _performElection(); + continue; + } + + waits[0] = hMonarch.get(); + auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); + + switch (waitResult) + { + case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the monarch process + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchDied", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // Connect to the new monarch, which might be us! + // If we become the monarch, then we'll return true and exit this thread. + exitThreadRequested = _performElection(); + break; + + case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchWaitInterrupted", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + exitThreadRequested = true; + break; + + case WAIT_TIMEOUT: + // This should be impossible. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchWaitTimeout", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + exitThreadRequested = true; + break; + + default: + { + // Returning any other value is invalid. Just die. + const auto gle = GetLastError(); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_WaitFailed", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + ExitProcess(0); + } + } + } + catch (...) + { + // Theoretically, if window[1] dies when we're trying to get + // it's PID we'll get here. If we just try to do the election + // once here, it's possible we might elect window[2], but have + // it die before we add ourselves as a peasant. That + // _performElection call will throw, and we wouldn't catch it + // here, and we'd die. + + // Instead, we're going to have a resilent election process. + // We're going to keep trying an election, until one _doesn't_ + // throw an exception. That might mean burning through all the + // other dying monarchs until we find us as the monarch. But if + // this process is alive, then there's _someone_ in the line of + // succession. TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_MonarchWaitInterrupted", - TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), + "WindowManager_ExceptionInWaitThread", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - exitRequested = true; - break; - - case WAIT_TIMEOUT: - printf("Wait timed out. This should be impossible.\n"); - exitRequested = true; - break; - - // Return value is invalid. - default: - { - auto gle = GetLastError(); - printf("WaitForMultipleObjects returned: %d\n", waitResult); - printf("Wait error: %d\n", gle); - ExitProcess(0); - } + bool foundNewMonarch = false; + while (!foundNewMonarch) + { + try + { + exitThreadRequested = _performElection(); + // It doesn't matter if we're the monarch, or someone + // else is, but if we complete the election, then we've + // registered with a new one. We can escape this jail + // and re-enter society. + foundNewMonarch = true; + } + catch (...) + { + // If we fail to acknowledge the results of the election, + // stay in this jail until we do. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ExceptionInNestedWaitThread", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } } } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 5e52210cb33..a577ab5e502 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -38,7 +38,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _areWeTheKing(); winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional givenID); - bool _electionNight2020(); + bool _performElection(); void _createPeasantThread(); void _waitOnMonarchThread(); void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index dd635eecd1f..247061fcc72 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -543,6 +543,60 @@ namespace RemotingUnitTests void RemotingTests::ProposeCommandlineDeadWindow() { Log::Comment(L"Test proposing a commandline for a peasant that previously died"); - VERIFY_ARE_EQUAL(true, false, L"TODO: Finish this test"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& /*cmdlineArgs*/) { + Log::Comment(L"Commandline dispatched to p1"); + VERIFY_IS_TRUE(false, L"This should not happen, this peasant should be dead."); + }); + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p2"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); + }); + + std::vector p1Args{ L"1", L"arg[1]" }; + std::vector p2Args{ L"2", L"this is for p2" }; + + Log::Comment(L"Kill peasant 1"); + + _killPeasant(m0, 1); + + { + Log::Comment(L"Send a commandline to p2, who is still alive. We won't create a new window."); + + Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + { + Log::Comment(L"Send a commandline to p1, who is dead. We will create a new window."); + Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(true, (bool)result.Id()); + VERIFY_ARE_EQUAL(1u, result.Id().Value()); + } } } From b2db3170ddf3d8c254c859baf2f31de316d8a35f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Jan 2021 11:51:06 -0600 Subject: [PATCH 40/88] man I can't spel --- .github/actions/spell-check/expect/web.txt | 1 + src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/Remoting/WindowManager.cpp | 4 ++-- src/cascadia/TerminalApp/AppLogic.cpp | 6 +++--- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- src/cascadia/WindowsTerminal/main.cpp | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/actions/spell-check/expect/web.txt b/.github/actions/spell-check/expect/web.txt index c0c237ed13c..919e5e9267a 100644 --- a/.github/actions/spell-check/expect/web.txt +++ b/.github/actions/spell-check/expect/web.txt @@ -13,3 +13,4 @@ fixterms uk winui appshellintegration +cppreference diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index fa15636a72a..5d38fcfeb2e 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -129,7 +129,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count(); // For now, we'll just pay attention to whoever the most recent peasant - // was. We're not too wooried about the mru peasant dying. Worst case - + // was. We're not too worried about the mru peasant dying. Worst case - // when the user executes a `wt -w 0`, we won't be able to find that // peasant, and it'll open in a new window instead of the current one. if (args.ActivatedTime() > _lastActivatedTime) diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 206a48d05db..6434a6092ec 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -13,7 +13,7 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass ProposeCommandlineResult { Windows.Foundation.IReference Id { get; }; - // TOOD:projects/5 - also return the name here, if the name was set on the commandline + // TODO:projects/5 - also return the name here, if the name was set on the commandline Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index df7006b3ca4..bcc927feaa9 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -167,7 +167,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } // Here, we're the king! // - // This is where you should do any aditional setup that might need to be + // This is where you should do any additional setup that might need to be // done when we become the king. THis will be called both for the first // window, and when the current monarch dies. @@ -327,7 +327,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // _performElection call will throw, and we wouldn't catch it // here, and we'd die. - // Instead, we're going to have a resilent election process. + // Instead, we're going to have a resilient election process. // We're going to keep trying an election, until one _doesn't_ // throw an exception. That might mean burning through all the // other dying monarchs until we find us as the monarch. But if diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 03fd9a05029..8b9c16d5279 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1162,7 +1162,7 @@ namespace winrt::TerminalApp::implementation // CWD. // Arguments: // - args: an array of strings to process as a commandline. These args can contain spaces - // - cwd: The directory to use as the CWD while performing thse actions. + // - cwd: The directory to use as the CWD while performing these actions. // Return Value: // - the result of the first command who's parsing returned a non-zero code, // or 0. (see AppLogic::_ParseArgs) @@ -1217,13 +1217,13 @@ namespace winrt::TerminalApp::implementation } // Any unsuccessful parse will be a new window. That new window will try - // to handle the commandline iteslf, and find that the commandline + // to handle the commandline itself, and find that the commandline // failed to parse. When that happens, the new window will display the // message box. // // This will also work for the case where the user specifies an invalid // commandline in conjunction with `-w 0`. This function will determine - // that the commandline hasa parse error, and indicate that we should + // that the commandline has a parse error, and indicate that we should // create a new window. Then, in that new window, we'll try to set the // StartupActions, which will again fail, returning the correct error // message. diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 406b08580e7..2a6ba0bc0d2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -375,7 +375,7 @@ namespace winrt::TerminalApp::implementation std::wstring originalCwd{ wil::GetCurrentDirectoryW() }; auto restoreCwd = wil::scope_exit([&originalCwd]() { // ignore errors, we'll just power on through. We'd rather do - // something rather than fail silently if the direcotry doesn't + // something rather than fail silently if the directory doesn't // actually exist. LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(originalCwd.c_str())); }); @@ -386,7 +386,7 @@ namespace winrt::TerminalApp::implementation else { // ignore errors, we'll just power on through. We'd rather do - // something rather than fail silently if the direcotry doesn't + // something rather than fail silently if the directory doesn't // actually exist. LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(cwd.c_str())); } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6fa2794b192..e2e7703de63 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -538,7 +538,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se // manager will ask us how to figure out what the target window is for a set // of commandline arguments. We'll take those arguments, and ask AppLogic to // parse them for us. We'll then set ResultTargetWindow in the given args, so -// the sender can use that reuslt. +// the sender can use that result. // Arguments: // - args: the bundle of a commandline and working directory to find the correct target window for. // Return Value: diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index f763edd9d8e..0bf491de744 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -127,7 +127,7 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) if (!host.HasWindow()) { // If we were told to not have a window, exit early. Make sure to use - // ExitProcess to commit sepuku here. If you try just `return 0`, then + // ExitProcess to die here. If you try just `return 0`, then // the XAML app host will crash during teardown. ExitProcess avoids // that. ExitProcess(0); From d2a34389a85888b65cf19e1a71209f2b9433a99c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 27 Jan 2021 10:23:47 -0600 Subject: [PATCH 41/88] Add try/catch's throughout Monarch.cpp --- src/cascadia/Remoting/CommandlineArgs.cpp | 4 +- src/cascadia/Remoting/CommandlineArgs.h | 4 +- src/cascadia/Remoting/Monarch.cpp | 127 ++++++++++++++-------- src/cascadia/Remoting/Peasant.idl | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 6 +- 5 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index db90f3381fd..4f200b7aa61 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -13,12 +13,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler // will give you just the most impossible template errors to try and // decipher. - void CommandlineArgs::Args(winrt::array_view const& value) + void CommandlineArgs::Commandline(winrt::array_view const& value) { _args = { value.begin(), value.end() }; } - winrt::com_array CommandlineArgs::Args() + winrt::com_array CommandlineArgs::Commandline() { return winrt::com_array{ _args.begin(), _args.end() }; } diff --git a/src/cascadia/Remoting/CommandlineArgs.h b/src/cascadia/Remoting/CommandlineArgs.h index 159071854f2..d76830f65d4 100644 --- a/src/cascadia/Remoting/CommandlineArgs.h +++ b/src/cascadia/Remoting/CommandlineArgs.h @@ -23,8 +23,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::hstring CurrentDirectory() { return _cwd; }; - void Args(winrt::array_view const& value); - winrt::com_array Args(); + void Commandline(winrt::array_view const& value); + winrt::com_array Commandline(); private: winrt::com_array _args; diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 5d38fcfeb2e..08eb5913303 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -46,35 +46,48 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - the ID assigned to the peasant. uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) { - // TODO:projects/5 This is terrible. There's gotta be a better way - // of finding the first opening in a non-consecutive map of int->object - const auto providedID = peasant.GetID(); - - if (providedID == 0) - { - // Peasant doesn't currently have an ID. Assign it a new one. - peasant.AssignID(_nextPeasantID++); - } - else + try { - // Peasant already had an ID (from an older monarch). Leave that one - // be. Make sure that the next peasant's ID is higher than it. - _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; - } + // TODO:projects/5 This is terrible. There's gotta be a better way + // of finding the first opening in a non-consecutive map of int->object + const auto providedID = peasant.GetID(); - auto newPeasantsId = peasant.GetID(); - _peasants[newPeasantsId] = peasant; + if (providedID == 0) + { + // Peasant doesn't currently have an ID. Assign it a new one. + peasant.AssignID(_nextPeasantID++); + } + else + { + // Peasant already had an ID (from an older monarch). Leave that one + // be. Make sure that the next peasant's ID is higher than it. + _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; + } - // Add an event listener to the peasant's WindowActivated event. - peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + auto newPeasantsId = peasant.GetID(); + // Add an event listener to the peasant's WindowActivated event. + peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_AddPeasant", - TraceLoggingUInt64(providedID, "providedID", "the provided ID for the peasant"), - TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + _peasants[newPeasantsId] = peasant; - return newPeasantsId; + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_AddPeasant", + TraceLoggingUInt64(providedID, "providedID", "the provided ID for the peasant"), + TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return newPeasantsId; + } + catch (...) + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_AddPeasant_Failed", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // We can only get into this try/catch if the peasant died on us. So + // the return value doesn't _really_ matter. They're not about to + // get it. + return -1; + } } // Method Description: @@ -104,6 +117,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto peasantSearch = _peasants.find(peasantID); auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + // Ask the peasant for their PID. This will validate that they're + // actually still alive. if (maybeThePeasant) { maybeThePeasant.GetPID(); @@ -148,23 +163,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Monarch::_getMostRecentPeasantID() { - if (_mostRecentPeasant == 0) + if (_mostRecentPeasant != 0) { - // We haven't yet been told the MRU peasant. Just use the first one. - // This is just gonna be a random one, but really shouldn't happen - // in practice. The WindowManager should set the MRU peasant - // immediately as soon as it creates the monarch/peasant for the - // first window. - if (_peasants.size() > 0) + return _mostRecentPeasant; + } + + // We haven't yet been told the MRU peasant. Just use the first one. + // This is just gonna be a random one, but really shouldn't happen + // in practice. The WindowManager should set the MRU peasant + // immediately as soon as it creates the monarch/peasant for the + // first window. + if (_peasants.size() > 0) + { + try { return _peasants.begin()->second.GetID(); } - return 0; - } - else - { - return _mostRecentPeasant; + catch (...) + { + // This shouldn't really happen. If we're the monarch, then the + // first peasant should also _be us_. So we should be able to + // get our own ID. + return 0; + } } + return 0; } // Method Description: @@ -187,6 +210,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto findWindowArgs = winrt::make_self(); findWindowArgs->Args(args); + // This is handled by some handler in-proc _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); // After the event was handled, ResultTargetWindow() will be filled with @@ -205,20 +229,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (auto targetPeasant{ _getPeasant(windowID) }) { - // This will raise the peasant's ExecuteCommandlineRequested - // event, which will then ask the AppHost to handle the - // commandline, which will then pass it to AppLogic for - // handling. - targetPeasant.ExecuteCommandline(args); + auto result = winrt::make_self(); + + result->ShouldCreateWindow(false); + try + { + // This will raise the peasant's ExecuteCommandlineRequested + // event, which will then ask the AppHost to handle the + // commandline, which will then pass it to AppLogic for + // handling. + targetPeasant.ExecuteCommandline(args); + } + catch (...) + { + // If we fail to propose the commandline to the peasant (it + // died?) then just tell this process to become a new window + // instead. + result->ShouldCreateWindow(false); + } TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", - TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"), TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"), + TraceLoggingBoolean(!result->ShouldCreateWindow(), "succeeded", "true if we successfully dispatched the commandline to the peasant"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - auto result = winrt::make_self(); - result->ShouldCreateWindow(false); return *result; } else if (windowID > 0) @@ -229,7 +264,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", - TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"), TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index e3fb5af1b86..c77b5673e5c 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -9,7 +9,7 @@ namespace Microsoft.Terminal.Remoting CommandlineArgs(); CommandlineArgs(String[] args, String cwd); - String[] Args { get; set; }; + String[] Commandline { get; set; }; String CurrentDirectory(); }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index e2e7703de63..6b678cae94c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -168,7 +168,7 @@ void AppHost::_HandleCommandlineArgs() { if (auto args{ peasant.InitialArgs() }) { - const auto result = _logic.SetStartupCommandline(args.Args()); + const auto result = _logic.SetStartupCommandline(args.Commandline()); const auto message = _logic.ParseCommandlineMessage(); if (!message.empty()) { @@ -530,7 +530,7 @@ bool AppHost::HasWindow() void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, Remoting::CommandlineArgs args) { - _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); + _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } // Method Description: @@ -546,7 +546,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::FindTargetWindowArgs& args) { - const auto targetWindow = _logic.FindTargetWindow(args.Args().Args()); + const auto targetWindow = _logic.FindTargetWindow(args.Args().Commandline()); args.ResultTargetWindow(targetWindow); } From a65f3419e39db50b2ee24e5e7e55fdfb651fe270 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 27 Jan 2021 12:15:06 -0600 Subject: [PATCH 42/88] trycatch the window manager. A lot more going on there. --- src/cascadia/Remoting/WindowManager.cpp | 73 +++++++++++++++++++++---- src/cascadia/Remoting/WindowManager.h | 2 +- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index bcc927feaa9..de4eb40a826 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -23,7 +23,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Register with COM as a server for the Monarch class _registerAsMonarch(); // Instantiate an instance of the Monarch. This may or may not be in-proc! - _createMonarchAndCallbacks(); + bool foundMonarch = false; + while (!foundMonarch) + { + try + { + _createMonarchAndCallbacks(); + // _createMonarchAndCallbacks will initialize _isKing + foundMonarch = true; + } + catch (...) + { + // If we fail to find the monarch, + // stay in this jail until we do. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ExceptionInCtor", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } } WindowManager::~WindowManager() @@ -36,6 +53,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; _monarchWaitInterrupt.SetEvent(); + // A thread is joinable once it's been started. Basically this just + // makes sure that the thread isn't just default-constructed. if (_electionThread.joinable()) { _electionThread.join(); @@ -44,15 +63,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) { - const bool isKing = _areWeTheKing(); // If we're the king, we _definitely_ want to process the arguments, we were // launched with them! // // Otherwise, the King will tell us if we should make a new window - _shouldCreateWindow = isKing; + _shouldCreateWindow = _isKing; std::optional givenID; - - if (!isKing) + if (!_isKing) { // The monarch may respond back "you should be a new // window, with ID,name of (id, name)". Really the responses are: @@ -106,7 +123,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _createOurPeasant({ givenID }); // Spawn a thread to wait on the monarch, and handle the election - if (!isKing) + if (!_isKing) { _createPeasantThread(); } @@ -144,15 +161,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CLSCTX_LOCAL_SERVER); } + // NOTE: This can throw! Callers include: + // - the constructor, who performs this in a loop until it successfully + // finda a monarch + // - the performElection method, which is called in the waitOnMonarch + // thread. All the calls in that thread are wrapped in try/catch's + // already. + // - _createOurPeasant, who might do this in a loop to establish us with the + // monarch. void WindowManager::_createMonarchAndCallbacks() { _createMonarch(); - const auto isKing = _areWeTheKing(); + // Save the result of checking if we're the king. We want to avoid + // unnecessary calls back and forth if we can. + _isKing = _areWeTheKing(); TraceLoggingWrite(g_hRemotingProvider, "WindowManager_ConnectedToMonarch", TraceLoggingUInt64(_monarch.GetPID(), "monarchPID", "The PID of the new Monarch"), - TraceLoggingBoolean(isKing, "isKing", "true if we are the new monarch"), + TraceLoggingBoolean(_isKing, "isKing", "true if we are the new monarch"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); if (_peasant) @@ -161,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs()); } - if (!isKing) + if (!_isKing) { return; } @@ -176,8 +203,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_areWeTheKing() { - const auto kingPID{ _monarch.GetPID() }; const auto ourPID{ GetCurrentProcessId() }; + const auto kingPID{ _monarch.GetPID() }; return (ourPID == kingPID); } @@ -189,7 +216,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation p->AssignID(givenID.value()); } _peasant = *p; - _monarch.AddPeasant(_peasant); + + // Try to add us to the monarch. If that fails, try to find a monarch + // again, until we find one (we will eventually find us) + while (true) + { + try + { + _monarch.AddPeasant(_peasant); + break; + } + catch (...) + { + try + { + // Wrap this in it's own try/catch, beause this can throw. + _createMonarchAndCallbacks(); + } + catch (...) + { + } + } + } TraceLoggingWrite(g_hRemotingProvider, "WindowManager_CreateOurPeasant", @@ -206,6 +254,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - // Return Value: // - true iff we're the new monarch process. + // NOTE: This can throw! bool WindowManager::_performElection() { _createMonarchAndCallbacks(); @@ -213,7 +262,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Tell the new monarch who we are. We might be that monarch! _monarch.AddPeasant(_peasant); - if (_areWeTheKing()) + if (_isKing) { // This method is only called when a _new_ monarch is elected. So // don't do anything here that needs to be done for all monarch diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index a577ab5e502..857b161485a 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -24,12 +24,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation private: bool _shouldCreateWindow{ false }; + bool _isKing{ false }; DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; wil::unique_event _monarchWaitInterrupt; - std::thread _electionThread; void _registerAsMonarch(); From 1c2f8e5d6a31828f806cf0df39c98c55ce1aaf93 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 08:00:28 -0600 Subject: [PATCH 43/88] This will always summon the monarch window --- src/cascadia/Remoting/WindowManager.cpp | 7 +++++ src/cascadia/Remoting/WindowManager.h | 2 ++ src/cascadia/Remoting/WindowManager.idl | 3 ++ src/cascadia/TerminalApp/AppLogic.cpp | 5 +++ src/cascadia/TerminalApp/AppLogic.h | 2 ++ src/cascadia/TerminalApp/AppLogic.idl | 2 ++ .../GlobalAppSettings.cpp | 31 +++++++++++++++++++ .../TerminalSettingsModel/GlobalAppSettings.h | 1 + .../GlobalAppSettings.idl | 4 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 13 ++++++++ src/cascadia/WindowsTerminal/AppHost.h | 2 ++ src/cascadia/WindowsTerminal/IslandWindow.cpp | 17 ++++++++++ src/cascadia/WindowsTerminal/IslandWindow.h | 2 ++ 13 files changed, 91 insertions(+) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index de4eb40a826..a7a146eabf5 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -199,6 +199,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // window, and when the current monarch dies. _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); + + _BecameMonarchHandlers(*this, nullptr); } bool WindowManager::_areWeTheKing() @@ -423,4 +425,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { _FindTargetWindowRequestedHandlers(sender, args); } + + bool WindowManager::IsMonarch() + { + return _isKing; + } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 857b161485a..ba897a0eed3 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -19,8 +19,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool ShouldCreateWindow(); winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); + bool IsMonarch(); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); private: bool _shouldCreateWindow{ false }; diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 3099f1128b5..531698a50f9 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -10,6 +10,9 @@ namespace Microsoft.Terminal.Remoting void ProposeCommandline(CommandlineArgs args); Boolean ShouldCreateWindow { get; }; IPeasant CurrentWindow(); + Boolean IsMonarch { get; }; + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; + event Windows.Foundation.TypedEventHandler BecameMonarch; }; } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 8b9c16d5279..464aaa8a1ba 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1276,6 +1276,11 @@ namespace winrt::TerminalApp::implementation return _root ? _root->AlwaysOnTop() : false; } + TerminalControl::KeyChord AppLogic::GlobalHotkey() + { + return _settings.GlobalSettings().GlobalHotkey(); + } + // -------------------------------- 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 e14dae49f12..fc8cc5c63ee 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -59,6 +59,8 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); + winrt::Microsoft::Terminal::TerminalControl::KeyChord GlobalHotkey(); + // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RequestedThemeChanged, _requestedThemeChangedHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index b38e0fe0303..1fd4e9854bd 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -58,6 +58,8 @@ namespace TerminalApp Int32 FindTargetWindow(String[] args); + Microsoft.Terminal.TerminalControl.KeyChord GlobalHotkey(); + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 4d2d904f878..8d748863617 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -7,6 +7,7 @@ #include "../../inc/DefaultSettings.h" #include "JsonUtils.h" #include "TerminalSettingsSerializationHelpers.h" +#include "KeyChordSerialization.h" #include "GlobalAppSettings.g.cpp" @@ -15,6 +16,7 @@ using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; using namespace winrt::Windows::UI::Xaml; using namespace ::Microsoft::Console; using namespace winrt::Microsoft::UI::Xaml::Controls; +using namespace winrt::Microsoft::Terminal::TerminalControl; static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" }; static constexpr std::string_view ActionsKey{ "actions" }; @@ -41,6 +43,7 @@ static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" }; static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" }; static constexpr std::string_view StartupActionsKey{ "startupActions" }; +static constexpr std::string_view GlobalHotkeyKey{ "globalHotkey" }; static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" }; @@ -304,6 +307,34 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, StartupActionsKey, _StartupActions); + try + { + const auto keys = json[JsonKey(GlobalHotkeyKey)]; + const auto validString = keys.isString(); + const auto validArray = keys.isArray() && keys.size() == 1; + + // GH#4239 - If the user provided more than one key + // chord to a "keys" array, warn the user here. + // TODO: GH#1334 - remove this check. + if (keys.isArray() && keys.size() > 1) + { + // TODO: add a warning + // warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord); + } + + if (validString || validArray) + { + const auto keyChordString = keys.isString() ? winrt::to_hstring(keys.asString()) : winrt::to_hstring(keys[0].asString()); + + const auto chord = KeyChordSerialization::FromString(keyChordString); + _GlobalHotkey = chord; + } + } + catch (...) + { + // TODO: add a settings warning + } + // This is a helper lambda to get the keybindings and commands out of both // and array of objects. We'll use this twice, once on the legacy // `keybindings` key, and again on the newer `bindings` key. diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 8e1e65942b4..336a4a91493 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -87,6 +87,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GETSET_SETTING(Model::TabSwitcherMode, TabSwitcherMode, Model::TabSwitcherMode::InOrder); GETSET_SETTING(bool, DisableAnimations, false); GETSET_SETTING(hstring, StartupActions, L""); + GETSET_SETTING(TerminalControl::KeyChord, GlobalHotkey, nullptr); private: guid _defaultProfile; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 6f0fc41dac1..bc30ca3c08b 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -146,5 +146,9 @@ namespace Microsoft.Terminal.Settings.Model Boolean HasStartupActions(); void ClearStartupActions(); String StartupActions(); + + Boolean HasGlobalHotkey(); + void ClearGlobalHotkey(); + Microsoft.Terminal.TerminalControl.KeyChord GlobalHotkey; } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6b678cae94c..faf6cd595c0 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -73,6 +73,12 @@ AppHost::AppHost() noexcept : _window->WindowActivated({ this, &AppHost::_WindowActivated }); _window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); _window->MakeWindow(); + + _windowManager.BecameMonarch({ this, &AppHost::_BecomeMonarch }); + if (_windowManager.IsMonarch()) + { + _BecomeMonarch(nullptr, nullptr); + } } AppHost::~AppHost() @@ -563,3 +569,10 @@ void AppHost::_WindowActivated() peasant.ActivateWindow(args); } } + +void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) +{ + auto hotkey{ _logic.GlobalHotkey() }; + _window->SetGlobalHotkey(hotkey); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index b3baf25bbc1..b9bd90fbcf9 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -51,4 +51,6 @@ class AppHost void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); }; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index db8f5cd8cf9..9d429246931 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -15,6 +15,7 @@ using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Hosting; using namespace winrt::Windows::Foundation::Numerics; using namespace winrt::Microsoft::Terminal::Settings::Model; +using namespace winrt::Microsoft::Terminal::TerminalControl; using namespace ::Microsoft::Console::Types; #define XAML_HOSTING_WINDOW_CLASS_NAME L"CASCADIA_HOSTING_WINDOW_CLASS" @@ -843,5 +844,21 @@ void IslandWindow::_ApplyWindowSize() SWP_FRAMECHANGED | SWP_NOACTIVATE)); } +void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& hotkey) +{ + if (hotkey) + { + auto modifiers = hotkey.Modifiers(); + WPARAM wParam = hotkey.Vkey() | + (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? HOTKEYF_ALT << 8 : 0) | + (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? HOTKEYF_CONTROL << 8 : 0) | + (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? HOTKEYF_SHIFT << 8 : 0); + auto result = SendMessage(_window.get(), WM_SETHOTKEY, wParam, 0); + result; + auto a = result + 1; + a; + } +} + DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index fe0cae1ec65..8bfeafffd6b 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -38,6 +38,8 @@ class IslandWindow : void FlashTaskbar(); void SetTaskbarProgress(const size_t state, const size_t progress); + void SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& hotkey); + #pragma endregion DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); From 71f6b581e28bb0e2d253cc8d356c8158b174d1b7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 10:56:57 -0600 Subject: [PATCH 44/88] RegisterHotKey will be more robust --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 9d429246931..3ce8b435f7c 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -352,6 +352,18 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { switch (message) { + case WM_HOTKEY: + { + auto a = 0; + a++; + a; + // ShowWindow(_window.get(), SW_RESTORE); + // SetFocus(_window.get()); + HWND myHwnd = _window.get(); + SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)myHwnd); + // break; + return 0; + } case WM_GETMINMAXINFO: { _OnGetMinMaxInfo(wparam, lparam); @@ -849,14 +861,29 @@ void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalCon if (hotkey) { auto modifiers = hotkey.Modifiers(); - WPARAM wParam = hotkey.Vkey() | - (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? HOTKEYF_ALT << 8 : 0) | - (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? HOTKEYF_CONTROL << 8 : 0) | - (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? HOTKEYF_SHIFT << 8 : 0); - auto result = SendMessage(_window.get(), WM_SETHOTKEY, wParam, 0); - result; - auto a = result + 1; - a; + // WPARAM wParam = hotkey.Vkey() | + // (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? HOTKEYF_ALT << 8 : 0) | + // (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? HOTKEYF_CONTROL << 8 : 0) | + // (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? HOTKEYF_SHIFT << 8 : 0); + // auto result = SendMessage(_window.get(), WM_SETHOTKEY, wParam, 0); + // result; + // auto a = result + 1; + // a; + + auto MODs = MOD_NOREPEAT | + (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? MOD_ALT : 0) | + (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? MOD_CONTROL : 0) | + (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? MOD_SHIFT : 0); + auto res = RegisterHotKey(_window.get(), 1, MODs, hotkey.Vkey()); + LOG_LAST_ERROR_IF(!res); + // if (!res) + // { + // // auto gle = GetLastError(); + + // } + // res; + // auto b = !res; + // b; } } From 18d1a205bb7c0c40f7c10c58e9ba402389373034 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 12:23:37 -0600 Subject: [PATCH 45/88] Add support for the `win` key in keybindings --- src/cascadia/TerminalControl/KeyChord.idl | 3 ++- .../KeyChordSerialization.cpp | 12 +++++++++++- src/cascadia/WindowsTerminal/IslandWindow.cpp | 18 ++---------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/cascadia/TerminalControl/KeyChord.idl b/src/cascadia/TerminalControl/KeyChord.idl index 4952688a602..81f7cefc451 100644 --- a/src/cascadia/TerminalControl/KeyChord.idl +++ b/src/cascadia/TerminalControl/KeyChord.idl @@ -9,7 +9,8 @@ namespace Microsoft.Terminal.TerminalControl None = 0x0000, Alt = 0x0001, Ctrl = 0x0002, - Shift = 0x0004 + Shift = 0x0004, + Windows = 0x0008 }; [default_interface] diff --git a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp index b57fdc5207c..0092b520149 100644 --- a/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyChordSerialization.cpp @@ -11,8 +11,9 @@ using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; static constexpr std::wstring_view CTRL_KEY{ L"ctrl" }; static constexpr std::wstring_view SHIFT_KEY{ L"shift" }; static constexpr std::wstring_view ALT_KEY{ L"alt" }; +static constexpr std::wstring_view WIN_KEY{ L"win" }; -static constexpr int MAX_CHORD_PARTS = 4; +static constexpr int MAX_CHORD_PARTS = 5; // win+ctrl+alt+shift+key // clang-format off static const std::unordered_map vkeyNamePairs { @@ -143,6 +144,10 @@ KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr) { modifiers |= KeyModifiers::Shift; } + else if (lowercase == WIN_KEY) + { + modifiers |= KeyModifiers::Windows; + } else { bool foundKey = false; @@ -224,6 +229,11 @@ winrt::hstring KeyChordSerialization::ToString(const KeyChord& chord) std::wstring buffer{ L"" }; // Add modifiers + if (WI_IsFlagSet(modifiers, KeyModifiers::Windows)) + { + buffer += WIN_KEY; + buffer += L"+"; + } if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl)) { buffer += CTRL_KEY; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 3ce8b435f7c..d7825627903 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -861,29 +861,15 @@ void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalCon if (hotkey) { auto modifiers = hotkey.Modifiers(); - // WPARAM wParam = hotkey.Vkey() | - // (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? HOTKEYF_ALT << 8 : 0) | - // (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? HOTKEYF_CONTROL << 8 : 0) | - // (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? HOTKEYF_SHIFT << 8 : 0); - // auto result = SendMessage(_window.get(), WM_SETHOTKEY, wParam, 0); - // result; - // auto a = result + 1; - // a; auto MODs = MOD_NOREPEAT | + (WI_IsFlagSet(modifiers, KeyModifiers::Windows) ? MOD_WIN : 0) | (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? MOD_ALT : 0) | (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? MOD_CONTROL : 0) | (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? MOD_SHIFT : 0); + auto res = RegisterHotKey(_window.get(), 1, MODs, hotkey.Vkey()); LOG_LAST_ERROR_IF(!res); - // if (!res) - // { - // // auto gle = GetLastError(); - - // } - // res; - // auto b = !res; - // b; } } From a41bee653ba0c66ebf5cf62135118e6fed7aefa7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 12:24:09 -0600 Subject: [PATCH 46/88] !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index d7825627903..d4699ef6479 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -390,6 +390,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { _WindowActivatedHandlers(); } + break; } case WM_NCLBUTTONDOWN: From 6e7ea615d2fab114fce69907caf0777e5b8b7b72 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 12:24:52 -0600 Subject: [PATCH 47/88] !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! --- src/cascadia/WindowsTerminal/AppHost.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index faf6cd595c0..cbd1aeac78c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -556,8 +556,10 @@ void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& args.ResultTargetWindow(targetWindow); } -void AppHost::_WindowActivated() +winrt::fire_and_forget AppHost::_WindowActivated() { + co_await winrt::resume_background(); + if (auto peasant{ _windowManager.CurrentWindow() }) { // TODO: projects/5 - in the future, we'll want to actually get the From 18099d2aa796b1f608ff6cc48e7d37640f371358 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 12:26:07 -0600 Subject: [PATCH 48/88] Summon the most recent window This roundtrips through the monarch to find the MRU peasant. Also ensures that we register for the hotkey on the right thread. The monarch will get elected on the BG thread, but registering the hotkey must be on the main thread --- src/cascadia/Remoting/Monarch.cpp | 15 +++++++++++++ src/cascadia/Remoting/Monarch.h | 1 + src/cascadia/Remoting/Monarch.idl | 1 + src/cascadia/Remoting/Peasant.cpp | 4 ++++ src/cascadia/Remoting/Peasant.h | 2 ++ src/cascadia/Remoting/Peasant.idl | 2 ++ src/cascadia/Remoting/WindowManager.cpp | 9 ++++++++ src/cascadia/Remoting/WindowManager.h | 1 + src/cascadia/Remoting/WindowManager.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 19 +++++++++++++++-- src/cascadia/WindowsTerminal/AppHost.h | 9 +++++--- src/cascadia/WindowsTerminal/IslandWindow.cpp | 21 ++++++++++++------- src/cascadia/WindowsTerminal/IslandWindow.h | 4 ++++ src/cascadia/WindowsTerminal/pch.h | 1 + 14 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 08eb5913303..516b189b8a7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -286,4 +286,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *result; } + void Monarch::SummonWindow() + { + try + { + auto windowID = _getMostRecentPeasantID(); + if (auto targetPeasant{ _getPeasant(windowID) }) + { + targetPeasant.Summon(); + } + } + catch (...) + { + } + } + } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 96456580e88..02126dc9732 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -54,6 +54,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); + void SummonWindow(); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 6434a6092ec..0caf21c7a0c 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -24,6 +24,7 @@ namespace Microsoft.Terminal.Remoting UInt64 AddPeasant(IPeasant peasant); ProposeCommandlineResult ProposeCommandline(CommandlineArgs args); void HandleActivatePeasant(WindowActivatedArgs args); + void SummonWindow(); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 699adf976b2..648d9361560 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -117,4 +117,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _lastActivatedArgs; } + void Peasant::Summon() + { + _SummonRequestedHandlers(*this, nullptr); + } } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 4d2bb24a01b..7dc3a89f589 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -22,12 +22,14 @@ 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 Summon(); winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); 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(SummonRequested, 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 c77b5673e5c..0934b802777 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -31,9 +31,11 @@ namespace Microsoft.Terminal.Remoting Boolean ExecuteCommandline(CommandlineArgs args); void ActivateWindow(WindowActivatedArgs args); WindowActivatedArgs GetLastActivatedArgs(); + void Summon(); event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; + event Windows.Foundation.TypedEventHandler SummonRequested; }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index a7a146eabf5..181de632b8e 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -430,4 +430,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { return _isKing; } + + void WindowManager::SummonWindow() + { + // We should only ever get called when we are the monarch, because only + // the monarch ever registers for the global hotkey. So the monarch is + // the only window that will be calling this. + _monarch.SummonWindow(); + } + } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index ba897a0eed3..e038d53a717 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -20,6 +20,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); bool IsMonarch(); + void SummonWindow(); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 531698a50f9..fccfa6f1fc3 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -11,6 +11,7 @@ namespace Microsoft.Terminal.Remoting Boolean ShouldCreateWindow { get; }; IPeasant CurrentWindow(); Boolean IsMonarch { get; }; + void SummonWindow(); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler BecameMonarch; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index cbd1aeac78c..f17d95d7f72 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -71,6 +71,7 @@ AppHost::AppHost() noexcept : std::placeholders::_2)); _window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled }); _window->WindowActivated({ this, &AppHost::_WindowActivated }); + _window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed }); _window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); _window->MakeWindow(); @@ -200,6 +201,7 @@ 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.SummonRequested({ this, &AppHost::_HandleSummon }); } } @@ -572,9 +574,22 @@ winrt::fire_and_forget AppHost::_WindowActivated() } } -void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) +winrt::fire_and_forget AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) { + // co_await winrt::resume_foreground(_window->Dispatcher()); + co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(), winrt::Windows::UI::Core::CoreDispatcherPriority::Normal); auto hotkey{ _logic.GlobalHotkey() }; _window->SetGlobalHotkey(hotkey); } + +void AppHost::_GlobalHotkeyPressed() +{ + _windowManager.SummonWindow(); +} + +void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) +{ + _window->SummonWindow(); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index b9bd90fbcf9..9322dff1b7a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -44,13 +44,16 @@ class AppHost void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const til::point coord, const int32_t delta); - void _WindowActivated(); + winrt::fire_and_forget _WindowActivated(); void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); - void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + winrt::fire_and_forget _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + void _GlobalHotkeyPressed(); + void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); }; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index d4699ef6479..2fa9df533aa 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -354,14 +354,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { case WM_HOTKEY: { - auto a = 0; - a++; - a; - // ShowWindow(_window.get(), SW_RESTORE); - // SetFocus(_window.get()); - HWND myHwnd = _window.get(); - SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)myHwnd); - // break; + _HotkeyPressedHandlers(); return 0; } case WM_GETMINMAXINFO: @@ -874,5 +867,17 @@ void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalCon } } +void IslandWindow::SummonWindow() +{ + // A SC_HOTKEY WM_SYSCOMMAND that's _not_ processed my a wndproc will summon + // the window exactly the way we want. So send that yo ourselves. + SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)(_window.get())); +} + +winrt::Windows::UI::Core::CoreDispatcher IslandWindow::Dispatcher() +{ + return _rootGrid.Dispatcher(); +} + DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 8bfeafffd6b..d47b6481a23 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -40,12 +40,16 @@ class IslandWindow : void SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& hotkey); + void SummonWindow(); + winrt::Windows::UI::Core::CoreDispatcher Dispatcher(); + #pragma endregion DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); WINRT_CALLBACK(MouseScrolled, winrt::delegate); WINRT_CALLBACK(WindowActivated, winrt::delegate); + WINRT_CALLBACK(HotkeyPressed, winrt::delegate); protected: void ForceResize() diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index b416ccccc4f..149f8a97e37 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -58,6 +58,7 @@ Module Name: // Additional headers for various xaml features. We need: // * Controls for grid // * Media for ScaleTransform +#include #include #include From 3e5d9276f113acc6b8608374352c91331e52e203 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 12:32:48 -0600 Subject: [PATCH 49/88] Cleanup, now that this isn't needed --- src/cascadia/WindowsTerminal/AppHost.cpp | 5 +++-- src/cascadia/WindowsTerminal/IslandWindow.cpp | 5 ----- src/cascadia/WindowsTerminal/IslandWindow.h | 1 - src/cascadia/WindowsTerminal/pch.h | 1 + 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index f17d95d7f72..94fdd49250a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -577,8 +577,9 @@ winrt::fire_and_forget AppHost::_WindowActivated() winrt::fire_and_forget AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - // co_await winrt::resume_foreground(_window->Dispatcher()); - co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(), winrt::Windows::UI::Core::CoreDispatcherPriority::Normal); + // The hotkey MUST be registered on the main thread. It will fail otherwise! + co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(), + winrt::Windows::UI::Core::CoreDispatcherPriority::Normal); auto hotkey{ _logic.GlobalHotkey() }; _window->SetGlobalHotkey(hotkey); } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 2fa9df533aa..ddd16ee03dd 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -874,10 +874,5 @@ void IslandWindow::SummonWindow() SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)(_window.get())); } -winrt::Windows::UI::Core::CoreDispatcher IslandWindow::Dispatcher() -{ - return _rootGrid.Dispatcher(); -} - DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index d47b6481a23..8cfdc8f445b 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -41,7 +41,6 @@ class IslandWindow : void SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& hotkey); void SummonWindow(); - winrt::Windows::UI::Core::CoreDispatcher Dispatcher(); #pragma endregion diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 149f8a97e37..e6eeb4a0705 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -56,6 +56,7 @@ Module Name: #include // Additional headers for various xaml features. We need: +// * Core for CoreDispatcher // * Controls for grid // * Media for ScaleTransform #include From 91b52d4e6dad210abf9fe8a8d7bb3a3e50f60e30 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 14:25:04 -0600 Subject: [PATCH 50/88] !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! > The **low-order word** specifies whether the window is being activated or deactivated. --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index ddd16ee03dd..c8e2eb8c237 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -379,7 +379,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize case WM_ACTIVATE: { // wparam = 0 indicates the window was deactivated - if (wparam != 0) + if (LOWORD(wparam) != 0) { _WindowActivatedHandlers(); } From 848682a27f2c763e82bb3bf703115c15c4a408ee Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 16:56:19 -0600 Subject: [PATCH 51/88] Oh hey SetForegroundWindow might be the thing --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 33 +++++++++++++++++-- src/cascadia/WindowsTerminal/IslandWindow.h | 2 +- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index c8e2eb8c237..ea353deadea 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -373,8 +373,9 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { // send focus to the child window SetFocus(_interopWindowHandle); - return 0; // eat the message + // return 0; // eat the message } + break; } case WM_ACTIVATE: { @@ -867,11 +868,37 @@ void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalCon } } -void IslandWindow::SummonWindow() +winrt::fire_and_forget IslandWindow::SummonWindow() { // A SC_HOTKEY WM_SYSCOMMAND that's _not_ processed my a wndproc will summon // the window exactly the way we want. So send that yo ourselves. - SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)(_window.get())); + + // Does the mysterious "cursor starts blinking but doesn't accept keyboard focus" + // SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)(_window.get())); + + // Does the mysterious "cursor starts blinking but doesn't accept keyboard focus" + // ShowWindow(_window.get(), SW_RESTORE); + // SetActiveWindow(_window.get()); + // SetFocus(_window.get()); + + // Does nothing at all + // ShowWindow(_window.get(), SW_SHOWNORMAL); + + // The co_await didn't help here + // co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + // ShowWindow(_window.get(), SW_RESTORE); + // SetActiveWindow(_window.get()); + // SetFocus(_window.get()); + + co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + SetWindowPos(_window.get(), + HWND_TOP, + 0, + 0, + 0, + 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + SetForegroundWindow(_window.get()); } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 8cfdc8f445b..4f91945953c 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -40,7 +40,7 @@ class IslandWindow : void SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalControl::KeyChord& hotkey); - void SummonWindow(); + winrt::fire_and_forget SummonWindow(); #pragma endregion From 1dcb4cb777edd619b6e05bc75813ea95feded87c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 17:02:24 -0600 Subject: [PATCH 52/88] man this is crisp --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index ea353deadea..86f8e1906f7 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -890,14 +890,19 @@ winrt::fire_and_forget IslandWindow::SummonWindow() // SetActiveWindow(_window.get()); // SetFocus(_window.get()); + // This (even with a co_await didn't work, and it doesn't restore a minimized window) + // SetWindowPos(_window.get(), + // HWND_TOP, + // 0, + // 0, + // 0, + // 0, + // SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - SetWindowPos(_window.get(), - HWND_TOP, - 0, - 0, - 0, - 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + ShowWindow(_window.get(), SW_RESTORE); + SetActiveWindow(_window.get()); + SetFocus(_window.get()); SetForegroundWindow(_window.get()); } From fee647373752d214098b51627fe866f18c0290c2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 17:14:51 -0600 Subject: [PATCH 53/88] Still trying to figure this out exactly. For linking: #653 --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 86f8e1906f7..cbdf194ff69 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -870,27 +870,22 @@ void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalCon winrt::fire_and_forget IslandWindow::SummonWindow() { - // A SC_HOTKEY WM_SYSCOMMAND that's _not_ processed my a wndproc will summon - // the window exactly the way we want. So send that yo ourselves. + // On the foreground thread: + // * Restore the window from minimized + // * Activate the window + // * focus the window + // * IMPORTANT: MAKE THE WINDOW THE FOREGROUND WINDOW + // - If you forget this last one, you'll start blinking the cursor for the + // Terminal, but it WON'T ACTUALLY BE THE FOREGROUND WINDOW. It won't + // recieve keyboard input. Crazy. + + // TODO: This isn't right either. This just causes the window to blink. Hmm. + // I wonder if this is because we're doing it from an RPC thread (likely). - // Does the mysterious "cursor starts blinking but doesn't accept keyboard focus" - // SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)(_window.get())); - - // Does the mysterious "cursor starts blinking but doesn't accept keyboard focus" - // ShowWindow(_window.get(), SW_RESTORE); - // SetActiveWindow(_window.get()); - // SetFocus(_window.get()); - - // Does nothing at all - // ShowWindow(_window.get(), SW_SHOWNORMAL); - - // The co_await didn't help here - // co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - // ShowWindow(_window.get(), SW_RESTORE); - // SetActiveWindow(_window.get()); - // SetFocus(_window.get()); + co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + ShowWindow(_window.get(), SW_RESTORE); // TODO: Frick this will restore down too. We don't want that. + // This doesn't _really_ help, but it might help put the window on top of the Z order. - // This (even with a co_await didn't work, and it doesn't restore a minimized window) // SetWindowPos(_window.get(), // HWND_TOP, // 0, @@ -898,12 +893,12 @@ winrt::fire_and_forget IslandWindow::SummonWindow() // 0, // 0, // SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - - co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - ShowWindow(_window.get(), SW_RESTORE); SetActiveWindow(_window.get()); SetFocus(_window.get()); SetForegroundWindow(_window.get()); + + // TODO: Why doesn't this work if the window is _not_ minimized? If the + // window is minimized, this works like a charm. W E I R D } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); From 342d3f20267fd9fcb5e5149e63875bbdac9ceb40 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 29 Jan 2021 06:25:01 -0600 Subject: [PATCH 54/88] This works if the window _isn't_ minimized --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index cbdf194ff69..0a672eb3788 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -882,23 +882,41 @@ winrt::fire_and_forget IslandWindow::SummonWindow() // TODO: This isn't right either. This just causes the window to blink. Hmm. // I wonder if this is because we're doing it from an RPC thread (likely). - co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - ShowWindow(_window.get(), SW_RESTORE); // TODO: Frick this will restore down too. We don't want that. - // This doesn't _really_ help, but it might help put the window on top of the Z order. - - // SetWindowPos(_window.get(), - // HWND_TOP, - // 0, - // 0, - // 0, - // 0, - // SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - SetActiveWindow(_window.get()); - SetFocus(_window.get()); - SetForegroundWindow(_window.get()); + // co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + // ShowWindow(_window.get(), SW_RESTORE); // TODO: Frick this will restore down too. We don't want that. + // // This doesn't _really_ help, but it might help put the window on top of the Z order. + + // // SetWindowPos(_window.get(), + // // HWND_TOP, + // // 0, + // // 0, + // // 0, + // // 0, + // // SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + // SetActiveWindow(_window.get()); + // SetFocus(_window.get()); + // SetForegroundWindow(_window.get()); // TODO: Why doesn't this work if the window is _not_ minimized? If the // window is minimized, this works like a charm. W E I R D + + // This works for the monarch, but not for the peasant + // co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + // SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)(_window.get())); + // SetForegroundWindow(_window.get()); + + // From: https://stackoverflow.com/a/59659421 + // > The trick is to make windows ‘think’ that our process and the target + // > window (hwnd) are related by attaching the threads (using + // > AttachThreadInput API) and using an alternative API: BringWindowToTop. + co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), LPDWORD(0)); + DWORD currentThreadId = GetCurrentThreadId(); + DWORD CONST_SW_SHOW = 5; + AttachThreadInput(windowThreadProcessId, currentThreadId, true); + BringWindowToTop(_window.get()); + ShowWindow(_window.get(), CONST_SW_SHOW); + AttachThreadInput(windowThreadProcessId, currentThreadId, false); } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); From 5052d318316b89333bcd32675bb7e5a6c0c36060 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 29 Jan 2021 06:54:30 -0600 Subject: [PATCH 55/88] deal with minimized windows too --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 0a672eb3788..7ba40f17f85 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -910,12 +910,18 @@ winrt::fire_and_forget IslandWindow::SummonWindow() // > window (hwnd) are related by attaching the threads (using // > AttachThreadInput API) and using an alternative API: BringWindowToTop. co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), LPDWORD(0)); - DWORD currentThreadId = GetCurrentThreadId(); - DWORD CONST_SW_SHOW = 5; + // If the window is minimized, then restore it. We don't want to do this + // always though, because SW_RESTORE'ing a maximized window will + // restore-down it. + if (IsIconic(_window.get())) + { + ShowWindow(_window.get(), SW_RESTORE); + } + const DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), LPDWORD(0)); + const DWORD currentThreadId = GetCurrentThreadId(); AttachThreadInput(windowThreadProcessId, currentThreadId, true); BringWindowToTop(_window.get()); - ShowWindow(_window.get(), CONST_SW_SHOW); + ShowWindow(_window.get(), SW_SHOW); AttachThreadInput(windowThreadProcessId, currentThreadId, false); } From eff18d1ec91697279cd6452ecf0e1cd32ae6364c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 29 Jan 2021 07:15:38 -0600 Subject: [PATCH 56/88] more cleanup --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 70 ++++++------------- 1 file changed, 22 insertions(+), 48 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 7ba40f17f85..80f3b0585fb 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -855,74 +855,48 @@ void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::TerminalCon { if (hotkey) { - auto modifiers = hotkey.Modifiers(); + const auto modifiers = hotkey.Modifiers(); + const auto hotkeyFlags = MOD_NOREPEAT | + (WI_IsFlagSet(modifiers, KeyModifiers::Windows) ? MOD_WIN : 0) | + (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? MOD_ALT : 0) | + (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? MOD_CONTROL : 0) | + (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? MOD_SHIFT : 0); - auto MODs = MOD_NOREPEAT | - (WI_IsFlagSet(modifiers, KeyModifiers::Windows) ? MOD_WIN : 0) | - (WI_IsFlagSet(modifiers, KeyModifiers::Alt) ? MOD_ALT : 0) | - (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl) ? MOD_CONTROL : 0) | - (WI_IsFlagSet(modifiers, KeyModifiers::Shift) ? MOD_SHIFT : 0); - - auto res = RegisterHotKey(_window.get(), 1, MODs, hotkey.Vkey()); - LOG_LAST_ERROR_IF(!res); + // `1` is an arbitrary ID. We only have one hotkey in the entire app, so + // we don't need to worry about setting a unique ID for each. + // + // TODO: (discussion) should we display a warning of some kind if this + // fails? This can fail if something else already bound this hotkey. + LOG_IF_WIN32_BOOL_FALSE(RegisterHotKey(_window.get(), + 1, + hotkeyFlags, + hotkey.Vkey())); } } winrt::fire_and_forget IslandWindow::SummonWindow() { // On the foreground thread: - // * Restore the window from minimized - // * Activate the window - // * focus the window - // * IMPORTANT: MAKE THE WINDOW THE FOREGROUND WINDOW - // - If you forget this last one, you'll start blinking the cursor for the - // Terminal, but it WON'T ACTUALLY BE THE FOREGROUND WINDOW. It won't - // recieve keyboard input. Crazy. - - // TODO: This isn't right either. This just causes the window to blink. Hmm. - // I wonder if this is because we're doing it from an RPC thread (likely). - - // co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - // ShowWindow(_window.get(), SW_RESTORE); // TODO: Frick this will restore down too. We don't want that. - // // This doesn't _really_ help, but it might help put the window on top of the Z order. - - // // SetWindowPos(_window.get(), - // // HWND_TOP, - // // 0, - // // 0, - // // 0, - // // 0, - // // SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - // SetActiveWindow(_window.get()); - // SetFocus(_window.get()); - // SetForegroundWindow(_window.get()); - - // TODO: Why doesn't this work if the window is _not_ minimized? If the - // window is minimized, this works like a charm. W E I R D - - // This works for the monarch, but not for the peasant - // co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - // SendMessage(_window.get(), WM_SYSCOMMAND, SC_HOTKEY, (LPARAM)(_window.get())); - // SetForegroundWindow(_window.get()); + co_await winrt::resume_foreground(_rootGrid.Dispatcher()); // From: https://stackoverflow.com/a/59659421 // > The trick is to make windows ‘think’ that our process and the target // > window (hwnd) are related by attaching the threads (using // > AttachThreadInput API) and using an alternative API: BringWindowToTop. - co_await winrt::resume_foreground(_rootGrid.Dispatcher()); // If the window is minimized, then restore it. We don't want to do this // always though, because SW_RESTORE'ing a maximized window will // restore-down it. if (IsIconic(_window.get())) { - ShowWindow(_window.get(), SW_RESTORE); + LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_RESTORE)); } const DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), LPDWORD(0)); const DWORD currentThreadId = GetCurrentThreadId(); - AttachThreadInput(windowThreadProcessId, currentThreadId, true); - BringWindowToTop(_window.get()); - ShowWindow(_window.get(), SW_SHOW); - AttachThreadInput(windowThreadProcessId, currentThreadId, false); + + LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true)); + LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); + LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW)); + LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false)); } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); From 10779ca310d191f8eccfabb038981b52036ac9b9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 12 Apr 2021 09:49:32 -0500 Subject: [PATCH 57/88] We caan now summon based on window name or just use the MRU one. Note: I haven't tested this at all --- .../Microsoft.Terminal.RemotingLib.vcxproj | 6 ++++ src/cascadia/Remoting/Monarch.cpp | 19 +++++++++-- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Monarch.idl | 12 ++++++- .../Remoting/SummonWindowSelectionArgs.cpp | 5 +++ .../Remoting/SummonWindowSelectionArgs.h | 32 +++++++++++++++++++ src/cascadia/Remoting/WindowManager.cpp | 4 +-- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/Remoting/WindowManager.idl | 2 +- .../UnitTests_Remoting/RemotingTests.cpp | 3 -- src/cascadia/WindowsTerminal/AppHost.cpp | 12 ++++++- 11 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 src/cascadia/Remoting/SummonWindowSelectionArgs.cpp create mode 100644 src/cascadia/Remoting/SummonWindowSelectionArgs.h diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index 519a9c69acc..34f70956ae8 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -25,6 +25,9 @@ Monarch.idl + + Monarch.idl + Peasant.idl @@ -54,6 +57,9 @@ Monarch.idl + + Monarch.idl + Peasant.idl diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 67c45949b40..7fe01e0bba7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -681,14 +681,27 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } - void Monarch::SummonWindow() + void Monarch::SummonWindow(const Remoting::SummonWindowSelectionArgs& args) { try { - auto windowID = _getMostRecentPeasantID(true); - if (auto targetPeasant{ _getPeasant(windowID) }) + args.FoundMatch(false); + const auto searchedForName{ args.WindowName() }; + uint64_t windowId = 0; + // If no name was provided, then just summon the MRU window. + if (searchedForName.empty()) + { + windowId = _getMostRecentPeasantID(true); + } + else + { + // Try to find a peasant that currently has this name + windowId = _lookupPeasantIdForName(searchedForName); + } + if (auto targetPeasant{ _getPeasant(windowId) }) { targetPeasant.Summon(); + args.FoundMatch(true); } } catch (...) diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 4707cd50d98..e7f2da0da62 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); - void SummonWindow(); + void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index e5ff6d7d4ec..f6a8ddb9226 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -18,6 +18,16 @@ namespace Microsoft.Terminal.Remoting Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode } + [default_interface] runtimeclass SummonWindowSelectionArgs { + SummonWindowSelectionArgs(); + String WindowName; + // Other options: + // * CurrentDesktop + // * CurrentMonitor + + Boolean FoundMatch; + } + [default_interface] runtimeclass Monarch { Monarch(); @@ -25,7 +35,7 @@ namespace Microsoft.Terminal.Remoting UInt64 AddPeasant(IPeasant peasant); ProposeCommandlineResult ProposeCommandline(CommandlineArgs args); void HandleActivatePeasant(WindowActivatedArgs args); - void SummonWindow(); + void SummonWindow(SummonWindowSelectionArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; diff --git a/src/cascadia/Remoting/SummonWindowSelectionArgs.cpp b/src/cascadia/Remoting/SummonWindowSelectionArgs.cpp new file mode 100644 index 00000000000..6db4cad3304 --- /dev/null +++ b/src/cascadia/Remoting/SummonWindowSelectionArgs.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "SummonWindowSelectionArgs.h" +#include "SummonWindowSelectionArgs.g.cpp" diff --git a/src/cascadia/Remoting/SummonWindowSelectionArgs.h b/src/cascadia/Remoting/SummonWindowSelectionArgs.h new file mode 100644 index 00000000000..8cc4ca3ec46 --- /dev/null +++ b/src/cascadia/Remoting/SummonWindowSelectionArgs.h @@ -0,0 +1,32 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Class Name: +- SummonWindowSelectionArgs.h + +Abstract: TODO! + +--*/ + +#pragma once + +#include "SummonWindowSelectionArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct SummonWindowSelectionArgs : public SummonWindowSelectionArgsT + { + public: + SummonWindowSelectionArgs() = default; + + WINRT_PROPERTY(winrt::hstring, WindowName); + WINRT_PROPERTY(bool, FoundMatch, true); + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(SummonWindowSelectionArgs); +} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 80bd6193a84..f96262dad0a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -486,12 +486,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _isKing; } - void WindowManager::SummonWindow() + void WindowManager::SummonWindow(const Remoting::SummonWindowSelectionArgs& args) { // We should only ever get called when we are the monarch, because only // the monarch ever registers for the global hotkey. So the monarch is // the only window that will be calling this. - _monarch.SummonWindow(); + _monarch.SummonWindow(args); } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index f1c76e31dc3..85451884b90 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -38,7 +38,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); bool IsMonarch(); - void SummonWindow(); + void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 2c8b8f0c2d3..547b96f9aa5 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -11,7 +11,7 @@ namespace Microsoft.Terminal.Remoting Boolean ShouldCreateWindow { get; }; IPeasant CurrentWindow(); Boolean IsMonarch { get; }; - void SummonWindow(); + void SummonWindow(SummonWindowSelectionArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler BecameMonarch; }; diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index d3f5e42d8d2..b827f014849 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -6,10 +6,7 @@ #include "../Remoting/CommandlineArgs.h" #include "../Remoting/FindTargetWindowArgs.h" #include "../Remoting/ProposeCommandlineResult.h" -<<<<<<< HEAD -======= #include "../inc/WindowingBehavior.h" ->>>>>>> origin/main using namespace Microsoft::Console; using namespace WEX::Logging; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 46d684a3a14..3580f2ff0cf 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -618,7 +618,17 @@ winrt::fire_and_forget AppHost::_BecomeMonarch(const winrt::Windows::Foundation: void AppHost::_GlobalHotkeyPressed() { - _windowManager.SummonWindow(); + Remoting::SummonWindowSelectionArgs args; + args.WindowName(L"_quake"); + _windowManager.SummonWindow(args); + if (args.FoundMatch()) + { + // Excellent, the window was found. + } + else + { + // We should make the window ourselves. + } } void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, From 25b31ff8540e53129ca9a871ca460bb78de2ef16 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 12 Apr 2021 14:15:28 -0500 Subject: [PATCH 58/88] Allow the user to set multiple globalSummon keys, and quakeMode for summoning the _quake window --- .../TerminalApp/AppActionHandlers.cpp | 11 ++++++ src/cascadia/TerminalApp/AppLogic.cpp | 9 ++++- src/cascadia/TerminalApp/AppLogic.h | 3 +- src/cascadia/TerminalApp/AppLogic.idl | 3 +- .../TerminalSettingsModel/ActionAndArgs.cpp | 7 ++++ .../TerminalSettingsModel/ActionArgs.cpp | 6 +++ .../TerminalSettingsModel/ActionArgs.h | 35 +++++++++++++++++ .../TerminalSettingsModel/ActionArgs.idl | 5 +++ .../AllShortcutActions.h | 4 +- .../TerminalSettingsModel/KeyMapping.cpp | 25 ++++++++++++ .../TerminalSettingsModel/KeyMapping.h | 2 + .../TerminalSettingsModel/KeyMapping.idl | 2 + src/cascadia/WindowsTerminal/AppHost.cpp | 39 ++++++++++++++----- src/cascadia/WindowsTerminal/AppHost.h | 5 ++- src/cascadia/WindowsTerminal/IslandWindow.cpp | 12 ++++-- src/cascadia/WindowsTerminal/IslandWindow.h | 5 ++- 16 files changed, 151 insertions(+), 22 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 48f1fc1bf62..960a4fa584d 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -752,4 +752,15 @@ namespace winrt::TerminalApp::implementation args.Handled(true); } + + void TerminalPage::_HandleGlobalSummon(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + args.Handled(false); + } + void TerminalPage::_HandleQuakeMode(const IInspectable& /*sender*/, + const ActionEventArgs& args) + { + args.Handled(false); + } } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index a08bec2fefb..9b9f6cc8273 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1380,9 +1380,14 @@ namespace winrt::TerminalApp::implementation return _root ? _root->AlwaysOnTop() : false; } - KeyChord AppLogic::GlobalHotkey() + // KeyChord AppLogic::GlobalHotkey() + // { + // return _settings.GlobalSettings().GlobalHotkey(); + // } + + Windows::Foundation::Collections::IMap AppLogic::GlobalHotkeys() { - return _settings.GlobalSettings().GlobalHotkey(); + return _settings.GlobalSettings().KeyMap().FetchGlobalHotkeys(); } void AppLogic::IdentifyWindow() diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index dd4d3b04b82..45cf879450b 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -89,7 +89,8 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); - winrt::Microsoft::Terminal::Control::KeyChord GlobalHotkey(); + // winrt::Microsoft::Terminal::Control::KeyChord GlobalHotkey(); + Windows::Foundation::Collections::IMap GlobalHotkeys(); // -------------------------------- WinRT Events --------------------------------- TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 75150189e38..e4236b2a9a0 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -70,7 +70,8 @@ namespace TerminalApp FindTargetWindowResult FindTargetWindow(String[] args); - Microsoft.Terminal.Control.KeyChord GlobalHotkey(); + // Microsoft.Terminal.Control.KeyChord GlobalHotkey(); + Windows.Foundation.Collections.IMap GlobalHotkeys(); // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index 7cdfc96796b..d8eb6b9c375 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -57,6 +57,8 @@ static constexpr std::string_view IdentifyWindowKey{ "identifyWindow" }; static constexpr std::string_view IdentifyWindowsKey{ "identifyWindows" }; static constexpr std::string_view RenameWindowKey{ "renameWindow" }; static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" }; +static constexpr std::string_view GlobalSummonKey{ "globalSummon" }; +static constexpr std::string_view QuakeModeKey{ "quakeMode" }; static constexpr std::string_view ActionKey{ "action" }; @@ -127,6 +129,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { IdentifyWindowsKey, ShortcutAction::IdentifyWindows }, { RenameWindowKey, ShortcutAction::RenameWindow }, { OpenWindowRenamerKey, ShortcutAction::OpenWindowRenamer }, + { GlobalSummonKey, ShortcutAction::GlobalSummon }, + { QuakeModeKey, ShortcutAction::QuakeMode }, }; using ParseResult = std::tuple>; @@ -162,6 +166,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::PrevTab, PrevTabArgs::FromJson }, { ShortcutAction::NextTab, NextTabArgs::FromJson }, { ShortcutAction::RenameWindow, RenameWindowArgs::FromJson }, + { ShortcutAction::GlobalSummon, GlobalSummonArgs::FromJson }, { ShortcutAction::Invalid, nullptr }, }; @@ -337,6 +342,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::IdentifyWindows, RS_(L"IdentifyWindowsCommandKey") }, { ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") }, { ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") }, + { ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName + { ShortcutAction::QuakeMode, L"" }, // Intentionally omitted, must be generated by GenerateName }; }(); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index aebc2f4ecdd..2ffd18001f6 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -29,6 +29,7 @@ #include "PrevTabArgs.g.cpp" #include "NextTabArgs.g.cpp" #include "RenameWindowArgs.g.cpp" +#include "GlobalSummonArgs.g.cpp" #include @@ -582,4 +583,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } return RS_(L"ResetWindowNameCommandKey"); } + + winrt::hstring GlobalSummonArgs::GenerateName() const + { + return L""; + } } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 3ef0c0bc9aa..38171428cc5 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -31,6 +31,7 @@ #include "PrevTabArgs.g.h" #include "NextTabArgs.g.h" #include "RenameWindowArgs.g.h" +#include "GlobalSummonArgs.g.h" #include "../../cascadia/inc/cppwinrt_utils.h" #include "JsonUtils.h" @@ -1035,6 +1036,40 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return *copy; } }; + + struct GlobalSummonArgs : public GlobalSummonArgsT + { + GlobalSummonArgs() = default; + WINRT_PROPERTY(winrt::hstring, Name, L""); + + static constexpr std::string_view NameKey{ "name" }; + + public: + hstring GenerateName() const; + + bool Equals(const IActionArgs& other) + { + if (auto otherAsUs = other.try_as(); otherAsUs) + { + return otherAsUs->_Name == _Name; + } + return false; + }; + static FromJsonResult FromJson(const Json::Value& json) + { + // LOAD BEARING: Not using make_self here _will_ break you in the future! + auto args = winrt::make_self(); + JsonUtils::GetValueForKey(json, NameKey, args->_Name); + return { *args, {} }; + } + IActionArgs Copy() const + { + auto copy{ winrt::make_self() }; + copy->_Name = _Name; + return *copy; + } + }; + } namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index a65c9b46bba..3b14a7a9694 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -251,4 +251,9 @@ namespace Microsoft.Terminal.Settings.Model { String Name { get; }; }; + + [default_interface] runtimeclass GlobalSummonArgs : IActionArgs + { + String Name { get; }; + }; } diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h index cf32e5f36ec..bc710a756b9 100644 --- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h +++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h @@ -72,4 +72,6 @@ ON_ALL_ACTIONS(IdentifyWindow) \ ON_ALL_ACTIONS(IdentifyWindows) \ ON_ALL_ACTIONS(RenameWindow) \ - ON_ALL_ACTIONS(OpenWindowRenamer) + ON_ALL_ACTIONS(OpenWindowRenamer) \ + ON_ALL_ACTIONS(GlobalSummon) \ + ON_ALL_ACTIONS(QuakeMode) diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp index a5dd04891c4..a6827b12edb 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp @@ -137,4 +137,29 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return keyModifiers; } + + Windows::Foundation::Collections::IMap KeyMapping::FetchGlobalHotkeys() + { + std::unordered_map justGlobals; + + for (const auto& [k, v] : _keyShortcuts) + { + if (v.Action() == ShortcutAction::GlobalSummon) + { + justGlobals[k] = v; + } + else if (v.Action() == ShortcutAction::QuakeMode) + { + // Manually replace the QuakeMode action with a globalSummon + // that has the appropriate action args. + auto args = winrt::make_self(); + args->Name(L"_quake"); + Model::ActionAndArgs actionAndArgs{ ShortcutAction::GlobalSummon, *args }; + justGlobals[k] = actionAndArgs; + } + } + + return winrt::single_threaded_map(std::move(justGlobals)); + } + } diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.h b/src/cascadia/TerminalSettingsModel/KeyMapping.h index ce5c811e389..f852328549b 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.h +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.h @@ -69,6 +69,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::vector LayerJson(const Json::Value& json); Json::Value ToJson(); + Windows::Foundation::Collections::IMap FetchGlobalHotkeys(); + private: std::unordered_map _keyShortcuts; std::vector> _keyShortcutsByInsertionOrder; diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.idl b/src/cascadia/TerminalSettingsModel/KeyMapping.idl index 540c74dea50..5234f2c7788 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.idl +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.idl @@ -35,5 +35,7 @@ namespace Microsoft.Terminal.Settings.Model Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action); Microsoft.Terminal.Control.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs); + + Windows.Foundation.Collections.IMap FetchGlobalHotkeys(); } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 3580f2ff0cf..712a91e8126 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -612,22 +612,41 @@ winrt::fire_and_forget AppHost::_BecomeMonarch(const winrt::Windows::Foundation: co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(), winrt::Windows::UI::Core::CoreDispatcherPriority::Normal); // TODO! This needs to be a list - auto hotkey{ _logic.GlobalHotkey() }; - _window->SetGlobalHotkey(hotkey); + // auto hotkey{ _logic.GlobalHotkey() }; + // _window->SetGlobalHotkey(hotkey); + _hotkeyActions = _logic.GlobalHotkeys(); + _hotkeys.clear(); + for (const auto& [k, v] : _hotkeyActions) + { + _hotkeys.push_back(k); + } + _window->SetGlobalHotkeys(_hotkeys); } -void AppHost::_GlobalHotkeyPressed() +void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) { - Remoting::SummonWindowSelectionArgs args; - args.WindowName(L"_quake"); - _windowManager.SummonWindow(args); - if (args.FoundMatch()) + if (hotkeyIndex < 0 || hotkeyIndex > _hotkeys.size()) { - // Excellent, the window was found. + return; } - else + Control::KeyChord kc = _hotkeys.at(hotkeyIndex); + const auto& actionAndArgs = _hotkeyActions.Lookup(kc); + if (actionAndArgs) { - // We should make the window ourselves. + if (const auto& summonArgs{ actionAndArgs.Args().try_as() }) + { + Remoting::SummonWindowSelectionArgs args; + args.WindowName(summonArgs.Name()); + _windowManager.SummonWindow(args); + if (args.FoundMatch()) + { + // Excellent, the window was found. + } + else + { + // We should make the window ourselves. + } + } } } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 471b6f7f12e..185d5f5d917 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -28,6 +28,9 @@ class AppHost bool _shouldCreateWindow{ false }; winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; + std::vector _hotkeys{ nullptr }; + winrt::Windows::Foundation::Collections::IMap _hotkeyActions{ nullptr }; + void _HandleCommandlineArgs(); void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); @@ -54,7 +57,7 @@ class AppHost winrt::fire_and_forget _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); - void _GlobalHotkeyPressed(); + void _GlobalHotkeyPressed(const long hotkeyIndex); void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index f8491a64309..788b81ccc4d 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -354,7 +354,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { case WM_HOTKEY: { - _HotkeyPressedHandlers(); + _HotkeyPressedHandlers(static_cast(wparam)); return 0; } case WM_GETMINMAXINFO: @@ -851,9 +851,11 @@ void IslandWindow::_ApplyWindowSize() SWP_FRAMECHANGED | SWP_NOACTIVATE)); } -void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) +// void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) +void IslandWindow::SetGlobalHotkeys(const std::vector& hotkeyList) { - if (hotkey) + int index = 0; + for (const auto& hotkey : hotkeyList) { const auto modifiers = hotkey.Modifiers(); const auto hotkeyFlags = MOD_NOREPEAT | @@ -868,9 +870,11 @@ void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::Control::Ke // TODO!: (discussion) should we display a warning of some kind if this // fails? This can fail if something else already bound this hotkey. LOG_IF_WIN32_BOOL_FALSE(RegisterHotKey(_window.get(), - 1, + index, hotkeyFlags, hotkey.Vkey())); + + index++; } } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 3e7f8d4ee5a..d222e4a3930 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -38,7 +38,8 @@ class IslandWindow : void FlashTaskbar(); void SetTaskbarProgress(const size_t state, const size_t progress); - void SetGlobalHotkey(const winrt::Microsoft::Terminal::Control::KeyChord& hotkey); + // void SetGlobalHotkey(const winrt::Microsoft::Terminal::Control::KeyChord& hotkey); + void SetGlobalHotkeys(const std::vector& hotkeyList); winrt::fire_and_forget SummonWindow(); @@ -48,7 +49,7 @@ class IslandWindow : DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); WINRT_CALLBACK(MouseScrolled, winrt::delegate); WINRT_CALLBACK(WindowActivated, winrt::delegate); - WINRT_CALLBACK(HotkeyPressed, winrt::delegate); + WINRT_CALLBACK(HotkeyPressed, winrt::delegate); protected: void ForceResize() From 7c2a514e5095f87a9dd882e545f7c1eb2025f97c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 12 Apr 2021 15:27:43 -0500 Subject: [PATCH 59/88] if there is no window with the name, then make a new window with the name --- src/cascadia/WindowsTerminal/AppHost.cpp | 54 +++++++++++++++++-- src/cascadia/WindowsTerminal/AppHost.h | 3 ++ src/cascadia/WindowsTerminal/IslandWindow.cpp | 17 +++--- src/cascadia/WindowsTerminal/IslandWindow.h | 2 +- src/cascadia/WindowsTerminal/pch.h | 2 + 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 712a91e8126..5240a886b2c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -6,6 +6,7 @@ #include "../types/inc/Viewport.hpp" #include "../types/inc/utils.hpp" #include "../types/inc/User32Utils.hpp" +#include "../WinRTUtils/inc/WtExeUtils.h" #include "resource.h" using namespace winrt::Windows::UI; @@ -611,15 +612,24 @@ winrt::fire_and_forget AppHost::_BecomeMonarch(const winrt::Windows::Foundation: // The hotkey MUST be registered on the main thread. It will fail otherwise! co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(), winrt::Windows::UI::Core::CoreDispatcherPriority::Normal); - // TODO! This needs to be a list - // auto hotkey{ _logic.GlobalHotkey() }; - // _window->SetGlobalHotkey(hotkey); + + _setupGlobalHotkeys(); +} + +void AppHost::_setupGlobalHotkeys() +{ + _window->UnsetHotkeys(_hotkeys); + _hotkeyActions = _logic.GlobalHotkeys(); _hotkeys.clear(); for (const auto& [k, v] : _hotkeyActions) { - _hotkeys.push_back(k); + if (k != nullptr) + { + _hotkeys.push_back(k); + } } + _window->SetGlobalHotkeys(_hotkeys); } @@ -645,11 +655,47 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) else { // We should make the window ourselves. + _createNewTerminalWindow(summonArgs); } } } } +winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::GlobalSummonArgs args) +{ + // Hop to the BG thread + co_await winrt::resume_background(); + + // This will get us the correct exe for dev/preview/release. If you + // don't stick this in a local, it'll get mangled by ShellExecute. I + // have no idea why. + const auto exePath{ GetWtExePath() }; + + // If we weren't given a name, then just use new to force the window to be + // unnamed. + winrt::hstring cmdline{ + fmt::format(L"-w {}", + args.Name().empty() ? L"new" : + args.Name()) + }; + // Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we + // can pass the SEE_MASK_NOASYNC flag. That flag allows us to safely + // call this on the background thread, and have ShellExecute _not_ call + // back to us on the main thread. Without this, if you close the + // Terminal quickly after the UAC prompt, the elevated WT will never + // actually spawn. + SHELLEXECUTEINFOW seInfo{ 0 }; + seInfo.cbSize = sizeof(seInfo); + seInfo.fMask = SEE_MASK_NOASYNC; + seInfo.lpVerb = L"open"; + seInfo.lpFile = exePath.c_str(); + seInfo.lpParameters = cmdline.c_str(); + seInfo.nShow = SW_SHOWNORMAL; + LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo)); + + co_return; +} + void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 185d5f5d917..e102d2f5d3b 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -69,4 +69,7 @@ class AppHost const winrt::TerminalApp::RenameWindowRequestedArgs args); GUID _CurrentDesktopGuid(); + + void _setupGlobalHotkeys(); + winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); }; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 788b81ccc4d..501c0c1939d 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -851,7 +851,15 @@ void IslandWindow::_ApplyWindowSize() SWP_FRAMECHANGED | SWP_NOACTIVATE)); } -// void IslandWindow::SetGlobalHotkey(const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) +void IslandWindow::UnsetHotkeys(const std::vector& hotkeyList) +{ + for (int i = 0; i < hotkeyList.size(); i++) + { + LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), + 1)); + } +} + void IslandWindow::SetGlobalHotkeys(const std::vector& hotkeyList) { int index = 0; @@ -864,11 +872,8 @@ void IslandWindow::SetGlobalHotkeys(const std::vector& hotkeyList); void SetGlobalHotkeys(const std::vector& hotkeyList); winrt::fire_and_forget SummonWindow(); diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index dcde843811c..a8befdf6106 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -59,9 +59,11 @@ Module Name: // * Core so we can resume_foreground with CoreDispatcher // * Controls for grid // * Media for ScaleTransform +// * ApplicationModel for finding the path to wt.exe #include #include #include +#include #include #include From ac8fef0e3906055e53710061ff6e9b3563faed43 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 12 Apr 2021 16:50:42 -0500 Subject: [PATCH 60/88] THATS RIGHT, IT HOT-RELOADS --- src/cascadia/TerminalApp/AppLogic.cpp | 4 +++- src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 22 +++++++++++++++------- src/cascadia/WindowsTerminal/AppHost.h | 8 +++++--- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 9b9f6cc8273..ca4f1a58a52 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -968,7 +968,7 @@ namespace winrt::TerminalApp::implementation CATCH_LOG(); // Method Description: - // - Reloads the settings from the profile.json. + // - Reloads the settings from the settings.json file. void AppLogic::_ReloadSettings() { // Attempt to load our settings. @@ -998,6 +998,8 @@ namespace winrt::TerminalApp::implementation _ApplyStartupTaskStateChange(); Jumplist::UpdateJumplist(_settings); + + _SettingsChangedHandlers(*this, nullptr); } // Method Description: diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 45cf879450b..5284ecb46a2 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -94,6 +94,7 @@ namespace winrt::TerminalApp::implementation // -------------------------------- WinRT Events --------------------------------- TYPED_EVENT(RequestedThemeChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::ElementTheme); + TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); private: bool _isUwp{ false }; diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index e4236b2a9a0..5791f444f84 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -88,5 +88,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; event Windows.Foundation.TypedEventHandler RenameWindowRequested; + event Windows.Foundation.TypedEventHandler SettingsChanged; } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 5240a886b2c..e35d148a19d 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -260,6 +260,7 @@ void AppHost::Initialize() _logic.SetTaskbarProgress({ this, &AppHost::SetTaskbarProgress }); _logic.IdentifyWindowsRequested({ this, &AppHost::_IdentifyWindowsRequested }); _logic.RenameWindowRequested({ this, &AppHost::_RenameWindowRequested }); + _logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged }); _window->UpdateTitle(_logic.Title()); @@ -606,18 +607,19 @@ winrt::fire_and_forget AppHost::_WindowActivated() } } -winrt::fire_and_forget AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) +void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) { - // The hotkey MUST be registered on the main thread. It will fail otherwise! - co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(), - winrt::Windows::UI::Core::CoreDispatcherPriority::Normal); - _setupGlobalHotkeys(); } -void AppHost::_setupGlobalHotkeys() +winrt::fire_and_forget AppHost::_setupGlobalHotkeys() { + // The hotkey MUST be registered on the main thread. It will fail otherwise! + co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher(), + winrt::Windows::UI::Core::CoreDispatcherPriority::Normal); + + // Remove all the already registered hotkeys before setting up the new ones. _window->UnsetHotkeys(_hotkeys); _hotkeyActions = _logic.GlobalHotkeys(); @@ -780,3 +782,9 @@ winrt::fire_and_forget AppHost::_RenameWindowRequested(const winrt::Windows::Fou } } } + +void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) +{ + _setupGlobalHotkeys(); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index e102d2f5d3b..5c234ea01c9 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -55,8 +55,8 @@ class AppHost void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); - winrt::fire_and_forget _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + void _BecomeMonarch(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); void _GlobalHotkeyPressed(const long hotkeyIndex); void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); @@ -70,6 +70,8 @@ class AppHost GUID _CurrentDesktopGuid(); - void _setupGlobalHotkeys(); + winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); + void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); }; From 1fdb6b1a3ca4d249be2d1ef5ce7cd8c67ab72809 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 14 Apr 2021 16:02:12 -0500 Subject: [PATCH 61/88] Tests and doc comments --- src/cascadia/Remoting/Monarch.cpp | 21 +- src/cascadia/Remoting/Peasant.cpp | 14 + src/cascadia/Remoting/Peasant.idl | 6 +- .../UnitTests_Remoting/RemotingTests.cpp | 337 ++++++++++++++++++ src/cascadia/WindowsTerminal/AppHost.cpp | 29 +- 5 files changed, 400 insertions(+), 7 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 7fe01e0bba7..c35ed452cc8 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -681,12 +681,26 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } + // Method Description: + // - Attempt to summon a window. `args` contains information about which + // window we should try to summon: + // * if a WindowName is provided, we'll try to find a window with exactly + // that name, and fail if there isn't one. + // - Calls Peasant::Summon on the matching peasant (which might be an RPC call) + // - This should only ever be called by the WindowManager in the monarch + // process itself. The monarch is the one registering for global hotkeys, + // so it's the one calling this method. + // Arguments: + // - args: contains information about the window that should be summoned. + // Return Value: + // - + // - Sets args.FoundMatch when a window matching args is found successfully. void Monarch::SummonWindow(const Remoting::SummonWindowSelectionArgs& args) { + const auto searchedForName{ args.WindowName() }; try { args.FoundMatch(false); - const auto searchedForName{ args.WindowName() }; uint64_t windowId = 0; // If no name was provided, then just summon the MRU window. if (searchedForName.empty()) @@ -706,6 +720,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } catch (...) { + LOG_CAUGHT_EXCEPTION(); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_SummonWindow_Failed", + TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } } } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index e34956c9ed7..990d1d591b0 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -117,9 +117,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _lastActivatedArgs; } + // Method Description: + // - Summon this peasant to become the active window. Currently, it just + // causes the peasant to become the active window wherever the window + // already was. + // - Will raise a SummonRequested event to ask the hosting window to handle for us. + // Arguments: + // - + // Return Value: + // - void Peasant::Summon() { _SummonRequestedHandlers(*this, nullptr); + + TraceLoggingWrite(g_hRemotingProvider, + "Peasant_Summon", + TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } // Method Description: diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 88f391d8e7f..6ff404b73fe 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -41,12 +41,12 @@ namespace Microsoft.Terminal.Remoting void ActivateWindow(WindowActivatedArgs args); WindowActivatedArgs GetLastActivatedArgs(); - void Summon(); - 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) + String WindowName { get; }; + void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested + void Summon(); event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index b827f014849..84944aeb603 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -60,11 +60,13 @@ namespace RemotingUnitTests Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; } void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error{}; } + void Summon() { 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); TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs); + TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); }; class RemotingTests @@ -105,6 +107,11 @@ namespace RemotingUnitTests TEST_METHOD(TestRenameSameNameAsAnother); TEST_METHOD(TestRenameSameNameAsADeadPeasant); + TEST_METHOD(TestSummonMostRecentWindow); + TEST_METHOD(TestSummonNamedWindow); + TEST_METHOD(TestSummonNamedDeadWindow); + TEST_METHOD(TestSummonMostRecentDeadWindow); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -1504,4 +1511,334 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"two")); } + void RemotingTests::TestSummonMostRecentWindow() + { + Log::Comment(L"Attempt to summon the most recent window"); + + 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()); + + bool p1ExpectedToBeSummoned = false; + bool p2ExpectedToBeSummoned = false; + + p1->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p1 summoned"); + VERIFY_IS_TRUE(p1ExpectedToBeSummoned); + }); + p2->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p2 summoned"); + VERIFY_IS_TRUE(p2ExpectedToBeSummoned); + }); + + { + 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); + } + + p2ExpectedToBeSummoned = true; + Remoting::SummonWindowSelectionArgs args; + // Without setting the WindowName, SummonWindowSelectionArgs defaults to + // the MRU window + Log::Comment(L"Summon the MRU window, which is window two"); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + + Log::Comment(L"Now that one is the MRU, summon it"); + p2ExpectedToBeSummoned = false; + p1ExpectedToBeSummoned = true; + args.FoundMatch(false); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + } + + void RemotingTests::TestSummonNamedWindow() + { + Log::Comment(L"Attempt to summon a window by name. When there isn't a " + L"window with that name, set FoundMatch to false, so the " + L"caller can handle that case."); + + 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()); + + bool p1ExpectedToBeSummoned = false; + bool p2ExpectedToBeSummoned = false; + + p1->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p1 summoned"); + VERIFY_IS_TRUE(p1ExpectedToBeSummoned); + }); + p2->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p2 summoned"); + VERIFY_IS_TRUE(p2ExpectedToBeSummoned); + }); + + Remoting::SummonWindowSelectionArgs args; + + Log::Comment(L"Summon window two by name"); + p2ExpectedToBeSummoned = true; + args.WindowName(L"two"); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Summon window one by name"); + p2ExpectedToBeSummoned = false; + p1ExpectedToBeSummoned = true; + args.FoundMatch(false); + args.WindowName(L"one"); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Fail to summon window three by name"); + p1ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.WindowName(L"three"); + m0->SummonWindow(args); + VERIFY_IS_FALSE(args.FoundMatch()); + } + + void RemotingTests::TestSummonNamedDeadWindow() + { + Log::Comment(L"Attempt to summon a dead window by name. This will fail, but not crash."); + + 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()); + + bool p1ExpectedToBeSummoned = false; + bool p2ExpectedToBeSummoned = false; + + p1->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p1 summoned"); + VERIFY_IS_TRUE(p1ExpectedToBeSummoned); + }); + p2->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p2 summoned"); + VERIFY_IS_TRUE(p2ExpectedToBeSummoned); + }); + + Remoting::SummonWindowSelectionArgs args; + + Log::Comment(L"Summon window two by name"); + p2ExpectedToBeSummoned = true; + args.WindowName(L"two"); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Summon window one by name"); + p2ExpectedToBeSummoned = false; + p1ExpectedToBeSummoned = true; + args.FoundMatch(false); + args.WindowName(L"one"); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Kill peasant one."); + RemotingTests::_killPeasant(m0, p1->GetID()); + + Log::Comment(L"Fail to summon window one by name"); + p1ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.WindowName(L"one"); + m0->SummonWindow(args); + VERIFY_IS_FALSE(args.FoundMatch()); + } + + void RemotingTests::TestSummonMostRecentDeadWindow() + { + Log::Comment(L"Attempt to summon the MRU window, when the MRU window " + L"has died. This will fall back to the next MRU window."); + + 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()); + + bool p1ExpectedToBeSummoned = false; + bool p2ExpectedToBeSummoned = false; + + p1->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p1 summoned"); + VERIFY_IS_TRUE(p1ExpectedToBeSummoned); + }); + p2->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p2 summoned"); + VERIFY_IS_TRUE(p2ExpectedToBeSummoned); + }); + + { + 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); + } + + p2ExpectedToBeSummoned = true; + Remoting::SummonWindowSelectionArgs args; + // Without setting the WindowName, SummonWindowSelectionArgs defaults to + // the MRU window + Log::Comment(L"Summon the MRU window, which is window two"); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + + Log::Comment(L"Now that one is the MRU, summon it"); + p2ExpectedToBeSummoned = false; + p1ExpectedToBeSummoned = true; + args.FoundMatch(false); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Kill peasant one."); + RemotingTests::_killPeasant(m0, p1->GetID()); + + Log::Comment(L"We now expect to summon two, since the MRU peasant (one) is actually dead."); + p2ExpectedToBeSummoned = true; + p1ExpectedToBeSummoned = false; + args.FoundMatch(false); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + } + } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index e35d148a19d..ddb94bbb4b6 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -635,15 +635,28 @@ winrt::fire_and_forget AppHost::_setupGlobalHotkeys() _window->SetGlobalHotkeys(_hotkeys); } +// Method Description: +// - Called whenever a registered hotkey is pressed. We'll look up the +// GlobalSummonArgs for the specifed hotkey, then dispatch a call to the +// Monarch with the selection information. +// - If the monarch finds a match for the window name (or no name was provided), +// it'll set FoundMatch=true. +// - If FoundMatch is false, and a name was provided, then we should create a +// new window with the given name. +// Arguments: +// - hotkeyIndex: the index of the entry in _hotkeys that was pressed. +// Return Value: +// - void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) { if (hotkeyIndex < 0 || hotkeyIndex > _hotkeys.size()) { return; } + // Lookup the matching keychord Control::KeyChord kc = _hotkeys.at(hotkeyIndex); - const auto& actionAndArgs = _hotkeyActions.Lookup(kc); - if (actionAndArgs) + // Get the stored ActionAndArgs for that chord + if (const auto& actionAndArgs{ _hotkeyActions.Lookup(kc) }) { if (const auto& summonArgs{ actionAndArgs.Args().try_as() }) { @@ -652,7 +665,7 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) _windowManager.SummonWindow(args); if (args.FoundMatch()) { - // Excellent, the window was found. + // Excellent, the window was found. We have nothing else to do here. } else { @@ -663,6 +676,16 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) } } +// Method Description: +// - Called when the monarch failed to summon a window for a given set of +// SummonWindowSelectionArgs. In this case, we should create the specified +// window ourselves. +// - This is to support the scenario like `globalSummon(Name="_quake")` being +// used to summon the window if it already exists, or create it if it doesn't. +// Arguments: +// - args: Contains information on how we should name the window +// Return Value: +// - winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::GlobalSummonArgs args) { // Hop to the BG thread From b20222f2c0d2782286d1ee614fac0626ccf9b0a9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 14 Apr 2021 16:16:12 -0500 Subject: [PATCH 62/88] More comments --- src/cascadia/TerminalSettingsModel/KeyMapping.cpp | 11 +++++++++++ src/cascadia/WindowsTerminal/IslandWindow.cpp | 15 +++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp index a6827b12edb..52cfe3e29cf 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp @@ -138,6 +138,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return keyModifiers; } + // Method Description: + // - Build a map of all the globalSummon actions. + // - quakeMode actions are included in this, but expanded to the equivalent + // set of GlobalSummonArgs + // Arguments: + // - + // Return Value: + // - a map of KeyChord -> ActionAndArgs containing all globally bindable actions. Windows::Foundation::Collections::IMap KeyMapping::FetchGlobalHotkeys() { std::unordered_map justGlobals; @@ -153,7 +161,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // Manually replace the QuakeMode action with a globalSummon // that has the appropriate action args. auto args = winrt::make_self(); + + // We want to summon the window with the name "_quake" specifically. args->Name(L"_quake"); + Model::ActionAndArgs actionAndArgs{ ShortcutAction::GlobalSummon, *args }; justGlobals[k] = actionAndArgs; } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 79b05c9bbd8..c4a2d20ba5c 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -902,6 +902,12 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) } } +// Method Description: +// - Call UnregisterHotKey once for each entry in hotkeyList, to unset all the bound global hotkeys. +// Arguments: +// - hotkeyList: a list of hotkeys to unbind +// Return Value: +// - void IslandWindow::UnsetHotkeys(const std::vector& hotkeyList) { for (int i = 0; i < hotkeyList.size(); i++) @@ -911,6 +917,15 @@ void IslandWindow::UnsetHotkeys(const std::vector void IslandWindow::SetGlobalHotkeys(const std::vector& hotkeyList) { int index = 0; From 9d76c62dae88f031a5182767be1d9ad52b56396a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 14 Apr 2021 16:21:17 -0500 Subject: [PATCH 63/88] Some dead code cleanup --- src/cascadia/TerminalApp/AppLogic.cpp | 5 ---- src/cascadia/TerminalApp/AppLogic.h | 1 - .../GlobalAppSettings.cpp | 29 ------------------- .../TerminalSettingsModel/GlobalAppSettings.h | 1 - .../GlobalAppSettings.idl | 1 - 5 files changed, 37 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index ca4f1a58a52..cb84c3da17d 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1382,11 +1382,6 @@ namespace winrt::TerminalApp::implementation return _root ? _root->AlwaysOnTop() : false; } - // KeyChord AppLogic::GlobalHotkey() - // { - // return _settings.GlobalSettings().GlobalHotkey(); - // } - Windows::Foundation::Collections::IMap AppLogic::GlobalHotkeys() { return _settings.GlobalSettings().KeyMap().FetchGlobalHotkeys(); diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 5284ecb46a2..82e6df1742d 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -89,7 +89,6 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); - // winrt::Microsoft::Terminal::Control::KeyChord GlobalHotkey(); Windows::Foundation::Collections::IMap GlobalHotkeys(); // -------------------------------- WinRT Events --------------------------------- diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 650b0bfdfff..aa35a6b322d 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -47,7 +47,6 @@ static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" }; static constexpr std::string_view StartupActionsKey{ "startupActions" }; static constexpr std::string_view FocusFollowMouseKey{ "focusFollowMouse" }; static constexpr std::string_view WindowingBehaviorKey{ "windowingBehavior" }; -static constexpr std::string_view GlobalHotkeyKey{ "globalHotkey" }; static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" }; @@ -319,34 +318,6 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, StartupActionsKey, _StartupActions); - try - { - const auto keys = json[JsonKey(GlobalHotkeyKey)]; - const auto validString = keys.isString(); - const auto validArray = keys.isArray() && keys.size() == 1; - - // GH#4239 - If the user provided more than one key - // chord to a "keys" array, warn the user here. - // TODO: GH#1334 - remove this check. - if (keys.isArray() && keys.size() > 1) - { - // TODO: add a warning - // warnings.push_back(SettingsLoadWarnings::TooManyKeysForChord); - } - - if (validString || validArray) - { - const auto keyChordString = keys.isString() ? winrt::to_hstring(keys.asString()) : winrt::to_hstring(keys[0].asString()); - - const auto chord = KeyChordSerialization::FromString(keyChordString); - _GlobalHotkey = chord; - } - } - catch (...) - { - // TODO: add a settings warning - } - JsonUtils::GetValueForKey(json, FocusFollowMouseKey, _FocusFollowMouse); JsonUtils::GetValueForKey(json, WindowingBehaviorKey, _WindowingBehavior); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index fd1e050048a..5f5f2456c9b 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -91,7 +91,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, StartupActions, L""); INHERITABLE_SETTING(Model::GlobalAppSettings, bool, FocusFollowMouse, false); INHERITABLE_SETTING(Model::GlobalAppSettings, Model::WindowingMode, WindowingBehavior, Model::WindowingMode::UseNew); - INHERITABLE_SETTING(Model::GlobalAppSettings, Control::KeyChord, GlobalHotkey, nullptr); private: guid _defaultProfile; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index ff56484ee61..c44f4b43fce 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -68,7 +68,6 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(String, StartupActions); INHERITABLE_SETTING(Boolean, FocusFollowMouse); INHERITABLE_SETTING(WindowingMode, WindowingBehavior); - INHERITABLE_SETTING(Microsoft.Terminal.Control.KeyChord, GlobalHotkey); Windows.Foundation.Collections.IMapView ColorSchemes(); void AddColorScheme(ColorScheme scheme); From 2a2f5cb9a5fea16d039d7acfd62167ec5d401910 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 14 Apr 2021 16:37:24 -0500 Subject: [PATCH 64/88] pre emptive spellbot --- .github/actions/spelling/dictionary/apis.txt | 1 + .github/actions/spelling/expect/expect.txt | 1 + .github/actions/spelling/expect/web.txt | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/actions/spelling/dictionary/apis.txt b/.github/actions/spelling/dictionary/apis.txt index 2abb30978f3..5f3bdfb6bd8 100644 --- a/.github/actions/spelling/dictionary/apis.txt +++ b/.github/actions/spelling/dictionary/apis.txt @@ -72,6 +72,7 @@ NOASYNC NOCHANGEDIR NOPROGRESS NOREDIRECTIONBITMAP +NOREPEAT ntprivapi oaidl ocidl diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 8a063cdf85c..c2c5c18d256 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -1009,6 +1009,7 @@ horiz HORZ hostable hostlib +hotkeys HPA HPAINTBUFFER HPCON diff --git a/.github/actions/spelling/expect/web.txt b/.github/actions/spelling/expect/web.txt index 919e5e9267a..70d924c4476 100644 --- a/.github/actions/spelling/expect/web.txt +++ b/.github/actions/spelling/expect/web.txt @@ -14,3 +14,4 @@ uk winui appshellintegration cppreference +gyfcat diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index ddb94bbb4b6..db0b0803baf 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -637,7 +637,7 @@ winrt::fire_and_forget AppHost::_setupGlobalHotkeys() // Method Description: // - Called whenever a registered hotkey is pressed. We'll look up the -// GlobalSummonArgs for the specifed hotkey, then dispatch a call to the +// GlobalSummonArgs for the specified hotkey, then dispatch a call to the // Monarch with the selection information. // - If the monarch finds a match for the window name (or no name was provided), // it'll set FoundMatch=true. From f892752da79d6ed86401ec7db70b68e27c00add2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 16 Apr 2021 09:02:46 -0500 Subject: [PATCH 65/88] Add some tracelogging to find a heisenbug that might not have ever been there --- src/cascadia/WindowsTerminal/AppHost.h | 2 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 5c234ea01c9..e492b8fa0e7 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -28,7 +28,7 @@ class AppHost bool _shouldCreateWindow{ false }; winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; - std::vector _hotkeys{ nullptr }; + std::vector _hotkeys{}; winrt::Windows::Foundation::Collections::IMap _hotkeyActions{ nullptr }; void _HandleCommandlineArgs(); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index c4a2d20ba5c..1381b916789 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -910,10 +910,15 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) // - void IslandWindow::UnsetHotkeys(const std::vector& hotkeyList) { + TraceLoggingWrite(g_hWindowsTerminalProvider, + "UnsetHotkeys", + TraceLoggingDescription("Emitted when clearing previously set hotkeys"), + TraceLoggingInt64(hotkeyList.size(), "numHotkeys", "The number of hotkeys to unset"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + for (int i = 0; i < hotkeyList.size(); i++) { - LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), - 1)); + LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), i)); } } @@ -928,6 +933,11 @@ void IslandWindow::UnsetHotkeys(const std::vector void IslandWindow::SetGlobalHotkeys(const std::vector& hotkeyList) { + TraceLoggingWrite(g_hWindowsTerminalProvider, + "SetGlobalHotkeys", + TraceLoggingDescription("Emitted when setting hotkeys"), + TraceLoggingInt64(hotkeyList.size(), "numHotkeys", "The number of hotkeys to set"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); int index = 0; for (const auto& hotkey : hotkeyList) { From 4166afaa9805a0faf7eb9cba0547259a2784fbc5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 16 Apr 2021 09:22:40 -0500 Subject: [PATCH 66/88] Okay that was bad pre-emptive spelling --- .github/actions/spelling/expect/web.txt | 2 +- src/cascadia/Remoting/SummonWindowSelectionArgs.h | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/actions/spelling/expect/web.txt b/.github/actions/spelling/expect/web.txt index 70d924c4476..af9a237d786 100644 --- a/.github/actions/spelling/expect/web.txt +++ b/.github/actions/spelling/expect/web.txt @@ -14,4 +14,4 @@ uk winui appshellintegration cppreference -gyfcat +gfycat diff --git a/src/cascadia/Remoting/SummonWindowSelectionArgs.h b/src/cascadia/Remoting/SummonWindowSelectionArgs.h index 8cc4ca3ec46..dda213d60b5 100644 --- a/src/cascadia/Remoting/SummonWindowSelectionArgs.h +++ b/src/cascadia/Remoting/SummonWindowSelectionArgs.h @@ -5,8 +5,14 @@ Licensed under the MIT license. Class Name: - SummonWindowSelectionArgs.h -Abstract: TODO! - +Abstract: +- This is a helper class for determining which window a should be summoned when + a global hotkey is pressed. Parameters from a GlobalSummon action will be + filled in here. The Monarch will use these to find the window that matches + these args, and Summon() that Peasant. +- When the monarch finds a match, it will set FoundMatch to true. If it doesn't, + then the Monarch window might need to create a new window matching these args + instead. --*/ #pragma once From 717db8130bbd68e85682de9a659774f1265e4e98 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 19 Apr 2021 10:10:54 -0500 Subject: [PATCH 67/88] Again, nobody's got disk space to build x86. This is a guess --- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index db0b0803baf..19acc879ae6 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -649,7 +649,7 @@ winrt::fire_and_forget AppHost::_setupGlobalHotkeys() // - void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) { - if (hotkeyIndex < 0 || hotkeyIndex > _hotkeys.size()) + if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) { return; } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 1381b916789..2c5612b02ad 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -916,7 +916,7 @@ void IslandWindow::UnsetHotkeys(const std::vector Date: Mon, 19 Apr 2021 12:05:43 -0500 Subject: [PATCH 68/88] oh man, this will magic summon the window to the current desktop. I'm shocked that it works, thanks powertoys --- src/cascadia/Remoting/Monarch.cpp | 3 +- src/cascadia/WindowsTerminal/AppHost.cpp | 43 ++++++ src/cascadia/WindowsTerminal/IslandWindow.cpp | 2 +- .../WindowsTerminal/VirtualDesktopUtils.cpp | 141 ++++++++++++++++++ .../WindowsTerminal/VirtualDesktopUtils.h | 9 ++ .../WindowsTerminal/WindowsTerminal.vcxproj | 2 + 6 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp create mode 100644 src/cascadia/WindowsTerminal/VirtualDesktopUtils.h diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index c35ed452cc8..5bb9693361e 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -705,7 +705,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // If no name was provided, then just summon the MRU window. if (searchedForName.empty()) { - windowId = _getMostRecentPeasantID(true); + // TODO!: let this optionally be true, in the SummonWindowSelectionArgs + windowId = _getMostRecentPeasantID(false); } else { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 19acc879ae6..931e911271c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -8,6 +8,7 @@ #include "../types/inc/User32Utils.hpp" #include "../WinRTUtils/inc/WtExeUtils.h" #include "resource.h" +#include "VirtualDesktopUtils.h" using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Composition; @@ -721,10 +722,52 @@ winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::Global co_return; } +GUID _currentlyActiveDesktop() +{ + // // HWND desktop = GetDesktopWindow(); + + // static const wchar_t* const PSEUDO_WINDOW_CLASS = L"Pseudo1ConsoleWindow"; + // WNDCLASS pseudoClass{ 0 }; + // pseudoClass.lpszClassName = PSEUDO_WINDOW_CLASS; + // pseudoClass.lpfnWndProc = DefWindowProc; + // RegisterClass(&pseudoClass); + // // Attempt to create window + // wil::unique_hwnd hwnd{ CreateWindowExW( + // 0, PSEUDO_WINDOW_CLASS, nullptr, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, HWND_DESKTOP, nullptr, nullptr, nullptr) }; + // HWND h = hwnd.get(); + // GUID currentDesktopGuid{ 0 }; + // try + // { + // const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + // if (manager) + // { + // DebugBreak(); + // LOG_IF_FAILED(manager->GetWindowDesktopId(h, ¤tDesktopGuid)); + // } + // } + // CATCH_LOG(); + GUID currentDesktopGuid; + VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tDesktopGuid); + return currentDesktopGuid; +} + void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { _window->SummonWindow(); + + const auto currentWindowDesktop{ _CurrentDesktopGuid() }; + const auto currentlyActiveDesktop{ _currentlyActiveDesktop() }; + // DebugBreak(); + try + { + const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + if (manager) + { + LOG_IF_FAILED(manager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); + } + } + CATCH_LOG(); } GUID AppHost::_CurrentDesktopGuid() diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 2c5612b02ad..aef5192ac73 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -916,7 +916,7 @@ void IslandWindow::UnsetHotkeys(const std::vector(hotkeyList.size()); i++) { LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), i)); } diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp new file mode 100644 index 00000000000..29ff96da18e --- /dev/null +++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// Shamelessly copied from PowerToys + +#include "pch.h" + +#include "VirtualDesktopUtils.h" + +// Non-Localizable strings +namespace NonLocalizable +{ + const wchar_t RegCurrentVirtualDesktop[] = L"CurrentVirtualDesktop"; + const wchar_t RegVirtualDesktopIds[] = L"VirtualDesktopIDs"; + const wchar_t RegKeyVirtualDesktops[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VirtualDesktops"; + const wchar_t RegKeyVirtualDesktopsFromSession[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d\\VirtualDesktops"; +} + +namespace VirtualDesktopUtils +{ + bool GetDesktopIdFromCurrentSession(GUID* desktopId) + { + DWORD sessionId; + if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) + { + return false; + } + + wchar_t sessionKeyPath[256]{}; + if (FAILED(StringCchPrintfW(sessionKeyPath, ARRAYSIZE(sessionKeyPath), NonLocalizable::RegKeyVirtualDesktopsFromSession, sessionId))) + { + return false; + } + + wil::unique_hkey key{}; + if (RegOpenKeyExW(HKEY_CURRENT_USER, sessionKeyPath, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS) + { + GUID value{}; + DWORD size = sizeof(GUID); + if (RegQueryValueExW(key.get(), NonLocalizable::RegCurrentVirtualDesktop, 0, nullptr, reinterpret_cast(&value), &size) == ERROR_SUCCESS) + { + *desktopId = value; + return true; + } + } + return false; + } + + bool GetVirtualDesktopIds(HKEY hKey, std::vector& ids) + { + if (!hKey) + { + return false; + } + DWORD bufferCapacity; + // request regkey binary buffer capacity only + if (RegQueryValueExW(hKey, NonLocalizable::RegVirtualDesktopIds, 0, nullptr, nullptr, &bufferCapacity) != ERROR_SUCCESS) + { + return false; + } + std::unique_ptr buffer = std::make_unique(bufferCapacity); + // request regkey binary content + if (RegQueryValueExW(hKey, NonLocalizable::RegVirtualDesktopIds, 0, nullptr, buffer.get(), &bufferCapacity) != ERROR_SUCCESS) + { + return false; + } + const size_t guidSize = sizeof(GUID); + std::vector temp; + temp.reserve(bufferCapacity / guidSize); + for (size_t i = 0; i < bufferCapacity; i += guidSize) + { + GUID* guid = reinterpret_cast(buffer.get() + i); + temp.push_back(*guid); + } + ids = std::move(temp); + return true; + } + + HKEY OpenVirtualDesktopsRegKey() + { + HKEY hKey{ nullptr }; + if (RegOpenKeyEx(HKEY_CURRENT_USER, NonLocalizable::RegKeyVirtualDesktops, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) + { + return hKey; + } + return nullptr; + } + + HKEY GetVirtualDesktopsRegKey() + { + static wil::unique_hkey virtualDesktopsKey{ OpenVirtualDesktopsRegKey() }; + return virtualDesktopsKey.get(); + } + bool GetVirtualDesktopIds(std::vector& ids) + { + return GetVirtualDesktopIds(GetVirtualDesktopsRegKey(), ids); + } + + bool GetVirtualDesktopIds(std::vector& ids) + { + std::vector guids{}; + if (GetVirtualDesktopIds(guids)) + { + for (auto& guid : guids) + { + wil::unique_cotaskmem_string guidString; + if (SUCCEEDED(StringFromCLSID(guid, &guidString))) + { + ids.push_back(guidString.get()); + } + } + return true; + } + return false; + } + + bool GetCurrentVirtualDesktopId(GUID* desktopId) + { + // Explorer persists current virtual desktop identifier to registry on a per session basis, but only + // after first virtual desktop switch happens. If the user hasn't switched virtual desktops in this + // session, value in registry will be empty. + if (GetDesktopIdFromCurrentSession(desktopId)) + { + return true; + } + // Fallback scenario is to get array of virtual desktops stored in registry, but not kept per session. + // Note that we are taking first element from virtual desktop array, which is primary desktop. + // If user has more than one virtual desktop, previous function should return correct value, as desktop + // switch occurred in current session. + else + { + std::vector ids{}; + if (GetVirtualDesktopIds(ids) && ids.size() > 0) + { + *desktopId = ids[0]; + return true; + } + } + return false; + } +} diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h new file mode 100644 index 00000000000..41ce71d068e --- /dev/null +++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace VirtualDesktopUtils +{ + bool GetCurrentVirtualDesktopId(GUID* desktopId); +} diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index fba24855f8e..806c83c0bf3 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -48,6 +48,7 @@ + @@ -57,6 +58,7 @@ + From 9f9eacb5d98c77214a819d80a7fe1cbe6c96eb2c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 19 Apr 2021 14:41:48 -0500 Subject: [PATCH 69/88] !! THIS NEEDS TO GO TO THE PARENT BRANCH !! --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index aef5192ac73..df8f0b0bdda 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -993,6 +993,10 @@ winrt::fire_and_forget IslandWindow::SummonWindow() }); LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW)); + + // Activate the window too. This will force us to the virtual desktop this + // window is on, if it's on another virtual desktop. + LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); From f71c948554beb4c1b27920b15b703b7e4ad51d95 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 19 Apr 2021 14:44:26 -0500 Subject: [PATCH 70/88] Add support for summoning to the current virtual desktop Tested with ```json { "keys": "ctrl+`", "command": { "action": "quakeMode" } }, { "keys": "ctrl+1", "command": { "action": "globalSummon" } }, { "keys": "ctrl+2", "command": { "action": "globalSummon", "desktop": "toCurrent" } }, { "keys": "ctrl+3", "command": { "action": "globalSummon", "desktop": "onCurrent" } }, { "keys": "ctrl+4", "command": { "action": "globalSummon", "desktop": "any" } }, ``` And holy god it works so well --- .../Microsoft.Terminal.RemotingLib.vcxproj | 6 ++ src/cascadia/Remoting/Monarch.cpp | 8 ++- src/cascadia/Remoting/Monarch.idl | 4 +- src/cascadia/Remoting/Peasant.cpp | 8 ++- src/cascadia/Remoting/Peasant.h | 4 +- src/cascadia/Remoting/Peasant.idl | 11 +++- .../Remoting/SummonWindowBehavior.cpp | 5 ++ src/cascadia/Remoting/SummonWindowBehavior.h | 35 ++++++++++++ .../Remoting/SummonWindowSelectionArgs.h | 2 + .../TerminalSettingsModel/ActionArgs.h | 9 ++- .../TerminalSettingsModel/ActionArgs.idl | 8 +++ .../TerminalSettingsSerializationHelpers.h | 9 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 56 ++++++------------- src/cascadia/WindowsTerminal/AppHost.h | 2 +- .../WindowsTerminal/VirtualDesktopUtils.cpp | 3 +- .../WindowsTerminal/VirtualDesktopUtils.h | 2 + 16 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 src/cascadia/Remoting/SummonWindowBehavior.cpp create mode 100644 src/cascadia/Remoting/SummonWindowBehavior.h diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index 34f70956ae8..2241d3c0e54 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -28,6 +28,9 @@ Monarch.idl + + Peasant.idl + Peasant.idl @@ -60,6 +63,9 @@ Monarch.idl + + Peasant.idl + Peasant.idl diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 5bb9693361e..3c24d07dfdd 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -705,8 +705,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // If no name was provided, then just summon the MRU window. if (searchedForName.empty()) { - // TODO!: let this optionally be true, in the SummonWindowSelectionArgs - windowId = _getMostRecentPeasantID(false); + // Use the value of the `desktop` arg to determine if we should + // limit to the current desktop (desktop:onCurrent) or not + // (desktop:any or desktop:toCurrent) + windowId = _getMostRecentPeasantID(args.OnCurrentDesktop()); } else { @@ -715,7 +717,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } if (auto targetPeasant{ _getPeasant(windowId) }) { - targetPeasant.Summon(); + targetPeasant.Summon(args.SummonBehavior()); args.FoundMatch(true); } } diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index f6a8ddb9226..f2078cf5085 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -21,13 +21,15 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass SummonWindowSelectionArgs { SummonWindowSelectionArgs(); String WindowName; + Boolean OnCurrentDesktop; // Other options: - // * CurrentDesktop // * CurrentMonitor Boolean FoundMatch; + SummonWindowBehavior SummonBehavior; } + [default_interface] runtimeclass Monarch { Monarch(); diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 990d1d591b0..a12110d8662 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "Peasant.h" #include "CommandlineArgs.h" +#include "SummonWindowBehavior.h" #include "Peasant.g.cpp" #include "../../types/inc/utils.hpp" @@ -126,14 +127,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - // Return Value: // - - void Peasant::Summon() + void Peasant::Summon(const Remoting::SummonWindowBehavior& summonBehavior) { - _SummonRequestedHandlers(*this, nullptr); + auto localCopy = winrt::make_self(summonBehavior); TraceLoggingWrite(g_hRemotingProvider, "Peasant_Summon", TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), + TraceLoggingUInt64(localCopy->MoveToCurrentDesktop(), "MoveToCurrentDesktop", "true if we should move to the current desktop"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + _SummonRequestedHandlers(*this, *localCopy); } // Method Description: diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 72c672c2e44..7b5b44e8138 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -24,7 +24,7 @@ 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 Summon(); + void Summon(const Remoting::SummonWindowBehavior& summonBehavior); void RequestIdentifyWindows(); void DisplayWindowId(); void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); @@ -39,7 +39,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(DisplayWindowIdRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RenameRequestArgs); - TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior); private: Peasant(const uint64_t testPID); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 6ff404b73fe..af74cf3d065 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -30,6 +30,13 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.DateTime ActivatedTime { get; }; }; + [default_interface] runtimeclass SummonWindowBehavior { + SummonWindowBehavior(); + Boolean MoveToCurrentDesktop; + // Other options: + // * CurrentMonitor + } + interface IPeasant { CommandlineArgs InitialArgs { get; }; @@ -46,14 +53,14 @@ namespace Microsoft.Terminal.Remoting String WindowName { get; }; void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested - void Summon(); + void Summon(SummonWindowBehavior behavior); event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; event Windows.Foundation.TypedEventHandler DisplayWindowIdRequested; event Windows.Foundation.TypedEventHandler RenameRequested; - event Windows.Foundation.TypedEventHandler SummonRequested; + event Windows.Foundation.TypedEventHandler SummonRequested; }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/Remoting/SummonWindowBehavior.cpp b/src/cascadia/Remoting/SummonWindowBehavior.cpp new file mode 100644 index 00000000000..516acff06d0 --- /dev/null +++ b/src/cascadia/Remoting/SummonWindowBehavior.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "SummonWindowBehavior.h" +#include "SummonWindowBehavior.g.cpp" diff --git a/src/cascadia/Remoting/SummonWindowBehavior.h b/src/cascadia/Remoting/SummonWindowBehavior.h new file mode 100644 index 00000000000..c460ef98843 --- /dev/null +++ b/src/cascadia/Remoting/SummonWindowBehavior.h @@ -0,0 +1,35 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Class Name: +- SummonWindowBehavior.h + +Abstract: +- TODO! + +--*/ + +#pragma once + +#include "SummonWindowBehavior.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct SummonWindowBehavior : public SummonWindowBehaviorT + { + public: + SummonWindowBehavior() = default; + WINRT_PROPERTY(bool, MoveToCurrentDesktop, true); + + public: + SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) : + _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() } {}; + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(SummonWindowBehavior); +} diff --git a/src/cascadia/Remoting/SummonWindowSelectionArgs.h b/src/cascadia/Remoting/SummonWindowSelectionArgs.h index dda213d60b5..676aee11d50 100644 --- a/src/cascadia/Remoting/SummonWindowSelectionArgs.h +++ b/src/cascadia/Remoting/SummonWindowSelectionArgs.h @@ -29,6 +29,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WINRT_PROPERTY(winrt::hstring, WindowName); WINRT_PROPERTY(bool, FoundMatch, true); + WINRT_PROPERTY(bool, OnCurrentDesktop, false); + WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior); }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 38171428cc5..25698cb0a58 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -1041,17 +1041,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { GlobalSummonArgs() = default; WINRT_PROPERTY(winrt::hstring, Name, L""); + WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent); static constexpr std::string_view NameKey{ "name" }; + static constexpr std::string_view DesktopKey{ "desktop" }; public: hstring GenerateName() const; bool Equals(const IActionArgs& other) { - if (auto otherAsUs = other.try_as(); otherAsUs) + if (auto otherAsUs = other.try_as()) { - return otherAsUs->_Name == _Name; + return otherAsUs->_Name == _Name && + otherAsUs->_Desktop == _Desktop; } return false; }; @@ -1060,12 +1063,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // LOAD BEARING: Not using make_self here _will_ break you in the future! auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, NameKey, args->_Name); + JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop); return { *args, {} }; } IActionArgs Copy() const { auto copy{ winrt::make_self() }; copy->_Name = _Name; + copy->_Desktop = _Desktop; return *copy; } }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 3b14a7a9694..881cfbf3a0b 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -84,6 +84,13 @@ namespace Microsoft.Terminal.Settings.Model Disabled, }; + enum DesktopBehavior + { + Any, + ToCurrent, + OnCurrent, + }; + [default_interface] runtimeclass NewTerminalArgs { NewTerminalArgs(); NewTerminalArgs(Int32 profileIndex); @@ -255,5 +262,6 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass GlobalSummonArgs : IActionArgs { String Name { get; }; + DesktopBehavior Desktop { get; }; }; } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index bdda3eef5a2..57607607ead 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -448,3 +448,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode) pair_type{ "useExisting", ValueType::UseExisting }, }; }; + +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::DesktopBehavior) +{ + JSON_MAPPINGS(3) = { + pair_type{ "any", ValueType::Any }, + pair_type{ "toCurrent", ValueType::ToCurrent }, + pair_type{ "onCurrent", ValueType::OnCurrent }, + }; +}; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 931e911271c..241341c1aa6 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -663,6 +663,13 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) { Remoting::SummonWindowSelectionArgs args; args.WindowName(summonArgs.Name()); + + // desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false + // desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false + // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true + args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); + args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); + _windowManager.SummonWindow(args); if (args.FoundMatch()) { @@ -722,52 +729,25 @@ winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::Global co_return; } -GUID _currentlyActiveDesktop() -{ - // // HWND desktop = GetDesktopWindow(); - - // static const wchar_t* const PSEUDO_WINDOW_CLASS = L"Pseudo1ConsoleWindow"; - // WNDCLASS pseudoClass{ 0 }; - // pseudoClass.lpszClassName = PSEUDO_WINDOW_CLASS; - // pseudoClass.lpfnWndProc = DefWindowProc; - // RegisterClass(&pseudoClass); - // // Attempt to create window - // wil::unique_hwnd hwnd{ CreateWindowExW( - // 0, PSEUDO_WINDOW_CLASS, nullptr, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, HWND_DESKTOP, nullptr, nullptr, nullptr) }; - // HWND h = hwnd.get(); - // GUID currentDesktopGuid{ 0 }; - // try - // { - // const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); - // if (manager) - // { - // DebugBreak(); - // LOG_IF_FAILED(manager->GetWindowDesktopId(h, ¤tDesktopGuid)); - // } - // } - // CATCH_LOG(); - GUID currentDesktopGuid; - VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tDesktopGuid); - return currentDesktopGuid; -} - void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) + const Remoting::SummonWindowBehavior& args) { _window->SummonWindow(); - const auto currentWindowDesktop{ _CurrentDesktopGuid() }; - const auto currentlyActiveDesktop{ _currentlyActiveDesktop() }; - // DebugBreak(); - try + if (args != nullptr && args.MoveToCurrentDesktop()) { - const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); - if (manager) + try { - LOG_IF_FAILED(manager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); + const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + if (manager) + { + GUID currentlyActiveDesktop; + VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop); + LOG_IF_FAILED(manager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); + } } + CATCH_LOG(); } - CATCH_LOG(); } GUID AppHost::_CurrentDesktopGuid() diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index e492b8fa0e7..8eeac8bcb45 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -59,7 +59,7 @@ class AppHost const winrt::Windows::Foundation::IInspectable& args); void _GlobalHotkeyPressed(const long hotkeyIndex); void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args); winrt::fire_and_forget _IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable sender, const winrt::Windows::Foundation::IInspectable args); diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp index 29ff96da18e..4b8b9202c3d 100644 --- a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp +++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. // -// Shamelessly copied from PowerToys +// Shamelessly copied from microsoft/PowerToys, at +// https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/lib/VirtualDesktopUtils.cpp #include "pch.h" diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h index 41ce71d068e..cd0dd56faea 100644 --- a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h +++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.h @@ -1,5 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +// +// A helper function for determining the GUID of the current Virtual Desktop. #pragma once From 51617cf6db3a105cad3dd4791a5673f900dd9843 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 20 Apr 2021 15:10:04 -0500 Subject: [PATCH 71/88] cache these results --- src/cascadia/WindowsTerminal/AppHost.cpp | 47 ++++++++++++++++-------- src/cascadia/WindowsTerminal/AppHost.h | 3 ++ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 241341c1aa6..2ce1869c72f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -729,6 +729,30 @@ winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::Global co_return; } +// Method Description: +// - Helper to initialize our instance of IVirtualDesktopManager. If we already +// got one, then this will just return true. Otherwise, we'll try and init a +// new instance of one, and store that. +// - This will return false if we weren't able to initialize one, which I'm not +// sure is actually possible. +// Arguments: +// - +// Return Value: +// - true iff _desktopManager points to a non-null instance of IVirtualDesktopManager +bool AppHost::_LazyLoadDesktopManager() +{ + if (_desktopManager == nullptr) + { + try + { + _desktopManager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + } + CATCH_LOG(); + } + + return _desktopManager != nullptr; +} + void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::SummonWindowBehavior& args) { @@ -736,32 +760,23 @@ void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*se if (args != nullptr && args.MoveToCurrentDesktop()) { - try + if (_LazyLoadDesktopManager()) { - const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); - if (manager) - { - GUID currentlyActiveDesktop; - VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop); - LOG_IF_FAILED(manager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); - } + GUID currentlyActiveDesktop; + VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop); + LOG_IF_FAILED(_desktopManager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); } - CATCH_LOG(); } } GUID AppHost::_CurrentDesktopGuid() { GUID currentDesktopGuid{ 0 }; - try + const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + if (_LazyLoadDesktopManager()) { - const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); - if (manager) - { - LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid)); - } + LOG_IF_FAILED(_desktopManager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid)); } - CATCH_LOG(); return currentDesktopGuid; } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 8eeac8bcb45..6c9564702b6 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -31,6 +31,8 @@ class AppHost std::vector _hotkeys{}; winrt::Windows::Foundation::Collections::IMap _hotkeyActions{ nullptr }; + winrt::com_ptr _desktopManager{ nullptr }; + void _HandleCommandlineArgs(); void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); @@ -69,6 +71,7 @@ class AppHost const winrt::TerminalApp::RenameWindowRequestedArgs args); GUID _CurrentDesktopGuid(); + void _LazyLoadDesktopManager(); winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); From 2eec961a87ed74c82c2b7ff61cf390e02ecb8b23 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 21 Apr 2021 08:42:15 -0500 Subject: [PATCH 72/88] All of carlos's feedback --- src/cascadia/Remoting/Monarch.idl | 3 ++- src/cascadia/Remoting/SummonWindowSelectionArgs.h | 4 +++- src/cascadia/TerminalApp/AppActionHandlers.cpp | 8 ++++++++ src/cascadia/TerminalApp/AppLogic.idl | 1 - src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp | 2 +- src/cascadia/TerminalSettingsModel/ActionArgs.cpp | 12 +++++++++++- .../TerminalSettingsModel/GlobalAppSettings.cpp | 1 - .../TerminalSettingsModel/GlobalAppSettings.idl | 1 - src/cascadia/TerminalSettingsModel/KeyMapping.cpp | 4 ++++ .../Resources/en-US/Resources.resw | 6 ++++++ src/cascadia/WindowsTerminal/AppHost.cpp | 4 ++-- src/cascadia/WindowsTerminal/IslandWindow.cpp | 4 ++-- 12 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index f6a8ddb9226..ca62cdc0440 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -20,8 +20,9 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass SummonWindowSelectionArgs { SummonWindowSelectionArgs(); + SummonWindowSelectionArgs(String windowName); String WindowName; - // Other options: + // TODO GH#8888 Other options: // * CurrentDesktop // * CurrentMonitor diff --git a/src/cascadia/Remoting/SummonWindowSelectionArgs.h b/src/cascadia/Remoting/SummonWindowSelectionArgs.h index dda213d60b5..4aec6488f41 100644 --- a/src/cascadia/Remoting/SummonWindowSelectionArgs.h +++ b/src/cascadia/Remoting/SummonWindowSelectionArgs.h @@ -26,9 +26,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { public: SummonWindowSelectionArgs() = default; + SummonWindowSelectionArgs(winrt::hstring name) : + _WindowName{ name } {}; WINRT_PROPERTY(winrt::hstring, WindowName); - WINRT_PROPERTY(bool, FoundMatch, true); + WINRT_PROPERTY(bool, FoundMatch, false); }; } diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index f875e517ec8..899fa20331d 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -756,11 +756,19 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleGlobalSummon(const IInspectable& /*sender*/, const ActionEventArgs& args) { + // Manually return false. These shouldn't ever get here, except for when + // we fail to register for the global hotkey. In that case, returning + // false here will let the underlying terminal still process the key, as + // if it wasn't bound at all. args.Handled(false); } void TerminalPage::_HandleQuakeMode(const IInspectable& /*sender*/, const ActionEventArgs& args) { + // Manually return false. These shouldn't ever get here, except for when + // we fail to register for the global hotkey. In that case, returning + // false here will let the underlying terminal still process the key, as + // if it wasn't bound at all. args.Handled(false); } } diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 5791f444f84..96589c86306 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -70,7 +70,6 @@ namespace TerminalApp FindTargetWindowResult FindTargetWindow(String[] args); - // Microsoft.Terminal.Control.KeyChord GlobalHotkey(); Windows.Foundation.Collections.IMap GlobalHotkeys(); // See IDialogPresenter and TerminalPage's DialogPresenter for more diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index d8eb6b9c375..ce179f0cd82 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -343,7 +343,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::RenameWindow, RS_(L"ResetWindowNameCommandKey") }, { ShortcutAction::OpenWindowRenamer, RS_(L"OpenWindowRenamerCommandKey") }, { ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName - { ShortcutAction::QuakeMode, L"" }, // Intentionally omitted, must be generated by GenerateName + { ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") }, }; }(); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 2ffd18001f6..9565d3d8132 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -586,6 +586,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring GlobalSummonArgs::GenerateName() const { - return L""; + std::wstringstream ss; + ss << std::wstring_view(RS_(L"GlobalSummonCommandKey")); + + // "Summon the Terminal window" + // "Summon the Terminal window, name:\"{_Name}\"" + if (!_Name.empty()) + { + ss << L", name: "; + ss << std::wstring_view(_Name); + } + return winrt::hstring{ ss.str() }; } } diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index aa35a6b322d..131d6f03e4d 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -16,7 +16,6 @@ using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; using namespace winrt::Windows::UI::Xaml; using namespace ::Microsoft::Console; using namespace winrt::Microsoft::UI::Xaml::Controls; -using namespace winrt::Microsoft::Terminal::Control; static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" }; static constexpr std::string_view ActionsKey{ "actions" }; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index c44f4b43fce..48542a0b0af 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -76,6 +76,5 @@ namespace Microsoft.Terminal.Settings.Model KeyMapping KeyMap(); Windows.Foundation.Collections.IMapView Commands(); - } } diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp index c41fe152b1a..212d505e847 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp @@ -146,6 +146,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - Build a map of all the globalSummon actions. // - quakeMode actions are included in this, but expanded to the equivalent // set of GlobalSummonArgs + // - This is only ever called in two scenarios: + // - on becoming the monarch (which only happens once per window) + // - when the settings reload (and the cache would inevitably be dirty) + // So it's perfectly reasonable to not cache these results. // Arguments: // - // Return Value: diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index c85aa014633..85982803e92 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -391,4 +391,10 @@ Rename window... + + Summon the Terminal window + + + Summon Quake window + diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 19acc879ae6..748981e1560 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -660,8 +660,8 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) { if (const auto& summonArgs{ actionAndArgs.Args().try_as() }) { - Remoting::SummonWindowSelectionArgs args; - args.WindowName(summonArgs.Name()); + Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() }; + _windowManager.SummonWindow(args); if (args.FoundMatch()) { diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 2c5612b02ad..1543948ec25 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -373,7 +373,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { // send focus to the child window SetFocus(_interopWindowHandle); - // return 0; // eat the message + return 0; } break; } @@ -918,7 +918,7 @@ void IslandWindow::UnsetHotkeys(const std::vector(i))); } } From 15a8a9cec0942799b21c0c32b25aa96815f6244f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 19 Apr 2021 14:41:48 -0500 Subject: [PATCH 73/88] !! THIS NEEDS TO GO TO THE PARENT BRANCH !! (cherry picked from commit 9f9eacb5d98c77214a819d80a7fe1cbe6c96eb2c) --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 1543948ec25..7f61c737d1b 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -993,6 +993,10 @@ winrt::fire_and_forget IslandWindow::SummonWindow() }); LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW)); + + // Activate the window too. This will force us to the virtual desktop this + // window is on, if it's on another virtual desktop. + LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); From 837078946276ffbe968f212b29c6722f572a8890 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 21 Apr 2021 12:02:56 -0500 Subject: [PATCH 74/88] replace todo with comment --- src/cascadia/WindowsTerminal/AppHost.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 748981e1560..429766b42cf 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -565,7 +565,8 @@ bool AppHost::HasWindow() void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, Remoting::CommandlineArgs args) { - // TODO! Should we summon the window whenever we dispatch a commandline to it? methinks yes, but not in this PR. + // Summon the window whenever we dispatch a commandline to it. This will + // make it obvious when a new tab/pane is created in a window. _window->SummonWindow(); _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } From 26d5194b0ce42c5910c568f1ad1c3d33944420aa Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 23 Apr 2021 09:01:51 -0500 Subject: [PATCH 75/88] this test is _fantastic_ --- .../UnitTests_Remoting/RemotingTests.cpp | 303 +++++++++++++++++- 1 file changed, 301 insertions(+), 2 deletions(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 84944aeb603..1463172bb76 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -36,6 +36,31 @@ using namespace winrt::Microsoft::Terminal; namespace RemotingUnitTests { + struct MockDesktopManager : implements + { + HRESULT GetWindowDesktopId(HWND /*topLevelWindow*/, GUID* /*desktopId*/) + { + VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests."); + return E_FAIL; + } + HRESULT MoveWindowToDesktop(HWND /*topLevelWindow*/, REFGUID /*desktopId*/) + { + VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests."); + return E_FAIL; + } + HRESULT IsWindowOnCurrentVirtualDesktop(HWND topLevelWindow, BOOL* onCurrentDesktop) + { + if (pfnIsWindowOnCurrentVirtualDesktop) + { + return pfnIsWindowOnCurrentVirtualDesktop(topLevelWindow, onCurrentDesktop); + } + VERIFY_IS_TRUE(false, L"You didn't set up the pfnIsWindowOnCurrentVirtualDesktop for this test!"); + return E_FAIL; + } + + std::function pfnIsWindowOnCurrentVirtualDesktop; + }; + // This is a silly helper struct. // It will always throw an hresult_error on any of its methods. // @@ -60,13 +85,13 @@ namespace RemotingUnitTests Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; } void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error{}; } - void Summon() { throw winrt::hresult_error{}; }; + void Summon(const Remoting::SummonWindowBehavior& /*args*/) { 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); TYPED_EVENT(RenameRequested, winrt::Windows::Foundation::IInspectable, Remoting::RenameRequestArgs); - TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior); }; class RemotingTests @@ -112,6 +137,10 @@ namespace RemotingUnitTests TEST_METHOD(TestSummonNamedDeadWindow); TEST_METHOD(TestSummonMostRecentDeadWindow); + TEST_METHOD(TestSummonOnCurrent); + TEST_METHOD(TestSummonOnCurrentWithName); + TEST_METHOD(TestSummonOnCurrentDeadWindow); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -1841,4 +1870,274 @@ namespace RemotingUnitTests VERIFY_IS_TRUE(args.FoundMatch()); } + void RemotingTests::TestSummonOnCurrent() + { + Log::Comment(L"TODO!"); + + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + constexpr auto monarch0PID = 12345u; + constexpr auto peasant1PID = 23456u; + constexpr auto peasant2PID = 34567u; + constexpr auto peasant3PID = 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 p3; + p3.attach(new Remoting::implementation::Peasant(peasant3PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + VERIFY_IS_NOT_NULL(p3); + p1->WindowName(L"one"); + p2->WindowName(L"two"); + p3->WindowName(L"three"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + VERIFY_ARE_EQUAL(0, p3->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + m0->AddPeasant(*p3); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + VERIFY_ARE_EQUAL(3, p3->GetID()); + + VERIFY_ARE_EQUAL(3u, m0->_peasants.size()); + + bool p1ExpectedToBeSummoned = false; + bool p2ExpectedToBeSummoned = false; + bool p3ExpectedToBeSummoned = false; + + p1->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p1 summoned"); + VERIFY_IS_TRUE(p1ExpectedToBeSummoned); + }); + p2->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p2 summoned"); + VERIFY_IS_TRUE(p2ExpectedToBeSummoned); + }); + p3->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p3 summoned"); + VERIFY_IS_TRUE(p3ExpectedToBeSummoned); + }); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid2, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the third peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid1, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + + Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop"); + winrt::com_ptr manager; + manager.attach(new MockDesktopManager()); + m0->_desktopManager = manager.try_as(); + + auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT { + Log::Comment(L"firstCallback: Checking if window is on desktop 1"); + + const uint64_t hwnd = reinterpret_cast(h); + if (hwnd == peasant1PID || hwnd == peasant3PID) + { + *result = true; + } + else if (hwnd == peasant2PID) + { + *result = false; + } + else + { + VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value"); + } + return S_OK; + }; + manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback; + + Remoting::SummonWindowSelectionArgs args; + + Log::Comment(L"Summon window three - it is the MRU on desktop 1"); + p3ExpectedToBeSummoned = true; + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + + Log::Comment(L"Summon window one - it is the MRU on desktop 1"); + p1ExpectedToBeSummoned = true; + p2ExpectedToBeSummoned = false; + p3ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Now we'll pretend we switched to desktop 2"); + + auto secondCallback = [&](HWND h, BOOL* result) -> HRESULT { + Log::Comment(L"secondCallback: Checking if window is on desktop 2"); + const uint64_t hwnd = reinterpret_cast(h); + if (hwnd == peasant1PID || hwnd == peasant3PID) + { + *result = false; + } + else if (hwnd == peasant2PID) + { + *result = true; + } + else + { + VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value"); + } + return S_OK; + }; + manager->pfnIsWindowOnCurrentVirtualDesktop = secondCallback; + + Log::Comment(L"Summon window one - it is the MRU on desktop 2"); + p1ExpectedToBeSummoned = false; + p2ExpectedToBeSummoned = true; + p3ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + { + Log::Comment(L"Activate the third peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid2, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + + auto thirdCallback = [&](HWND h, BOOL* result) -> HRESULT { + Log::Comment(L"thirdCallback: Checking if window is on desktop 2. (windows 2 and 3 are)"); + const uint64_t hwnd = reinterpret_cast(h); + if (hwnd == peasant1PID) + { + *result = false; + } + else if (hwnd == peasant2PID || hwnd == peasant3PID) + { + *result = true; + } + else + { + VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value"); + } + return S_OK; + }; + manager->pfnIsWindowOnCurrentVirtualDesktop = thirdCallback; + + Log::Comment(L"Summon window three - it is the MRU on desktop 2"); + p1ExpectedToBeSummoned = false; + p2ExpectedToBeSummoned = false; + p3ExpectedToBeSummoned = true; + args.FoundMatch(false); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Now we'll pretend we switched to desktop 1"); + + auto fourthCallback = [&](HWND h, BOOL* result) -> HRESULT { + Log::Comment(L"fourthCallback: Checking if window is on desktop 1. (window 1 is)"); + const uint64_t hwnd = reinterpret_cast(h); + if (hwnd == peasant1PID) + { + *result = true; + } + else if (hwnd == peasant2PID || hwnd == peasant3PID) + { + *result = false; + } + else + { + VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value"); + } + return S_OK; + }; + manager->pfnIsWindowOnCurrentVirtualDesktop = fourthCallback; + + Log::Comment(L"Summon window one - it is the only window on desktop 1"); + p1ExpectedToBeSummoned = true; + p2ExpectedToBeSummoned = false; + p3ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Now we'll pretend we switched to desktop 3"); + + auto fifthCallback = [&](HWND h, BOOL* result) -> HRESULT { + Log::Comment(L"fifthCallback: Checking if window is on desktop 3. (none are)"); + const uint64_t hwnd = reinterpret_cast(h); + if (hwnd == peasant1PID || hwnd == peasant2PID || hwnd == peasant3PID) + { + *result = false; + } + else + { + VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value"); + } + return S_OK; + }; + manager->pfnIsWindowOnCurrentVirtualDesktop = fifthCallback; + + Log::Comment(L"This summon won't find a window."); + p1ExpectedToBeSummoned = false; + p2ExpectedToBeSummoned = false; + p3ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_FALSE(args.FoundMatch()); + } + + void RemotingTests::TestSummonOnCurrentWithName() + { + VERIFY_IS_TRUE(false, L"TODO! write tests"); + } + void RemotingTests::TestSummonOnCurrentDeadWindow() + { + VERIFY_IS_TRUE(false, L"TODO! write tests"); + } } From 91372ea1f91d7391e76185346a698bed82f9fec0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 23 Apr 2021 11:14:39 -0500 Subject: [PATCH 76/88] more tests, because I like being thorough --- .../UnitTests_Remoting/RemotingTests.cpp | 286 +++++++++++++++++- 1 file changed, 283 insertions(+), 3 deletions(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 1463172bb76..35028e6512f 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -1872,7 +1872,8 @@ namespace RemotingUnitTests void RemotingTests::TestSummonOnCurrent() { - Log::Comment(L"TODO!"); + Log::Comment(L"Tests summoning a window, using OnCurrentDesktop to only" + L"select windows on the current desktop."); const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; @@ -1990,6 +1991,7 @@ namespace RemotingUnitTests args.OnCurrentDesktop(true); m0->SummonWindow(args); VERIFY_IS_TRUE(args.FoundMatch()); + { Log::Comment(L"Activate the first peasant, first desktop"); Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), @@ -2134,10 +2136,288 @@ namespace RemotingUnitTests void RemotingTests::TestSummonOnCurrentWithName() { - VERIFY_IS_TRUE(false, L"TODO! write tests"); + Log::Comment(L"Test that specifying a WindowName forces us to ignore OnCurrentDesktop"); + + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + constexpr auto monarch0PID = 12345u; + constexpr auto peasant1PID = 23456u; + constexpr auto peasant2PID = 34567u; + constexpr auto peasant3PID = 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 p3; + p3.attach(new Remoting::implementation::Peasant(peasant3PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + VERIFY_IS_NOT_NULL(p3); + p1->WindowName(L"one"); + p2->WindowName(L"two"); + p3->WindowName(L"three"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + VERIFY_ARE_EQUAL(0, p3->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + m0->AddPeasant(*p3); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + VERIFY_ARE_EQUAL(3, p3->GetID()); + + VERIFY_ARE_EQUAL(3u, m0->_peasants.size()); + + bool p1ExpectedToBeSummoned = false; + bool p2ExpectedToBeSummoned = false; + bool p3ExpectedToBeSummoned = false; + + p1->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p1 summoned"); + VERIFY_IS_TRUE(p1ExpectedToBeSummoned); + }); + p2->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p2 summoned"); + VERIFY_IS_TRUE(p2ExpectedToBeSummoned); + }); + p3->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p3 summoned"); + VERIFY_IS_TRUE(p3ExpectedToBeSummoned); + }); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid2, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the third peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid1, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + + Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop"); + winrt::com_ptr manager; + manager.attach(new MockDesktopManager()); + m0->_desktopManager = manager.try_as(); + + auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT { + Log::Comment(L"firstCallback: Checking if window is on desktop 1"); + + const uint64_t hwnd = reinterpret_cast(h); + if (hwnd == peasant1PID || hwnd == peasant3PID) + { + *result = true; + } + else if (hwnd == peasant2PID) + { + *result = false; + } + else + { + VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value"); + } + return S_OK; + }; + manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback; + + Remoting::SummonWindowSelectionArgs args; + + Log::Comment(L"Summon window three - it is the MRU on desktop 1"); + p3ExpectedToBeSummoned = true; + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Look for window 1 by name. When given a name, we don't care about OnCurrentDesktop."); + p1ExpectedToBeSummoned = true; + p2ExpectedToBeSummoned = false; + p3ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.WindowName(L"one"); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Look for window 2 by name. When given a name, we don't care about OnCurrentDesktop."); + p1ExpectedToBeSummoned = false; + p2ExpectedToBeSummoned = true; + p3ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.WindowName(L"two"); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Look for window 3 by name. When given a name, we don't care about OnCurrentDesktop."); + p1ExpectedToBeSummoned = false; + p2ExpectedToBeSummoned = false; + p3ExpectedToBeSummoned = true; + args.FoundMatch(false); + args.WindowName(L"three"); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); } + void RemotingTests::TestSummonOnCurrentDeadWindow() { - VERIFY_IS_TRUE(false, L"TODO! write tests"); + Log::Comment(L"Test that we can summon a window on the current desktop," + L" when the MRU window on that desktop dies."); + + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + constexpr auto monarch0PID = 12345u; + constexpr auto peasant1PID = 23456u; + constexpr auto peasant2PID = 34567u; + constexpr auto peasant3PID = 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 p3; + p3.attach(new Remoting::implementation::Peasant(peasant3PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + VERIFY_IS_NOT_NULL(p3); + p1->WindowName(L"one"); + p2->WindowName(L"two"); + p3->WindowName(L"three"); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + VERIFY_ARE_EQUAL(0, p3->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + m0->AddPeasant(*p3); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + VERIFY_ARE_EQUAL(3, p3->GetID()); + + VERIFY_ARE_EQUAL(3u, m0->_peasants.size()); + + bool p1ExpectedToBeSummoned = false; + bool p2ExpectedToBeSummoned = false; + bool p3ExpectedToBeSummoned = false; + + p1->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p1 summoned"); + VERIFY_IS_TRUE(p1ExpectedToBeSummoned); + }); + p2->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p2 summoned"); + VERIFY_IS_TRUE(p2ExpectedToBeSummoned); + }); + p3->SummonRequested([&](auto&&, auto&&) { + Log::Comment(L"p3 summoned"); + VERIFY_IS_TRUE(p3ExpectedToBeSummoned); + }); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + p1->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + p2->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid2, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the third peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + p3->GetPID(), // USE PID as HWND, because these values don't _really_ matter + guid1, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + + Log::Comment(L"Create a mock IVirtualDesktopManager to handle checking if a window is on a given desktop"); + winrt::com_ptr manager; + manager.attach(new MockDesktopManager()); + m0->_desktopManager = manager.try_as(); + + auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT { + Log::Comment(L"firstCallback: Checking if window is on desktop 1"); + + const uint64_t hwnd = reinterpret_cast(h); + if (hwnd == peasant1PID || hwnd == peasant3PID) + { + *result = true; + } + else if (hwnd == peasant2PID) + { + *result = false; + } + else + { + VERIFY_IS_TRUE(false, L"IsWindowOnCurrentVirtualDesktop called with unexpected value"); + } + return S_OK; + }; + manager->pfnIsWindowOnCurrentVirtualDesktop = firstCallback; + + Remoting::SummonWindowSelectionArgs args; + + Log::Comment(L"Summon window three - it is the MRU on desktop 1"); + p3ExpectedToBeSummoned = true; + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); + + Log::Comment(L"Kill window 3. Window 1 is now the MRU on desktop 1."); + RemotingTests::_killPeasant(m0, p3->GetID()); + + Log::Comment(L"Summon window three - it is the MRU on desktop 1"); + p1ExpectedToBeSummoned = true; + p2ExpectedToBeSummoned = false; + p3ExpectedToBeSummoned = false; + args.FoundMatch(false); + args.OnCurrentDesktop(true); + m0->SummonWindow(args); + VERIFY_IS_TRUE(args.FoundMatch()); } } From 045a8db5e1d34e3d861c3d933965c546d4f42f74 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 23 Apr 2021 12:26:26 -0500 Subject: [PATCH 77/88] fix a build break --- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 6c9564702b6..173f8d4b239 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -71,7 +71,7 @@ class AppHost const winrt::TerminalApp::RenameWindowRequestedArgs args); GUID _CurrentDesktopGuid(); - void _LazyLoadDesktopManager(); + bool _LazyLoadDesktopManager(); winrt::fire_and_forget _setupGlobalHotkeys(); winrt::fire_and_forget _createNewTerminalWindow(winrt::Microsoft::Terminal::Settings::Model::GlobalSummonArgs args); From c95a4297cf8a0ff2fb08d9525f9be48a629e931a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 26 Apr 2021 09:20:19 -0500 Subject: [PATCH 78/88] Give Clint the credit they deserce --- NOTICE.md | 30 +++++++++++++++++++ .../WindowsTerminal/VirtualDesktopUtils.cpp | 3 ++ 2 files changed, 33 insertions(+) diff --git a/NOTICE.md b/NOTICE.md index a63cd566485..ccff11e177e 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -251,3 +251,33 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` + +## `VirtualDesktopUtils` + +**Source**: [https://github.com/microsoft/PowerToys](https://github.com/microsoft/PowerToys) + +### License + +``` +The MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp index 4b8b9202c3d..9f953e0c362 100644 --- a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp +++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp @@ -3,6 +3,9 @@ // // Shamelessly copied from microsoft/PowerToys, at // https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/lib/VirtualDesktopUtils.cpp +// +// The code style is left untouched, as to make contributions from/to the +// upstream source easier. #include "pch.h" From b5e0f2d660052f3628b38f40a7039a873bf7e93a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 26 Apr 2021 11:25:10 -0500 Subject: [PATCH 79/88] When the window is summoned and is already active, minimize it. This works like a charm. Let's add settings. --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 37 +++++++++++++++++-- src/cascadia/WindowsTerminal/IslandWindow.h | 3 ++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 1c959185830..17c2071ab42 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -960,9 +960,7 @@ void IslandWindow::SetGlobalHotkeys(const std::vector // Return Value: @@ -972,6 +970,27 @@ winrt::fire_and_forget IslandWindow::SummonWindow() // On the foreground thread: co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + HWND foregroundWindow = GetForegroundWindow(); + if (foregroundWindow == _window.get()) + { + _globalDismissWindow(); + } + else + { + _globalActivateWindow(); + } +} + +// Method Description: +// - Force activate this window. This method will bring us to the foreground and +// activate us. If the window is minimized, it will restore the window. If the +// window is on another desktop, the OS will switch to that desktop. +// Arguments: +// - +// Return Value: +// - +void IslandWindow::_globalActivateWindow() +{ // From: https://stackoverflow.com/a/59659421 // > The trick is to make windows ‘think’ that our process and the target // > window (hwnd) are related by attaching the threads (using @@ -999,5 +1018,17 @@ winrt::fire_and_forget IslandWindow::SummonWindow() LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); } +// Method Description: +// - Minimize the window. This is called when the window is summoned, but is +// already active +// Arguments: +// - +// Return Value: +// - +void IslandWindow::_globalDismissWindow() +{ + LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_MINIMIZE)); +} + DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 036553b8af2..090265f6058 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -91,6 +91,9 @@ class IslandWindow : void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam); long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize); + void _globalActivateWindow(); + void _globalDismissWindow(); + private: // This minimum width allows for width the tabs fit static constexpr long minimumWidth = 460L; From 6c4238b49c7909e381be749e7d7a02759edffe01 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 26 Apr 2021 12:41:16 -0500 Subject: [PATCH 80/88] Toggle the visibility with an optional argument --- src/cascadia/Remoting/Peasant.idl | 1 + src/cascadia/Remoting/SummonWindowBehavior.h | 4 +++- src/cascadia/TerminalSettingsModel/ActionArgs.h | 7 ++++++- src/cascadia/TerminalSettingsModel/ActionArgs.idl | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 5 +++-- src/cascadia/WindowsTerminal/IslandWindow.cpp | 9 ++++++--- src/cascadia/WindowsTerminal/IslandWindow.h | 2 +- 7 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index af74cf3d065..90ff1ab4513 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -33,6 +33,7 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass SummonWindowBehavior { SummonWindowBehavior(); Boolean MoveToCurrentDesktop; + Boolean ToggleVisibility; // Other options: // * CurrentMonitor } diff --git a/src/cascadia/Remoting/SummonWindowBehavior.h b/src/cascadia/Remoting/SummonWindowBehavior.h index c460ef98843..d26a21aff98 100644 --- a/src/cascadia/Remoting/SummonWindowBehavior.h +++ b/src/cascadia/Remoting/SummonWindowBehavior.h @@ -22,10 +22,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation public: SummonWindowBehavior() = default; WINRT_PROPERTY(bool, MoveToCurrentDesktop, true); + WINRT_PROPERTY(bool, ToggleVisibility, true); public: SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) : - _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() } {}; + _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() }, + _ToggleVisibility{ other.ToggleVisibility() } {}; }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 9dd80f735bc..4734d2f09a0 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -1044,9 +1044,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GlobalSummonArgs() = default; WINRT_PROPERTY(winrt::hstring, Name, L""); WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent); + WINRT_PROPERTY(bool, ToggleVisibility, true); static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view DesktopKey{ "desktop" }; + static constexpr std::string_view ToggleVisibilityKey{ "toggleVisibility" }; public: hstring GenerateName() const; @@ -1056,7 +1058,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation if (auto otherAsUs = other.try_as()) { return otherAsUs->_Name == _Name && - otherAsUs->_Desktop == _Desktop; + otherAsUs->_Desktop == _Desktop && + otherAsUs->_ToggleVisibility == _ToggleVisibility; } return false; }; @@ -1066,6 +1069,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, NameKey, args->_Name); JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop); + JsonUtils::GetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility); return { *args, {} }; } IActionArgs Copy() const @@ -1073,6 +1077,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto copy{ winrt::make_self() }; copy->_Name = _Name; copy->_Desktop = _Desktop; + copy->_ToggleVisibility = _ToggleVisibility; return *copy; } }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index abf52e50b86..527da24efd4 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -264,5 +264,6 @@ namespace Microsoft.Terminal.Settings.Model { String Name { get; }; DesktopBehavior Desktop { get; }; + Boolean ToggleVisibility { get; }; }; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index b7b528b30e4..99283e398a5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -568,7 +568,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se { // Summon the window whenever we dispatch a commandline to it. This will // make it obvious when a new tab/pane is created in a window. - _window->SummonWindow(); + _window->SummonWindow(false); _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } @@ -669,6 +669,7 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); + args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); _windowManager.SummonWindow(args); if (args.FoundMatch()) @@ -756,7 +757,7 @@ bool AppHost::_LazyLoadDesktopManager() void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::SummonWindowBehavior& args) { - _window->SummonWindow(); + _window->SummonWindow(args.ToggleVisibility()); if (args != nullptr && args.MoveToCurrentDesktop()) { diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 17c2071ab42..7b68bbf6807 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -965,13 +965,16 @@ void IslandWindow::SetGlobalHotkeys(const std::vector // Return Value: // - -winrt::fire_and_forget IslandWindow::SummonWindow() +winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility) { // On the foreground thread: co_await winrt::resume_foreground(_rootGrid.Dispatcher()); - HWND foregroundWindow = GetForegroundWindow(); - if (foregroundWindow == _window.get()) + // * If the user doesn't want to toggleVisibility, then just always try to + // activate. + // * If the user does want to toggleVisibility, then dismiss the window if + // we're the current foreground window. + if (toggleVisibility && GetForegroundWindow() == _window.get()) { _globalDismissWindow(); } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 090265f6058..f3dcce2bd6f 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -41,7 +41,7 @@ class IslandWindow : void UnsetHotkeys(const std::vector& hotkeyList); void SetGlobalHotkeys(const std::vector& hotkeyList); - winrt::fire_and_forget SummonWindow(); + winrt::fire_and_forget SummonWindow(const bool toggleVisibility); #pragma endregion From 0abd963a8178a6dd58d84269c02b5b83fcbf5033 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 26 Apr 2021 16:46:18 -0500 Subject: [PATCH 81/88] update the comment --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 7b68bbf6807..caf62a41707 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -960,9 +960,11 @@ void IslandWindow::SetGlobalHotkeys(const std::vector +// - toggleVisibility: controls how we should behave when already in the foreground. // Return Value: // - winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility) From 24bfbe640c17fc3c8d06da09004b139410eac68c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 28 Apr 2021 06:10:30 -0500 Subject: [PATCH 82/88] BODGY fix for newer builds --- .../WindowsTerminal/VirtualDesktopUtils.cpp | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp index 9f953e0c362..a912e7e1b0f 100644 --- a/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp +++ b/src/cascadia/WindowsTerminal/VirtualDesktopUtils.cpp @@ -4,8 +4,9 @@ // Shamelessly copied from microsoft/PowerToys, at // https://github.com/microsoft/PowerToys/blob/master/src/modules/fancyzones/lib/VirtualDesktopUtils.cpp // -// The code style is left untouched, as to make contributions from/to the -// upstream source easier. +// The code style is left (relatively) untouched, as to make contributions +// from/to the upstream source easier. `NewGetCurrentDesktopId` was added in +// April 2021. #include "pch.h" @@ -22,6 +23,25 @@ namespace NonLocalizable namespace VirtualDesktopUtils { + // Look for the guid stored as the value `CurrentVirtualDesktop` under the + // key + // `HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops` + bool NewGetCurrentDesktopId(GUID* desktopId) + { + wil::unique_hkey key{}; + if (RegOpenKeyExW(HKEY_CURRENT_USER, NonLocalizable::RegKeyVirtualDesktops, 0, KEY_ALL_ACCESS, &key) == ERROR_SUCCESS) + { + GUID value{}; + DWORD size = sizeof(GUID); + if (RegQueryValueExW(key.get(), NonLocalizable::RegCurrentVirtualDesktop, 0, nullptr, reinterpret_cast(&value), &size) == ERROR_SUCCESS) + { + *desktopId = value; + return true; + } + } + return false; + } + bool GetDesktopIdFromCurrentSession(GUID* desktopId) { DWORD sessionId; @@ -120,6 +140,14 @@ namespace VirtualDesktopUtils bool GetCurrentVirtualDesktopId(GUID* desktopId) { + // BODGY + // On newer Windows builds, the current virtual desktop is persisted to + // a totally different reg key. Look there first. + if (NewGetCurrentDesktopId(desktopId)) + { + return true; + } + // Explorer persists current virtual desktop identifier to registry on a per session basis, but only // after first virtual desktop switch happens. If the user hasn't switched virtual desktops in this // session, value in registry will be empty. From 173720f71f92447ad15751e6e1d7d3efd447874b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 28 Apr 2021 08:49:09 -0500 Subject: [PATCH 83/88] Add property to control dropdown speed of global summon --- src/cascadia/Remoting/Peasant.idl | 1 + src/cascadia/Remoting/SummonWindowBehavior.h | 2 + .../TerminalSettingsModel/ActionArgs.h | 5 + .../TerminalSettingsModel/ActionArgs.idl | 1 + .../TerminalSettingsModel/KeyMapping.cpp | 2 + src/cascadia/WindowsTerminal/AppHost.cpp | 5 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 155 +++++++++++++++--- src/cascadia/WindowsTerminal/IslandWindow.h | 9 +- 8 files changed, 152 insertions(+), 28 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 90ff1ab4513..33064bc9dfa 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -34,6 +34,7 @@ namespace Microsoft.Terminal.Remoting SummonWindowBehavior(); Boolean MoveToCurrentDesktop; Boolean ToggleVisibility; + UInt32 DropdownDuration; // Other options: // * CurrentMonitor } diff --git a/src/cascadia/Remoting/SummonWindowBehavior.h b/src/cascadia/Remoting/SummonWindowBehavior.h index d26a21aff98..caa11ee3bdc 100644 --- a/src/cascadia/Remoting/SummonWindowBehavior.h +++ b/src/cascadia/Remoting/SummonWindowBehavior.h @@ -23,10 +23,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation SummonWindowBehavior() = default; WINRT_PROPERTY(bool, MoveToCurrentDesktop, true); WINRT_PROPERTY(bool, ToggleVisibility, true); + WINRT_PROPERTY(uint32_t, DropdownDuration, 0); public: SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) : _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() }, + _DropdownDuration{ other.DropdownDuration() }, _ToggleVisibility{ other.ToggleVisibility() } {}; }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 4734d2f09a0..a5e0aa7e1a8 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -1045,10 +1045,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation WINRT_PROPERTY(winrt::hstring, Name, L""); WINRT_PROPERTY(Model::DesktopBehavior, Desktop, Model::DesktopBehavior::ToCurrent); WINRT_PROPERTY(bool, ToggleVisibility, true); + WINRT_PROPERTY(uint32_t, DropdownDuration, 0); static constexpr std::string_view NameKey{ "name" }; static constexpr std::string_view DesktopKey{ "desktop" }; static constexpr std::string_view ToggleVisibilityKey{ "toggleVisibility" }; + static constexpr std::string_view DropdownDurationKey{ "dropdownDuration" }; public: hstring GenerateName() const; @@ -1059,6 +1061,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { return otherAsUs->_Name == _Name && otherAsUs->_Desktop == _Desktop && + otherAsUs->_DropdownDuration == _DropdownDuration && otherAsUs->_ToggleVisibility == _ToggleVisibility; } return false; @@ -1069,6 +1072,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto args = winrt::make_self(); JsonUtils::GetValueForKey(json, NameKey, args->_Name); JsonUtils::GetValueForKey(json, DesktopKey, args->_Desktop); + JsonUtils::GetValueForKey(json, DropdownDurationKey, args->_DropdownDuration); JsonUtils::GetValueForKey(json, ToggleVisibilityKey, args->_ToggleVisibility); return { *args, {} }; } @@ -1077,6 +1081,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto copy{ winrt::make_self() }; copy->_Name = _Name; copy->_Desktop = _Desktop; + copy->_DropdownDuration = _DropdownDuration; copy->_ToggleVisibility = _ToggleVisibility; return *copy; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 527da24efd4..9e829715ab8 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -265,5 +265,6 @@ namespace Microsoft.Terminal.Settings.Model String Name { get; }; DesktopBehavior Desktop { get; }; Boolean ToggleVisibility { get; }; + UInt32 DropdownDuration { get; }; }; } diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp index 212d505e847..92ace349784 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp @@ -172,6 +172,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // We want to summon the window with the name "_quake" specifically. args->Name(L"_quake"); + // We want the window to dropdown, with a 200ms duration. + args->DropdownDuration(200); Model::ActionAndArgs actionAndArgs{ ShortcutAction::GlobalSummon, *args }; justGlobals[k] = actionAndArgs; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 99283e398a5..5f53059d607 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -568,7 +568,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se { // Summon the window whenever we dispatch a commandline to it. This will // make it obvious when a new tab/pane is created in a window. - _window->SummonWindow(false); + _window->SummonWindow(false, 0); _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } @@ -670,6 +670,7 @@ void AppHost::_GlobalHotkeyPressed(const long hotkeyIndex) args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); + args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); _windowManager.SummonWindow(args); if (args.FoundMatch()) @@ -757,7 +758,7 @@ bool AppHost::_LazyLoadDesktopManager() void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::SummonWindowBehavior& args) { - _window->SummonWindow(args.ToggleVisibility()); + _window->SummonWindow(args.ToggleVisibility(), args.DropdownDuration()); if (args != nullptr && args.MoveToCurrentDesktop()) { diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index caf62a41707..2d96e853eec 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -967,7 +967,7 @@ void IslandWindow::SetGlobalHotkeys(const std::vector -winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility) +winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility, const uint32_t dropdownDuration) { // On the foreground thread: co_await winrt::resume_foreground(_rootGrid.Dispatcher()); @@ -978,23 +978,112 @@ winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility) // we're the current foreground window. if (toggleVisibility && GetForegroundWindow() == _window.get()) { - _globalDismissWindow(); + _globalDismissWindow(dropdownDuration); } else { - _globalActivateWindow(); + _globalActivateWindow(dropdownDuration); } } +// Method Description: +// - Helper for performing a sliding animation. This will animate our _Xaml +// Island_, either growing down or shrinking up, using SetWindowRgn. +// - This function does the entire animation on the main thread (the UI thread), +// and **DOES NOT YIELD IT**. The window will be animating for the entire +// duration of dropdownDuration. +// - At the end of the animation, we'll reset the window region, so that it's as +// if nothing occured. +// Arguments: +// - dropdownDuration: The duration to play the animation, in milliseconds. If +// 0, we won't perform a dropdown animation. +// - down: if true, increase the height from top to bottom. otherwise, decrease +// the height, from bottom to top. +// Return Value: +// - +void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool down) +{ + til::rectangle fullWindowSize{ GetWindowRect() }; + const double fullHeight = fullWindowSize.height(); + + const double animationDuration = dropdownDuration; // use floating-point math throughout + const auto start = std::chrono::system_clock::now(); + + // Do at most dropdownDuration frames. After that, just bail straight to the + // final state. + for (uint32_t i = 0; i < dropdownDuration; i++) + { + const auto end = std::chrono::system_clock::now(); + const auto millis = std::chrono::duration_cast(end - start); + const double dt = ::base::saturated_cast(millis.count()); + + if (dt > animationDuration) + { + break; + } + + // If going down, increase the height over time. If going up, decrease the height. + const double currentHeight = ::base::saturated_cast( + down ? ((dt / animationDuration) * fullHeight) : + ((1.0 - (dt / animationDuration)) * fullHeight)); + + wil::unique_hrgn rgn{ CreateRectRgn(0, + 0, + fullWindowSize.width(), + ::base::saturated_cast(currentHeight)) }; + + SetWindowRgn(_interopWindowHandle, rgn.get(), true); + + // Go immediately into another frame. This prevents the window from + // doing anything else (tearing our state). A Sleep() here is a little + // janky, causes the animation to not be as smooth. + } + + // Reset the winddow. + SetWindowRgn(_interopWindowHandle, nullptr, true); +} + +void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration) +{ + // First, restore the window. SetWindowPlacement has a fun undocumented + // piece of functionality where it will restore the window position + // _without_ the animation, so use that instead of ShowWindow(SW_RESTORE). + WINDOWPLACEMENT wpc; + wpc.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(_window.get(), &wpc); + wpc.showCmd = SW_RESTORE; + SetWindowPlacement(_window.get(), &wpc); + + // Now that we're visible, animate the dropdown. + _doSlideAnimation(dropdownDuration, true); +} + +void IslandWindow::_slideUpWindow(const uint32_t dropdownDuration) +{ + // First, animate the window sliding up. + _doSlideAnimation(dropdownDuration, false); + + // Then, use SetWindowPlacement to minimize without the animation. + WINDOWPLACEMENT wpc; + wpc.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(_window.get(), &wpc); + wpc.showCmd = SW_MINIMIZE; + SetWindowPlacement(_window.get(), &wpc); +} + // Method Description: // - Force activate this window. This method will bring us to the foreground and // activate us. If the window is minimized, it will restore the window. If the // window is on another desktop, the OS will switch to that desktop. +// - If the window is minimized, and dropdownDuration is greater than 0, we'll +// perform a "slide in" animation. We won't do this if the window is already +// on the screen (since that seems silly). // Arguments: -// - +// - dropdownDuration: The duration to play the dropdown animation, in +// milliseconds. If 0, we won't perform a dropdown animation. // Return Value: // - -void IslandWindow::_globalActivateWindow() +void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration) { // From: https://stackoverflow.com/a/59659421 // > The trick is to make windows ‘think’ that our process and the target @@ -1005,34 +1094,54 @@ void IslandWindow::_globalActivateWindow() // restore-down the window. if (IsIconic(_window.get())) { - LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_RESTORE)); + if (dropdownDuration > 0) + { + _dropdownWindow(dropdownDuration); + } + else + { + LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_RESTORE)); + } + } + else + { + const DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), nullptr); + const DWORD currentThreadId = GetCurrentThreadId(); + + LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true)); + // Just in case, add the thread detach as a scope_exit, to make _sure_ we do it. + auto detachThread = wil::scope_exit([windowThreadProcessId, currentThreadId]() { + LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false)); + }); + LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); + LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW)); + + // Activate the window too. This will force us to the virtual desktop this + // window is on, if it's on another virtual desktop. + LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); } - const DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(), nullptr); - const DWORD currentThreadId = GetCurrentThreadId(); - - LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, true)); - // Just in case, add the thread detach as a scope_exit, to make _sure_ we do it. - auto detachThread = wil::scope_exit([windowThreadProcessId, currentThreadId]() { - LOG_IF_WIN32_BOOL_FALSE(AttachThreadInput(windowThreadProcessId, currentThreadId, false)); - }); - LOG_IF_WIN32_BOOL_FALSE(BringWindowToTop(_window.get())); - LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_SHOW)); - - // Activate the window too. This will force us to the virtual desktop this - // window is on, if it's on another virtual desktop. - LOG_LAST_ERROR_IF_NULL(SetActiveWindow(_window.get())); } // Method Description: // - Minimize the window. This is called when the window is summoned, but is // already active +// - If dropdownDuration is greater than 0, we'll perform a "slide in" +// animation, before minimizing the window. // Arguments: -// - +// - dropdownDuration: The duration to play the slide-up animation, in +// milliseconds. If 0, we won't perform a slide-up animation. // Return Value: // - -void IslandWindow::_globalDismissWindow() +void IslandWindow::_globalDismissWindow(const uint32_t dropdownDuration) { - LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_MINIMIZE)); + if (dropdownDuration > 0) + { + _slideUpWindow(dropdownDuration); + } + else + { + LOG_IF_WIN32_BOOL_FALSE(ShowWindow(_window.get(), SW_MINIMIZE)); + } } DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index f3dcce2bd6f..a62fdf4a661 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -41,7 +41,7 @@ class IslandWindow : void UnsetHotkeys(const std::vector& hotkeyList); void SetGlobalHotkeys(const std::vector& hotkeyList); - winrt::fire_and_forget SummonWindow(const bool toggleVisibility); + winrt::fire_and_forget SummonWindow(const bool toggleVisibility, const uint32_t dropdownDuration); #pragma endregion @@ -91,8 +91,11 @@ class IslandWindow : void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam); long _calculateTotalSize(const bool isWidth, const long clientSize, const long nonClientSize); - void _globalActivateWindow(); - void _globalDismissWindow(); + void _globalActivateWindow(const uint32_t dropdownDuration); + void _dropdownWindow(const uint32_t dropdownDuration); + void _slideUpWindow(const uint32_t dropdownDuration); + void _doSlideAnimation(const uint32_t dropdownDuration, const bool down); + void _globalDismissWindow(const uint32_t dropdownDuration); private: // This minimum width allows for width the tabs fit From b607a55846b21d1a3339c51bfc8156836093e590 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 28 Apr 2021 09:00:13 -0500 Subject: [PATCH 84/88] The OS has first dibs on animation settings You do respect dibs, don't you? --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 2d96e853eec..98a11b889b7 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -972,17 +972,37 @@ winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility, c // On the foreground thread: co_await winrt::resume_foreground(_rootGrid.Dispatcher()); + uint32_t actualDropdownDuration = dropdownDuration; + // If the user requested an animation, let's check if animations are enabled in the OS. + if (dropdownDuration > 0) + { + BOOL animationsEnabled = TRUE; + SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0); + if (!animationsEnabled) + { + // The OS has animations disabled - we should respect that and + // disable the animation here. + // + // We're doing this here, rather than in _doSlideAnimation, to + // preempt any other specific behavior that + // _globalActivateWindow/_globalDismissWindow might do if they think + // there should be an animation (like making the window appear with + // SetWindowPlacement rather than ShowWindow) + actualDropdownDuration = 0; + } + } + // * If the user doesn't want to toggleVisibility, then just always try to // activate. // * If the user does want to toggleVisibility, then dismiss the window if // we're the current foreground window. if (toggleVisibility && GetForegroundWindow() == _window.get()) { - _globalDismissWindow(dropdownDuration); + _globalDismissWindow(actualDropdownDuration); } else { - _globalActivateWindow(dropdownDuration); + _globalActivateWindow(actualDropdownDuration); } } From 63b36582492d3c4dbbefb224f174f3b51e471dd9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 28 Apr 2021 09:18:37 -0500 Subject: [PATCH 85/88] spel --- .github/actions/spelling/dictionary/apis.txt | 1 + src/cascadia/WindowsTerminal/IslandWindow.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/actions/spelling/dictionary/apis.txt b/.github/actions/spelling/dictionary/apis.txt index 73dd5e50c60..d55fc9f2033 100644 --- a/.github/actions/spelling/dictionary/apis.txt +++ b/.github/actions/spelling/dictionary/apis.txt @@ -31,6 +31,7 @@ HIGHCONTRASTON HIGHCONTRASTW hotkeys href +hrgn IActivation IApp IAppearance diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 98a11b889b7..b4e17d8d96b 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -1013,7 +1013,7 @@ winrt::fire_and_forget IslandWindow::SummonWindow(const bool toggleVisibility, c // and **DOES NOT YIELD IT**. The window will be animating for the entire // duration of dropdownDuration. // - At the end of the animation, we'll reset the window region, so that it's as -// if nothing occured. +// if nothing occurred. // Arguments: // - dropdownDuration: The duration to play the animation, in milliseconds. If // 0, we won't perform a dropdown animation. @@ -1055,11 +1055,11 @@ void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool SetWindowRgn(_interopWindowHandle, rgn.get(), true); // Go immediately into another frame. This prevents the window from - // doing anything else (tearing our state). A Sleep() here is a little - // janky, causes the animation to not be as smooth. + // doing anything else (tearing our state). A Sleep() here will cause a + // weird stutter, and causes the animation to not be as smooth. } - // Reset the winddow. + // Reset the window. SetWindowRgn(_interopWindowHandle, nullptr, true); } From 06cb41b805a56862eb38905fa2d596b617a42da6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 28 Apr 2021 16:46:16 -0500 Subject: [PATCH 86/88] Address Dustin's PR comments --- src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 3 +-- .../TerminalSettingsModel/ActionAndArgs.cpp | 1 + src/cascadia/TerminalSettingsModel/ActionArgs.h | 10 ++++++++++ .../TerminalSettingsModel/KeyMapping.cpp | 16 ++-------------- src/cascadia/TerminalSettingsModel/KeyMapping.h | 2 +- .../TerminalSettingsModel/KeyMapping.idl | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 7 +------ src/cascadia/inc/WindowingBehavior.h | 2 ++ 10 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index eefbc685adf..66cbe6ad21b 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -375,7 +375,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation continue; } - if (peasant.WindowName() == L"_quake") + if (peasant.WindowName() == QuakeWindowName) { // The _quake window should never be treated as the MRU window. // Skip it if we see it. Users can still target it with `wt -w diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 11fb720eed6..e8229157b7d 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1413,7 +1413,7 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IMap AppLogic::GlobalHotkeys() { - return _settings.GlobalSettings().KeyMap().FetchGlobalHotkeys(); + return _settings.GlobalSettings().KeyMap().GlobalHotkeys(); } void AppLogic::IdentifyWindow() diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 9094d7cc5db..62c0f8e2dd4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -16,6 +16,7 @@ #include "DebugTapConnection.h" #include "SettingsTab.h" #include "RenameWindowRequestedArgs.g.cpp" +#include "../inc/WindowingBehavior.h" using namespace winrt; using namespace winrt::Windows::Foundation::Collections; @@ -42,8 +43,6 @@ namespace winrt using IInspectable = Windows::Foundation::IInspectable; } -static constexpr std::wstring_view QuakeWindowName{ L"_quake" }; - namespace winrt::TerminalApp::implementation { TerminalPage::TerminalPage() : diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp index ce179f0cd82..aa7fc7c7d0b 100644 --- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp @@ -167,6 +167,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { ShortcutAction::NextTab, NextTabArgs::FromJson }, { ShortcutAction::RenameWindow, RenameWindowArgs::FromJson }, { ShortcutAction::GlobalSummon, GlobalSummonArgs::FromJson }, + { ShortcutAction::QuakeMode, GlobalSummonArgs::QuakeModeFromJson }, { ShortcutAction::Invalid, nullptr }, }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 1860e6b2208..68132691604 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -36,6 +36,7 @@ #include "../../cascadia/inc/cppwinrt_utils.h" #include "JsonUtils.h" #include "TerminalWarnings.h" +#include "../inc/WindowingBehavior.h" #include "TerminalSettingsSerializationHelpers.h" @@ -1070,6 +1071,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_Name = _Name; return *copy; } + // SPECIAL! This deserializer creates a GlobalSummonArgs with the + // default values for quakeMode + static FromJsonResult QuakeModeFromJson(const Json::Value& /*json*/) + { + // LOAD BEARING: Not using make_self here _will_ break you in the future! + auto args = winrt::make_self(); + args->_Name = QuakeWindowName; + return { *args, {} }; + } }; } diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp index 212d505e847..3bd93cfe59f 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.cpp +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.cpp @@ -154,28 +154,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // - // Return Value: // - a map of KeyChord -> ActionAndArgs containing all globally bindable actions. - Windows::Foundation::Collections::IMap KeyMapping::FetchGlobalHotkeys() + Windows::Foundation::Collections::IMap KeyMapping::GlobalHotkeys() { std::unordered_map justGlobals; for (const auto& [k, v] : _keyShortcuts) { - if (v.Action() == ShortcutAction::GlobalSummon) + if (v.Action() == ShortcutAction::GlobalSummon || v.Action() == ShortcutAction::QuakeMode) { justGlobals[k] = v; } - else if (v.Action() == ShortcutAction::QuakeMode) - { - // Manually replace the QuakeMode action with a globalSummon - // that has the appropriate action args. - auto args = winrt::make_self(); - - // We want to summon the window with the name "_quake" specifically. - args->Name(L"_quake"); - - Model::ActionAndArgs actionAndArgs{ ShortcutAction::GlobalSummon, *args }; - justGlobals[k] = actionAndArgs; - } } return winrt::single_threaded_map(std::move(justGlobals)); diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.h b/src/cascadia/TerminalSettingsModel/KeyMapping.h index f852328549b..eea978b59a6 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.h +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.h @@ -69,7 +69,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::vector LayerJson(const Json::Value& json); Json::Value ToJson(); - Windows::Foundation::Collections::IMap FetchGlobalHotkeys(); + Windows::Foundation::Collections::IMap GlobalHotkeys(); private: std::unordered_map _keyShortcuts; diff --git a/src/cascadia/TerminalSettingsModel/KeyMapping.idl b/src/cascadia/TerminalSettingsModel/KeyMapping.idl index 5234f2c7788..1b6886acded 100644 --- a/src/cascadia/TerminalSettingsModel/KeyMapping.idl +++ b/src/cascadia/TerminalSettingsModel/KeyMapping.idl @@ -36,6 +36,6 @@ namespace Microsoft.Terminal.Settings.Model Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(ShortcutAction action); Microsoft.Terminal.Control.KeyChord GetKeyBindingForActionWithArgs(ActionAndArgs actionAndArgs); - Windows.Foundation.Collections.IMap FetchGlobalHotkeys(); + Windows.Foundation.Collections.IMap GlobalHotkeys(); } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index cdc61c50b82..f4054fbb7d4 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -727,12 +727,7 @@ winrt::fire_and_forget AppHost::_createNewTerminalWindow(Settings::Model::Global args.Name().empty() ? L"new" : args.Name()) }; - // Build the args to ShellExecuteEx. We need to use ShellExecuteEx so we - // can pass the SEE_MASK_NOASYNC flag. That flag allows us to safely - // call this on the background thread, and have ShellExecute _not_ call - // back to us on the main thread. Without this, if you close the - // Terminal quickly after the UAC prompt, the elevated WT will never - // actually spawn. + SHELLEXECUTEINFOW seInfo{ 0 }; seInfo.cbSize = sizeof(seInfo); seInfo.fMask = SEE_MASK_NOASYNC; diff --git a/src/cascadia/inc/WindowingBehavior.h b/src/cascadia/inc/WindowingBehavior.h index bce36f7a693..04260893191 100644 --- a/src/cascadia/inc/WindowingBehavior.h +++ b/src/cascadia/inc/WindowingBehavior.h @@ -9,3 +9,5 @@ constexpr int32_t WindowingBehaviorUseNew{ -1 }; constexpr int32_t WindowingBehaviorUseExisting{ -2 }; constexpr int32_t WindowingBehaviorUseAnyExisting{ -3 }; constexpr int32_t WindowingBehaviorUseName{ -4 }; + +static constexpr std::wstring_view QuakeWindowName{ L"_quake" }; From 6c424f729302a596795c6f174bd1caceae441b98 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 28 Apr 2021 16:51:54 -0500 Subject: [PATCH 87/88] pr nits --- src/cascadia/Remoting/Peasant.cpp | 4 ++-- src/cascadia/WindowsTerminal/AppHost.cpp | 17 ++++++++++++++--- src/cascadia/WindowsTerminal/IslandWindow.cpp | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index a12110d8662..d7b58b73b8b 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -129,7 +129,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - void Peasant::Summon(const Remoting::SummonWindowBehavior& summonBehavior) { - auto localCopy = winrt::make_self(summonBehavior); + auto localCopy = winrt::make(summonBehavior); TraceLoggingWrite(g_hRemotingProvider, "Peasant_Summon", @@ -137,7 +137,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingUInt64(localCopy->MoveToCurrentDesktop(), "MoveToCurrentDesktop", "true if we should move to the current desktop"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - _SummonRequestedHandlers(*this, *localCopy); + _SummonRequestedHandlers(*this, localCopy); } // Method Description: diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index ac75487996f..3430e69ba28 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -780,13 +780,24 @@ void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*se { if (_LazyLoadDesktopManager()) { - GUID currentlyActiveDesktop; - VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop); - LOG_IF_FAILED(_desktopManager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); + GUID currentlyActiveDesktop{ 0 }; + if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop)) + { + LOG_IF_FAILED(_desktopManager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); + } + // If GetCurrentVirtualDesktopId failed, then just leave the window + // where it is. Nothing else to be done :/ } } } +// Method Description: +// - This gets the GUID of the desktop our window is currently on. It does NOT +// get the GUID of the desktop that's currently active. +// Arguments: +// - +// Return Value: +// - the GUID of the desktop our window is currently on GUID AppHost::_CurrentDesktopGuid() { GUID currentDesktopGuid{ 0 }; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 9015381ec32..a4c249a5bb7 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -958,7 +958,7 @@ void IslandWindow::UnsetHotkeys(const std::vector(hotkeyList.size()); i++) { - LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), static_cast(i))); + LOG_IF_WIN32_BOOL_FALSE(UnregisterHotKey(_window.get(), i)); } } From f4c99936262c654557c3f9e63ad1fac1f66b8c00 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 17 May 2021 06:42:22 -0500 Subject: [PATCH 88/88] Do the windowplacement thing --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 293fd76e850..f74de76ed03 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -1110,7 +1110,7 @@ void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration) // First, restore the window. SetWindowPlacement has a fun undocumented // piece of functionality where it will restore the window position // _without_ the animation, so use that instead of ShowWindow(SW_RESTORE). - WINDOWPLACEMENT wpc; + WINDOWPLACEMENT wpc{}; wpc.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(_window.get(), &wpc); wpc.showCmd = SW_RESTORE; @@ -1126,7 +1126,7 @@ void IslandWindow::_slideUpWindow(const uint32_t dropdownDuration) _doSlideAnimation(dropdownDuration, false); // Then, use SetWindowPlacement to minimize without the animation. - WINDOWPLACEMENT wpc; + WINDOWPLACEMENT wpc{}; wpc.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(_window.get(), &wpc); wpc.showCmd = SW_MINIMIZE;