diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 3ffc52f9fb2..f7671d04f45 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -99,11 +99,9 @@ IImage IInheritable IMap imm -IMonarch IObject iosfwd IPackage -IPeasant isa ISetup isspace diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 9d38873e30c..34bbd331916 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -173,6 +173,7 @@ CCom CConsole CCRT cdd +cds CELLSIZE cfae cfie @@ -280,6 +281,8 @@ contypes conwinuserrefs coordnew COPYCOLOR +COPYDATA +COPYDATASTRUCT CORESYSTEM cotaskmem countof @@ -572,6 +575,7 @@ EOK EPres EQU ERASEBKGND +ERRORONEXIT ESFCIB esrp ESV diff --git a/OpenConsole.sln b/OpenConsole.sln index a92c45a48ac..2534dbe9618 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -178,7 +178,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Control" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj", "{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}" ProjectSection(ProjectDependencies) = postProject - {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} @@ -348,26 +347,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_SettingsModel", " {CA5CAD1A-F542-4635-A069-7CAEFB930070} = {CA5CAD1A-F542-4635-A069-7CAEFB930070} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MonarchPeasantSample", "src\tools\MonarchPeasantSample\MonarchPeasantSample.vcxproj", "{21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}" - ProjectSection(ProjectDependencies) = postProject - {18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263} - EndProjectSection -EndProject -Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "MonarchPeasantPackage", "src\tools\MonarchPeasantPackage\MonarchPeasantPackage.wapproj", "{F75E29D0-D288-478B-8D83-2C190F321A3F}" -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}") = "UnitTests_Remoting", "src\cascadia\UnitTests_Remoting\Remoting.UnitTests.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 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wpf", "wpf", "{4DAF0299-495E-4CD1-A982-9BAC16A45932}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenConsoleProxy", "src\host\proxy\Host.Proxy.vcxproj", "{71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}" @@ -1925,135 +1904,6 @@ 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 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.AuditMode|x64.ActiveCfg = Release|x64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.AuditMode|x86.ActiveCfg = Release|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Debug|ARM64.Build.0 = Debug|ARM64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Debug|x64.ActiveCfg = Debug|x64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Debug|x64.Build.0 = Debug|x64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Debug|x86.ActiveCfg = Debug|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Debug|x86.Build.0 = Debug|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|Any CPU.ActiveCfg = Release|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|ARM64.ActiveCfg = Release|ARM64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|ARM64.Build.0 = Release|ARM64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x64.ActiveCfg = Release|x64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x64.Build.0 = Release|x64 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x86.ActiveCfg = Release|Win32 - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D}.Release|x86.Build.0 = Release|Win32 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|Any CPU.ActiveCfg = Release|Any CPU - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM64.ActiveCfg = Debug|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM64.Build.0 = Debug|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|ARM64.Deploy.0 = Debug|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.ActiveCfg = Debug|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.Build.0 = Debug|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x64.Deploy.0 = Debug|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x86.ActiveCfg = Debug|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x86.Build.0 = Debug|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.AuditMode|x86.Deploy.0 = Debug|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM64.Build.0 = Debug|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|ARM64.Deploy.0 = Debug|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.ActiveCfg = Debug|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.Build.0 = Debug|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x64.Deploy.0 = Debug|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x86.ActiveCfg = Debug|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x86.Build.0 = Debug|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Debug|x86.Deploy.0 = Debug|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Fuzzing|Any CPU.ActiveCfg = Release|Any CPU - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Fuzzing|x64.ActiveCfg = Release|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Fuzzing|x86.ActiveCfg = Release|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM64.ActiveCfg = Release|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM64.Build.0 = Release|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|ARM64.Deploy.0 = Release|ARM64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.ActiveCfg = Release|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.Build.0 = Release|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x64.Deploy.0 = Release|x64 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x86.ActiveCfg = Release|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x86.Build.0 = Release|x86 - {F75E29D0-D288-478B-8D83-2C190F321A3F}.Release|x86.Deploy.0 = Release|x86 - {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|x64.ActiveCfg = Release|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 - {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|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}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {43CE4CE5-0010-4B99-9569-672670D26E26}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {43CE4CE5-0010-4B99-9569-672670D26E26}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {43CE4CE5-0010-4B99-9569-672670D26E26}.Fuzzing|x86.ActiveCfg = Fuzzing|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|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|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|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}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Fuzzing|x86.ActiveCfg = Fuzzing|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|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|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|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}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 - {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 - {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Fuzzing|x86.ActiveCfg = Fuzzing|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|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 {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 @@ -2304,10 +2154,10 @@ Global {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x64.Build.0 = Release|x64 {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.ActiveCfg = Release|Win32 {6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}.Release|x86.Build.0 = Release|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|Any CPU.ActiveCfg = Debug|Win32 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x64.ActiveCfg = Release|x64 - {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x86.ActiveCfg = Release|Win32 + {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|Any CPU.ActiveCfg = AuditMode|x64 + {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.AuditMode|x86.ActiveCfg = AuditMode|Win32 {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|Any CPU.ActiveCfg = Debug|x64 {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|ARM64.ActiveCfg = Debug|ARM64 {7615F03F-E56D-4DB4-B23D-BD4FB80DB36F}.Debug|ARM64.Build.0 = Debug|ARM64 @@ -2457,11 +2307,6 @@ Global {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {77875138-BB08-49F9-8BB1-409C2150E0E1} {CA5CAD1A-082C-4476-9F33-94B339494076} = {77875138-BB08-49F9-8BB1-409C2150E0E1} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {21B7EA5E-1EF8-49B6-AC07-11714AF0E37D} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {F75E29D0-D288-478B-8D83-2C190F321A3F} = {A10C4720-DCA4-4640-9749-67F4314F527C} - {43CE4CE5-0010-4B99-9569-672670D26E26} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE} - {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {2D17E75D-2DDC-42C4-AD70-704D95A937AE} - {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} {4DAF0299-495E-4CD1-A982-9BAC16A45932} = {59840756-302F-44DF-AA47-441A9D673202} {71CC9D78-BA29-4D93-946F-BEF5D9A3A6EF} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} {2D17E75D-2DDC-42C4-AD70-704D95A937AE} = {59840756-302F-44DF-AA47-441A9D673202} diff --git a/build/scripts/New-UnpackagedTerminalDistribution.ps1 b/build/scripts/New-UnpackagedTerminalDistribution.ps1 index 9605f132562..9f8d295be85 100644 --- a/build/scripts/New-UnpackagedTerminalDistribution.ps1 +++ b/build/scripts/New-UnpackagedTerminalDistribution.ps1 @@ -34,7 +34,7 @@ Param( ) $filesToRemove = @("*.xml", "*.winmd", "Appx*", "Images/*Tile*", "Images/*Logo*") # Remove from Terminal -$filesToKeep = @("Microsoft.Terminal.Remoting.winmd") # ... except for these +$filesToKeep = @() # ... except for these $filesToCopyFromXaml = @("Microsoft.UI.Xaml.dll", "Microsoft.UI.Xaml") # We don't need the .winmd $ErrorActionPreference = 'Stop' diff --git a/build/scripts/Test-WindowsTerminalPackage.ps1 b/build/scripts/Test-WindowsTerminalPackage.ps1 index b405d98c212..b492eca05f1 100644 --- a/build/scripts/Test-WindowsTerminalPackage.ps1 +++ b/build/scripts/Test-WindowsTerminalPackage.ps1 @@ -58,7 +58,7 @@ Try { ### Check the activatable class entries for a few DLLs we need. $inProcServers = $Manifest.Package.Extensions.Extension.InProcessServer.Path - $RequiredInProcServers = ("TerminalApp.dll", "Microsoft.Terminal.Control.dll", "Microsoft.Terminal.Remoting.dll", "Microsoft.Terminal.Settings.Editor.dll", "Microsoft.Terminal.Settings.Model.dll", "TerminalConnection.dll") + $RequiredInProcServers = ("TerminalApp.dll", "Microsoft.Terminal.Control.dll", "Microsoft.Terminal.Settings.Editor.dll", "Microsoft.Terminal.Settings.Model.dll", "TerminalConnection.dll") Write-Verbose "InProc Servers: $inProcServers" diff --git a/consolegit2gitfilters.json b/consolegit2gitfilters.json index 3dbcfdc9a20..1d570f24975 100644 --- a/consolegit2gitfilters.json +++ b/consolegit2gitfilters.json @@ -24,8 +24,6 @@ "/doc/specs/", "/doc/cascadia/", "/doc/user-docs/", - "/src/tools/MonarchPeasantSample/", - "/src/tools/MonarchPeasantPackage/", "/src/tools/ansi-color/", "/src/tools/ColorTool/", "/scratch/", diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 46eea7daed8..7834c55e7a9 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2371,11 +2371,6 @@ "description": "When set to true, Windows Terminal will run in the background. This allows globalSummon and quakeMode actions to work even when no windows are open.", "type": "boolean" }, - "compatibility.isolatedMode": { - "default": false, - "description": "When set to true, Terminal windows will not be able to interact with each other (including global hotkeys, tab drag/drop, running commandlines in existing windows, etc.). This is a compatibility escape hatch for users who are running into certain windowing issues.", - "type": "boolean" - }, "compatibility.allowDECRQCRA": { "default": false, "description": "When set to true, the terminal will support the DECRQCRA (Request Checksum of Rectangular Area) escape sequence.", diff --git a/src/Terminal.wprp b/src/Terminal.wprp index 1abe7df41a3..754cc1af260 100644 --- a/src/Terminal.wprp +++ b/src/Terminal.wprp @@ -10,7 +10,6 @@ - @@ -30,7 +29,6 @@ - @@ -51,7 +49,6 @@ - diff --git a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp index f3c0efa463d..ff69ebf51ec 100644 --- a/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp +++ b/src/cascadia/LocalTests_TerminalApp/CommandlineTest.cpp @@ -8,7 +8,6 @@ #include "../TerminalApp/TerminalPage.h" #include "../TerminalApp/AppLogic.h" #include "../TerminalApp/AppCommandlineArgs.h" -#include "../inc/WindowingBehavior.h" using namespace WEX::Logging; using namespace WEX::Common; @@ -74,10 +73,6 @@ namespace TerminalAppLocalTests TEST_METHOD(TestMultipleSplitPaneSizes); - TEST_METHOD(TestFindTargetWindow); - TEST_METHOD(TestFindTargetWindowHelp); - TEST_METHOD(TestFindTargetWindowVersion); - private: void _buildCommandlinesHelper(AppCommandlineArgs& appArgs, const size_t expectedSubcommands, @@ -1934,209 +1929,4 @@ namespace TerminalAppLocalTests } } } - - void CommandlineTest::TestFindTargetWindow() - { - { - Log::Comment(L"wt.exe with no args should always use the value from" - L" the settings (passed as the second argument)."); - - std::vector args{ L"wt.exe" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"-w -1 should always result in a new window"); - - std::vector args{ L"wt.exe", L"-w", L"-1" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"\"new\" should always result in a new window"); - - std::vector args{ L"wt.exe", L"-w", L"new" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"-w with a negative number should always result in a " - L"new window"); - - std::vector args{ L"wt.exe", L"-w", L"-12345" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"-w with a positive number should result in us trying" - L" to either make a new one or find an existing one " - L"with that ID, depending on the provided argument"); - - std::vector args{ L"wt.exe", L"-w", L"12345" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(12345, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(12345, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(12345, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"-w 0 should always use the \"current\" window"); - - std::vector args{ L"wt.exe", L"-w", L"0" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseCurrent, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"-w last should always use the most recent window on " - L"this desktop"); - - std::vector args{ L"wt.exe", L"-w", L"last" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"Make sure we follow the provided argument when a " - L"--window-id wasn't explicitly provided"); - - std::vector args{ L"wt.exe", L"new-tab" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseExisting, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseAnyExisting, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - { - Log::Comment(L"Even if someone uses a subcommand as a window name, " - L"that should work"); - - std::vector args{ L"wt.exe", L"-w", L"new-tab" }; - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId()); - VERIFY_ARE_EQUAL(L"new-tab", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId()); - VERIFY_ARE_EQUAL(L"new-tab", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseName, result.WindowId()); - VERIFY_ARE_EQUAL(L"new-tab", result.WindowName()); - } - } - - void CommandlineTest::TestFindTargetWindowHelp() - { - Log::Comment(L"--help should always create a new window"); - - // This is a little helper to make sure that these args _always_ return - // UseNew, regardless of the windowing behavior. - auto testHelper = [](auto&& args) { - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - }; - - testHelper(std::vector{ L"wt.exe", L"--help" }); - testHelper(std::vector{ L"wt.exe", L"new-tab", L"--help" }); - testHelper(std::vector{ L"wt.exe", L"-w", L"0", L"new-tab", L"--help" }); - testHelper(std::vector{ L"wt.exe", L"-w", L"foo", L"new-tab", L"--help" }); - testHelper(std::vector{ L"wt.exe", L"new-tab", L";", L"--help" }); - } - - void CommandlineTest::TestFindTargetWindowVersion() - { - Log::Comment(L"--version should always create a new window"); - - // This is a little helper to make sure that these args _always_ return - // UseNew, regardless of the windowing behavior. - auto testHelper = [](auto&& args) { - auto result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseNew); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - - result = appImpl::AppLogic::_doFindTargetWindow({ args }, WindowingMode::UseAnyExisting); - VERIFY_ARE_EQUAL(WindowingBehaviorUseNew, result.WindowId()); - VERIFY_ARE_EQUAL(L"", result.WindowName()); - }; - - testHelper(std::vector{ L"wt.exe", L"--version" }); - } } diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index d21497a51d9..477e6dcf0ea 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -1288,12 +1288,6 @@ namespace TerminalAppLocalTests END_TEST_METHOD_PROPERTIES() auto page = _commonSetup(); - page->RenameWindowRequested([&page](auto&&, auto&&) { - // In the real terminal, this would bounce up to the monarch and - // come back down. Instead, immediately call back to tell the terminal it failed. - page->RenameFailed(); - }); - auto windowNameChanged = false; page->PropertyChanged([&page, &windowNameChanged](auto&&, const winrt::WUX::Data::PropertyChangedEventArgs& args) mutable { diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp deleted file mode 100644 index db763169328..00000000000 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#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 -{ - // 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 - // decipher. - void CommandlineArgs::Commandline(const winrt::array_view& value) - { - _args = { value.begin(), value.end() }; - } - - 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 deleted file mode 100644 index 2f618e53efc..00000000000 --- a/src/cascadia/Remoting/CommandlineArgs.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "CommandlineArgs.g.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, - const uint32_t showWindowCommand, - winrt::hstring envString) : - _args{ args.begin(), args.end() }, - _cwd{ currentDirectory }, - _ShowWindowCommand{ showWindowCommand }, - CurrentEnvironment{ envString } - { - } - - winrt::hstring CurrentDirectory() { return _cwd; }; - - void Commandline(const winrt::array_view& value); - winrt::com_array Commandline(); - - til::property CurrentEnvironment; - - WINRT_PROPERTY(uint32_t, ShowWindowCommand, SW_NORMAL); // SW_NORMAL is 1, 0 is SW_HIDE - - private: - winrt::com_array _args; - winrt::hstring _cwd; - }; - -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(CommandlineArgs); -} diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.cpp b/src/cascadia/Remoting/FindTargetWindowArgs.cpp deleted file mode 100644 index 1781c1c9c12..00000000000 --- a/src/cascadia/Remoting/FindTargetWindowArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index b6dfc7638f7..00000000000 --- a/src/cascadia/Remoting/FindTargetWindowArgs.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- FindTargetWindowArgs.h - -Abstract: -- This is a helper class for determining which window a specific commandline is - intended for. The Monarch will create one of these, then toss it over to - TerminalApp. TerminalApp actually contains the logic for parsing a - commandline, as well as settings like the windowing behavior. Once the - TerminalApp determines the correct window, it'll fill in the - ResultTargetWindow property. The monarch will then read that value out to - invoke the commandline in the appropriate window. - ---*/ - -#pragma once - -#include "FindTargetWindowArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct FindTargetWindowArgs : public FindTargetWindowArgsT - { - WINRT_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr); - WINRT_PROPERTY(int, ResultTargetWindow, -1); - WINRT_PROPERTY(winrt::hstring, ResultTargetWindowName); - - public: - FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) : - _Args{ args } {}; - }; -} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj deleted file mode 100644 index b5c7ada4790..00000000000 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ /dev/null @@ -1,135 +0,0 @@ - - - - {43ce4ce5-0010-4b99-9569-672670d26e26} - Win32Proj - Microsoft.Terminal.Remoting.Lib - Microsoft.Terminal.Remoting - Microsoft.Terminal.Remoting.Lib - StaticLibrary - Console - true - true - - - - - - - - Monarch.idl - - - Monarch.idl - - - Monarch.idl - - - Monarch.idl - - - Peasant.idl - - - Peasant.idl - - - Peasant.idl - - - - - Peasant.idl - - - WindowManager.idl - - - Peasant.idl - - - - - - Monarch.idl - - - Monarch.idl - - - Monarch.idl - - - Monarch.idl - - - Peasant.idl - - - Peasant.idl - - - Peasant.idl - - - Create - - - Peasant.idl - - - WindowManager.idl - - - Peasant.idl - - - - - - - - - - - - - - - - - - - - - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} - false - - - - - - - pch.h - - - WindowsApp.lib;user32.lib;shell32.lib;%(AdditionalDependencies) - - - false - - - - - - - - - diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp deleted file mode 100644 index b986bfc8630..00000000000 --- a/src/cascadia/Remoting/Monarch.cpp +++ /dev/null @@ -1,1114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "../inc/WindowingBehavior.h" -#include "Monarch.h" -#include "CommandlineArgs.h" -#include "FindTargetWindowArgs.h" -#include "ProposeCommandlineResult.h" - -#include "Monarch.g.cpp" -#include "WindowRequestedArgs.g.cpp" -#include "../../types/inc/utils.hpp" - -using namespace winrt; -using namespace winrt::Microsoft::Terminal; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - Monarch::Monarch() : - _ourPID{ GetCurrentProcessId() } - { - try - { - _desktopManager = winrt::create_instance(__uuidof(VirtualDesktopManager)); - } - CATCH_LOG(); - } - - // This constructor is intended to be used in unit tests, - // but we need to make it public in order to use make_self - // in the tests. It's not exposed through the idl though - // so it's not _truly_ fully public which should be acceptable. - Monarch::Monarch(const uint64_t testPID) : - _ourPID{ testPID } - { - } - - Monarch::~Monarch() = default; - - uint64_t Monarch::GetPID() - { - 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. - // - NB: this takes a unique_lock on _peasantsMutex. - // Arguments: - // - peasant: the new Peasant to track. - // Return Value: - // - the ID assigned to the peasant. - uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) - { - try - { - // 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 - { - // 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. - // If multiple peasants are added concurrently we keep trying to update - // until we get to set the new id. - uint64_t current; - do - { - current = _nextPeasantID.load(std::memory_order_relaxed); - } while (current <= providedID && !_nextPeasantID.compare_exchange_weak(current, providedID + 1, std::memory_order_relaxed)); - } - - auto newPeasantsId = peasant.GetID(); - - // Keep track of which peasant we are - // SAFETY: this is only true for one peasant, and each peasant - // is only added to a monarch once, so we do not need synchronization here. - if (peasant.GetPID() == _ourPID) - { - _ourPeasantId = newPeasantsId; - } - // Add an event listener to the peasant's WindowActivated event. - peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); - peasant.IdentifyWindowsRequested({ this, &Monarch::_identifyWindows }); - peasant.RenameRequested({ this, &Monarch::_renameRequested }); - - peasant.ShowNotificationIconRequested([this](auto&&, auto&&) { ShowNotificationIconRequested.raise(*this, nullptr); }); - peasant.HideNotificationIconRequested([this](auto&&, auto&&) { HideNotificationIconRequested.raise(*this, nullptr); }); - - { - std::unique_lock lock{ _peasantsMutex }; - _peasants[newPeasantsId] = peasant; - } - - 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), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - WindowCreated.raise(nullptr, nullptr); - return newPeasantsId; - } - catch (...) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_AddPeasant_Failed", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // 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: - // - Gives the host process an opportunity to run any pre-close logic then - // requests all peasants to close. - // Arguments: - // - used - // Return Value: - // - - void Monarch::QuitAll() - { - if (_quitting.exchange(true, std::memory_order_relaxed)) - { - return; - } - - const auto callback = [&](const auto& id, const auto& p) { - // We want to tell our peasant to quit last, so that we don't try - // to perform a bunch of elections on quit. - if (id != _ourPeasantId) - { - p.Quit(); - } - }; - const auto onError = [&](const auto& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_handleQuitAll_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not close"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - - _forEachPeasant(callback, onError); - - { - std::shared_lock lock{ _peasantsMutex }; - const auto peasantSearch = _peasants.find(_ourPeasantId); - if (peasantSearch != _peasants.end()) - { - peasantSearch->second.Quit(); - } - } - } - - // Method Description: - // - Tells the monarch that a peasant is being closed. - // - NB: this (separately) takes unique locks on _peasantsMutex and - // _mruPeasantsMutex. - // Arguments: - // - peasantId: the id of the peasant - // Return Value: - // - - void Monarch::SignalClose(const uint64_t peasantId) - { - // If we are quitting we don't care about maintaining our list of - // peasants anymore, and don't need to notify the host that something - // changed. - if (_quitting.load(std::memory_order_relaxed)) - { - return; - } - - _clearOldMruEntries({ peasantId }); - { - std::unique_lock lock{ _peasantsMutex }; - _peasants.erase(peasantId); - } - WindowClosed.raise(nullptr, nullptr); - } - - // Method Description: - // - Counts the number of living peasants. - // Arguments: - // - - // Return Value: - // - the number of active peasants. - uint64_t Monarch::GetNumberOfPeasants() - { - std::shared_lock lock{ _peasantsMutex }; - return _peasants.size(); - } - - // 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! - // - 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 Remoting::WindowActivatedArgs& args) - { - HandleActivatePeasant(args); - } - - // Method Description: - // - Lookup a peasant by its ID. If the peasant has died, this will also - // remove the peasant from our list of peasants. - // - NB: this (separately) takes unique locks on _peasantsMutex and - // _mruPeasantsMutex. - // Arguments: - // - peasantID: The ID Of the peasant to find - // - clearMruPeasantOnFailure: When true this function will handle clearing - // from _mruPeasants if a peasant was not found, otherwise the caller is - // expected to handle that cleanup themselves. - // Return Value: - // - the peasant if it exists in our map, otherwise null - Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID, bool clearMruPeasantOnFailure) - { - try - { - IPeasant maybeThePeasant = nullptr; - { - std::shared_lock lock{ _peasantsMutex }; - const auto peasantSearch = _peasants.find(peasantID); - 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(); - } - return maybeThePeasant; - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - - // Remove the peasant from the list of peasants - { - std::unique_lock lock{ _peasantsMutex }; - _peasants.erase(peasantID); - } - - if (clearMruPeasantOnFailure) - { - // Remove the peasant from the list of MRU windows. They're dead. - // They can't be the MRU anymore. - _clearOldMruEntries({ peasantID }); - } - return nullptr; - } - } - - // Method Description: - // - Find the ID of the peasant with the given name. If no such peasant - // exists, then we'll return 0. If we encounter any peasants who have died - // during this process, then we'll remove them from the set of _peasants - // Arguments: - // - name: The window name to look for - // Return Value: - // - 0 if we didn't find the given peasant, otherwise a positive number for - // the window's ID. - uint64_t Monarch::_lookupPeasantIdForName(std::wstring_view name) - { - if (name.empty()) - { - return 0; - } - if (name == L"new") - { - return 0; - } - - uint64_t result = 0; - - const auto callback = [&](const auto& id, const auto& p) { - auto otherName = p.WindowName(); - if (otherName == name) - { - result = id; - return false; - } - return true; - }; - - const auto onError = [&](const auto& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_lookupPeasantIdForName_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not get the name of"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - - _forEachPeasant(callback, onError); - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_lookupPeasantIdForName", - TraceLoggingWideString(std::wstring{ name }.c_str(), "name", "the name we're looking for"), - TraceLoggingUInt64(result, "peasantID", "the ID of the peasant with that name"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - return result; - } - - // Method Description: - // - Handler for the `Peasant::WindowActivated` event. We'll make a in-proc - // copy of the WindowActivatedArgs from the peasant. That way, we won't - // need to worry about the origin process dying when working with the - // WindowActivatedArgs. - // - If the peasant process dies while we're making this copy, then we'll - // just log it and do nothing. We certainly don't want to track a dead - // peasant. - // - We'll pass that copy of the WindowActivatedArgs to - // _doHandleActivatePeasant, which will actually insert the - // WindowActivatedArgs into the list we're using to track the most recent - // peasants. - // Arguments: - // - args: the WindowActivatedArgs describing when and where the peasant was activated. - // Return Value: - // - - void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args) - { - if (args == nullptr) - { - // MSFT:35731327, GH #12624. There's a chance that the way the - // window gets set up for defterm, the ActivatedArgs haven't been - // created for this window yet. Check here and just ignore them if - // they're null. They'll come back with real args soon - return; - } - // Start by making a local copy of these args. It's easier for us if our - // tracking of these args is all in-proc. That way, the only thing that - // could fail due to the peasant dying is _this first copy_. - winrt::com_ptr localArgs{ nullptr }; - try - { - localArgs = winrt::make_self(args); - // This method will actually do the hard work - _doHandleActivatePeasant(localArgs); - } - catch (...) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_HandleActivatePeasant_Failed", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - - // Method Description: - // - Helper for removing a peasant from the list of MRU peasants. We want to - // do this both when the peasant dies, and also when the peasant is newly - // activated (so that we don't leave an old entry for it in the list). - // - NB: This takes a unique lock on _mruPeasantsMutex. - // Arguments: - // - peasantIds: The list of peasant IDs to remove from the MRU list - // Return Value: - // - - void Monarch::_clearOldMruEntries(const std::unordered_set& peasantIds) - { - if (peasantIds.size() == 0) - { - return; - } - - std::unique_lock lock{ _mruPeasantsMutex }; - auto partition = std::remove_if(_mruPeasants.begin(), _mruPeasants.end(), [&](const auto& p) { - const auto id = p.PeasantID(); - // remove the element if it was found in the list to erase. - if (peasantIds.count(id) == 1) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_RemovedPeasantFromDesktop", - TraceLoggingUInt64(id, "peasantID", "The ID of the peasant"), - TraceLoggingGuid(p.DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - return true; - } - return false; - }); - - // Remove everything that was in the list - _mruPeasants.erase(partition, _mruPeasants.end()); - } - - // Method Description: - // - Actually handle inserting the WindowActivatedArgs into our list of MRU windows. - // - NB: this takes a unique_lock on _mruPeasantsMutex. - // Arguments: - // - localArgs: an in-proc WindowActivatedArgs that we should add to our list of MRU windows. - // Return Value: - // - - void Monarch::_doHandleActivatePeasant(const winrt::com_ptr& localArgs) - { - // We're sure that localArgs isn't null here, we checked before in our - // one caller (in Monarch::HandleActivatePeasant) - - const auto newLastActiveTime = localArgs->ActivatedTime().time_since_epoch().count(); - - // * Check all the current lists to look for this peasant. - // remove it from any where it exists. - _clearOldMruEntries({ localArgs->PeasantID() }); - - // * If the current desktop doesn't have a vector, add one. - const auto desktopGuid{ localArgs->DesktopID() }; - - { - std::unique_lock lock{ _mruPeasantsMutex }; - // * Add this args list. By using lower_bound with insert, we can get it - // into exactly the right spot, without having to re-sort the whole - // array. - _mruPeasants.insert(std::lower_bound(_mruPeasants.begin(), - _mruPeasants.end(), - *localArgs, - [](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }), - *localArgs); - } - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_SetMostRecentPeasant", - TraceLoggingUInt64(localArgs->PeasantID(), "peasantID", "the ID of the activated peasant"), - TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), - TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided localArgs->ActivatedTime()"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - // Method Description: - // - Retrieves the ID of the MRU peasant window. If requested, will limit - // the search to windows that are on the current desktop. - // - NB: This method will hold a shared lock on _mruPeasantsMutex and - // potentially a unique_lock on _peasantsMutex at the same time. - // Separately it might hold a unique_lock on _mruPeasantsMutex. - // Arguments: - // - limitToCurrentDesktop: if true, only return the MRU peasant that's - // actually on the current desktop. - // - ignoreQuakeWindow: if true, then don't return the _quake window when we - // find it. This allows us to change our behavior for glomming vs - // summoning. When summoning the window, this parameter should be true. - // When glomming, this should be false, as to prevent glomming to the - // _quake window. - // Return Value: - // - the ID of the most recent peasant, otherwise 0 if we could not find one. - uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop, const bool ignoreQuakeWindow) - { - std::shared_lock lock{ _mruPeasantsMutex }; - if (_mruPeasants.empty()) - { - // unlock the mruPeasants mutex to make sure we can't deadlock here. - lock.unlock(); - // Only need a shared lock for read - std::shared_lock peasantsLock{ _peasantsMutex }; - // 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(); - } - 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; - } - } - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_getMostRecentPeasantID_NoPeasants", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - return 0; - } - - // Here, there's at least one MRU peasant. - // - // We're going to iterate over these peasants until we find one that both: - // 1. Is alive - // 2. Meets our selection criteria (do we care if it is on this desktop?) - // - // If the peasant is dead, then we'll remove it, and try the next one. - // Once we find one that's alive, we'll either: - // * check if we only want a peasant on the current desktop, and if so, - // check if this peasant is on the current desktop. - // - If it isn't on the current desktop, we'll loop again, on the - // following peasant. - // * If we don't care, then we'll just return that one. - uint64_t result = 0; - std::unordered_set peasantsToErase{}; - for (const auto& mruWindowArgs : _mruPeasants) - { - // Try to get the peasant, but do not have _getPeasant clean up old - // _mruPeasants because we are iterating here. - // SAFETY: _getPeasant can take a unique_lock on _peasantsMutex if - // it detects a peasant is dead. Currently _getMostRecentPeasantId - // is the only method that holds a lock on both _mruPeasantsMutex and - // _peasantsMutex at the same time so there cannot be a deadlock here. - const auto peasant{ _getPeasant(mruWindowArgs.PeasantID(), false) }; - if (!peasant) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_Collect_WasDead", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), - "peasantID", - "We thought this peasant was the MRU one, but it was actually already dead."), - TraceLoggingGuid(mruWindowArgs.DesktopID(), "desktopGuid", "The GUID of the desktop the window is on"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - // We'll go through the loop again. We removed the current one - // at positionInList, so the next one in positionInList will be - // a new, different peasant. - peasantsToErase.emplace(mruWindowArgs.PeasantID()); - continue; - } - - if (ignoreQuakeWindow && 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 - // _quake`, which will hit `_lookupPeasantIdForName` instead. - } - else if (limitToCurrentDesktop && _desktopManager) - { - // Check if this peasant is actually on this desktop. We can't - // simply get the GUID of the current desktop. We have to ask if - // the HWND is on the current desktop. - BOOL onCurrentDesktop{ false }; - - // SUCCEEDED_LOG will log if it failed, and return true if it SUCCEEDED - if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop(reinterpret_cast(mruWindowArgs.Hwnd()), - &onCurrentDesktop)) && - onCurrentDesktop) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_Collect", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), - "peasantID", - "the ID of the MRU peasant for a desktop"), - TraceLoggingGuid(mruWindowArgs.DesktopID(), - "desktopGuid", - "The GUID of the desktop the window is on"), - TraceLoggingBoolean(limitToCurrentDesktop, - "limitToCurrentDesktop", - "True if we should only search for a window on the current desktop"), - TraceLoggingBool(onCurrentDesktop, - "onCurrentDesktop", - "true if this window was in fact on the current desktop"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - result = mruWindowArgs.PeasantID(); - break; - } - // If this window wasn't on the current desktop, another one - // might be. We'll increment positionInList below, and try - // again. - } - else - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_getMostRecentPeasantID_Found", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "The ID of the MRU peasant"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - result = mruWindowArgs.PeasantID(); - break; - } - } - - lock.unlock(); - - if (peasantsToErase.size() > 0) - { - _clearOldMruEntries(peasantsToErase); - } - - if (result == 0) - { - // Here, we've checked all the windows, and none of them was both alive - // and the most recent (on this desktop). Just return 0 - the caller - // will use this to create a new window. - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_getMostRecentPeasantID_NotFound", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - return result; - } - - // 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 themselves. - // Arguments: - // - - // Return Value: - // - 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. - 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 - // dependency hell). The WindowManager will raise this up to the app - // host, which will then ask the AppLogic, who will then parse the - // commandline and determine the provided ID of the window. - auto findWindowArgs{ winrt::make_self(args) }; - - // This is handled by some handler in-proc - FindTargetWindowRequested.raise(*this, *findWindowArgs); - - // After the event was handled, ResultTargetWindow() will be filled with - // the parsed result. - const auto targetWindow = findWindowArgs->ResultTargetWindow(); - const auto targetWindowName = findWindowArgs->ResultTargetWindowName(); - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_ProposeCommandline", - TraceLoggingInt64(targetWindow, "targetWindow", "The window ID the args specified"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - if (targetWindow == WindowingBehaviorUseNone) - { - // In this case, the targetWindow was UseNone, which means that we - // want to make a message box, but otherwise not make a Terminal - // window. - return winrt::make(false); - } - // If there's a valid ID returned, then let's try and find the peasant - // that goes with it. Alternatively, if we were given a magic windowing - // constant, we can use that to look up an appropriate peasant. - if (targetWindow >= 0 || - targetWindow == WindowingBehaviorUseName || - targetWindow == WindowingBehaviorUseExisting || - targetWindow == WindowingBehaviorUseAnyExisting) - { - uint64_t windowID = 0; - switch (targetWindow) - { - case WindowingBehaviorUseCurrent: - case WindowingBehaviorUseExisting: - // TODO:projects/5 for now, just use the MRU window. Technically, - // UseExisting and UseCurrent are different. - // UseCurrent implies that we should try to do the WT_SESSION - // lookup to find the window that spawned this process (then - // fall back to sameDesktop if we can't find a match). For now, - // it's good enough to just try to find a match on this desktop. - // - // GH#projects/5#card-60325142 - Don't try to glom to the quake window. - windowID = _getMostRecentPeasantID(true, true); - break; - case WindowingBehaviorUseAnyExisting: - windowID = _getMostRecentPeasantID(false, true); - break; - case WindowingBehaviorUseName: - windowID = _lookupPeasantIdForName(targetWindowName); - break; - case WindowingBehaviorUseNone: - // This should be impossible. The if statement above should have - // prevented WindowingBehaviorUseNone from falling in here. - // Explode, because this is a programming error. - THROW_HR(E_UNEXPECTED); - default: - windowID = ::base::saturated_cast(targetWindow); - break; - } - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_ProposeCommandline", - TraceLoggingInt64(windowID, - "windowID", - "The actual peasant ID we evaluated the window ID as"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // If_getMostRecentPeasantID returns 0 above, then we couldn't find - // a matching window for that style of windowing. _getPeasant will - // return nullptr, and we'll fall through to the "create a new - // window" branch below. - - if (auto targetPeasant{ _getPeasant(windowID) }) - { - auto result{ winrt::make_self(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->WindowName(targetWindowName); - result->ShouldCreateWindow(true); - - RequestNewWindow.raise(*this, *winrt::make_self(*result, args)); - - // If this fails, it'll be logged in the following - // TraceLoggingWrite statement, with succeeded=false - } - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_ProposeCommandline_Existing", - 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), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - return *result; - } - else if (windowID > 0) - { - // 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_. - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_ProposeCommandline_Existing", - 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), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - auto result{ winrt::make_self(true) }; - result->Id(windowID); - result->WindowName(targetWindowName); - - RequestNewWindow.raise(*this, *winrt::make_self(*result, args)); - - return *result; - } - } - - // If we get here, we couldn't find an existing window. Make a new one. - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_ProposeCommandline_NewWindow", - TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // In this case, no usable ID was provided. Return { true, nullopt } - auto result = winrt::make_self(true); - result->WindowName(targetWindowName); - - RequestNewWindow.raise(*this, *winrt::make_self(*result, args)); - - return *result; - } - - // Method Description: - // - This is an event handler for the IdentifyWindowsRequested event. A - // Peasant may raise that event if they want _all_ windows to identify - // themselves. - // - This will tell each and every peasant to identify themselves. This will - // eventually propagate down to TerminalPage::IdentifyWindow. - // Arguments: - // - - // Return Value: - // - - void Monarch::_identifyWindows(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Windows::Foundation::IInspectable& /*args*/) - { - // Notify all the peasants to display their ID. - const auto callback = [&](const auto& /*id*/, const auto& p) { - p.DisplayWindowId(); - }; - const auto onError = [&](const auto& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_identifyWindows_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - - _forEachPeasant(callback, onError); - } - - // Method Description: - // - This is an event handler for the RenameRequested event. A - // Peasant may raise that event when they want to be renamed to something else. - // - We will check if there are any other windows with this name. If there - // are, then we'll reject the rename by setting args.Succeeded=false. - // - If there aren't any other windows with this name, then we'll set - // args.Succeeded=true, allowing the window to keep this name. - // Arguments: - // - args: Contains the requested window name and a boolean (Succeeded) - // indicating if the request was successful. - // Return Value: - // - - void Monarch::_renameRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args) - { - auto successfullyRenamed = false; - - try - { - args.Succeeded(false); - const auto name{ args.NewName() }; - // Try to find a peasant that currently has this name - const auto id = _lookupPeasantIdForName(name); - if (_getPeasant(id) == nullptr) - { - // If there is one, then oh no! The requestor is not allowed to - // be renamed. - args.Succeeded(true); - successfullyRenamed = true; - } - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_renameRequested", - TraceLoggingWideString(name.c_str(), "name", "The newly proposed name"), - TraceLoggingInt64(successfullyRenamed, "successfullyRenamed", "true if the peasant is allowed to rename themselves to that name."), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - // If this fails, we don't _really_ care. The peasant died, but - // they're the only one who cares about the result. - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_renameRequested_Failed", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - - // 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); - - // If a WindowID is provided from the args, use that first. - uint64_t windowId = 0; - if (args.WindowID()) - { - windowId = args.WindowID().Value(); - } - else - { - // If no name was provided, then just summon the MRU window. - if (searchedForName.empty()) - { - // 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(), false); - } - else - { - // Try to find a peasant that currently has this name - windowId = _lookupPeasantIdForName(searchedForName); - } - } - - if (auto targetPeasant{ _getPeasant(windowId) }) - { - targetPeasant.Summon(args.SummonBehavior()); - args.FoundMatch(true); - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_SummonWindow_Success", - TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"), - TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"), - TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"), - TraceLoggingBoolean(args.SummonBehavior().MoveToCurrentDesktop(), "MoveToCurrentDesktop", "if true, move the window to the current virtual desktop"), - TraceLoggingBoolean(args.SummonBehavior().ToggleVisibility(), "ToggleVisibility", "true if we should toggle the visibility of the window"), - TraceLoggingUInt32(args.SummonBehavior().DropdownDuration(), "DropdownDuration", "the duration to dropdown the window"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - else - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_SummonWindow_NoPeasant", - TraceLoggingWideString(searchedForName.c_str(), "searchedForName", "The name of the window we tried to summon"), - TraceLoggingUInt64(windowId, "peasantID", "The id of the window we tried to summon"), - TraceLoggingBoolean(args.OnCurrentDesktop(), "OnCurrentDesktop", "true iff the window needs to be on the current virtual desktop"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - 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), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - - // Method Description: - // - This method creates a map of peasant IDs to peasant names - // while removing dead peasants. - // Arguments: - // - - // Return Value: - // - A map of peasant IDs to their names. - Windows::Foundation::Collections::IVectorView Monarch::GetPeasantInfos() - { - std::vector names; - { - std::shared_lock lock{ _peasantsMutex }; - names.reserve(_peasants.size()); - } - - const auto func = [&](const auto& id, const auto& p) -> void { - names.push_back({ id, p.WindowName(), p.ActiveTabTitle() }); - }; - - const auto onError = [&](const auto& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_identifyWindows_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - - _forEachPeasant(func, onError); - - return winrt::single_threaded_vector(std::move(names)).GetView(); - } - - bool Monarch::DoesQuakeWindowExist() - { - auto result = false; - const auto func = [&](const auto& /*id*/, const auto& p) { - if (p.WindowName() == QuakeWindowName) - { - result = true; - } - // continue if we didn't get a positive result - return !result; - }; - - const auto onError = [&](const auto& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_DoesQuakeWindowExist_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not ask for its name"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - - _forEachPeasant(func, onError); - return result; - } - - void Monarch::SummonAllWindows() - { - const auto func = [&](const auto& /*id*/, const auto& p) { - SummonWindowBehavior args{}; - args.ToggleVisibility(false); - p.Summon(args); - }; - - const auto onError = [&](const auto& id) { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_SummonAll_Failed", - TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - }; - - _forEachPeasant(func, onError); - } - - void Monarch::RequestMoveContent(winrt::hstring window, - winrt::hstring content, - uint32_t tabIndex, - const Windows::Foundation::IReference& windowBounds) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_MoveContent_Requested", - TraceLoggingWideString(window.c_str(), "window", "The name of the window we tried to move to"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - uint64_t windowId = _lookupPeasantIdForName(window); - if (windowId == 0) - { - // Try the name as an integer ID - uint32_t temp; - if (!Utils::StringToUint(window.c_str(), temp)) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_MoveContent_FailedToParseId", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - else - { - windowId = temp; - } - } - - if (auto targetPeasant{ _getPeasant(windowId) }) - { - auto request = winrt::make_self(content, tabIndex); - targetPeasant.AttachContentToWindow(*request); - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_MoveContent_Completed", - TraceLoggingInt64(windowId, "windowId", "The ID of the peasant which we sent the content to"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - else - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_MoveContent_NoWindow", - TraceLoggingInt64(windowId, "windowId", "We could not find a peasant with this ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // In the case where window couldn't be found, then create a window - // for that name / ID. - // - // Don't let the window literally be named "-1", because that's silly. Same with "new" - const bool nameIsReserved = window == L"-1" || window == L"new"; - auto request = winrt::make_self(nameIsReserved ? L"" : window, - content, - windowBounds); - RequestNewWindow.raise(*this, *request); - } - } - - // Very similar to the above. Someone came and told us that they were the target of a drag/drop, and they know who started it. - // We will go tell the person who started it that they should send that target the content which was dragged. - void Monarch::RequestSendContent(const Remoting::RequestReceiveContentArgs& args) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_SendContent_Requested", - TraceLoggingUInt64(args.SourceWindow(), "source", "The window which started the drag"), - TraceLoggingUInt64(args.TargetWindow(), "target", "The window which was the target of the drop"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - if (auto senderPeasant{ _getPeasant(args.SourceWindow()) }) - { - senderPeasant.SendContent(args); - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_SendContent_Completed", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - else - { - // We couldn't find the peasant that started the drag. Well that - // sure is weird, but that would indicate that the sender closed - // after starting the drag. No matter. We can just do nothing. - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_SendContent_NoWindow", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } -} diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h deleted file mode 100644 index 4cc74d0c5bd..00000000000 --- a/src/cascadia/Remoting/Monarch.h +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -#include "Monarch.g.h" -#include "Peasant.h" -#include "WindowActivatedArgs.h" -#include "WindowRequestedArgs.g.h" -#include - -// We sure different GUIDs here depending on whether we're running a Release, -// 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} -// * Canary: {09222022-7eb1-4f3e-85f5-8bdd7386cce3} -// * Dev: {08302020-7eb1-4f3e-85f5-8bdd7386cce3} -constexpr GUID Monarch_clsid -{ -#if defined(WT_BRANDING_RELEASE) - 0x06171993, -#elif defined(WT_BRANDING_PREVIEW) - 0x04221993, -#elif defined(WT_BRANDING_CANARY) - 0x09222022, -#else - 0x08302020, -#endif - 0x7eb1, - 0x4f3e, - { - 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe4 - } -}; - -namespace RemotingUnitTests -{ - class RemotingTests; -}; - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct WindowRequestedArgs : public WindowRequestedArgsT - { - public: - WindowRequestedArgs(const Remoting::ProposeCommandlineResult& windowInfo, const Remoting::CommandlineArgs& command) : - _Id{ windowInfo.Id() ? windowInfo.Id().Value() : 0 }, // We'll use 0 as a sentinel, since no window will ever get to have that ID - _WindowName{ windowInfo.WindowName() }, - _args{ command.Commandline() }, - _CurrentDirectory{ command.CurrentDirectory() }, - _ShowWindowCommand{ command.ShowWindowCommand() }, - _CurrentEnvironment{ command.CurrentEnvironment() } {}; - - WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content, const Windows::Foundation::IReference& bounds) : - _Id{ 0u }, - _WindowName{ window }, - _args{}, - _CurrentDirectory{}, - _Content{ content }, - _InitialBounds{ bounds } {}; - - void Commandline(const winrt::array_view& value) { _args = { value.begin(), value.end() }; }; - winrt::com_array Commandline() { return winrt::com_array{ _args.begin(), _args.end() }; } - - WINRT_PROPERTY(uint64_t, Id); - WINRT_PROPERTY(winrt::hstring, WindowName); - WINRT_PROPERTY(winrt::hstring, CurrentDirectory); - WINRT_PROPERTY(winrt::hstring, Content); - WINRT_PROPERTY(uint32_t, ShowWindowCommand, SW_NORMAL); - WINRT_PROPERTY(winrt::hstring, CurrentEnvironment); - WINRT_PROPERTY(Windows::Foundation::IReference, InitialBounds); - - private: - winrt::com_array _args; - }; - - struct Monarch : public MonarchT - { - Monarch(); - Monarch(const uint64_t testPID); - ~Monarch(); - - uint64_t GetPID(); - - uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); - void SignalClose(const uint64_t peasantId); - void QuitAll(); - - uint64_t GetNumberOfPeasants(); - - winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); - void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); - - void SummonAllWindows(); - bool DoesQuakeWindowExist(); - Windows::Foundation::Collections::IVectorView GetPeasantInfos(); - - void RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, const Windows::Foundation::IReference& windowBounds); - void RequestSendContent(const Remoting::RequestReceiveContentArgs& args); - - til::typed_event FindTargetWindowRequested; - til::typed_event<> ShowNotificationIconRequested; - til::typed_event<> HideNotificationIconRequested; - til::typed_event<> WindowCreated; - til::typed_event<> WindowClosed; - - til::typed_event RequestNewWindow; - - private: - uint64_t _ourPID; - - std::atomic _nextPeasantID{ 1 }; - uint64_t _ourPeasantId{ 0 }; - - // When we're quitting we do not care as much about handling some events that we know will be triggered - std::atomic _quitting{ false }; - - winrt::com_ptr _desktopManager{ nullptr }; - - std::unordered_map _peasants; - std::vector _mruPeasants; - // These should not be locked at the same time to prevent deadlocks - // unless they are both shared_locks. - std::shared_mutex _peasantsMutex{}; - std::shared_mutex _mruPeasantsMutex{}; - - winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID, bool clearMruPeasantOnFailure = true); - uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop, const bool ignoreQuakeWindow); - uint64_t _lookupPeasantIdForName(std::wstring_view name); - - void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); - void _doHandleActivatePeasant(const winrt::com_ptr& args); - void _clearOldMruEntries(const std::unordered_set& peasantIds); - - void _forAllPeasantsIgnoringTheDead(std::function callback, - std::function errorCallback); - - void _identifyWindows(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); - - void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); - - void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); - - // Method Description: - // - Helper for doing something on each and every peasant. - // - We'll try calling func on every peasant. - // - If the return type of func is void, it will perform it for all peasants. - // - If the return type is a boolean, we'll break out of the loop once func - // returns false. - // - If any single peasant is dead, then we'll call onError and then add it to a - // list of peasants to clean up once the loop ends. - // - NB: this (separately) takes unique locks on _peasantsMutex and - // _mruPeasantsMutex. - // Arguments: - // - func: The function to call on each peasant - // - onError: The function to call if a peasant is dead. - // Return Value: - // - - template - void _forEachPeasant(F&& func, T&& onError) - { - using Map = decltype(_peasants); - using R = std::invoke_result_t; - static constexpr auto IsVoid = std::is_void_v; - - std::unordered_set peasantsToErase; - { - std::shared_lock lock{ _peasantsMutex }; - - for (const auto& [id, p] : _peasants) - { - try - { - if constexpr (IsVoid) - { - func(id, p); - } - else - { - if (!func(id, p)) - { - break; - } - } - } - catch (const winrt::hresult_error& exception) - { - onError(id); - - if (exception.code() == 0x800706ba) // The RPC server is unavailable. - { - peasantsToErase.emplace(id); - } - else - { - LOG_CAUGHT_EXCEPTION(); - throw; - } - } - } - } - - if (peasantsToErase.size() > 0) - { - // Don't hold a lock on _peasants and _mruPeasants at the same - // time to avoid deadlocks. - { - std::unique_lock lock{ _peasantsMutex }; - for (const auto& id : peasantsToErase) - { - _peasants.erase(id); - } - } - _clearOldMruEntries(peasantsToErase); - - // A peasant died, let the app host know that the number of - // windows has changed. - WindowClosed.raise(nullptr, nullptr); - } - } - - friend class RemotingUnitTests::RemotingTests; - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(Monarch); - BASIC_FACTORY(WindowRequestedArgs); -} diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl deleted file mode 100644 index ba00cf47cfe..00000000000 --- a/src/cascadia/Remoting/Monarch.idl +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import "Peasant.idl"; - -namespace Microsoft.Terminal.Remoting -{ - - [default_interface] runtimeclass FindTargetWindowArgs { - CommandlineArgs Args { get; }; - Int32 ResultTargetWindow; - String ResultTargetWindowName; - } - - [default_interface] runtimeclass ProposeCommandlineResult { - Windows.Foundation.IReference Id { get; }; - String WindowName { get; }; - Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode - } - - [default_interface] runtimeclass WindowRequestedArgs { - WindowRequestedArgs(ProposeCommandlineResult windowInfo, CommandlineArgs command); - - UInt64 Id { get; }; - String WindowName { get; }; - - String[] Commandline { get; }; - String CurrentDirectory { get; }; - UInt32 ShowWindowCommand { get; }; - String CurrentEnvironment { get; }; - - String Content { get; }; - Windows.Foundation.IReference InitialBounds { get; }; - } - - [default_interface] runtimeclass SummonWindowSelectionArgs { - SummonWindowSelectionArgs(); - SummonWindowSelectionArgs(String windowName); - String WindowName; - Boolean OnCurrentDesktop; - // TODO GH#8888 Other options: - // * CurrentMonitor - - Boolean FoundMatch; - SummonWindowBehavior SummonBehavior; - Windows.Foundation.IReference WindowID; - } - - struct PeasantInfo - { - UInt64 Id; - String Name; - String TabTitle; - }; - - interface IMonarch - { - - UInt64 GetPID(); - UInt64 AddPeasant(IPeasant peasant); - UInt64 GetNumberOfPeasants(); - ProposeCommandlineResult ProposeCommandline(CommandlineArgs args); - void HandleActivatePeasant(WindowActivatedArgs args); - void SummonWindow(SummonWindowSelectionArgs args); - void SignalClose(UInt64 peasantId); - void QuitAll(); - - void SummonAllWindows(); - Boolean DoesQuakeWindowExist(); - Windows.Foundation.Collections.IVectorView GetPeasantInfos { get; }; - - void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference bounds); - void RequestSendContent(RequestReceiveContentArgs args); - - event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; - event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; - event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; - event Windows.Foundation.TypedEventHandler WindowCreated; - event Windows.Foundation.TypedEventHandler WindowClosed; - - event Windows.Foundation.TypedEventHandler RequestNewWindow; - }; - - runtimeclass Monarch : [default] IMonarch - { - Monarch(); - }; -} diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h deleted file mode 100644 index 496a6094686..00000000000 --- a/src/cascadia/Remoting/MonarchFactory.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#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. - -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) - { - return CLASS_E_NOAGGREGATION; - } - - // 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 - 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! - 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 deleted file mode 100644 index e09f5ac67ee..00000000000 --- a/src/cascadia/Remoting/Peasant.cpp +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "Peasant.h" -#include "CommandlineArgs.h" -#include "SummonWindowBehavior.h" -#include "Peasant.g.cpp" -#include "../../types/inc/utils.hpp" -#include "AttachRequest.g.cpp" -#include "RequestReceiveContentArgs.g.cpp" - -using namespace winrt; -using namespace winrt::Microsoft::Terminal; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - Peasant::Peasant() : - _ourPID{ GetCurrentProcessId() } - { - } - - // This constructor is intended to be used in unit tests, - // but we need to make it public in order to use make_self - // in the tests. It's not exposed through the idl though - // so it's not _truly_ fully public which should be acceptable. - Peasant::Peasant(const uint64_t testPID) : - _ourPID{ testPID } - { - } - - void Peasant::AssignID(uint64_t id) - { - _id = id; - } - - uint64_t Peasant::GetID() - { - return _id; - } - - uint64_t Peasant::GetPID() - { - return _ourPID; - } - - 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; - } - - TraceLoggingWrite(g_hRemotingProvider, - "Peasant_ExecuteCommandline", - TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), - TraceLoggingWideString(args.CurrentDirectory().c_str(), "directory", "the provided cwd"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // 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. - ExecuteCommandlineRequested.raise(*this, args); - - return true; - } - - Remoting::CommandlineArgs Peasant::InitialArgs() - { - return _initialArgs; - } - - 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; - - auto successfullyNotified = false; - // Raise our WindowActivated event, to let the monarch know we've been - // activated. - try - { - // Try/catch this, because the other side of this event is handled - // by the monarch. The monarch might have died. If they have, this - // will throw an exception. Just eat it, the election thread will - // handle hooking up the new one. - WindowActivated.raise(*this, args); - successfullyNotified = true; - } - catch (...) - { - 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), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - // 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; - } - - // 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(const Remoting::SummonWindowBehavior& summonBehavior) - { - auto localCopy = winrt::make(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), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - SummonRequested.raise(*this, localCopy); - } - - // Method Description: - // - Tell this window to display its window ID. We'll raise a - // DisplayWindowIdRequested event, which will get handled in the AppHost, - // and used to tell the app to display the ID toast. - // Arguments: - // - - // Return Value: - // - - void Peasant::DisplayWindowId() - { - // Not worried about try/catching this. The handler is in AppHost, which - // is in-proc for us. - DisplayWindowIdRequested.raise(*this, nullptr); - } - - // Method Description: - // - Raises an event to ask that all windows be identified. This will come - // back to us when the Monarch handles the event and calls our - // DisplayWindowId method. - // Arguments: - // - - // Return Value: - // - - void Peasant::RequestIdentifyWindows() - { - auto successfullyNotified = false; - - try - { - // Try/catch this, because the other side of this event is handled - // by the monarch. The monarch might have died. If they have, this - // will throw an exception. Just eat it, the election thread will - // handle hooking up the new one. - IdentifyWindowsRequested.raise(*this, nullptr); - successfullyNotified = true; - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - } - TraceLoggingWrite(g_hRemotingProvider, - "Peasant_RequestIdentifyWindows", - TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), - TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - void Peasant::RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args) - { - auto successfullyNotified = false; - const auto oldName{ _WindowName }; - try - { - // Try/catch this, because the other side of this event is handled - // by the monarch. The monarch might have died. If they have, this - // will throw an exception. Just eat it, the election thread will - // handle hooking up the new one. - RenameRequested.raise(*this, args); - if (args.Succeeded()) - { - _WindowName = args.NewName(); - } - successfullyNotified = true; - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - } - TraceLoggingWrite(g_hRemotingProvider, - "Peasant_RequestRename", - TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), - TraceLoggingWideString(oldName.c_str(), "oldName", "Our old name"), - TraceLoggingWideString(args.NewName().c_str(), "newName", "The proposed name"), - TraceLoggingBoolean(args.Succeeded(), "succeeded", "true if the monarch ok'd this new name for us."), - TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - void Peasant::RequestShowNotificationIcon() - { - try - { - ShowNotificationIconRequested.raise(*this, nullptr); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - } - TraceLoggingWrite(g_hRemotingProvider, - "Peasant_RequestShowNotificationIcon", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - void Peasant::RequestHideNotificationIcon() - { - try - { - HideNotificationIconRequested.raise(*this, nullptr); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - } - TraceLoggingWrite(g_hRemotingProvider, - "Peasant_RequestHideNotificationIcon", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - void Peasant::AttachContentToWindow(Remoting::AttachRequest request) - { - try - { - AttachRequested.raise(*this, request); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - } - TraceLoggingWrite(g_hRemotingProvider, - "Peasant_AttachContentToWindow", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - void Peasant::Quit() - { - try - { - QuitRequested.raise(*this, nullptr); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - } - TraceLoggingWrite(g_hRemotingProvider, - "Peasant_Quit", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - void Peasant::SendContent(const Remoting::RequestReceiveContentArgs& args) - { - SendContentRequested.raise(*this, args); - } -} diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h deleted file mode 100644 index 1291c8287b9..00000000000 --- a/src/cascadia/Remoting/Peasant.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -#include "Peasant.g.h" -#include "RenameRequestArgs.h" -#include "AttachRequest.g.h" -#include "RequestReceiveContentArgs.g.h" - -namespace RemotingUnitTests -{ - class RemotingTests; -}; -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct AttachRequest : public AttachRequestT - { - WINRT_PROPERTY(winrt::hstring, Content); - WINRT_PROPERTY(uint32_t, TabIndex); - - public: - AttachRequest(winrt::hstring content, - uint32_t tabIndex) : - _Content{ content }, - _TabIndex{ tabIndex } {}; - }; - - struct RequestReceiveContentArgs : RequestReceiveContentArgsT - { - WINRT_PROPERTY(uint64_t, SourceWindow); - WINRT_PROPERTY(uint64_t, TargetWindow); - WINRT_PROPERTY(uint32_t, TabIndex); - - public: - RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) : - _SourceWindow{ src }, - _TargetWindow{ tgt }, - _TabIndex{ tabIndex } {}; - }; - - struct Peasant : public PeasantT - { - Peasant(); - - void AssignID(uint64_t id); - uint64_t GetID(); - uint64_t GetPID(); - - bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); - - void Summon(const Remoting::SummonWindowBehavior& summonBehavior); - void RequestIdentifyWindows(); - void DisplayWindowId(); - void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); - void RequestShowNotificationIcon(); - void RequestHideNotificationIcon(); - void Quit(); - - void AttachContentToWindow(Remoting::AttachRequest request); - - winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); - - winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); - - void SendContent(const winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs& args); - - til::typed_event WindowActivated; - til::typed_event ExecuteCommandlineRequested; - til::typed_event<> IdentifyWindowsRequested; - til::typed_event<> DisplayWindowIdRequested; - til::typed_event RenameRequested; - til::typed_event SummonRequested; - - til::typed_event<> ShowNotificationIconRequested; - til::typed_event<> HideNotificationIconRequested; - til::typed_event<> QuitRequested; - - til::typed_event AttachRequested; - til::typed_event SendContentRequested; - - WINRT_PROPERTY(winrt::hstring, WindowName); - WINRT_PROPERTY(winrt::hstring, ActiveTabTitle); - - private: - Peasant(const uint64_t testPID); - uint64_t _ourPID; - - uint64_t _id{ 0 }; - - winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; - winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs _lastActivatedArgs{ nullptr }; - - friend class RemotingUnitTests::RemotingTests; - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(Peasant); - BASIC_FACTORY(RequestReceiveContentArgs); -} diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl deleted file mode 100644 index 3e01e6a6d3f..00000000000 --- a/src/cascadia/Remoting/Peasant.idl +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -namespace Microsoft.Terminal.Remoting -{ - - runtimeclass CommandlineArgs - { - CommandlineArgs(); - CommandlineArgs(String[] args, String cwd, UInt32 showWindowCommand, String env); - - String[] Commandline { get; set; }; - String CurrentDirectory { get; }; - UInt32 ShowWindowCommand { get; }; - String CurrentEnvironment { get; }; - }; - - runtimeclass RenameRequestArgs - { - RenameRequestArgs(String newName); - String NewName { get; }; - Boolean Succeeded; - }; - - runtimeclass WindowActivatedArgs - { - WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime); - WindowActivatedArgs(UInt64 peasantID, UInt64 hwnd, Guid desktopID, Windows.Foundation.DateTime activatedTime); - UInt64 PeasantID { get; }; - UInt64 Hwnd { get; }; - Guid DesktopID { get; }; - Windows.Foundation.DateTime ActivatedTime { get; }; - }; - - enum MonitorBehavior - { - InPlace, - ToCurrent, - ToMouse, - }; - - [default_interface] runtimeclass SummonWindowBehavior { - SummonWindowBehavior(); - Boolean MoveToCurrentDesktop; - Boolean ToggleVisibility; - UInt32 DropdownDuration; - MonitorBehavior ToMonitor; - } - - [default_interface] runtimeclass AttachRequest { - String Content { get; }; - UInt32 TabIndex { get; }; - } - [default_interface] runtimeclass RequestReceiveContentArgs { - RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex); - - UInt64 SourceWindow { get; }; - UInt64 TargetWindow { get; }; - UInt32 TabIndex { get; }; - }; - - interface IPeasant - { - CommandlineArgs InitialArgs { get; }; - - void AssignID(UInt64 id); - UInt64 GetID(); - UInt64 GetPID(); - Boolean ExecuteCommandline(CommandlineArgs args); - void ActivateWindow(WindowActivatedArgs args); - WindowActivatedArgs GetLastActivatedArgs(); - - void DisplayWindowId(); // Tells us to display its own ID (which causes a DisplayWindowIdRequested to be raised) - - String WindowName { get; }; - String ActiveTabTitle { get; }; - void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested - void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested - void Summon(SummonWindowBehavior behavior); - - void RequestShowNotificationIcon(); - void RequestHideNotificationIcon(); - void Quit(); - - void AttachContentToWindow(AttachRequest request); - void SendContent(RequestReceiveContentArgs args); - - event Windows.Foundation.TypedEventHandler WindowActivated; - event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; - event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; - event Windows.Foundation.TypedEventHandler DisplayWindowIdRequested; - event Windows.Foundation.TypedEventHandler RenameRequested; - event Windows.Foundation.TypedEventHandler SummonRequested; - - event Windows.Foundation.TypedEventHandler ShowNotificationIconRequested; - event Windows.Foundation.TypedEventHandler HideNotificationIconRequested; - event Windows.Foundation.TypedEventHandler QuitRequested; - - event Windows.Foundation.TypedEventHandler AttachRequested; - - event Windows.Foundation.TypedEventHandler SendContentRequested; - }; - - [default_interface] runtimeclass Peasant : IPeasant - { - Peasant(); - }; -} diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.cpp b/src/cascadia/Remoting/ProposeCommandlineResult.cpp deleted file mode 100644 index fc9cc8f616b..00000000000 --- a/src/cascadia/Remoting/ProposeCommandlineResult.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index 2578ce83783..00000000000 --- a/src/cascadia/Remoting/ProposeCommandlineResult.h +++ /dev/null @@ -1,41 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- ProposeCommandlineResult.h - -Abstract: -- This is a helper class for encapsulating the result of a - Monarch::ProposeCommandline call. The monarch will be telling the new process - whether it should create a new window or not. If the value of - ShouldCreateWindow is false, that implies that some other window process was - given the commandline for handling, and the caller should just exit. -- If ShouldCreateWindow is true, the Id property may or may not contain an ID - that the new window should use as its ID. - ---*/ - -#pragma once - -#include "ProposeCommandlineResult.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct ProposeCommandlineResult : public ProposeCommandlineResultT - { - public: - ProposeCommandlineResult(const Remoting::ProposeCommandlineResult& other) : - _Id{ other.Id() }, - _WindowName{ other.WindowName() }, - _ShouldCreateWindow{ other.ShouldCreateWindow() } {}; - - WINRT_PROPERTY(Windows::Foundation::IReference, Id); - WINRT_PROPERTY(winrt::hstring, WindowName); - WINRT_PROPERTY(bool, ShouldCreateWindow, true); - - public: - ProposeCommandlineResult(bool shouldCreateWindow) : - _ShouldCreateWindow{ shouldCreateWindow } {}; - }; -} diff --git a/src/cascadia/Remoting/RenameRequestArgs.cpp b/src/cascadia/Remoting/RenameRequestArgs.cpp deleted file mode 100644 index cab69f40866..00000000000 --- a/src/cascadia/Remoting/RenameRequestArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#include "pch.h" -#include "RenameRequestArgs.h" -#include "RenameRequestArgs.g.cpp" diff --git a/src/cascadia/Remoting/RenameRequestArgs.h b/src/cascadia/Remoting/RenameRequestArgs.h deleted file mode 100644 index b22cbeb074b..00000000000 --- a/src/cascadia/Remoting/RenameRequestArgs.h +++ /dev/null @@ -1,29 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- RenameRequestArgs.h - ---*/ -#pragma once - -#include "RenameRequestArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct RenameRequestArgs : public RenameRequestArgsT - { - WINRT_PROPERTY(winrt::hstring, NewName); - WINRT_PROPERTY(bool, Succeeded, false); - - public: - RenameRequestArgs(winrt::hstring newName) : - _NewName{ newName } {}; - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(RenameRequestArgs); -} diff --git a/src/cascadia/Remoting/Resources/de-DE/Resources.resw b/src/cascadia/Remoting/Resources/de-DE/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/de-DE/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/en-US/Resources.resw b/src/cascadia/Remoting/Resources/en-US/Resources.resw deleted file mode 100644 index ed0aef9346c..00000000000 --- a/src/cascadia/Remoting/Resources/en-US/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/Resources/es-ES/Resources.resw b/src/cascadia/Remoting/Resources/es-ES/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/es-ES/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/fr-FR/Resources.resw b/src/cascadia/Remoting/Resources/fr-FR/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/fr-FR/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/it-IT/Resources.resw b/src/cascadia/Remoting/Resources/it-IT/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/it-IT/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/ja-JP/Resources.resw b/src/cascadia/Remoting/Resources/ja-JP/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/ja-JP/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/ko-KR/Resources.resw b/src/cascadia/Remoting/Resources/ko-KR/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/ko-KR/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/pt-BR/Resources.resw b/src/cascadia/Remoting/Resources/pt-BR/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/pt-BR/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/qps-ploc/Resources.resw b/src/cascadia/Remoting/Resources/qps-ploc/Resources.resw deleted file mode 100644 index ed0aef9346c..00000000000 --- a/src/cascadia/Remoting/Resources/qps-ploc/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/Resources/qps-ploca/Resources.resw b/src/cascadia/Remoting/Resources/qps-ploca/Resources.resw deleted file mode 100644 index ed0aef9346c..00000000000 --- a/src/cascadia/Remoting/Resources/qps-ploca/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/Resources/qps-plocm/Resources.resw b/src/cascadia/Remoting/Resources/qps-plocm/Resources.resw deleted file mode 100644 index ed0aef9346c..00000000000 --- a/src/cascadia/Remoting/Resources/qps-plocm/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/Resources/ru-RU/Resources.resw b/src/cascadia/Remoting/Resources/ru-RU/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/ru-RU/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/zh-CN/Resources.resw b/src/cascadia/Remoting/Resources/zh-CN/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/zh-CN/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/Resources/zh-TW/Resources.resw b/src/cascadia/Remoting/Resources/zh-TW/Resources.resw deleted file mode 100644 index 4f24d55cd6b..00000000000 --- a/src/cascadia/Remoting/Resources/zh-TW/Resources.resw +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - \ No newline at end of file diff --git a/src/cascadia/Remoting/SummonWindowBehavior.cpp b/src/cascadia/Remoting/SummonWindowBehavior.cpp deleted file mode 100644 index 516acff06d0..00000000000 --- a/src/cascadia/Remoting/SummonWindowBehavior.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index d8d96a90bdb..00000000000 --- a/src/cascadia/Remoting/SummonWindowBehavior.h +++ /dev/null @@ -1,44 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- SummonWindowBehavior.h - -Abstract: -- This is a helper class for encapsulating all the information about how a - window should be summoned. Includes info like if we should switch desktops or - monitors, if we should dropdown (and how fast), and if we should toggle to - hidden if we're already visible. Used by the Monarch to tell a Peasant how it - should behave. - ---*/ - -#pragma once - -#include "SummonWindowBehavior.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct SummonWindowBehavior : public SummonWindowBehaviorT - { - public: - SummonWindowBehavior() = default; - WINRT_PROPERTY(bool, MoveToCurrentDesktop, true); - WINRT_PROPERTY(bool, ToggleVisibility, true); - WINRT_PROPERTY(uint32_t, DropdownDuration, 0); - WINRT_PROPERTY(Remoting::MonitorBehavior, ToMonitor, Remoting::MonitorBehavior::ToCurrent); - - public: - SummonWindowBehavior(const Remoting::SummonWindowBehavior& other) : - _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() }, - _ToMonitor{ other.ToMonitor() }, - _DropdownDuration{ other.DropdownDuration() }, - _ToggleVisibility{ other.ToggleVisibility() } {}; - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(SummonWindowBehavior); -} diff --git a/src/cascadia/Remoting/SummonWindowSelectionArgs.cpp b/src/cascadia/Remoting/SummonWindowSelectionArgs.cpp deleted file mode 100644 index 6db4cad3304..00000000000 --- a/src/cascadia/Remoting/SummonWindowSelectionArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index 5f15e0985b0..00000000000 --- a/src/cascadia/Remoting/SummonWindowSelectionArgs.h +++ /dev/null @@ -1,44 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- SummonWindowSelectionArgs.h - -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 - -#include "SummonWindowSelectionArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct SummonWindowSelectionArgs : public SummonWindowSelectionArgsT - { - public: - SummonWindowSelectionArgs() = default; - SummonWindowSelectionArgs(winrt::hstring name) : - _WindowName{ name } {}; - - WINRT_PROPERTY(winrt::hstring, WindowName); - - WINRT_PROPERTY(bool, FoundMatch, false); - WINRT_PROPERTY(bool, OnCurrentDesktop, false); - WINRT_PROPERTY(SummonWindowBehavior, SummonBehavior); - - WINRT_PROPERTY(Windows::Foundation::IReference, WindowID); - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(SummonWindowSelectionArgs); -} diff --git a/src/cascadia/Remoting/WindowActivatedArgs.cpp b/src/cascadia/Remoting/WindowActivatedArgs.cpp deleted file mode 100644 index 4466383c2c7..00000000000 --- a/src/cascadia/Remoting/WindowActivatedArgs.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index 353aeb152c7..00000000000 --- a/src/cascadia/Remoting/WindowActivatedArgs.h +++ /dev/null @@ -1,60 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Class Name: -- WindowActivatedArgs.h - -Abstract: -- This is a helper class for encapsulating all the information about when and - where a window was activated. This will be used by the Monarch to determine - who the most recent peasant is. - ---*/ -#pragma once - -#include "WindowActivatedArgs.g.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct CompareWindowActivatedArgs - { - bool operator()(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) const - { - return lhs.ActivatedTime() > rhs.ActivatedTime(); - } - }; - struct WindowActivatedArgs : public WindowActivatedArgsT - { - WINRT_PROPERTY(uint64_t, PeasantID, 0); - WINRT_PROPERTY(winrt::guid, DesktopID); - WINRT_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {}); - WINRT_PROPERTY(uint64_t, Hwnd, 0); - - public: - WindowActivatedArgs(uint64_t peasantID, - uint64_t hwnd, - winrt::guid desktopID, - winrt::Windows::Foundation::DateTime timestamp) : - _PeasantID{ peasantID }, - _Hwnd{ hwnd }, - _DesktopID{ desktopID }, - _ActivatedTime{ timestamp } {}; - - WindowActivatedArgs(uint64_t peasantID, - winrt::guid desktopID, - winrt::Windows::Foundation::DateTime timestamp) : - WindowActivatedArgs(peasantID, 0, desktopID, timestamp){}; - - WindowActivatedArgs(const Remoting::WindowActivatedArgs& other) : - _PeasantID{ other.PeasantID() }, - _Hwnd{ other.Hwnd() }, - _DesktopID{ other.DesktopID() }, - _ActivatedTime{ other.ActivatedTime() } {}; - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(WindowActivatedArgs); -} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp deleted file mode 100644 index 11ddbf45f48..00000000000 --- a/src/cascadia/Remoting/WindowManager.cpp +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" - -#include "WindowManager.h" - -#include "../inc/WindowingBehavior.h" -#include "MonarchFactory.h" - -#include "CommandlineArgs.h" -#include "FindTargetWindowArgs.h" -#include "ProposeCommandlineResult.h" - -#include "WindowManager.g.cpp" -#include "../../types/inc/utils.hpp" - -#include - -using namespace winrt; -using namespace winrt::Microsoft::Terminal; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -namespace -{ - const GUID& MonarchCLSID() - { - if (!IsPackaged()) [[unlikely]] - { - // Unpackaged installations don't have the luxury of magic package isolation - // to stop them from accidentally touching each other's monarchs. - // We need to enforce that ourselves by making their monarch CLSIDs unique - // per install. - // This applies in both portable mode and normal unpackaged mode. - // We'll use a v5 UUID based on the install folder to unique them. - static GUID processRootHashedGuid = []() { - // {5456C4DB-557D-4A22-B043-B1577418E4AF} - static constexpr GUID processRootHashedGuidBase = { 0x5456c4db, 0x557d, 0x4a22, { 0xb0, 0x43, 0xb1, 0x57, 0x74, 0x18, 0xe4, 0xaf } }; - - // Make a temporary monarch CLSID based on the unpackaged install root - std::filesystem::path modulePath{ wil::GetModuleFileNameW(wil::GetModuleInstanceHandle()) }; - modulePath.remove_filename(); - - return Utils::CreateV5Uuid(processRootHashedGuidBase, std::as_bytes(std::span{ modulePath.native() })); - }(); - return processRootHashedGuid; - } - return Monarch_clsid; - } -} - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - WindowManager::WindowManager() - { - } - 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::_createMonarch() - { - // Heads up! This only works because we're using - // "metadata-based-marshalling" for our WinRT types. That means the OS is - // using the .winmd file we generate to figure out the proxy/stub - // definitions for our types automatically. This only works in the following - // cases: - // - // * If we're running unpackaged: the .winmd must be a sibling of the .exe - // * If we're running packaged: the .winmd must be in the package root - _monarch = try_create_instance(MonarchCLSID(), - CLSCTX_LOCAL_SERVER); - } - - // Check if we became the king, and if we are, wire up callbacks. - void WindowManager::_createCallbacks() - { - assert(_monarch); - // Here, we're the king! - // - // This is where you should do any additional setup that might need to be - // done when we become the king. This will be called both for the first - // window, and when the current monarch dies. - - _monarch.WindowCreated({ get_weak(), &WindowManager::_bubbleWindowCreated }); - _monarch.WindowClosed({ get_weak(), &WindowManager::_bubbleWindowClosed }); - _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); - - _monarch.RequestNewWindow({ get_weak(), &WindowManager::_raiseRequestNewWindow }); - } - - void WindowManager::_registerAsMonarch() - { - winrt::check_hresult(CoRegisterClassObject(MonarchCLSID(), - winrt::make<::MonarchFactory>().get(), - CLSCTX_LOCAL_SERVER, - REGCLS_MULTIPLEUSE, - &_registrationHostClass)); - } - - void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) - { - FindTargetWindowRequested.raise(sender, args); - } - void WindowManager::_raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args) - { - RequestNewWindow.raise(sender, args); - } - - Remoting::ProposeCommandlineResult WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args, const bool isolatedMode) - { - if (!isolatedMode) - { - // _createMonarch always attempts to connect an existing monarch. In - // isolated mode, we don't want to do that. - _createMonarch(); - } - - if (_monarch) - { - // We connected to a monarch instance, not us though. This won't hit - // in isolated mode. - - LOG_IF_FAILED(CoAllowSetForegroundWindow(winrt::get_unknown(_monarch), nullptr)); - - // Send the commandline over to the monarch process - if (_proposeToMonarch(args)) - { - // If that succeeded, then we don't need to make a new window. - // Our job is done. Either the monarch is going to run the - // commandline in an existing window, or a new one, but either way, - // this process doesn't need to make a new window. - - return winrt::make(false); - } - // Otherwise, we'll try to handle this ourselves. - } - - // Theoretically, this condition is always true here: - // - // if (_monarch == nullptr) - // - // If we do still have a _monarch at this point, then we must have - // successfully proposed to it in _proposeToMonarch, so we can't get - // here with a monarch. - { - // No preexisting instance. - - // Raise an event, to ask how to handle this commandline. We can't ask - // the app ourselves - we exist isolated from that knowledge (and - // dependency hell). The WindowManager will raise this up to the app - // host, which will then ask the AppLogic, who will then parse the - // commandline and determine the provided ID of the window. - auto findWindowArgs{ winrt::make_self(args) }; - - // This is handled by some handler in-proc - FindTargetWindowRequested.raise(*this, *findWindowArgs); - - // After the event was handled, ResultTargetWindow() will be filled with - // the parsed result. - const auto targetWindow = findWindowArgs->ResultTargetWindow(); - const auto targetWindowName = findWindowArgs->ResultTargetWindowName(); - - if (targetWindow == WindowingBehaviorUseNone) - { - // This commandline doesn't deserve a window. Don't make a monarch - // either. - return winrt::make(false); - } - else - { - // This commandline _does_ want a window, which means we do want - // to create a window, and a monarch. - // - // Congrats! This is now THE PROCESS. It's the only one that's - // getting any windows. - - // In isolated mode, we don't want to register as the monarch, - // we just want to make a local one. So we'll skip this step. - // The condition below it will handle making the unregistered - // local monarch. - - if (!isolatedMode) - { - _registerAsMonarch(); - _createMonarch(); - } - else - { - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_IntentionallyIsolated", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - - if (!_monarch) - { - // Something catastrophically bad happened here OR we were - // intentionally in isolated mode. We don't want to just - // exit immediately. Instead, we'll just instantiate a local - // Monarch instance, without registering it. We're firmly in - // the realm of undefined behavior, but better to have some - // window than not. - _monarch = winrt::make(); - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_FailedToCoCreate", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - _createCallbacks(); - - // So, we wanted a new peasant. Cool! - // - // We need to fill in args.ResultTargetWindow, - // args.ResultTargetWindowName so that we can create the new - // window with those values. Otherwise, the very first window - // won't obey the given name / ID. - // - // So let's just ask the monarch (ourselves) to get those values. - return _monarch.ProposeCommandline(args); - } - } - } - - // Method Description: - // - Helper attempting to call to the monarch multiple times. If the monarch - // fails to respond, or we encounter any sort of error, we'll try again - // until we find one, or decisively determine there isn't one. - bool WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args) - { - // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below. - static constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); - static constexpr auto RPC_CALL_FAILED_HR = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED); - - // The monarch may respond back "you should be a new - // window, with ID,name of (id, name)". Really the responses are: - // * You should not create a new window - // * Create a new window (but without a given ID or name). The - // Monarch will assign your ID/name later - // * Create a new window, and you'll have this ID or name - // - This is the case where the user provides `wt -w 1`, and - // there's no existing window 1 - - // You can emulate the monarch dying by: starting a terminal, sticking a - // breakpoint in - // TerminalApp!winrt::TerminalApp::implementation::AppLogic::_doFindTargetWindow, - // starting a defterm, and when that BP gets hit, kill the original - // monarch, and see what happens here. - - auto proposedCommandline = false; - Remoting::ProposeCommandlineResult result{ nullptr }; - auto attempts = 0; - while (!proposedCommandline) - { - try - { - // MSFT:38542548 _We believe_ that this is the source of the - // crash here. After we get the result, stash its values into a - // local copy, so that we can check them later. If the Monarch - // dies between now and the inspection of - // `result.ShouldCreateWindow` below, we don't want to explode - // (since _proposeToMonarch is not try/caught). - - _monarch.ProposeCommandline(args); - return true; - } - catch (...) - { - // We did not successfully ask the king what to do. This could - // be for many reasons. Most commonly, the monarch died as we - // were talking to it. That could be a RPC_SERVER_UNAVAILABLE_HR - // or RPC_CALL_FAILED_HR (GH#12666). We also saw a - // RPC_S_CALL_FAILED_DNE in GH#11790. Ultimately, if this is - // gonna fail, we want to just try again, regardless of the - // cause. That's why we're no longer checking what the exception - // was, we're just always gonna try again regardless. - // - // They hopefully just died here. That's okay, let's just go - // ask the next in the line of succession. At the very worst, - // we'll find _us_, (likely last in the line). - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_proposeToMonarch_unexpectedExceptionFromKing", - TraceLoggingInt32(attempts, "attempts", "How many times we've tried"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - LOG_CAUGHT_EXCEPTION(); - attempts++; - - if (attempts >= 10) - { - // We've tried 10 times to find the monarch, failing each - // time. Since we have no idea why, we're guessing that in - // this case, there's just a Monarch registered that's - // misbehaving. In this case, just fall back to - // "IsolatedMonarchMode" - we can't trust the currently - // registered one. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_TooManyAttempts_NullMonarchIsolateMode", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - // Set the monarch to null, so that we'll create a new one - // (or just generally check if we need to even make a window - // for this commandline.) - _monarch = nullptr; - return false; - } - else - { - // We failed to ask the monarch. It must have died. Try and - // find another monarch. - _createMonarch(); - if (!_monarch) - { - // We failed to create a monarch. That means there - // aren't any other windows, and we can become the monarch. - return false; - } - // Go back around the loop. - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_proposeToMonarch_tryAgain", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - } - } - } - - // I don't think we can ever get here, but the compiler doesn't know - return false; - } - - Remoting::Peasant WindowManager::CreatePeasant(const Remoting::WindowRequestedArgs& args) - { - auto p = winrt::make_self(); - // This will be false if the Id is 0, which is our sentinel for "no specific ID was requested" - if (const auto id = args.Id()) - { - p->AssignID(id); - } - - // If the name wasn't specified, this will be an empty string. - p->WindowName(args.WindowName()); - - p->ExecuteCommandline(*winrt::make_self(args.Commandline(), - args.CurrentDirectory(), - args.ShowWindowCommand(), - args.CurrentEnvironment())); - - _monarch.AddPeasant(*p); - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_CreateOurPeasant", - TraceLoggingUInt64(p->GetID(), "peasantID", "The ID of our new peasant"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - return *p; - } - - void WindowManager::_bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e) - { - WindowCreated.raise(s, e); - } - void WindowManager::_bubbleWindowClosed(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e) - { - WindowClosed.raise(s, e); - } - - void WindowManager::QuitAll() - { - if (_monarch) - { - try - { - _monarch.QuitAll(); - } - CATCH_LOG() - } - } - - void WindowManager::SignalClose(const Remoting::Peasant& peasant) - { - if (_monarch) - { - try - { - _monarch.SignalClose(peasant.GetID()); - } - CATCH_LOG() - } - } - - 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(args); - } - - void WindowManager::SummonAllWindows() - { - _monarch.SummonAllWindows(); - } - - Windows::Foundation::Collections::IVectorView WindowManager::GetPeasantInfos() - { - // We should only get called when we're the monarch since the monarch - // is the only one that knows about all peasants. - return _monarch.GetPeasantInfos(); - } - - uint64_t WindowManager::GetNumberOfPeasants() - { - if (_monarch) - { - try - { - return _monarch.GetNumberOfPeasants(); - } - CATCH_LOG() - } - return 0; - } - - bool WindowManager::DoesQuakeWindowExist() - { - return _monarch.DoesQuakeWindowExist(); - } - - void WindowManager::UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant) - { - winrt::get_self(peasant)->ActiveTabTitle(title); - } - - safe_void_coroutine WindowManager::RequestMoveContent(winrt::hstring window, - winrt::hstring content, - uint32_t tabIndex, - Windows::Foundation::IReference windowBounds) - { - co_await winrt::resume_background(); - _monarch.RequestMoveContent(window, content, tabIndex, windowBounds); - } - - safe_void_coroutine WindowManager::RequestSendContent(Remoting::RequestReceiveContentArgs args) - { - co_await winrt::resume_background(); - _monarch.RequestSendContent(args); - } -} diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h deleted file mode 100644 index b2aa219aeea..00000000000 --- a/src/cascadia/Remoting/WindowManager.h +++ /dev/null @@ -1,77 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. -Class Name: -- WindowManager.h -Abstract: -- The Window Manager takes care of coordinating the monarch and peasant for this - process. -- It's responsible for registering as a potential future monarch. It's also - responsible for creating the Peasant for this process when it's determined - this process should become a window process. -- If we aren't the monarch, it's responsible for watching the current monarch - process, and finding the new one if the current monarch dies. -- When the monarch needs to ask the TerminalApp about how to parse a - commandline, it'll ask by raising an event that we'll bubble up to the - AppHost. ---*/ -#pragma once - -#include "WindowManager.g.h" -#include "Peasant.h" -#include "Monarch.h" - -namespace winrt::Microsoft::Terminal::Remoting::implementation -{ - struct WindowManager : public WindowManagerT - { - public: - WindowManager(); - ~WindowManager(); - winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args, const bool isolatedMode); - Remoting::Peasant CreatePeasant(const Remoting::WindowRequestedArgs& args); - - void SignalClose(const Remoting::Peasant& peasant); - void QuitAll(); - void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); - void SummonAllWindows(); - Windows::Foundation::Collections::IVectorView GetPeasantInfos(); - - uint64_t GetNumberOfPeasants(); - - void UpdateActiveTabTitle(const winrt::hstring& title, const Remoting::Peasant& peasant); - - bool DoesQuakeWindowExist(); - - safe_void_coroutine RequestMoveContent(winrt::hstring window, winrt::hstring content, uint32_t tabIndex, Windows::Foundation::IReference windowBounds); - safe_void_coroutine RequestSendContent(Remoting::RequestReceiveContentArgs args); - - til::typed_event FindTargetWindowRequested; - - til::typed_event<> WindowCreated; - til::typed_event<> WindowClosed; - til::typed_event RequestNewWindow; - - private: - DWORD _registrationHostClass{ 0 }; - winrt::Microsoft::Terminal::Remoting::IMonarch _monarch{ nullptr }; - - void _createMonarch(); - void _registerAsMonarch(); - - bool _proposeToMonarch(const Remoting::CommandlineArgs& args); - - void _createCallbacks(); - void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); - void _raiseRequestNewWindow(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); - void _bubbleWindowCreated(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e); - void _bubbleWindowClosed(const winrt::Windows::Foundation::IInspectable& s, const winrt::Windows::Foundation::IInspectable& e); - }; -} - -namespace winrt::Microsoft::Terminal::Remoting::factory_implementation -{ - BASIC_FACTORY(WindowManager); -} diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl deleted file mode 100644 index 1f1b4c4345d..00000000000 --- a/src/cascadia/Remoting/WindowManager.idl +++ /dev/null @@ -1,39 +0,0 @@ -import "Peasant.idl"; -import "Monarch.idl"; - - -namespace Microsoft.Terminal.Remoting -{ - [default_interface] runtimeclass WindowManager - { - WindowManager(); - - ProposeCommandlineResult ProposeCommandline(CommandlineArgs args, Boolean isolatedMode); - Peasant CreatePeasant(WindowRequestedArgs args); - - void SignalClose(Peasant p); - void QuitAll(); - - void UpdateActiveTabTitle(String title, Peasant p); - - void SummonWindow(SummonWindowSelectionArgs args); - void SummonAllWindows(); - - Windows.Foundation.Collections.IVectorView GetPeasantInfos(); - - UInt64 GetNumberOfPeasants(); - - Boolean DoesQuakeWindowExist(); - - void RequestMoveContent(String window, String content, UInt32 tabIndex, Windows.Foundation.IReference bounds); - void RequestSendContent(RequestReceiveContentArgs args); - - event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; - - event Windows.Foundation.TypedEventHandler WindowCreated; - event Windows.Foundation.TypedEventHandler WindowClosed; - - event Windows.Foundation.TypedEventHandler RequestNewWindow; - - }; -} diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def deleted file mode 100644 index ba15818ddb1..00000000000 --- a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index e5eb4559148..00000000000 --- a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj +++ /dev/null @@ -1,78 +0,0 @@ - - - - {27b5aaeb-a548-44cf-9777-f8baa32af7ae} - Microsoft.Terminal.Remoting - Microsoft.Terminal.Remoting - - - DynamicLibrary - Console - - true - true - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {18D09A24-8240-42D6-8CB6-236EEE820263} - - - - true - true - - - - - - - User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) - - /INCLUDE:_DllMain@12 %(AdditionalOptions) - /INCLUDE:DllMain %(AdditionalOptions) - - - - false - - - - - - - diff --git a/src/cascadia/Remoting/init.cpp b/src/cascadia/Remoting/init.cpp deleted file mode 100644 index 51496a8f71c..00000000000 --- a/src/cascadia/Remoting/init.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// 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.cpp b/src/cascadia/Remoting/pch.cpp deleted file mode 100644 index 398a99f6653..00000000000 --- a/src/cascadia/Remoting/pch.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// 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 deleted file mode 100644 index 4c4bcb1ac96..00000000000 --- a/src/cascadia/Remoting/pch.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -// -// pch.h -// Header for platform projection include files -// - -#pragma once - -// Block minwindef.h min/max macros to prevent conflict -#define NOMINMAX - -#define WIN32_LEAN_AND_MEAN -#define NOMCX -#define NOHELP -#define NOCOMM - -#include -#include - -// 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 - -// Including TraceLogging essentials for the binary -#include -#include -TRACELOGGING_DECLARE_PROVIDER(g_hRemotingProvider); -#include -#include - -#include - -// Manually include til after we include Windows.Foundation to give it winrt superpowers -#include "til.h" - -#include -#include diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 017129ca5fc..b51fcb3abc5 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -4,7 +4,6 @@ #include "pch.h" #include "App.h" #include "App.g.cpp" -#include using namespace winrt; using namespace winrt::Windows::ApplicationModel::Activation; @@ -37,27 +36,6 @@ namespace winrt::TerminalApp::implementation if (!dispatcherQueue) { _windowsXamlManager = xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread(); - - // As of Process Model v3, terminal windows are all created on their - // own threads, but we still initiate XAML for the App on the main - // thread. Thing is, just initializing XAML creates a CoreWindow for - // us. On Windows 10, that CoreWindow will show up as a visible - // window on the taskbar, unless we hide it manually. So, go get it - // and do the SW_HIDE thing on it. - if (const auto& coreWindow{ winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread() }) - { - if (const auto& interop{ coreWindow.try_as() }) - { - HWND coreHandle{ 0 }; - interop->get_WindowHandle(&coreHandle); - if (coreHandle) - { - // This prevents an empty "DesktopWindowXamlSource" from - // appearing on the taskbar - ShowWindow(coreHandle, SW_HIDE); - } - } - } } else { @@ -71,31 +49,6 @@ namespace winrt::TerminalApp::implementation return logic; } - void App::Close() - { - if (_bIsClosed) - { - return; - } - - _bIsClosed = true; - - if (_windowsXamlManager) - { - _windowsXamlManager.Close(); - } - _windowsXamlManager = nullptr; - - Exit(); - { - MSG msg = {}; - while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) - { - ::DispatchMessageW(&msg); - } - } - } - /// /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index a2c9b6a7c0c..0f3d7a771f5 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -18,7 +18,6 @@ namespace winrt::TerminalApp::implementation TerminalApp::AppLogic Logic(); - void Close(); void PrepareForSettingsUI(); bool IsDisposed() const diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index 516442148cd..3319f7b6462 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -7,7 +7,7 @@ namespace TerminalApp { // ADD ARBITRARY APP LOGIC TO AppLogic.idl, NOT HERE. // This is for XAML platform setup only. - [default_interface] runtimeclass App : Windows.UI.Xaml.Application, Windows.Foundation.IClosable + [default_interface] runtimeclass App : Windows.UI.Xaml.Application { App(); diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index ca67dac64d5..1e4b7a3b106 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -833,7 +833,7 @@ void AppCommandlineArgs::_resetStateToDefault() // Return Value: // - a list of Commandline objects, where each one represents a single // commandline to parse. -std::vector AppCommandlineArgs::BuildCommands(winrt::array_view& args) +std::vector AppCommandlineArgs::BuildCommands(winrt::array_view args) { std::vector commands; commands.emplace_back(Commandline{}); @@ -983,7 +983,7 @@ bool AppCommandlineArgs::IsHandoffListener() const noexcept // Return Value: // - The help text, or an error message, generated from parsing the input // provided by the user. -const std::string& AppCommandlineArgs::GetExitMessage() +const std::string& AppCommandlineArgs::GetExitMessage() const noexcept { return _exitMessage; } @@ -1080,7 +1080,7 @@ std::optional AppCommandlineArgs::GetSize() const noexcept // - args: an array of strings to process as a commandline. These args can contain spaces // Return Value: // - 0 if the commandline was successfully parsed -int AppCommandlineArgs::ParseArgs(winrt::array_view& args) +int AppCommandlineArgs::ParseArgs(winrt::array_view args) { for (const auto& arg : args) { diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index 7eb2516bb38..9f580e957b6 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -28,15 +28,15 @@ class TerminalApp::AppCommandlineArgs final ~AppCommandlineArgs() = default; int ParseCommand(const Commandline& command); - int ParseArgs(winrt::array_view& args); + int ParseArgs(winrt::array_view args); static std::vector BuildCommands(const std::vector& args); - static std::vector BuildCommands(winrt::array_view& args); + static std::vector BuildCommands(winrt::array_view args); void ValidateStartupCommands(); std::vector& GetStartupActions(); bool IsHandoffListener() const noexcept; - const std::string& GetExitMessage(); + const std::string& GetExitMessage() const noexcept; bool ShouldExitEarly() const noexcept; std::optional GetPersistedLayoutIdx() const noexcept; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 812a1cf1b7d..c1c56ca9af2 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -3,9 +3,7 @@ #include "pch.h" #include "AppLogic.h" -#include "../inc/WindowingBehavior.h" #include "AppLogic.g.cpp" -#include "FindTargetWindowResult.g.cpp" #include "SettingsLoadEventArgs.h" #include @@ -121,7 +119,7 @@ namespace winrt::TerminalApp::implementation { auto appLogic{ ::winrt::TerminalApp::implementation::AppLogic::Current() }; THROW_HR_IF_NULL(E_INVALIDARG, appLogic); - return appLogic->GetSettings(); + return appLogic->Settings(); } AppLogic::AppLogic() @@ -507,179 +505,18 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Returns a pointer to the global shared settings. - [[nodiscard]] CascadiaSettings AppLogic::GetSettings() const noexcept + [[nodiscard]] CascadiaSettings AppLogic::Settings() const noexcept { return _settings; } - // Method Description: - // - Parse the given commandline args in an attempt to find the specified - // window. The rest of the args are ignored for now (they'll be handled - // whenever the commandline gets to the window it was intended for). - // - Note that this function will only ever be called by the monarch. A - // return value of `0` in this case does not mean "run the commandline in - // _this_ process", rather it means "run the commandline in the current - // process", whoever that may be. - // Arguments: - // - args: an array of strings to process as a commandline. These args can contain spaces - // Return Value: - // - 0: We should handle the args "in the current window". - // - WindowingBehaviorUseNew: We should handle the args in a new window - // - WindowingBehaviorUseExisting: We should handle the args "in - // the current window ON THIS DESKTOP" - // - WindowingBehaviorUseAnyExisting: We should handle the args "in the current - // window ON ANY DESKTOP" - // - anything else: We should handle the commandline in the window with the given ID. - TerminalApp::FindTargetWindowResult AppLogic::FindTargetWindow(array_view args) - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - - return AppLogic::_doFindTargetWindow(args, _settings.GlobalSettings().WindowingBehavior()); - } - - // The main body of this function is a static helper, to facilitate unit-testing - TerminalApp::FindTargetWindowResult AppLogic::_doFindTargetWindow(array_view args, - const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior) - { - ::TerminalApp::AppCommandlineArgs appArgs; - const auto result = appArgs.ParseArgs(args); - if (result == 0) - { - if (!appArgs.GetExitMessage().empty()) - { - return winrt::make(WindowingBehaviorUseNone); - } - - // Validate the args now. This will make sure that in the case of a - // single x-save command, we toss that commandline to the current - // terminal window - appArgs.ValidateStartupCommands(); - const std::string parsedTarget{ appArgs.GetTargetWindow() }; - - // If the user did not provide any value on the commandline, - // then lookup our windowing behavior to determine what to do - // now. - if (parsedTarget.empty()) - { - auto windowId = WindowingBehaviorUseNew; - switch (windowingBehavior) - { - case WindowingMode::UseNew: - windowId = WindowingBehaviorUseNew; - break; - case WindowingMode::UseExisting: - windowId = WindowingBehaviorUseExisting; - break; - case WindowingMode::UseAnyExisting: - windowId = WindowingBehaviorUseAnyExisting; - break; - } - return winrt::make(windowId); - } - - // Here, the user _has_ provided a window-id on the commandline. - // What is it? Let's start by checking if it's an int, for the - // window's ID: - try - { - auto windowId = ::base::saturated_cast(std::stoi(parsedTarget)); - - // If the user provides _any_ negative number, then treat it as - // -1, for "use a new window". - if (windowId < 0) - { - windowId = -1; - } - - // Hooray! This is a valid integer. The set of possible values - // here is {-1, 0, ℤ+}. Let's return that window ID. - return winrt::make(windowId); - } - catch (...) - { - // Value was not a valid int. It could be any other string to - // use as a title though! - // - // First, check the reserved keywords: - if (parsedTarget == NewWindow) - { - return winrt::make(WindowingBehaviorUseNew); - } - else if (parsedTarget == MostRecentlyUsedWindow) - { - return winrt::make(WindowingBehaviorUseExisting); - } - else - { - // The string they provided wasn't an int, it wasn't "new" - // or "last", so whatever it is, that's the name they get. - winrt::hstring winrtName{ til::u8u16(parsedTarget) }; - return winrt::make(WindowingBehaviorUseName, winrtName); - } - } - } - - // Any unsuccessful parse will result in _no_ window. We will indicate - // to the caller that they shouldn't make a window. They can still find - // the commandline failed to parse and choose to display the message - // box. - // - // This will also work for the case where the user specifies an invalid - // commandline in conjunction with `-w 0`. - return winrt::make(WindowingBehaviorUseNone); - } - Windows::Foundation::Collections::IMapView AppLogic::GlobalHotkeys() { return _settings.GlobalSettings().ActionMap().GlobalHotkeys(); } - Microsoft::Terminal::Settings::Model::Theme AppLogic::Theme() - { - return _settings.GlobalSettings().CurrentTheme(); - } - - bool AppLogic::IsolatedMode() - { - if (!_loadedInitialSettings) - { - ReloadSettings(); - } - return _settings.GlobalSettings().IsolatedMode(); - } - bool AppLogic::RequestsTrayIcon() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - const auto& globals{ _settings.GlobalSettings() }; - return globals.AlwaysShowNotificationIcon() || - globals.MinimizeToNotificationArea(); - } - - bool AppLogic::AllowHeadless() - { - if (!_loadedInitialSettings) - { - // Load settings if we haven't already - ReloadSettings(); - } - return _settings.GlobalSettings().AllowHeadless(); - } - TerminalApp::TerminalWindow AppLogic::CreateNewWindow() { - if (_settings == nullptr) - { - ReloadSettings(); - } - auto warnings{ winrt::multi_threaded_vector() }; for (auto&& warn : _warnings) { @@ -705,18 +542,6 @@ namespace winrt::TerminalApp::implementation return _contentManager; } - bool AppLogic::ShouldUsePersistedLayout() const - { - return _settings.GlobalSettings().ShouldUsePersistedLayout(); - } - - TerminalApp::ParseCommandlineResult AppLogic::GetParseCommandlineMessage(array_view args) - { - ::TerminalApp::AppCommandlineArgs _appArgs; - const auto r = _appArgs.ParseArgs(args); - return TerminalApp::ParseCommandlineResult{ winrt::to_hstring(_appArgs.GetExitMessage()), r }; - } - // Function Description // * Adds a `WT_SETTINGS_DIR` env var to our own environment block, that // points at our settings directory. This allows portable installs to diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 42f89942ad8..8960a476f2a 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -4,7 +4,6 @@ #pragma once #include "AppLogic.g.h" -#include "FindTargetWindowResult.g.h" #include "Jumplist.h" #include "LanguageProfileNotifier.h" @@ -25,19 +24,6 @@ namespace TerminalAppLocalTests namespace winrt::TerminalApp::implementation { - struct FindTargetWindowResult : FindTargetWindowResultT - { - WINRT_PROPERTY(int32_t, WindowId, -1); - WINRT_PROPERTY(winrt::hstring, WindowName, L""); - - public: - FindTargetWindowResult(const int32_t id, const winrt::hstring& name) : - _WindowId{ id }, _WindowName{ name } {}; - - FindTargetWindowResult(const int32_t id) : - FindTargetWindowResult(id, L""){}; - }; - struct AppLogic : AppLogicT { public: @@ -53,25 +39,15 @@ namespace winrt::TerminalApp::implementation void NotifyRootInitialized(); bool HasSettingsStartupActions() const noexcept; - bool ShouldUsePersistedLayout() const; - - [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; - TerminalApp::FindTargetWindowResult FindTargetWindow(array_view actions); + Microsoft::Terminal::Settings::Model::CascadiaSettings Settings() const noexcept; Windows::Foundation::Collections::IMapView GlobalHotkeys(); - Microsoft::Terminal::Settings::Model::Theme Theme(); - bool IsolatedMode(); - bool AllowHeadless(); - bool RequestsTrayIcon(); - TerminalApp::TerminalWindow CreateNewWindow(); winrt::TerminalApp::ContentManager ContentManager(); - TerminalApp::ParseCommandlineResult GetParseCommandlineMessage(array_view args); - til::typed_event SettingsChanged; private: @@ -99,9 +75,6 @@ namespace winrt::TerminalApp::implementation TerminalApp::ContentManager _contentManager{ winrt::make() }; - static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view args, - const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior); - void _ApplyLanguageSettingChange() noexcept; safe_void_coroutine _ApplyStartupTaskStateChange(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 362c7405644..4f6bc262c37 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -4,18 +4,6 @@ import "TerminalWindow.idl"; namespace TerminalApp { - [default_interface] runtimeclass FindTargetWindowResult - { - Int32 WindowId { get; }; - String WindowName { get; }; - }; - - struct ParseCommandlineResult - { - String Message; - Int32 ExitCode; - }; - // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. [default_interface] runtimeclass AppLogic @@ -36,22 +24,11 @@ namespace TerminalApp Boolean HasSettingsStartupActions(); - Boolean ShouldUsePersistedLayout(); - void ReloadSettings(); - - // Selected settings to expose - Microsoft.Terminal.Settings.Model.Theme Theme { get; }; - Boolean IsolatedMode { get; }; - Boolean AllowHeadless { get; }; - Boolean RequestsTrayIcon { get; }; - - FindTargetWindowResult FindTargetWindow(String[] args); + Microsoft.Terminal.Settings.Model.CascadiaSettings Settings { get; }; TerminalWindow CreateNewWindow(); - ParseCommandlineResult GetParseCommandlineMessage(String[] args); - IMapView GlobalHotkeys(); event Windows.Foundation.TypedEventHandler SettingsChanged; diff --git a/src/cascadia/TerminalApp/Remoting.cpp b/src/cascadia/TerminalApp/Remoting.cpp new file mode 100644 index 00000000000..8383fb98fd2 --- /dev/null +++ b/src/cascadia/TerminalApp/Remoting.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Remoting.h" + +#include "CommandlineArgs.g.cpp" +#include "RequestReceiveContentArgs.g.cpp" +#include "SummonWindowBehavior.g.cpp" +#include "WindowRequestedArgs.g.cpp" + +using namespace winrt; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Windows::Foundation; + +namespace winrt::TerminalApp::implementation +{ + CommandlineArgs::CommandlineArgs(winrt::array_view args, winrt::hstring currentDirectory, uint32_t showWindowCommand, winrt::hstring envString) : + _args{ args.begin(), args.end() }, + _cwd{ std::move(currentDirectory) }, + ShowWindowCommand{ showWindowCommand }, + CurrentEnvironment{ std::move(envString) } + { + _parseResult = _parsed.ParseArgs(_args); + if (_parseResult == 0) + { + _parsed.ValidateStartupCommands(); + } + } + + ::TerminalApp::AppCommandlineArgs& CommandlineArgs::ParsedArgs() noexcept + { + return _parsed; + } + + winrt::com_array& CommandlineArgs::CommandlineRef() noexcept + { + return _args; + } + + int32_t CommandlineArgs::ExitCode() const noexcept + { + return _parseResult; + } + + winrt::hstring CommandlineArgs::ExitMessage() const + { + return winrt::to_hstring(_parsed.GetExitMessage()); + } + + winrt::hstring CommandlineArgs::TargetWindow() const + { + return winrt::to_hstring(_parsed.GetTargetWindow()); + } + + void CommandlineArgs::Commandline(const winrt::array_view& value) + { + _args = { value.begin(), value.end() }; + } + + winrt::com_array CommandlineArgs::Commandline() + { + return winrt::com_array{ _args.begin(), _args.end() }; + } +} diff --git a/src/cascadia/TerminalApp/Remoting.h b/src/cascadia/TerminalApp/Remoting.h new file mode 100644 index 00000000000..a86619c4dc2 --- /dev/null +++ b/src/cascadia/TerminalApp/Remoting.h @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "AppCommandlineArgs.h" +#include "CommandlineArgs.g.h" +#include "RequestReceiveContentArgs.g.h" +#include "SummonWindowBehavior.g.h" +#include "WindowRequestedArgs.g.h" + +namespace winrt::TerminalApp::implementation +{ + struct CommandlineArgs : public CommandlineArgsT + { + CommandlineArgs() = default; + CommandlineArgs(winrt::array_view args, winrt::hstring currentDirectory, uint32_t showWindowCommand, winrt::hstring envString); + + ::TerminalApp::AppCommandlineArgs& ParsedArgs() noexcept; + winrt::com_array& CommandlineRef() noexcept; + + // These bits are exposed via WinRT: + public: + int32_t ExitCode() const noexcept; + winrt::hstring ExitMessage() const; + winrt::hstring TargetWindow() const; + + void Commandline(const winrt::array_view& value); + winrt::com_array Commandline(); + + til::property CurrentDirectory; + til::property CurrentEnvironment; + til::property ShowWindowCommand{ static_cast(SW_NORMAL) }; // SW_NORMAL is 1, 0 is SW_HIDE + + private: + ::TerminalApp::AppCommandlineArgs _parsed; + int32_t _parseResult = 0; + winrt::com_array _args; + winrt::hstring _cwd; + }; + + struct RequestReceiveContentArgs : RequestReceiveContentArgsT + { + WINRT_PROPERTY(uint64_t, SourceWindow); + WINRT_PROPERTY(uint64_t, TargetWindow); + WINRT_PROPERTY(uint32_t, TabIndex); + + public: + RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) : + _SourceWindow{ src }, + _TargetWindow{ tgt }, + _TabIndex{ tabIndex } {}; + }; + + struct SummonWindowBehavior : public SummonWindowBehaviorT + { + public: + SummonWindowBehavior() = default; + WINRT_PROPERTY(bool, MoveToCurrentDesktop, true); + WINRT_PROPERTY(bool, ToggleVisibility, true); + WINRT_PROPERTY(uint32_t, DropdownDuration, 0); + WINRT_PROPERTY(MonitorBehavior, ToMonitor, MonitorBehavior::ToCurrent); + + public: + SummonWindowBehavior(const SummonWindowBehavior& other) : + _MoveToCurrentDesktop{ other.MoveToCurrentDesktop() }, + _ToMonitor{ other.ToMonitor() }, + _DropdownDuration{ other.DropdownDuration() }, + _ToggleVisibility{ other.ToggleVisibility() } {}; + }; + + struct WindowRequestedArgs : public WindowRequestedArgsT + { + public: + WindowRequestedArgs(uint64_t id, const winrt::TerminalApp::CommandlineArgs& command) : + _Id{ id }, + _Command{ std::move(command) } + { + } + + WindowRequestedArgs(const winrt::hstring& window, const winrt::hstring& content, const Windows::Foundation::IReference& bounds) : + _WindowName{ window }, + _Content{ content }, + _InitialBounds{ bounds } + { + } + + WINRT_PROPERTY(uint64_t, Id); + WINRT_PROPERTY(winrt::hstring, WindowName); + WINRT_PROPERTY(TerminalApp::CommandlineArgs, Command); + WINRT_PROPERTY(winrt::hstring, Content); + WINRT_PROPERTY(Windows::Foundation::IReference, InitialBounds); + + private: + }; +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(SummonWindowBehavior); + BASIC_FACTORY(CommandlineArgs); + BASIC_FACTORY(RequestReceiveContentArgs); + BASIC_FACTORY(WindowRequestedArgs); +} diff --git a/src/cascadia/TerminalApp/Remoting.idl b/src/cascadia/TerminalApp/Remoting.idl new file mode 100644 index 00000000000..17f13184220 --- /dev/null +++ b/src/cascadia/TerminalApp/Remoting.idl @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace TerminalApp +{ + runtimeclass CommandlineArgs + { + CommandlineArgs(); + CommandlineArgs(String[] args, String cwd, UInt32 showWindowCommand, String env); + + Int32 ExitCode { get; }; + String ExitMessage { get; }; + String TargetWindow { get; }; + + String[] Commandline; + String CurrentDirectory { get; }; + UInt32 ShowWindowCommand { get; }; + String CurrentEnvironment { get; }; + }; + + enum MonitorBehavior + { + InPlace, + ToCurrent, + ToMouse, + }; + + [default_interface] runtimeclass SummonWindowBehavior { + SummonWindowBehavior(); + Boolean MoveToCurrentDesktop; + Boolean ToggleVisibility; + UInt32 DropdownDuration; + MonitorBehavior ToMonitor; + } + + [default_interface] runtimeclass RequestReceiveContentArgs { + RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex); + + UInt64 SourceWindow { get; }; + UInt64 TargetWindow { get; }; + UInt32 TabIndex { get; }; + }; + + [default_interface] runtimeclass WindowRequestedArgs { + WindowRequestedArgs(UInt64 id, CommandlineArgs command); + WindowRequestedArgs(String window, String content, Windows.Foundation.IReference bounds); + + UInt64 Id; + String WindowName; + + CommandlineArgs Command { get; }; + String Content { get; }; + Windows.Foundation.IReference InitialBounds { get; }; + }; +} diff --git a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw index eec7f854bde..dafd51d375b 100644 --- a/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/de-DE/Resources.resw @@ -728,12 +728,6 @@ Abbrechen - - Das Fenster konnte nicht umbenannt werden - - - Ein anderes Fenster mit diesem Namen existiert bereits - Maximieren diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 217f4e88879..74d09214ba6 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -733,12 +733,6 @@ Cancel - - Failed to rename window - - - Another window with that name already exists - Maximize diff --git a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw index 1b107a9bdb8..5554ab65d91 100644 --- a/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/es-ES/Resources.resw @@ -725,12 +725,6 @@ Cancelar - - No se pudo cambiar el nombre de la ventana - - - Ya existe otra ventana con ese nombre - Maximizar diff --git a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw index e0a08f70332..7985c115fed 100644 --- a/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/fr-FR/Resources.resw @@ -725,12 +725,6 @@ Annuler - - Échec du renommage de la fenêtre - - - Une autre fenêtre portant ce nom existe déjà - Agrandir diff --git a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw index fd005675100..09638c2271f 100644 --- a/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/it-IT/Resources.resw @@ -725,12 +725,6 @@ Annulla - - Non è stato possibile rinominare la finestra - - - Un'altra finestra con questo nome esiste già - Ingrandisci diff --git a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw index 22567ab8ec8..bf2fe3e5191 100644 --- a/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ja-JP/Resources.resw @@ -726,12 +726,6 @@ キャンセル - - ウィンドウの名前を変更できませんでした - - - その名前の別のウィンドウがすでに存在します - 最大化 diff --git a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw index 4686a859bff..af54a3ad3e2 100644 --- a/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ko-KR/Resources.resw @@ -725,12 +725,6 @@ 취소 - - 창 이름을 바꾸지 못함 - - - 해당 이름으로 된 다른 창이 이미 있습니다 - 최대화 diff --git a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw index 90d7a068ff1..4fb173f76c4 100644 --- a/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/pt-BR/Resources.resw @@ -725,12 +725,6 @@ Cancelar - - Falha ao renomear janela - - - Já existe outra janela com esse nome - Maximizar diff --git a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw index 63c3326dc6f..f9c83a98c91 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploc/Resources.resw @@ -733,12 +733,6 @@ Сªⁿċĕĺ ! - - ₣âιļĕđ ŧо ѓëňąмë щϊπδǿω !!! !!! - - - Άņόтĥěŗ ẃїлðοω ẁιťĥ ťћаť пâmě ăļřéàδў ёжìśŧѕ !!! !!! !!! !!! ! - Μą×ìmϊżé !! diff --git a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw index 63c3326dc6f..f9c83a98c91 100644 --- a/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-ploca/Resources.resw @@ -733,12 +733,6 @@ Сªⁿċĕĺ ! - - ₣âιļĕđ ŧо ѓëňąмë щϊπδǿω !!! !!! - - - Άņόтĥěŗ ẃїлðοω ẁιťĥ ťћаť пâmě ăļřéàδў ёжìśŧѕ !!! !!! !!! !!! ! - Μą×ìmϊżé !! diff --git a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw index 63c3326dc6f..f9c83a98c91 100644 --- a/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/qps-plocm/Resources.resw @@ -733,12 +733,6 @@ Сªⁿċĕĺ ! - - ₣âιļĕđ ŧо ѓëňąмë щϊπδǿω !!! !!! - - - Άņόтĥěŗ ẃїлðοω ẁιťĥ ťћаť пâmě ăļřéàδў ёжìśŧѕ !!! !!! !!! !!! ! - Μą×ìmϊżé !! diff --git a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw index 811bc2f61cf..c1410ce2ddd 100644 --- a/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/ru-RU/Resources.resw @@ -725,12 +725,6 @@ Отмена - - Не удалось переименовать окно - - - Окно с таким именем уже существует - Развернуть diff --git a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw index 7e3b5bc07ec..a2e85cd6077 100644 --- a/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-CN/Resources.resw @@ -725,12 +725,6 @@ 取消 - - 未能重命名窗口 - - - 已存在另一个具有该名称的窗口 - 最大化 diff --git a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw index e4c77d18939..ec807127203 100644 --- a/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/zh-TW/Resources.resw @@ -725,12 +725,6 @@ 取消 - - 無法重新命名視窗 - - - 已存在另一個具有該名稱的視窗 - 最大化 diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp index 7c80e366861..19668feec91 100644 --- a/src/cascadia/TerminalApp/TabBase.cpp +++ b/src/cascadia/TerminalApp/TabBase.cpp @@ -7,7 +7,6 @@ #include "TabBase.g.cpp" #include "Utils.h" #include "ColorHelper.h" -#include "../inc/WindowingBehavior.h" using namespace winrt; using namespace winrt::Windows::UI::Xaml; @@ -75,7 +74,7 @@ namespace winrt::TerminalApp::implementation _moveToNewWindowMenuItem.Click([weakThis](auto&&, auto&&) { if (auto tab{ weakThis.get() }) { - MoveTabArgs args{ winrt::to_hstring(NewWindow), MoveTabDirection::Forward }; + MoveTabArgs args{ L"new", MoveTabDirection::Forward }; ActionAndArgs actionAndArgs{ ShortcutAction::MoveTab, args }; tab->_dispatch.DoAction(*tab, actionAndArgs); } diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index a1c6a4e6a8f..0896bc114a8 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -91,6 +91,9 @@ MinMaxCloseControl.xaml + + Remoting.idl + PaletteItemTemplateSelector.idl Code @@ -198,6 +201,9 @@ MinMaxCloseControl.xaml + + Remoting.idl + PaletteItemTemplateSelector.idl Code @@ -320,6 +326,7 @@ App.xaml + Designer diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters index 0f6468fd6b9..12b3fa2add3 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters @@ -2,9 +2,11 @@ + + @@ -21,9 +23,6 @@ - - tab - commandPalette @@ -44,6 +43,8 @@ + + @@ -58,9 +59,6 @@ - - tab - commandPalette @@ -81,6 +79,9 @@ + + + @@ -92,10 +93,6 @@ settings - - - tab - tab @@ -110,9 +107,11 @@ - - - + + + + + @@ -151,6 +150,9 @@ highlightedText + + + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 42c96ce6dd0..f227c39e707 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -19,11 +19,11 @@ #include "SnippetsPaneContent.h" #include "MarkdownPaneContent.h" #include "TabRowControl.h" +#include "Remoting.h" #include "TerminalPage.g.cpp" #include "RenameWindowRequestedArgs.g.cpp" #include "RequestMoveContentArgs.g.cpp" -#include "RequestReceiveContentArgs.g.cpp" #include "LaunchPositionRequest.g.cpp" using namespace winrt; @@ -171,7 +171,6 @@ namespace winrt::TerminalApp::implementation const auto canDragDrop = CanDragDrop(); - _tabRow.PointerMoved({ get_weak(), &TerminalPage::_RestorePointerCursorHandler }); _tabView.CanReorderTabs(canDragDrop); _tabView.CanDragTabs(canDragDrop); _tabView.TabDragStarting({ get_weak(), &TerminalPage::_TabDragStarted }); @@ -275,18 +274,7 @@ namespace winrt::TerminalApp::implementation // initialized the first time they're opened, in whatever method opens // them. - // Setup mouse vanish attributes - SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &_shouldMouseVanish, false); - _tabRow.ShowElevationShield(IsRunningElevated() && _settings.GlobalSettings().ShowAdminShield()); - - // Store cursor, so we can restore it, e.g., after mouse vanishing - // (we'll need to adapt this logic once we make cursor context aware) - try - { - _defaultPointerCursor = CoreWindow::GetForCurrentThread().PointerCursor(); - } - CATCH_LOG(); } Windows::UI::Xaml::Automation::Peers::AutomationPeer TerminalPage::OnCreateAutomationPeer() @@ -1724,8 +1712,6 @@ namespace winrt::TerminalApp::implementation term.OpenHyperlink({ this, &TerminalPage::_OpenHyperlinkHandler }); - term.HidePointerCursor({ get_weak(), &TerminalPage::_HidePointerCursorHandler }); - term.RestorePointerCursor({ get_weak(), &TerminalPage::_RestorePointerCursorHandler }); // Add an event handler for when the terminal or tab wants to set a // progress indicator on the taskbar term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler }); @@ -2360,18 +2346,14 @@ namespace winrt::TerminalApp::implementation // reattach instead of create new content, so this method simply needs to // parse the JSON and pump it into our action handler. Almost the same as // doing something like `wt -w 0 nt`. - safe_void_coroutine TerminalPage::AttachContent(IVector args, - uint32_t tabIndex) + void TerminalPage::AttachContent(IVector args, uint32_t tabIndex) { if (args == nullptr || args.Size() == 0) { - co_return; + return; } - // Switch to the UI thread before selecting a tab or dispatching actions. - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::High); - const auto& firstAction = args.GetAt(0); const bool firstIsSplitPane{ firstAction.Action() == ShortcutAction::SplitPane }; @@ -4300,46 +4282,6 @@ namespace winrt::TerminalApp::implementation return winrt::hstring{ text }; } - // Method Description: - // - Hides cursor if required - // Return Value: - // - - void TerminalPage::_HidePointerCursorHandler(const IInspectable& /*sender*/, const IInspectable& /*eventArgs*/) - { - if (_shouldMouseVanish && !_isMouseHidden) - { - if (auto window{ CoreWindow::GetForCurrentThread() }) - { - try - { - window.PointerCursor(nullptr); - _isMouseHidden = true; - } - CATCH_LOG(); - } - } - } - - // Method Description: - // - Restores cursor if required - // Return Value: - // - - void TerminalPage::_RestorePointerCursorHandler(const IInspectable& /*sender*/, const IInspectable& /*eventArgs*/) - { - if (_isMouseHidden) - { - if (auto window{ CoreWindow::GetForCurrentThread() }) - { - try - { - window.PointerCursor(_defaultPointerCursor); - _isMouseHidden = false; - } - CATCH_LOG(); - } - } - } - // Method Description: // - Update the RequestedTheme of the specified FrameworkElement and all its // Parent elements. We need to do this so that we can actually theme all @@ -4370,93 +4312,49 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - safe_void_coroutine TerminalPage::IdentifyWindow() + void TerminalPage::IdentifyWindow() { - auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher()); - if (auto page{ weakThis.get() }) + // If we haven't ever loaded the TeachingTip, then do so now and + // create the toast for it. + if (_windowIdToast == nullptr) { - // If we haven't ever loaded the TeachingTip, then do so now and - // create the toast for it. - if (page->_windowIdToast == nullptr) + if (auto tip{ FindName(L"WindowIdToast").try_as() }) { - if (auto tip{ page->FindName(L"WindowIdToast").try_as() }) - { - page->_windowIdToast = std::make_shared(tip); - // Make sure to use the weak ref when setting up this - // callback. - tip.Closed({ page->get_weak(), &TerminalPage::_FocusActiveControl }); - } - } - _UpdateTeachingTipTheme(WindowIdToast().try_as()); - - if (page->_windowIdToast != nullptr) - { - page->_windowIdToast->Open(); + _windowIdToast = std::make_shared(tip); + // IsLightDismissEnabled == true is bugged and poorly interacts with multi-windowing. + // It causes the tip to be immediately dismissed when another tip is opened in another window. + tip.IsLightDismissEnabled(false); + // Make sure to use the weak ref when setting up this callback. + tip.Closed({ get_weak(), &TerminalPage::_FocusActiveControl }); } } - } + _UpdateTeachingTipTheme(WindowIdToast().try_as()); - // Method Description: - // - Called when an attempt to rename the window has failed. This will open - // the toast displaying a message to the user that the attempt to rename - // the window has failed. - // - This will load the RenameFailedToast TeachingTip the first time it's called. - // Arguments: - // - - // Return Value: - // - - safe_void_coroutine TerminalPage::RenameFailed() - { - auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher()); - if (auto page{ weakThis.get() }) + if (_windowIdToast != nullptr) { - // If we haven't ever loaded the TeachingTip, then do so now and - // create the toast for it. - if (page->_windowRenameFailedToast == nullptr) - { - if (auto tip{ page->FindName(L"RenameFailedToast").try_as() }) - { - page->_windowRenameFailedToast = std::make_shared(tip); - // Make sure to use the weak ref when setting up this - // callback. - tip.Closed({ page->get_weak(), &TerminalPage::_FocusActiveControl }); - } - } - _UpdateTeachingTipTheme(RenameFailedToast().try_as()); - - if (page->_windowRenameFailedToast != nullptr) - { - page->_windowRenameFailedToast->Open(); - } + _windowIdToast->Open(); } } - safe_void_coroutine TerminalPage::ShowTerminalWorkingDirectory() + void TerminalPage::ShowTerminalWorkingDirectory() { - auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher()); - if (auto page{ weakThis.get() }) + // If we haven't ever loaded the TeachingTip, then do so now and + // create the toast for it. + if (_windowCwdToast == nullptr) { - // If we haven't ever loaded the TeachingTip, then do so now and - // create the toast for it. - if (page->_windowCwdToast == nullptr) + if (auto tip{ FindName(L"WindowCwdToast").try_as() }) { - if (auto tip{ page->FindName(L"WindowCwdToast").try_as() }) - { - page->_windowCwdToast = std::make_shared(tip); - // Make sure to use the weak ref when setting up this - // callback. - tip.Closed({ page->get_weak(), &TerminalPage::_FocusActiveControl }); - } + _windowCwdToast = std::make_shared(tip); + // Make sure to use the weak ref when setting up this + // callback. + tip.Closed({ get_weak(), &TerminalPage::_FocusActiveControl }); } - _UpdateTeachingTipTheme(WindowCwdToast().try_as()); + } + _UpdateTeachingTipTheme(WindowCwdToast().try_as()); - if (page->_windowCwdToast != nullptr) - { - page->_windowCwdToast->Open(); - } + if (_windowCwdToast != nullptr) + { + _windowCwdToast->Open(); } } @@ -4464,8 +4362,7 @@ namespace winrt::TerminalApp::implementation // - Called when the user hits the "Ok" button on the WindowRenamer TeachingTip. // - Will raise an event that will bubble up to the monarch, asking if this // name is acceptable. - // - If it is, we'll eventually get called back in TerminalPage::WindowName(hstring). - // - If not, then TerminalPage::RenameFailed will get called. + // - we'll eventually get called back in TerminalPage::WindowName(hstring). // Arguments: // - // Return Value: @@ -5162,25 +5059,18 @@ namespace winrt::TerminalApp::implementation // Handler for our WindowProperties's PropertyChanged event. We'll use this // to pop the "Identify Window" toast when the user renames our window. - safe_void_coroutine TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/, - const WUX::Data::PropertyChangedEventArgs& args) + void TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/, const WUX::Data::PropertyChangedEventArgs& args) { if (args.PropertyName() != L"WindowName") { - co_return; + return; } - auto weakThis{ get_weak() }; - // On the foreground thread, raise property changed notifications, and - // display the success toast. - co_await wil::resume_foreground(Dispatcher()); - if (auto page{ weakThis.get() }) + + // DON'T display the confirmation if this is the name we were + // given on startup! + if (_startupState == StartupState::Initialized) { - // DON'T display the confirmation if this is the name we were - // given on startup! - if (page->_startupState == StartupState::Initialized) - { - page->IdentifyWindow(); - } + IdentifyWindow(); } } @@ -5201,19 +5091,12 @@ namespace winrt::TerminalApp::implementation // Stash the offset from where we started the drag to the // tab's origin. We'll use that offset in the future to help // position the dropped window. - - // First, the position of the pointer, from the CoreWindow - const auto pointerPosition = CoreWindow::GetForCurrentThread().PointerPosition(); - // Next, the position of the tab itself: - const auto tabPosition = eventTab.TransformToVisual(nullptr).TransformPoint({ 0, 0 }); - // Now, we need to add the origin of our CoreWindow to the tab - // position. - const auto windowOrigin = CoreWindow::GetForCurrentThread().Bounds(); - // Subtract the two to get the offset. - _stashed.dragOffset = { - pointerPosition.X - windowOrigin.X - tabPosition.X, - pointerPosition.Y - windowOrigin.Y - tabPosition.Y, - }; + const auto inverseScale = 1.0f / static_cast(eventTab.XamlRoot().RasterizationScale()); + POINT cursorPos; + GetCursorPos(&cursorPos); + ScreenToClient(*_hostingHwnd, &cursorPos); + _stashed.dragOffset.X = cursorPos.x * inverseScale; + _stashed.dragOffset.Y = cursorPos.y * inverseScale; // Into the DataPackage, let's stash our own window ID. const auto id{ _WindowProperties.WindowId() }; @@ -5260,8 +5143,8 @@ namespace winrt::TerminalApp::implementation // - Called on the TARGET of a tab drag/drop. We'll unpack the DataPackage // to find who the tab came from. We'll then ask the Monarch to ask the // sender to move that tab to us. - safe_void_coroutine TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable /*sender*/, - winrt::Windows::UI::Xaml::DragEventArgs e) + void TerminalPage::_onTabStripDrop(winrt::Windows::Foundation::IInspectable /*sender*/, + winrt::Windows::UI::Xaml::DragEventArgs e) { // Get the PID and make sure it is the same as ours. if (const auto& pidObj{ e.DataView().Properties().TryLookup(L"pid") }) @@ -5270,20 +5153,20 @@ namespace winrt::TerminalApp::implementation if (pid != GetCurrentProcessId()) { // The PID doesn't match ours. We can't handle this drop. - co_return; + return; } } else { // No PID? We can't handle this drop. Bail. - co_return; + return; } const auto& windowIdObj{ e.DataView().Properties().TryLookup(L"windowId") }; if (windowIdObj == nullptr) { // No windowId? Bail. - co_return; + return; } const uint64_t src{ winrt::unbox_value(windowIdObj) }; @@ -5291,38 +5174,32 @@ namespace winrt::TerminalApp::implementation // index to the request. This is largely taken from the WinUI sample // app. - // We need to be on OUR UI thread to figure out where we dropped - auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher()); - if (const auto& page{ weakThis.get() }) - { - // First we need to get the position in the List to drop to - auto index = -1; + // First we need to get the position in the List to drop to + auto index = -1; - // Determine which items in the list our pointer is between. - for (auto i = 0u; i < _tabView.TabItems().Size(); i++) + // Determine which items in the list our pointer is between. + for (auto i = 0u; i < _tabView.TabItems().Size(); i++) + { + if (const auto& item{ _tabView.ContainerFromIndex(i).try_as() }) { - if (const auto& item{ _tabView.ContainerFromIndex(i).try_as() }) + const auto posX{ e.GetPosition(item).X }; // The point of the drop, relative to the tab + const auto itemWidth{ item.ActualWidth() }; // The right of the tab + // If the drag point is on the left half of the tab, then insert here. + if (posX < itemWidth / 2) { - const auto posX{ e.GetPosition(item).X }; // The point of the drop, relative to the tab - const auto itemWidth{ item.ActualWidth() }; // The right of the tab - // If the drag point is on the left half of the tab, then insert here. - if (posX < itemWidth / 2) - { - index = i; - break; - } + index = i; + break; } } + } - // `this` is safe to use - const auto request = winrt::make_self(src, _WindowProperties.WindowId(), index); + // `this` is safe to use + const auto request = winrt::make_self(src, _WindowProperties.WindowId(), index); - // This will go up to the monarch, who will then dispatch the request - // back down to the source TerminalPage, who will then perform a - // RequestMoveContent to move their tab to us. - RequestReceiveContent.raise(*this, *request); - } + // This will go up to the monarch, who will then dispatch the request + // back down to the source TerminalPage, who will then perform a + // RequestMoveContent to move their tab to us. + RequestReceiveContent.raise(*this, *request); } // Method Description: @@ -5332,33 +5209,23 @@ namespace winrt::TerminalApp::implementation // the destination window. // - Fortunately, sending the tab is basically just a MoveTab action, so we // can largely reuse that. - safe_void_coroutine TerminalPage::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args) + void TerminalPage::SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args) { // validate that we're the source window of the tab in this request if (args.SourceWindow() != _WindowProperties.WindowId()) { - co_return; + return; } if (!_stashed.draggedTab) { - co_return; + return; } - // must do the work of adding/removing tabs on the UI thread. - auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); - if (const auto& page{ weakThis.get() }) - { - // `this` is safe to use in here. - - _sendDraggedTabToWindow(winrt::to_hstring(args.TargetWindow()), - args.TabIndex(), - std::nullopt); - } + _sendDraggedTabToWindow(winrt::to_hstring(args.TargetWindow()), args.TabIndex(), std::nullopt); } - safe_void_coroutine TerminalPage::_onTabDroppedOutside(winrt::IInspectable sender, - winrt::MUX::Controls::TabViewTabDroppedOutsideEventArgs e) + void TerminalPage::_onTabDroppedOutside(winrt::IInspectable sender, + winrt::MUX::Controls::TabViewTabDroppedOutsideEventArgs e) { // Get the current pointer point from the CoreWindow const auto& pointerPoint{ CoreWindow::GetForCurrentThread().PointerPosition() }; @@ -5370,29 +5237,21 @@ namespace winrt::TerminalApp::implementation if (!_stashed.draggedTab) { - co_return; + return; } - // must do the work of adding/removing tabs on the UI thread. - auto weakThis{ get_weak() }; - co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); - if (const auto& page{ weakThis.get() }) - { - // `this` is safe to use in here. - - // We need to convert the pointer point to a point that we can use - // to position the new window. We'll use the drag offset from before - // so that the tab in the new window is positioned so that it's - // basically still directly under the cursor. + // We need to convert the pointer point to a point that we can use + // to position the new window. We'll use the drag offset from before + // so that the tab in the new window is positioned so that it's + // basically still directly under the cursor. - // -1 is the magic number for "new window" - // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. - const winrt::Windows::Foundation::Point adjusted = { - pointerPoint.X - _stashed.dragOffset.X, - pointerPoint.Y - _stashed.dragOffset.Y, - }; - _sendDraggedTabToWindow(winrt::hstring{ L"-1" }, 0, adjusted); - } + // -1 is the magic number for "new window" + // 0 as the tab index, because we don't care. It's making a new window. It'll be the only tab. + const winrt::Windows::Foundation::Point adjusted = { + pointerPoint.X - _stashed.dragOffset.X, + pointerPoint.Y - _stashed.dragOffset.Y, + }; + _sendDraggedTabToWindow(winrt::hstring{ L"-1" }, 0, adjusted); } void TerminalPage::_sendDraggedTabToWindow(const winrt::hstring& windowId, diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 415bbec8110..87e7b17437b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -9,7 +9,6 @@ #include "AppCommandlineArgs.h" #include "RenameWindowRequestedArgs.g.h" #include "RequestMoveContentArgs.g.h" -#include "RequestReceiveContentArgs.g.h" #include "LaunchPositionRequest.g.h" #include "Toast.h" @@ -69,19 +68,6 @@ namespace winrt::TerminalApp::implementation _TabIndex{ tabIndex } {}; }; - struct RequestReceiveContentArgs : RequestReceiveContentArgsT - { - WINRT_PROPERTY(uint64_t, SourceWindow); - WINRT_PROPERTY(uint64_t, TargetWindow); - WINRT_PROPERTY(uint32_t, TabIndex); - - public: - RequestReceiveContentArgs(const uint64_t src, const uint64_t tgt, const uint32_t tabIndex) : - _SourceWindow{ src }, - _TargetWindow{ tgt }, - _TabIndex{ tabIndex } {}; - }; - struct LaunchPositionRequest : LaunchPositionRequestT { LaunchPositionRequest() = default; @@ -153,11 +139,10 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning() const; winrt::hstring KeyboardServiceDisabledText(); - safe_void_coroutine IdentifyWindow(); + void IdentifyWindow(); void ActionSaved(winrt::hstring input, winrt::hstring name, winrt::hstring keyChord); void ActionSaveFailed(winrt::hstring message); - safe_void_coroutine RenameFailed(); - safe_void_coroutine ShowTerminalWorkingDirectory(); + void ShowTerminalWorkingDirectory(); safe_void_coroutine ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial, @@ -174,8 +159,8 @@ namespace winrt::TerminalApp::implementation bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); - safe_void_coroutine AttachContent(Windows::Foundation::Collections::IVector args, uint32_t tabIndex); - safe_void_coroutine SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args); + void AttachContent(Windows::Foundation::Collections::IVector args, uint32_t tabIndex); + void SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs args); uint32_t NumberOfTabs() const; @@ -275,7 +260,6 @@ namespace winrt::TerminalApp::implementation std::shared_ptr _windowIdToast{ nullptr }; std::shared_ptr _actionSavedToast{ nullptr }; std::shared_ptr _actionSaveFailedToast{ nullptr }; - std::shared_ptr _windowRenameFailedToast{ nullptr }; std::shared_ptr _windowCwdToast{ nullptr }; winrt::Windows::UI::Xaml::Controls::TextBox::LayoutUpdated_revoker _renamerLayoutUpdatedRevoker; @@ -496,12 +480,6 @@ namespace winrt::TerminalApp::implementation void _TryMoveTab(const uint32_t currentTabIndex, const int32_t suggestedNewTabIndex); - bool _shouldMouseVanish{ false }; - bool _isMouseHidden{ false }; - Windows::UI::Core::CoreCursor _defaultPointerCursor{ nullptr }; - void _HidePointerCursorHandler(const IInspectable& sender, const IInspectable& eventArgs); - void _RestorePointerCursorHandler(const IInspectable& sender, const IInspectable& eventArgs); - void _PreviewAction(const Microsoft::Terminal::Settings::Model::ActionAndArgs& args); void _PreviewActionHandler(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::Command& args); void _EndPreview(); @@ -549,12 +527,12 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::IAsyncOperation> _FindPackageAsync(hstring query); void _WindowSizeChanged(const IInspectable sender, const winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs args); - safe_void_coroutine _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); + void _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); void _onTabDragStarting(const winrt::Microsoft::UI::Xaml::Controls::TabView& sender, const winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs& e); void _onTabStripDragOver(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::DragEventArgs& e); - safe_void_coroutine _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); - safe_void_coroutine _onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e); + void _onTabStripDrop(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::UI::Xaml::DragEventArgs e); + void _onTabDroppedOutside(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::UI::Xaml::Controls::TabViewTabDroppedOutsideEventArgs e); void _DetachPaneFromWindow(std::shared_ptr pane); void _DetachTabFromWindow(const winrt::com_ptr& terminalTab); @@ -590,5 +568,4 @@ namespace winrt::TerminalApp::implementation namespace winrt::TerminalApp::factory_implementation { BASIC_FACTORY(TerminalPage); - BASIC_FACTORY(RequestReceiveContentArgs); } diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 56d30eb3d6d..2679d775b09 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. import "TaskbarState.idl"; +import "Remoting.idl"; namespace TerminalApp { @@ -25,13 +26,6 @@ namespace TerminalApp UInt32 TabIndex { get; }; Windows.Foundation.IReference WindowPosition { get; }; }; - [default_interface] runtimeclass RequestReceiveContentArgs { - RequestReceiveContentArgs(UInt64 src, UInt64 tgt, UInt32 tabIndex); - - UInt64 SourceWindow { get; }; - UInt64 TargetWindow { get; }; - UInt32 TabIndex { get; }; - }; interface IDialogPresenter { @@ -70,7 +64,6 @@ namespace TerminalApp WindowProperties WindowProperties { get; }; void IdentifyWindow(); - void RenameFailed(); String SavedActionName { get; }; String SavedActionKeyChord { get; }; diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index ce6fc576071..2feb9cd33a6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -180,11 +180,6 @@ IsLightDismissEnabled="True" Subtitle="{x:Bind WindowProperties.WindowNameForDisplay, Mode=OneWay}" /> - - +#include #include "TerminalWindow.g.cpp" #include "SettingsLoadEventArgs.g.cpp" @@ -169,9 +169,9 @@ namespace winrt::TerminalApp::implementation } _root->SetStartupActions(actions); } - else + else if (_appArgs) { - _root->SetStartupActions(_appArgs.GetStartupActions()); + _root->SetStartupActions(_appArgs->ParsedArgs().GetStartupActions()); } } @@ -181,7 +181,7 @@ namespace winrt::TerminalApp::implementation // to register a handler to hear about the requests first and is all ready to receive // them before the COM server registers itself. Otherwise, the request might come // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs.IsHandoffListener()) + if (_appArgs && _appArgs->ParsedArgs().IsHandoffListener()) { _root->SetInboundListener(true); } @@ -219,6 +219,7 @@ namespace winrt::TerminalApp::implementation _root->Loaded({ get_weak(), &TerminalWindow::_OnLoaded }); _root->Initialized({ get_weak(), &TerminalWindow::_pageInitialized }); _root->WindowSizeChanged({ get_weak(), &TerminalWindow::_WindowSizeChanged }); + _root->RenameWindowRequested({ get_weak(), &TerminalWindow::_RenameWindowRequested }); _root->Create(); AppLogic::Current()->SettingsChanged({ get_weak(), &TerminalWindow::UpdateSettingsHandler }); @@ -569,7 +570,7 @@ namespace winrt::TerminalApp::implementation // --focusMode on the commandline here, and the mode in the settings. // Below, we'll also account for if focus mode was persisted into the // session for restoration. - bool focusMode = _appArgs.GetLaunchMode().value_or(_settings.GlobalSettings().LaunchMode()) == LaunchMode::FocusMode; + bool focusMode = _appArgs && _appArgs->ParsedArgs().GetLaunchMode().value_or(_settings.GlobalSettings().LaunchMode()) == LaunchMode::FocusMode; const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); if (const auto layout = LoadPersistedLayout()) @@ -589,13 +590,13 @@ namespace winrt::TerminalApp::implementation } } - if (_appArgs.GetSize().has_value() || (proposedSize.Width == 0 && proposedSize.Height == 0)) + if ((_appArgs && _appArgs->ParsedArgs().GetSize().has_value()) || (proposedSize.Width == 0 && proposedSize.Height == 0)) { // Use the default profile to determine how big of a window we need. const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) }; const til::size emptySize{}; - const auto commandlineSize = _appArgs.GetSize().value_or(emptySize); + const auto commandlineSize = _appArgs ? _appArgs->ParsedArgs().GetSize().value_or(emptySize) : til::size{}; proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), dpi, commandlineSize.width, @@ -668,7 +669,7 @@ namespace winrt::TerminalApp::implementation // GH#4620/#5801 - If the user passed --maximized or --fullscreen on the // commandline, then use that to override the value from the settings. const auto valueFromSettings = _settings.GlobalSettings().LaunchMode(); - const auto valueFromCommandlineArgs = _appArgs.GetLaunchMode(); + const auto valueFromCommandlineArgs = _appArgs ? _appArgs->ParsedArgs().GetLaunchMode() : std::nullopt; if (const auto layout = LoadPersistedLayout()) { if (layout.LaunchMode()) @@ -704,9 +705,9 @@ namespace winrt::TerminalApp::implementation } // Commandline args trump everything except for content bounds (tear-out) - if (_appArgs.GetPosition().has_value()) + if (_appArgs && _appArgs->ParsedArgs().GetPosition().has_value()) { - initialPosition = _appArgs.GetPosition().value(); + initialPosition = _appArgs->ParsedArgs().GetPosition().value(); } if (_contentBounds) @@ -715,11 +716,10 @@ namespace winrt::TerminalApp::implementation // that to determine the initial position of the window. This is // used when the user is dragging a tab out of the window, to create // a new window. - // - // contentBounds is in screen pixels, but that's okay! we want to - // return screen pixels out of here. Nailed it. + // BODY: Technically we don't have a guarantee to be within the XAML stack right now to be have as a view to reference. + const auto scale = static_cast(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel()); const auto bounds = _contentBounds.Value(); - initialPosition = { lroundf(bounds.X), lroundf(bounds.Y) }; + initialPosition = { lroundf(bounds.X * scale), lroundf(bounds.Y * scale) }; } return { initialPosition.X ? initialPosition.X.Value() : defaultInitialX, @@ -743,7 +743,7 @@ namespace winrt::TerminalApp::implementation return !_contentBounds && !hadPersistedPosition && _settings.GlobalSettings().CenterOnLaunch() && - !_appArgs.GetPosition().has_value(); + (_appArgs && !_appArgs->ParsedArgs().GetPosition().has_value()); } // Method Description: @@ -767,51 +767,36 @@ namespace winrt::TerminalApp::implementation // This may be called on a background thread, or the main thread, but almost // definitely not on OUR UI thread. - safe_void_coroutine TerminalWindow::UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args) + void TerminalWindow::UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args) { - // GH#17620: We have a bug somewhere where a window doesn't get unregistered from the window list. - // This causes UpdateSettings calls where the thread dispatcher is already null. - const auto dispatcher = _root->Dispatcher(); - if (!dispatcher) - { - co_return; - } - - const auto weakThis{ get_weak() }; - co_await wil::resume_foreground(dispatcher); - - // Back on our UI thread... - if (auto logic{ weakThis.get() }) - { - _settings = args.NewSettings(); + _settings = args.NewSettings(); - // Update the settings in TerminalPage - // We're on our UI thread right now, so this is safe - _root->SetSettings(_settings, true); + // Update the settings in TerminalPage + // We're on our UI thread right now, so this is safe + _root->SetSettings(_settings, true); - // Bubble the notification up to the AppHost, now that we've updated our _settings. - SettingsChanged.raise(*this, args); + // Bubble the notification up to the AppHost, now that we've updated our _settings. + SettingsChanged.raise(*this, args); - if (FAILED(args.Result())) - { - const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); - const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, - textKey, - gsl::narrow_cast(args.Result()), - args.ExceptionText()); - co_return; - } - else if (args.Result() == S_FALSE) - { - _ShowLoadWarningsDialog(args.Warnings()); - } - else if (args.Result() == S_OK) - { - DismissDialog(); - } - _RefreshThemeRoutine(); + if (FAILED(args.Result())) + { + const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); + _ShowLoadErrorsDialog(titleKey, + textKey, + gsl::narrow_cast(args.Result()), + args.ExceptionText()); + return; } + else if (args.Result() == S_FALSE) + { + _ShowLoadWarningsDialog(args.Warnings()); + } + else if (args.Result() == S_OK) + { + DismissDialog(); + } + _RefreshThemeRoutine(); } void TerminalWindow::_OpenSettingsUI() @@ -973,19 +958,6 @@ namespace winrt::TerminalApp::implementation } } - // Method Description: - // - Returns true if we should exit the application before even starting the - // window. We might want to do this if we're displaying an error message or - // the version string, or if we want to open the settings file. - // Arguments: - // - - // Return Value: - // - true iff we should exit the application before even starting the window - bool TerminalWindow::ShouldExitEarly() - { - return _appArgs.ShouldExitEarly(); - } - bool TerminalWindow::FocusMode() const { return _root ? _root->FocusMode() : false; @@ -1038,34 +1010,33 @@ namespace winrt::TerminalApp::implementation // Return Value: // - the result of the first command who's parsing returned a non-zero code, // or 0. (see TerminalWindow::_ParseArgs) - int32_t TerminalWindow::SetStartupCommandline(array_view args, - winrt::hstring cwd, - winrt::hstring env) + int32_t TerminalWindow::SetStartupCommandline(TerminalApp::CommandlineArgs args) { - _WindowProperties->SetInitialCwd(std::move(cwd)); - _WindowProperties->VirtualEnvVars(std::move(env)); + _appArgs = winrt::get_self(args); + auto& parsedArgs = _appArgs->ParsedArgs(); + + _WindowProperties->SetInitialCwd(_appArgs->CurrentDirectory()); + _WindowProperties->VirtualEnvVars(_appArgs->CurrentEnvironment()); // This is called in AppHost::ctor(), before we've created the window // (or called TerminalWindow::Initialize) - const auto result = _appArgs.ParseArgs(args); - if (result == 0) + if (_appArgs->ExitCode() == 0) { // If the size of the arguments list is 1, // then it contains only the executable name and no other arguments. - _hasCommandLineArguments = args.size() > 1; - _appArgs.ValidateStartupCommands(); + _hasCommandLineArguments = _appArgs->CommandlineRef().size() > 1; // DON'T pass the args into the page yet. It doesn't exist yet. // Instead, we'll handle that in Initialize, when we first instantiate the page. } // If we have a -s param passed to us to load a saved layout, cache that now. - if (const auto idx = _appArgs.GetPersistedLayoutIdx()) + if (const auto idx = parsedArgs.GetPersistedLayoutIdx()) { SetPersistedLayoutIdx(idx.value()); } - return result; + return _appArgs->ExitCode(); } void TerminalWindow::SetStartupContent(const winrt::hstring& content, @@ -1097,43 +1068,24 @@ namespace winrt::TerminalApp::implementation // Return Value: // - the result of the first command who's parsing returned a non-zero code, // or 0. (see TerminalWindow::_ParseArgs) - int32_t TerminalWindow::ExecuteCommandline(array_view args, - const winrt::hstring& cwd, - const winrt::hstring& env) + int32_t TerminalWindow::ExecuteCommandline(TerminalApp::CommandlineArgs args) { - ::TerminalApp::AppCommandlineArgs appArgs; - auto result = appArgs.ParseArgs(args); - if (result == 0) + _appArgs = winrt::get_self(args); + + if (_appArgs->ExitCode() == 0) { - auto actions = winrt::single_threaded_vector(std::move(appArgs.GetStartupActions())); + auto& parsedArgs = _appArgs->ParsedArgs(); + auto actions = winrt::single_threaded_vector(std::move(parsedArgs.GetStartupActions())); - _root->ProcessStartupActions(actions, false, cwd, env); + _root->ProcessStartupActions(actions, false, _appArgs->CurrentDirectory(), _appArgs->CurrentEnvironment()); - if (appArgs.IsHandoffListener()) + if (parsedArgs.IsHandoffListener()) { _root->SetInboundListener(true); } } // Return the result of parsing with commandline, though it may or may not be used. - return result; - } - - // Method Description: - // - If there were any errors parsing the commandline that was used to - // initialize the terminal, this will return a string containing that - // message. If there were no errors, this message will be blank. - // - If the user requested help on any command (using --help), this will - // contain the help message. - // - If the user requested the version number (using --version), this will - // contain the version string. - // Arguments: - // - - // Return Value: - // - the help text or error message for the provided commandline, if one - // exists, otherwise the empty string. - winrt::hstring TerminalWindow::ParseCommandlineMessage() - { - return winrt::to_hstring(_appArgs.GetExitMessage()); + return _appArgs->ExitCode(); } void TerminalWindow::SetPersistedLayoutIdx(const uint32_t idx) @@ -1205,14 +1157,6 @@ namespace winrt::TerminalApp::implementation } } - void TerminalWindow::RenameFailed() - { - if (_root) - { - _root->RenameFailed(); - } - } - void TerminalWindow::WindowName(const winrt::hstring& name) { const auto oldIsQuakeMode = _WindowProperties->IsQuakeWindow(); @@ -1368,6 +1312,11 @@ namespace winrt::TerminalApp::implementation WindowSizeChanged.raise(*this, args); } + void TerminalWindow::_RenameWindowRequested(const IInspectable&, const winrt::TerminalApp::RenameWindowRequestedArgs args) + { + WindowName(args.ProposedName()); + } + winrt::hstring WindowProperties::WindowName() const noexcept { return _WindowName; @@ -1428,7 +1377,7 @@ namespace winrt::TerminalApp::implementation bool WindowProperties::IsQuakeWindow() const noexcept { - return _WindowName == QuakeWindowName; + return _WindowName == L"_quake"; } }; diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 29eda951587..a722468a547 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -7,12 +7,10 @@ #include "SystemMenuChangeArgs.g.h" #include "WindowProperties.g.h" -#include "SettingsLoadEventArgs.h" +#include "Remoting.h" #include "TerminalPage.h" -#include "SettingsLoadEventArgs.h" -#include -#include +#include #ifdef UNIT_TESTING // fwdecl unittest classes @@ -75,16 +73,14 @@ namespace winrt::TerminalApp::implementation void PersistState(); - safe_void_coroutine UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args); + void UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args); bool HasCommandlineArguments() const noexcept; - int32_t SetStartupCommandline(array_view actions, winrt::hstring cwd, winrt::hstring env); + int32_t SetStartupCommandline(TerminalApp::CommandlineArgs args); void SetStartupContent(const winrt::hstring& content, const Windows::Foundation::IReference& contentBounds); - int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd, const winrt::hstring& env); + int32_t ExecuteCommandline(TerminalApp::CommandlineArgs args); void SetSettingsStartupArgs(const std::vector& actions); - winrt::hstring ParseCommandlineMessage(); - bool ShouldExitEarly(); bool ShouldImmediatelyHandoffToElevated(); void HandoffToElevated(); @@ -94,9 +90,7 @@ namespace winrt::TerminalApp::implementation void Maximized(bool newMaximized); bool AlwaysOnTop() const; bool AutoHideWindow(); - void IdentifyWindow(); - void RenameFailed(); std::optional LoadPersistedLayoutIdx() const; winrt::Microsoft::Terminal::Settings::Model::WindowLayout LoadPersistedLayout(); @@ -174,8 +168,8 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Controls::ContentDialog _dialog{ nullptr }; std::shared_mutex _dialogLock; + wil::com_ptr _appArgs{ nullptr }; bool _hasCommandLineArguments{ false }; - ::TerminalApp::AppCommandlineArgs _appArgs; bool _gotSettingsStartupActions{ false }; std::vector _settingsStartupArgs{}; Windows::Foundation::IReference _contentBounds{ nullptr }; @@ -204,6 +198,7 @@ namespace winrt::TerminalApp::implementation void _pageInitialized(const IInspectable& sender, const IInspectable& eventArgs); void _OpenSettingsUI(); void _WindowSizeChanged(const IInspectable& sender, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs args); + void _RenameWindowRequested(const IInspectable& sender, const winrt::TerminalApp::RenameWindowRequestedArgs args); winrt::Windows::Foundation::Collections::IVector _contentStringToActions(const winrt::hstring& content, const bool replaceFirstWithNewTab); @@ -223,7 +218,6 @@ namespace winrt::TerminalApp::implementation FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell); FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress); FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested); - FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested); FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested); FORWARDED_TYPED_EVENT(CloseRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, CloseRequested); FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu); diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index 8b1bca78f94..24ce090e378 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -52,11 +52,9 @@ namespace TerminalApp Boolean HasCommandlineArguments(); - Int32 SetStartupCommandline(String[] commands, String cwd, String env); + Int32 SetStartupCommandline(CommandlineArgs args); void SetStartupContent(String json, Windows.Foundation.IReference bounds); - Int32 ExecuteCommandline(String[] commands, String cwd, String env); - String ParseCommandlineMessage { get; }; - Boolean ShouldExitEarly { get; }; + Int32 ExecuteCommandline(CommandlineArgs args); Boolean ShouldImmediatelyHandoffToElevated(); void HandoffToElevated(); @@ -74,8 +72,6 @@ namespace TerminalApp void IdentifyWindow(); void SetPersistedLayoutIdx(UInt32 idx); - - void RenameFailed(); void RequestExitFullscreen(); Windows.Foundation.Size GetLaunchDimensions(UInt32 dpi); @@ -124,7 +120,6 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RaiseVisualBell; event Windows.Foundation.TypedEventHandler SetTaskbarProgress; event Windows.Foundation.TypedEventHandler IdentifyWindowsRequested; - event Windows.Foundation.TypedEventHandler RenameWindowRequested; event Windows.Foundation.TypedEventHandler IsQuakeWindowChanged; event Windows.Foundation.TypedEventHandler SummonWindowRequested; event Windows.Foundation.TypedEventHandler CloseRequested; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index d7bf9f496a4..88f5eca1e43 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -5,6 +5,7 @@ #include "TermControl.h" #include +#include #include "TermControlAutomationPeer.h" #include "../../renderer/atlas/AtlasEngine.h" @@ -46,13 +47,110 @@ constexpr std::wstring_view StateCollapsed{ L"Collapsed" }; DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::CopyFormat); DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState); +// WinUI 3's UIElement.ProtectedCursor property allows someone to set the cursor on a per-element basis. +// This would allow us to hide the cursor when the TermControl has input focus and someone starts typing. +// Unfortunately, no equivalent exists for WinUI 2 so we fake it with the CoreWindow. +// There are 3 downsides: +// * SetPointerCapture() is global state and may interfere with other components. +// * You can't start dragging the cursor (for text selection) while it's still hidden. +// * The CoreWindow covers the union of all window rectangles, so the cursor is hidden even if it's outside +// the current foreground window, but still on top of another Terminal window in the background. +static void hideCursorUntilMoved() +{ + static CoreCursor previousCursor{ nullptr }; + static auto shouldVanish = []() { + BOOL shouldVanish = TRUE; + SystemParametersInfoW(SPI_GETMOUSEVANISH, 0, &shouldVanish, 0); + if (!shouldVanish) + { + return false; + } + + const auto window = CoreWindow::GetForCurrentThread(); + static constexpr auto releaseCapture = [](CoreWindow window, PointerEventArgs) { + if (previousCursor) + { + window.ReleasePointerCapture(); + } + }; + static constexpr auto restoreCursor = [](CoreWindow window, PointerEventArgs) { + if (previousCursor) + { + window.PointerCursor(previousCursor); + previousCursor = nullptr; + } + }; + + winrt::Windows::Foundation::TypedEventHandler releaseCaptureHandler{ releaseCapture }; + std::ignore = window.PointerMoved(releaseCaptureHandler); + std::ignore = window.PointerPressed(releaseCaptureHandler); + std::ignore = window.PointerReleased(releaseCaptureHandler); + std::ignore = window.PointerWheelChanged(releaseCaptureHandler); + std::ignore = window.PointerCaptureLost(restoreCursor); + return true; + }(); + + if (shouldVanish && !previousCursor) + { + try + { + const auto window = CoreWindow::GetForCurrentThread(); + + previousCursor = window.PointerCursor(); + if (!previousCursor) + { + return; + } + + window.PointerCursor(nullptr); + window.SetPointerCapture(); + } + catch (...) + { + // Swallow the 0x80070057 "Failed to get pointer information." exception that randomly occurs. + // Curiously, it doesn't happen during the PointerCursor() but during the SetPointerCapture() call (thanks, WinUI). + } + } +} + +// InputPane::GetForCurrentView() does not reliably work for XAML islands, +// as it assumes that there's a 1:1 relationship between windows and threads. +// +// During testing, I found that the input pane shows up when touching into the terminal even if +// TryShow is never called. This surprised me, but I figured it's worth trying it without this. +static void setInputPaneVisibility(HWND hwnd, bool visible) +{ + static const auto inputPaneInterop = []() { + return winrt::try_get_activation_factory(); + }(); + + if (!inputPaneInterop) + { + return; + } + + winrt::com_ptr inputPane; + if (FAILED(inputPaneInterop->GetForWindow(hwnd, winrt::guid_of(), inputPane.put_void()))) + { + return; + } + + bool result; + if (visible) + { + std::ignore = inputPane->TryShow(&result); + } + else + { + std::ignore = inputPane->TryHide(&result); + } +} + static Microsoft::Console::TSF::Handle& GetTSFHandle() { - // https://en.cppreference.com/w/cpp/language/storage_duration - // > Variables declared at block scope with the specifier static or thread_local - // > [...] are initialized the first time control passes through their declaration - // --> Lazy, per-(window-)thread initialization of the TSF handle - thread_local auto s_tsf = ::Microsoft::Console::TSF::Handle::Create(); + // NOTE: If we ever go back to 1 thread per 1 window, + // you need to swap the `static` with a `thread_local`. + static auto s_tsf = ::Microsoft::Console::TSF::Handle::Create(); return s_tsf; } @@ -144,22 +242,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation RECT TsfDataProvider::GetViewport() { - const auto scaleFactor = static_cast(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel()); - const auto globalOrigin = CoreWindow::GetForCurrentThread().Bounds(); - const auto localOrigin = _termControl->TransformToVisual(nullptr).TransformPoint({}); - const auto size = _termControl->ActualSize(); - - const auto left = globalOrigin.X + localOrigin.X; - const auto top = globalOrigin.Y + localOrigin.Y; - const auto right = left + size.x; - const auto bottom = top + size.y; - - return { - lroundf(left * scaleFactor), - lroundf(top * scaleFactor), - lroundf(right * scaleFactor), - lroundf(bottom * scaleFactor), - }; + const auto hwnd = reinterpret_cast(_termControl->OwningHwnd()); + RECT clientRect; + GetWindowRect(hwnd, &clientRect); + return clientRect; } RECT TsfDataProvider::GetCursorPosition() @@ -170,16 +256,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation return {}; } + const auto hwnd = reinterpret_cast(_termControl->OwningHwnd()); + RECT clientRect; + GetWindowRect(hwnd, &clientRect); + const auto scaleFactor = static_cast(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel()); - const auto globalOrigin = CoreWindow::GetForCurrentThread().Bounds(); const auto localOrigin = _termControl->TransformToVisual(nullptr).TransformPoint({}); const auto padding = _termControl->GetPadding(); const auto cursorPosition = core->CursorPosition(); const auto fontSize = core->FontSize(); // fontSize is not in DIPs, so we need to first multiply by scaleFactor and then do the rest. - const auto left = (globalOrigin.X + localOrigin.X + static_cast(padding.Left)) * scaleFactor + cursorPosition.X * fontSize.Width; - const auto top = (globalOrigin.Y + localOrigin.Y + static_cast(padding.Top)) * scaleFactor + cursorPosition.Y * fontSize.Height; + const auto left = clientRect.left + (localOrigin.X + static_cast(padding.Left)) * scaleFactor + cursorPosition.X * fontSize.Width; + const auto top = clientRect.top + (localOrigin.Y + static_cast(padding.Top)) * scaleFactor + cursorPosition.Y * fontSize.Height; const auto right = left + fontSize.Width; const auto bottom = top + fontSize.Height; @@ -756,40 +845,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - Given Settings having been updated, applies the settings to the current terminal. // Return Value: // - - safe_void_coroutine TermControl::UpdateControlSettings(IControlSettings settings, - IControlAppearance unfocusedAppearance) + void TermControl::UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance) { - auto weakThis{ get_weak() }; - - // Dispatch a call to the UI thread to apply the new settings to the - // terminal. - co_await wil::resume_foreground(Dispatcher()); - - if (auto strongThis{ weakThis.get() }) - { - _core.UpdateSettings(settings, unfocusedAppearance); + _core.UpdateSettings(settings, unfocusedAppearance); - _UpdateSettingsFromUIThread(); + _UpdateSettingsFromUIThread(); - _UpdateAppearanceFromUIThread(_focused ? _core.FocusedAppearance() : _core.UnfocusedAppearance()); - } + _UpdateAppearanceFromUIThread(_focused ? _core.FocusedAppearance() : _core.UnfocusedAppearance()); } // Method Description: // - Dispatches a call to the UI thread and updates the appearance // Arguments: // - newAppearance: the new appearance to set - safe_void_coroutine TermControl::UpdateAppearance(IControlAppearance newAppearance) + void TermControl::UpdateAppearance(IControlAppearance newAppearance) { - auto weakThis{ get_weak() }; - - // Dispatch a call to the UI thread - co_await wil::resume_foreground(Dispatcher()); - - if (auto strongThis{ weakThis.get() }) - { - _UpdateAppearanceFromUIThread(newAppearance); - } + _UpdateAppearanceFromUIThread(newAppearance); } // Method Description: @@ -1474,7 +1545,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - HidePointerCursor.raise(*this, nullptr); + hideCursorUntilMoved(); const auto ch = e.Character(); const auto keyStatus = e.KeyStatus(); @@ -1883,8 +1954,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown) { - const auto window = CoreWindow::GetForCurrentThread(); - // If the terminal translated the key, mark the event as handled. // This will prevent the system from trying to get the character out // of it and sending us a CharacterReceived event. @@ -1919,6 +1988,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_TappedHandler(const IInspectable& /*sender*/, const TappedRoutedEventArgs& e) { Focus(FocusState::Pointer); + + if (e.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch) + { + // Normally TSF would be responsible for showing the touch keyboard, but it's buggy for us: + // If you have focus on a TermControl and type on your physical keyboard then touching + // the TermControl will not show the touch keyboard ever again unless you focus another app. + // Why that happens is unclear, but it can be fixed by us showing it manually. + setInputPaneVisibility(reinterpret_cast(OwningHwnd()), true); + } + e.Handled(true); } @@ -1943,12 +2022,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto point = args.GetCurrentPoint(*this); const auto type = ptr.PointerDeviceType(); - // We also TryShow in GotFocusHandler, but this call is specifically - // for the case where the Terminal is in focus but the user closed the - // on-screen keyboard. This lets the user simply tap on the terminal - // again to bring it up. - InputPane::GetForCurrentView().TryShow(); - if (!_focused) { Focus(FocusState::Pointer); @@ -2170,10 +2243,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - // Return Value: // - - safe_void_coroutine TermControl::_coreTransparencyChanged(IInspectable /*sender*/, - Control::TransparencyChangedEventArgs /*args*/) + void TermControl::_coreTransparencyChanged(IInspectable /*sender*/, Control::TransparencyChangedEventArgs /*args*/) { - co_await wil::resume_foreground(Dispatcher()); try { _changeBackgroundOpacity(); @@ -2351,8 +2422,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _focused = true; - InputPane::GetForCurrentView().TryShow(); - // GH#5421: Enable the UiaEngine before checking for the SearchBox // That way, new selections are notified to automation clients. // The _uiaEngine lives in _interactivity, so call into there to enable it. @@ -3978,14 +4047,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_contextMenuHandler(IInspectable /*sender*/, Control::ContextMenuRequestedEventArgs args) { - // Position the menu where the pointer is. This was the best way I found how. - const auto absolutePointerPos = CoreWindow::GetForCurrentThread().PointerPosition(); - const auto absoluteWindowOrigin = CoreWindow::GetForCurrentThread().Bounds(); - // Get the offset (margin + tabs, etc..) of the control within the window - const auto controlOrigin = TransformToVisual(nullptr).TransformPoint({}); + const auto inverseScale = 1.0f / static_cast(XamlRoot().RasterizationScale()); + const auto padding = GetPadding(); + const auto pos = args.Position(); _showContextMenuAt({ - absolutePointerPos.X - absoluteWindowOrigin.X - controlOrigin.X, - absolutePointerPos.Y - absoluteWindowOrigin.Y - controlOrigin.Y, + pos.X * inverseScale + static_cast(padding.Left), + pos.Y * inverseScale + static_cast(padding.Top), }); } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 42398b751f0..9a080f138be 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -53,7 +53,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation static Control::TermControl NewControlByAttachingContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings); void UpdateControlSettings(Control::IControlSettings settings); - safe_void_coroutine UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance); + void UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance); IControlSettings Settings() const; uint64_t ContentId() const; @@ -345,7 +345,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _UpdateAppearanceFromUIThread(Control::IControlAppearance newAppearance); void _ApplyUISettings(); - safe_void_coroutine UpdateAppearance(Control::IControlAppearance newAppearance); + void UpdateAppearance(Control::IControlAppearance newAppearance); void _SetBackgroundImage(const IControlAppearance& newAppearance); void _InitializeBackgroundBrush(); @@ -426,7 +426,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation safe_void_coroutine _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args); void _coreFontSizeChanged(const IInspectable& s, const Control::FontSizeChangedArgs& args); - safe_void_coroutine _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); + void _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args); void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args); void _coreWarningBell(const IInspectable& sender, const IInspectable& args); void _coreOutputIdle(const IInspectable& sender, const IInspectable& args); diff --git a/src/cascadia/TerminalSettingsEditor/Compatibility.h b/src/cascadia/TerminalSettingsEditor/Compatibility.h index 018bb276ceb..a95dbc7d2e1 100644 --- a/src/cascadia/TerminalSettingsEditor/Compatibility.h +++ b/src/cascadia/TerminalSettingsEditor/Compatibility.h @@ -21,7 +21,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation using ViewModelHelper::PropertyChanged; PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AllowHeadless); - PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, IsolatedMode); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, DebugFeaturesEnabled); GETSET_BINDABLE_ENUM_SETTING(TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement, _GlobalSettings.TextMeasurement); diff --git a/src/cascadia/TerminalSettingsEditor/Compatibility.idl b/src/cascadia/TerminalSettingsEditor/Compatibility.idl index a05ed30e7ad..88c030e1d5c 100644 --- a/src/cascadia/TerminalSettingsEditor/Compatibility.idl +++ b/src/cascadia/TerminalSettingsEditor/Compatibility.idl @@ -14,7 +14,6 @@ namespace Microsoft.Terminal.Settings.Editor Boolean DebugFeaturesAvailable { get; }; PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, AllowHeadless); - PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, IsolatedMode); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, DebugFeaturesEnabled); IInspectable CurrentTextMeasurement; diff --git a/src/cascadia/TerminalSettingsEditor/Compatibility.xaml b/src/cascadia/TerminalSettingsEditor/Compatibility.xaml index b982cd50d30..7345cdd2e9d 100644 --- a/src/cascadia/TerminalSettingsEditor/Compatibility.xaml +++ b/src/cascadia/TerminalSettingsEditor/Compatibility.xaml @@ -31,12 +31,6 @@ Style="{StaticResource ToggleSwitchInExpanderStyle}" /> - - - - - Ausführung von Windows Terminal im Hintergrund zulassen Header for a control to toggle support for Windows Terminal to run in the background. - - Ausführung jedes Fensters in einem eigenen Prozess erzwingen - Header for a control to toggle making each window be its own process. - Debugfeatures aktivieren Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Auf diese Weise können Aktionen wie globale Korrektur- und Quakemodusaktionen auch dann funktionieren, wenn keine Fenster geöffnet sind. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Bestimmte Funktionen funktionieren nicht ordnungsgemäß, darunter (jedoch nicht beschränkt auf) globale Hotkeys, Drag &amp; Drop von Registerkarten und die Fensterausrichtung von wt.exe. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Modus Registerkartenbreite Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 52e359a946f..0ccba775141 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -572,10 +572,6 @@ Allow Windows Terminal to run in the background Header for a control to toggle support for Windows Terminal to run in the background. - - Force each window to run in its own process - Header for a control to toggle making each window be its own process. - Enable debugging features Header for a control to toggle debug features. @@ -592,10 +588,6 @@ This allows actions such as Global Summon and Quake Mode actions to work even when no windows are open. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Certain features will not work properly, including—but not limited to—global hotkeys, tab drag and drop, and wt.exe window targeting. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Tab width mode Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw index b3a07bbc663..3353c4dd704 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/es-ES/Resources.resw @@ -572,10 +572,6 @@ Permitir que Terminal Windows se ejecuten en segundo plano Header for a control to toggle support for Windows Terminal to run in the background. - - Forzar que cada ventana se ejecute en su propio proceso - Header for a control to toggle making each window be its own process. - Habilitar características de depuración Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Esto permite que acciones como las acciones de la convocatoria global y el modo de conferencia funcionen incluso cuando no hay ventanas abiertas. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Algunas características no funcionarán correctamente, incluidas(pero no limitadas a) teclas de acceso rápido globales, arrastrar y colocar pestañas y wt.exe destino de ventana. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Modo de ancho de pestaña Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw index 3d21eb966ee..d65b5fcfbe3 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/fr-FR/Resources.resw @@ -572,10 +572,6 @@ Autoriser Terminal Windows à s’exécuter en arrière-plan Header for a control to toggle support for Windows Terminal to run in the background. - - Forcer chaque fenêtre à s’exécuter dans son propre processus - Header for a control to toggle making each window be its own process. - Activer les fonctionnalités de débogage Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Cela permet aux actions telles que les actions en mode d’engagement global et de mode Dent de fonctionner même lorsqu’aucune fenêtre n’est ouverte. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Certaines fonctionnalités ne fonctionnent pas correctement, notamment les touches de raccourci globales, le glisser-déplacer des onglets et le ciblage de fenêtres wt.exe. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Largeur des onglets Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw index 62ca9bbc2c6..e1631f64c87 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/it-IT/Resources.resw @@ -572,10 +572,6 @@ Consenti l'esecuzione di Terminale Windows in background Header for a control to toggle support for Windows Terminal to run in the background. - - Forza l'esecuzione di ogni finestra nel proprio processo - Header for a control to toggle making each window be its own process. - Abilita funzionalità di debug Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Questo consente ad azioni quali richiamo globale e modalità quake di funzionare anche quando non sono aperte finestre. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Alcune funzionalità non funzioneranno correttamente, tra cui tasti di scelta rapida globali, trascinamento tabulazione e wt.exe destinazione finestra. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Modalità larghezza tabulazione Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw index e802f35d28a..49c42b1ef67 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ja-JP/Resources.resw @@ -572,10 +572,6 @@ Windows ターミナルのバックグラウンドでの実行を許可する Header for a control to toggle support for Windows Terminal to run in the background. - - 各ウィンドウを独自のプロセスで強制的に実行する - Header for a control to toggle making each window be its own process. - デバッグ機能を有効にする Header for a control to toggle debug features. @@ -592,10 +588,6 @@ これにより、ウィンドウが開かなくても、グローバル デコレーションや Quake モードのアクションなどのアクションを実行できます。 Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - グローバル ホットキー、タブ ドラッグ アンド ドロップ、ウィンドウターゲット設定など、一部の機能は正しく動作しません(ただし wt.exe、これらに限定されません)。 - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - タブ幅モード Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw index 6fff21ae59b..55a21c11d94 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ko-KR/Resources.resw @@ -572,10 +572,6 @@ Windows 터미널 백그라운드에서 실행하도록 허용 Header for a control to toggle support for Windows Terminal to run in the background. - - 각 창이 자체 프로세스에서 실행되도록 강제 적용 - Header for a control to toggle making each window be its own process. - 디버깅 기능 사용 Header for a control to toggle debug features. @@ -592,10 +588,6 @@ 이렇게 하면 창이 열려 있지 않아도 전역 관리자 및 관리자 모드 작업과 같은 작업이 작동할 수 있습니다. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - 전역 바로 가기 키, 탭 끌어서 놓기, 창 대상 지정 wt.exe 포함하지만 제한되지 않는 특정 기능은 제대로 작동하지 않습니다. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - 탭 너비 모드 Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw index 2f225ef84ad..4827b404013 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/pt-BR/Resources.resw @@ -572,10 +572,6 @@ Permitir Terminal do Windows ser executado em segundo plano Header for a control to toggle support for Windows Terminal to run in the background. - - Forçar cada janela a ser executada em seu próprio processo - Header for a control to toggle making each window be its own process. - Habilitar recursos de depuração Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Isso permite que ações como Ações de Bloqueio Global e Modo de Quake funcionem mesmo quando nenhuma janela estiver aberta. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Determinados recursos não funcionarão corretamente, incluindo, mas não se limitando a, teclas de atalho globais, arrastar e soltar guias e wt.exe de janela. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Modo da largura da guia Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw index e3950288134..dae98cab51d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploc/Resources.resw @@ -572,10 +572,6 @@ Ǻℓĺŏщ Ẁιйđòώѕ Тёŗмїⁿàļ тǿ řϋŋ ϊň тђě ьąςќġѓőůήď !!! !!! !!! !!! !! Header for a control to toggle support for Windows Terminal to run in the background. - - ₣ōґçê éãćĥ шĭňđθẁ тǿ ґυή ĭñ ïťş ǿŵʼn ρŕόčэşś !!! !!! !!! !!! - Header for a control to toggle making each window be its own process. - Єлάвĺé ðèъûġğįñģ ƒέàŧµřэš !!! !!! ! Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Ťнϊš äłℓσωš ąçťїőňš ŝΰ¢ђ ãѕ Ğℓøъâļ Ŝûmmǿл αⁿð Qũăķэ Μöδе äçťϊõпѕ το ŵθřќ èνεπ ẅћěи πό ẁįňδòŵš άяę öрęŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Ćěřţªіʼn ƒěąτùŕєś ẅіľĺ иοŧ ώóѓķ ρřσрèřĺỳ, ίпčļūďîйğ—ьϋŧ ņότ ĺîmìτĕđ ţõ—ğℓõъªł ћотĸëўŝ, ŧªъ δяªģ äлđ δřőρ, ąŋď wt.exe щїлďǿш ţäŕġэтīπġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Ťâв ώΐđťн mбðě !!! ! Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw index e3950288134..dae98cab51d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-ploca/Resources.resw @@ -572,10 +572,6 @@ Ǻℓĺŏщ Ẁιйđòώѕ Тёŗмїⁿàļ тǿ řϋŋ ϊň тђě ьąςќġѓőůήď !!! !!! !!! !!! !! Header for a control to toggle support for Windows Terminal to run in the background. - - ₣ōґçê éãćĥ шĭňđθẁ тǿ ґυή ĭñ ïťş ǿŵʼn ρŕόčэşś !!! !!! !!! !!! - Header for a control to toggle making each window be its own process. - Єлάвĺé ðèъûġğįñģ ƒέàŧµřэš !!! !!! ! Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Ťнϊš äłℓσωš ąçťїőňš ŝΰ¢ђ ãѕ Ğℓøъâļ Ŝûmmǿл αⁿð Qũăķэ Μöδе äçťϊõпѕ το ŵθřќ èνεπ ẅћěи πό ẁįňδòŵš άяę öрęŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Ćěřţªіʼn ƒěąτùŕєś ẅіľĺ иοŧ ώóѓķ ρřσрèřĺỳ, ίпčļūďîйğ—ьϋŧ ņότ ĺîmìτĕđ ţõ—ğℓõъªł ћотĸëўŝ, ŧªъ δяªģ äлđ δřőρ, ąŋď wt.exe щїлďǿш ţäŕġэтīπġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Ťâв ώΐđťн mбðě !!! ! Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw index e3950288134..dae98cab51d 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/qps-plocm/Resources.resw @@ -572,10 +572,6 @@ Ǻℓĺŏщ Ẁιйđòώѕ Тёŗмїⁿàļ тǿ řϋŋ ϊň тђě ьąςќġѓőůήď !!! !!! !!! !!! !! Header for a control to toggle support for Windows Terminal to run in the background. - - ₣ōґçê éãćĥ шĭňđθẁ тǿ ґυή ĭñ ïťş ǿŵʼn ρŕόčэşś !!! !!! !!! !!! - Header for a control to toggle making each window be its own process. - Єлάвĺé ðèъûġğįñģ ƒέàŧµřэš !!! !!! ! Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Ťнϊš äłℓσωš ąçťїőňš ŝΰ¢ђ ãѕ Ğℓøъâļ Ŝûmmǿл αⁿð Qũăķэ Μöδе äçťϊõпѕ το ŵθřќ èνεπ ẅћěи πό ẁįňδòŵš άяę öрęŋ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Ćěřţªіʼn ƒěąτùŕєś ẅіľĺ иοŧ ώóѓķ ρřσрèřĺỳ, ίпčļūďîйğ—ьϋŧ ņότ ĺîmìτĕđ ţõ—ğℓõъªł ћотĸëўŝ, ŧªъ δяªģ äлđ δřőρ, ąŋď wt.exe щїлďǿш ţäŕġэтīπġ. !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Ťâв ώΐđťн mбðě !!! ! Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw index dafd0223b28..259001b5c85 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/ru-RU/Resources.resw @@ -572,10 +572,6 @@ Разрешить запуск терминала Windows в фоновом режиме Header for a control to toggle support for Windows Terminal to run in the background. - - Принудительное выполнение каждого окна в отдельном процессе - Header for a control to toggle making each window be its own process. - Включить функции отладки Header for a control to toggle debug features. @@ -592,10 +588,6 @@ Это позволяет выполнять такие действия, как глобальный вызов и режим землетрясения, даже если ни одно окно не открыто. Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - Некоторые функции будут работать неправильно, включая глобальные сочетания клавиш, перетаскивание вкладок и wt.exe нацеливание окна. - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - Режим ширины вкладки Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw index fe3119fedd6..7c261996728 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-CN/Resources.resw @@ -572,10 +572,6 @@ 允许Windows 终端在后台运行 Header for a control to toggle support for Windows Terminal to run in the background. - - 强制每个窗口在其自己的进程中运行 - Header for a control to toggle making each window be its own process. - 启用调试功能 Header for a control to toggle debug features. @@ -592,10 +588,6 @@ 通过此设置,即使没有打开任何窗口,全局召唤等操作和震动模式操作也可以正常工作。 Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - 某些功能将无法正常工作,包括全局热键、选项卡拖放以及 wt.exe 窗口目标。 - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - 选项卡宽度 Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw index 7ec7e50e1e6..8229baea709 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/zh-TW/Resources.resw @@ -572,10 +572,6 @@ 允許 Windows 終端機 在背景執行 Header for a control to toggle support for Windows Terminal to run in the background. - - 強制每個視窗在自己的處理程式中執行 - Header for a control to toggle making each window be its own process. - 啟用偵錯功能 Header for a control to toggle debug features. @@ -592,10 +588,6 @@ 這可允許全域傳送與「受擷取模式」等動作運作,即使沒有開啟視窗也可運作。 Additional description for what the "allow headless" setting does. Presented near "Globals_AllowHeadless.Header". - - 某些功能將無法正常運作,包括但不限於全域快捷鍵、索引標籤拖放以及 wt.exe 窗口目標。 - {Locked="wt.exe"} Additional description for what the "isolated mode" setting does. Presented near "Globals_IsolatedMode.Header". - 索引標籤寬度模式 Header for a control to choose how wide the tabs are. diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index cd854b19cf3..8b7849f3aef 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -56,7 +56,6 @@ #include "JsonUtils.h" #include "HashUtils.h" #include "TerminalWarnings.h" -#include "../inc/WindowingBehavior.h" #include "TerminalSettingsSerializationHelpers.h" @@ -852,7 +851,7 @@ 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(); // We want to summon the window with the name "_quake" specifically. - args->_Name = QuakeWindowName; + args->_Name = L"_quake"; // We want the window to dropdown, with a 200ms duration. args->_DropdownDuration = 200; return { *args, {} }; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 7ea21b045da..fb9fc5566e1 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -359,7 +359,7 @@ void GlobalAppSettings::ExpandCommands(const winrt::Windows::Foundation::Collect bool GlobalAppSettings::ShouldUsePersistedLayout() const { - return FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout && !IsolatedMode(); + return FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; } void GlobalAppSettings::_logSettingSet(const std::string_view& setting) diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index e212fc6181c..b04073273ba 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -101,7 +101,6 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, EnableColorSelection); INHERITABLE_SETTING(Boolean, EnableShellCompletionMenu); INHERITABLE_SETTING(Boolean, EnableUnfocusedAcrylic); - INHERITABLE_SETTING(Boolean, IsolatedMode); INHERITABLE_SETTING(Boolean, AllowHeadless); INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 820e426c4d3..c2553db2e47 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -68,7 +68,6 @@ Author(s): X(bool, EnableUnfocusedAcrylic, "compatibility.enableUnfocusedAcrylic", true) \ X(winrt::Windows::Foundation::Collections::IVector, NewTabMenu, "newTabMenu", winrt::single_threaded_vector({ Model::RemainingProfilesEntry{} })) \ X(bool, AllowHeadless, "compatibility.allowHeadless", false) \ - X(bool, IsolatedMode, "compatibility.isolatedMode", false) \ X(hstring, SearchWebDefaultQueryUrl, "searchWebDefaultQueryUrl", L"https://www.bing.com/search?q=%22%s%22") // Also add these settings to: diff --git a/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj deleted file mode 100644 index 91ed42a2c53..00000000000 --- a/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - {68a10cd3-aa64-465b-af5f-ed4e9700543c} - Win32Proj - RemotingUnitTests - UnitTests_Remoting - Remoting.Unit.Tests - DynamicLibrary - true - - - - - - - - - - - - - - - - - Create - - - - - - - - - - - - - - - - - - ..;$(OpenConsoleDir)\dep;$(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/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp deleted file mode 100644 index 4078363a9da..00000000000 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ /dev/null @@ -1,2604 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "../Remoting/Monarch.h" -#include "../Remoting/CommandlineArgs.h" -#include "../Remoting/FindTargetWindowArgs.h" -#include "../Remoting/ProposeCommandlineResult.h" -#include "../inc/WindowingBehavior.h" - -using namespace Microsoft::Console; -using namespace WEX::Logging; -using namespace WEX::TestExecution; -using namespace WEX::Common; - -using namespace winrt; -using namespace winrt::Microsoft::Terminal; - -namespace RemotingUnitTests -{ - struct MockDesktopManager : implements - { - IFACEMETHOD(GetWindowDesktopId) - (HWND /*topLevelWindow*/, GUID* /*desktopId*/) - { - VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests."); - return E_FAIL; - } - IFACEMETHOD(MoveWindowToDesktop) - (HWND /*topLevelWindow*/, REFGUID /*desktopId*/) - { - VERIFY_IS_TRUE(false, L"We shouldn't need GetWindowDesktopId in the tests."); - return E_FAIL; - } - IFACEMETHOD(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; - }; - -#define DIE \ - { \ - throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); \ - }; - - // This is a silly helper struct. - // It will always throw an hresult_error of "RPC server is unavailable" on any of its methods. - // The monarch uses this particular error code to check for a dead peasant vs another 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*/) DIE; - uint64_t GetID() DIE; - winrt::hstring WindowName() DIE; - winrt::hstring ActiveTabTitle() DIE; - void ActiveTabTitle(const winrt::hstring& /*value*/) DIE; - uint64_t GetPID() DIE; - bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) DIE; - void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) DIE; - void RequestIdentifyWindows() DIE; - void DisplayWindowId() DIE; - Remoting::CommandlineArgs InitialArgs() DIE; - Remoting::WindowActivatedArgs GetLastActivatedArgs() DIE; - void RequestRename(const Remoting::RenameRequestArgs& /*args*/) DIE; - void Summon(const Remoting::SummonWindowBehavior& /*args*/) DIE; - void RequestShowNotificationIcon() DIE; - void RequestHideNotificationIcon() DIE; - void Quit() DIE; - void AttachContentToWindow(Remoting::AttachRequest) DIE; - void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs) DIE; - til::typed_event WindowActivated; - til::typed_event ExecuteCommandlineRequested; - til::typed_event<> IdentifyWindowsRequested; - til::typed_event<> DisplayWindowIdRequested; - til::typed_event RenameRequested; - til::typed_event SummonRequested; - til::typed_event<> ShowNotificationIconRequested; - til::typed_event<> HideNotificationIconRequested; - til::typed_event<> QuitRequested; - til::typed_event AttachRequested; - til::typed_event SendContentRequested; - }; - - // Same idea. - struct DeadMonarch : implements - { - DeadMonarch() = default; - uint64_t GetPID() DIE; - uint64_t AddPeasant(Remoting::IPeasant /*peasant*/) DIE; - uint64_t GetNumberOfPeasants() DIE; - Remoting::ProposeCommandlineResult ProposeCommandline(Remoting::CommandlineArgs /*args*/) DIE; - void HandleActivatePeasant(Remoting::WindowActivatedArgs /*args*/) DIE; - void SummonWindow(Remoting::SummonWindowSelectionArgs /*args*/) DIE; - void SignalClose(uint64_t /*peasantId*/) DIE; - void QuitAll() DIE; - - void SummonAllWindows() DIE; - bool DoesQuakeWindowExist() DIE; - winrt::Windows::Foundation::Collections::IVectorView GetPeasantInfos() DIE; - void RequestMoveContent(winrt::hstring, winrt::hstring, uint32_t, winrt::Windows::Foundation::IReference) DIE; - void RequestSendContent(Remoting::RequestReceiveContentArgs) DIE; - - til::typed_event FindTargetWindowRequested; - til::typed_event<> ShowNotificationIconRequested; - til::typed_event<> HideNotificationIconRequested; - til::typed_event<> WindowCreated; - til::typed_event<> WindowClosed; - til::typed_event RequestNewWindow; - }; - - class RemotingTests - { - BEGIN_TEST_CLASS(RemotingTests) - END_TEST_CLASS() - - TEST_METHOD(CreateMonarch); - TEST_METHOD(CreatePeasant); - TEST_METHOD(CreatePeasantWithNew); - TEST_METHOD(AddPeasants); - TEST_METHOD(GetPeasantsByID); - 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_METHOD(MostRecentWindowSameDesktops); - TEST_METHOD(MostRecentWindowDifferentDesktops); - TEST_METHOD(MostRecentWindowMoveDesktops); - TEST_METHOD(GetMostRecentAnyDesktop); - TEST_METHOD(MostRecentIsDead); - - TEST_METHOD(MostRecentIsQuake); - - TEST_METHOD(GetPeasantsByName); - TEST_METHOD(AddNamedPeasantsToNewMonarch); - TEST_METHOD(LookupNamedPeasantWhenOthersDied); - TEST_METHOD(LookupNamedPeasantWhenItDied); - TEST_METHOD(GetMruPeasantAfterNameLookupForDeadPeasant); - - TEST_METHOD(ProposeCommandlineForNamedDeadWindow); - - TEST_METHOD(TestRenameWindowSuccessfully); - TEST_METHOD(TestRenameSameNameAsAnother); - TEST_METHOD(TestRenameSameNameAsADeadPeasant); - - TEST_METHOD(TestSummonMostRecentWindow); - TEST_METHOD(TestSummonNamedWindow); - TEST_METHOD(TestSummonNamedDeadWindow); - TEST_METHOD(TestSummonMostRecentDeadWindow); - - TEST_METHOD(TestSummonOnCurrent); - TEST_METHOD(TestSummonOnCurrentWithName); - TEST_METHOD(TestSummonOnCurrentDeadWindow); - - TEST_METHOD(TestSummonMostRecentIsQuake); - - TEST_METHOD(TestSummonAfterWindowClose); - - TEST_METHOD(TestProposeCommandlineWithDeadMonarch); - - TEST_CLASS_SETUP(ClassSetup) - { - return true; - } - - static void _closePeasant(const com_ptr& m, - const uint64_t peasantID); - - 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); - - static void _findTargetWindowByNameHelper(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); - - // This template that lets us call a private ctor for Monarch/Peasant, unlike make_self. - // Currently I use a private implementation detail of winrt::make_self, - // which is bad, but I didn't want to deal with the alternatives. - template - static winrt::com_ptr make_private(Args&&... args) - { - return { new winrt::impl::heap_implements(std::forward(args)...), winrt::take_ownership_from_abi }; - } - }; - - // Helper to tell the monarch that a peasant is closing, this emulates when - // a peasant is closed normally instead of when it crashes. - void RemotingTests::_closePeasant(const com_ptr& m, - const uint64_t peasantID) - { - if (peasantID <= 0) - { - return; - } - - m->SignalClose(peasantID); - } - - // 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) - { - if (peasantID <= 0) - { - return; - } - - m->_peasants[peasantID] = winrt::make(); - } - - // 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) - { - const auto arguments = args.Args().Commandline(); - if (arguments.size() > 0) - { - const auto index = std::stoi(arguments.at(0).c_str()); - args.ResultTargetWindow(index >= 0 ? index : -1); - } - } - - // Helper to get the first argument out of the commandline, and return it as - // a name to use. - void RemotingTests::_findTargetWindowByNameHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) - { - const auto arguments = args.Args().Commandline(); - if (arguments.size() > 0) - { - args.ResultTargetWindow(WindowingBehaviorUseName); - args.ResultTargetWindowName(arguments.at(0)); - } - } - - void RemotingTests::CreateMonarch() - { - auto m1 = make_private(); - 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; - auto m2 = make_private(expectedFakePID); - VERIFY_ARE_EQUAL(expectedFakePID, - m2->GetPID(), - L"A Monarch with an explicit PID should use the one we provided"); - } - - void RemotingTests::CreatePeasant() - { - auto p1 = make_private(); - 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; - auto p2 = make_private(expectedFakePID); - VERIFY_ARE_EQUAL(expectedFakePID, - p2->GetPID(), - 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 = make_private(); - VERIFY_ARE_EQUAL(GetCurrentProcessId(), - p1->GetPID(), - L"A Peasant without an explicit PID should use the current PID"); - - auto expectedFakePID = 2345u; - - auto p2 = make_private(expectedFakePID); - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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_ARE_EQUAL(peasant1PID, maybeP1.GetPID()); - - auto maybeP2 = m0->_getPeasant(2); - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - auto m3 = make_private(monarch3PID); - - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - 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); - VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID()); - - auto maybeP1 = m0->_getPeasant(1); - VERIFY_IS_NULL(maybeP1); - - 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; - - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - std::vector args{}; - Remoting::CommandlineArgs eventArgs{ { args }, { L"" }, SW_NORMAL, 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; - auto p1 = make_private(peasant1PID); - 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; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { - Log::Comment(L"Commandline dispatched to p1"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); - VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1)); - }); - - std::vector args{ L"1", L"arg[1]" }; - Remoting::CommandlineArgs eventArgs{ { args }, { L"" }, SW_NORMAL, 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; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - { - std::vector args{ L"-1" }; - Remoting::CommandlineArgs eventArgs{ { args }, { L"" }, SW_NORMAL, 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"" }, SW_NORMAL, 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; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { - Log::Comment(L"Commandline dispatched to p1"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); - VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1)); - }); - - 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"" }, SW_NORMAL, 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; - auto p2 = make_private(peasant2PID); - m0->AddPeasant(*p2); - p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { - Log::Comment(L"Commandline dispatched to p2"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); - VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1)); - }); - - { - 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"" }, SW_NORMAL, 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"" }, SW_NORMAL, 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; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - { - std::vector args{ L"2" }; - Remoting::CommandlineArgs eventArgs{ { args }, { L"" }, SW_NORMAL, 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"" }, SW_NORMAL, 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"); - - const auto monarch0PID = 12345u; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - 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; - auto p2 = make_private(peasant2PID); - m0->AddPeasant(*p2); - p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { - Log::Comment(L"Commandline dispatched to p2"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); - VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1)); - }); - - 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"" }, SW_NORMAL, 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"" }, SW_NORMAL, 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()); - } - } - - // TODO:projects/5 - // - // In order to test WindowingBehaviorUseExisting, we'll have to - // create our own IVirtualDesktopManager implementation that can be subbed - // in for testing. We can't _actually_ create HWNDs as a part of the test - // and move them to different desktops. Instead, we'll have to create a stub - // impl that can fake a result for IsWindowOnCurrentVirtualDesktop. - - void RemotingTests::MostRecentWindowSameDesktops() - { - Log::Comment(L"Make windows on the same desktop. Validate the contents of _mruPeasants are as expected."); - - const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; - - const auto monarch0PID = 12345u; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - Log::Comment(L"Add a second peasant"); - const auto peasant2PID = 34567u; - auto p2 = make_private(peasant2PID); - m0->AddPeasant(*p2); - - { - 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); - } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); - - { - Log::Comment(L"Activate the first peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), - guid1, - winrt::clock().now() }; - p1->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID()); - } - - void RemotingTests::MostRecentWindowDifferentDesktops() - { - Log::Comment(L"Make windows on different desktops. Validate the contents of _mruPeasants are as expected."); - - const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; - - const auto monarch0PID = 12345u; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - Log::Comment(L"Add a second peasant"); - const auto peasant2PID = 34567u; - auto p2 = make_private(peasant2PID); - m0->AddPeasant(*p2); - - { - 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, second desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), - guid2, - winrt::clock().now() }; - p2->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); - - Log::Comment(L"Add a third peasant"); - const auto peasant3PID = 45678u; - auto p3 = make_private(peasant3PID); - m0->AddPeasant(*p3); - { - Log::Comment(L"Activate the third peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), - guid1, - winrt::clock().now() }; - p3->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID()); - - { - Log::Comment(L"Activate the first peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), - guid1, - winrt::clock().now() }; - p1->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID()); - } - - void RemotingTests::MostRecentWindowMoveDesktops() - { - Log::Comment(L"Make windows on different desktops. Move one to another " - L"desktop. Validate the contents of _mruPeasants are as expected."); - - const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; - - const auto monarch0PID = 12345u; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - Log::Comment(L"Add a second peasant"); - const auto peasant2PID = 34567u; - auto p2 = make_private(peasant2PID); - m0->AddPeasant(*p2); - - { - 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, second desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), - guid2, - winrt::clock().now() }; - p2->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); - - Log::Comment(L"Add a third peasant"); - const auto peasant3PID = 45678u; - auto p3 = make_private(peasant3PID); - m0->AddPeasant(*p3); - { - Log::Comment(L"Activate the third peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), - guid1, - winrt::clock().now() }; - p3->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID()); - - { - Log::Comment(L"Activate the first peasant, second desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), - guid2, - winrt::clock().now() }; - p1->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID()); - - { - Log::Comment(L"Activate the third peasant, second desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), - guid2, - winrt::clock().now() }; - p3->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID()); - - { - Log::Comment(L"Activate the second peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), - guid1, - winrt::clock().now() }; - p2->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID()); - } - - void RemotingTests::GetMostRecentAnyDesktop() - { - Log::Comment(L"Make windows on different desktops. Confirm that " - L"getting the most recent of all windows works as expected."); - - const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; - - const auto monarch0PID = 12345u; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - Log::Comment(L"Add a second peasant"); - const auto peasant2PID = 34567u; - auto p2 = make_private(peasant2PID); - m0->AddPeasant(*p2); - - { - 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, second desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), - guid2, - winrt::clock().now() }; - p2->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(p2->GetID(), m0->_getMostRecentPeasantID(false, true)); - - Log::Comment(L"Add a third peasant"); - const auto peasant3PID = 45678u; - auto p3 = make_private(peasant3PID); - m0->AddPeasant(*p3); - { - Log::Comment(L"Activate the third peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), - guid1, - winrt::clock().now() }; - p3->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(p3->GetID(), m0->_getMostRecentPeasantID(false, true)); - - { - Log::Comment(L"Activate the first peasant, second desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), - guid2, - winrt::clock().now() }; - p1->ActivateWindow(activatedArgs); - } - VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false, true)); - } - - void RemotingTests::MostRecentIsDead() - { - Log::Comment(L"Make two windows. Activate the first, then the second. " - L"Kill the second. The most recent should be the _first_ window."); - - const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; - - const auto monarch0PID = 12345u; - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - Log::Comment(L"Add a peasant"); - const auto peasant1PID = 23456u; - auto p1 = make_private(peasant1PID); - m0->AddPeasant(*p1); - - Log::Comment(L"Add a second peasant"); - const auto peasant2PID = 34567u; - auto p2 = make_private(peasant2PID); - m0->AddPeasant(*p2); - - { - 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); - } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); - - Log::Comment(L"Kill peasant 2"); - RemotingTests::_killPeasant(m0, p2->GetID()); - Log::Comment(L"Peasant 1 should be the new MRU peasant"); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false, true)); - - Log::Comment(L"Peasant 2 should not be in the monarch at all anymore"); - VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); - } - - void RemotingTests::MostRecentIsQuake() - { - Log::Comment(L"When a window is named _quake, it shouldn't participate " - L"in window glomming via the 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - p1->WindowName(L"one"); - p2->WindowName(L"_quake"); - - 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()); - - { - 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 _quake peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), - guid1, - winrt::clock().now() }; - p2->ActivateWindow(activatedArgs); - } - - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); - - Log::Comment(L"When we look up the MRU window, we find peasant 1 (who's name is \"one\"), not 2 (who's name is \"_quake\")"); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false, true)); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"_quake")); - - { - Log::Comment(L"rename p2 to \"two\""); - Remoting::RenameRequestArgs eventArgs{ L"two" }; - p2->RequestRename(eventArgs); - VERIFY_IS_TRUE(eventArgs.Succeeded()); - } - VERIFY_ARE_EQUAL(L"two", p2->WindowName()); - - Log::Comment(L"Now, the MRU window will correctly be p2"); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_getMostRecentPeasantID(false, true)); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - { - Log::Comment(L"rename p1 to \"_quake\""); - Remoting::RenameRequestArgs eventArgs{ L"_quake" }; - p1->RequestRename(eventArgs); - VERIFY_IS_TRUE(eventArgs.Succeeded()); - } - VERIFY_ARE_EQUAL(L"_quake", p1->WindowName()); - - Log::Comment(L"Now, the MRU window will still be p2"); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_getMostRecentPeasantID(false, true)); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"_quake")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - { - Log::Comment(L"Activate the first peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), - guid1, - winrt::clock().now() }; - p1->ActivateWindow(activatedArgs); - } - - Log::Comment(L"Now, the MRU window will still be p2, because p1 is still named \"_quake\""); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_getMostRecentPeasantID(false, true)); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"_quake")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - } - - void RemotingTests::GetPeasantsByName() - { - Log::Comment(L"Test that looking up a peasant by name finds the window we expect"); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - p1->WindowName(L"one"); - p2->WindowName(L"two"); - - VERIFY_ARE_EQUAL(0, p1->GetID()); - VERIFY_ARE_EQUAL(0, p2->GetID()); - VERIFY_ARE_EQUAL(L"one", p1->WindowName()); - VERIFY_ARE_EQUAL(L"two", p2->WindowName()); - - m0->AddPeasant(*p1); - m0->AddPeasant(*p2); - - VERIFY_ARE_EQUAL(1, p1->GetID()); - VERIFY_ARE_EQUAL(2, p2->GetID()); - VERIFY_ARE_EQUAL(L"one", p1->WindowName()); - VERIFY_ARE_EQUAL(L"two", p2->WindowName()); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - Log::Comment(L"Rename p2"); - - p2->WindowName(L"foo"); - - VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"two")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"foo")); - } - - void RemotingTests::AddNamedPeasantsToNewMonarch() - { - Log::Comment(L"Test that moving peasants to a new monarch persists their original names"); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - const auto monarch3PID = 45678u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - auto m3 = make_private(monarch3PID); - - p1->WindowName(L"one"); - p2->WindowName(L"two"); - - VERIFY_ARE_EQUAL(0, p1->GetID()); - VERIFY_ARE_EQUAL(0, p2->GetID()); - - m0->AddPeasant(*p1); - m0->AddPeasant(*p2); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - VERIFY_ARE_EQUAL(1, p1->GetID()); - VERIFY_ARE_EQUAL(2, p2->GetID()); - VERIFY_ARE_EQUAL(L"one", p1->WindowName()); - VERIFY_ARE_EQUAL(L"two", p2->WindowName()); - - Log::Comment(L"When the peasants go to a new monarch, make sure they have the same name"); - m3->AddPeasant(*p1); - m3->AddPeasant(*p2); - - VERIFY_ARE_EQUAL(1, p1->GetID()); - VERIFY_ARE_EQUAL(2, p2->GetID()); - VERIFY_ARE_EQUAL(L"one", p1->WindowName()); - VERIFY_ARE_EQUAL(L"two", p2->WindowName()); - - VERIFY_ARE_EQUAL(p1->GetID(), m3->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m3->_lookupPeasantIdForName(L"two")); - } - - void RemotingTests::LookupNamedPeasantWhenOthersDied() - { - Log::Comment(L"Test that looking for a peasant by name when a different" - L" peasant has died cleans up the corpses of any peasants " - L"we may have tripped over."); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - p1->WindowName(L"one"); - p2->WindowName(L"two"); - - VERIFY_ARE_EQUAL(0, p1->GetID()); - VERIFY_ARE_EQUAL(0, p2->GetID()); - - m0->AddPeasant(*p1); - m0->AddPeasant(*p2); - - VERIFY_ARE_EQUAL(1, p1->GetID()); - VERIFY_ARE_EQUAL(2, p2->GetID()); - - VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch."); - RemotingTests::_killPeasant(m0, p1->GetID()); - - // By killing 1, then looking for "two", we happen to iterate over the - // corpse of 1 when looking for the peasant named "two". This causes us - // to remove 1 while looking for "two". Technically, we shouldn't be - // relying on any sort of ordering for an unordered_map iterator, but - // this one just so happens to work. - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - Log::Comment(L"Peasant 1 should have been pruned"); - VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); - } - - void RemotingTests::LookupNamedPeasantWhenItDied() - { - Log::Comment(L"Test that looking up a dead peasant by name returns 0, " - L"indicating there's no peasant with that name."); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - p1->WindowName(L"one"); - p2->WindowName(L"two"); - - VERIFY_ARE_EQUAL(0, p1->GetID()); - VERIFY_ARE_EQUAL(0, p2->GetID()); - - m0->AddPeasant(*p1); - m0->AddPeasant(*p2); - - VERIFY_ARE_EQUAL(1, p1->GetID()); - VERIFY_ARE_EQUAL(2, p2->GetID()); - - VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch."); - RemotingTests::_killPeasant(m0, p1->GetID()); - - VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"one")); - - Log::Comment(L"Peasant 1 should have been pruned"); - VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - } - - void RemotingTests::GetMruPeasantAfterNameLookupForDeadPeasant() - { - // This test is trying to hit the catch in Monarch::_lookupPeasantIdForName. - // - // We need to: - // * add some peasants, - // * make one the mru, then make a named two the mru - // * then kill two - // * then try to get the mru peasant -> it should be one - - const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - p1->WindowName(L"one"); - p2->WindowName(L"two"); - - VERIFY_ARE_EQUAL(0, p1->GetID()); - VERIFY_ARE_EQUAL(0, p2->GetID()); - - m0->AddPeasant(*p1); - m0->AddPeasant(*p2); - - VERIFY_ARE_EQUAL(1, p1->GetID()); - VERIFY_ARE_EQUAL(2, p2->GetID()); - - VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - { - Log::Comment(L"Activate the first peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), - guid1, - winrt::clock().now() }; - p1->ActivateWindow(activatedArgs); - } - { - Log::Comment(L"Activate the second peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), - guid1, - winrt::clock().now() }; - p2->ActivateWindow(activatedArgs); - } - Log::Comment(L"Kill peasant 2."); - RemotingTests::_killPeasant(m0, p2->GetID()); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false, true)); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(true, true)); - } - - void RemotingTests::ProposeCommandlineForNamedDeadWindow() - { - Log::Comment(L"Test proposing a commandline for a named window that's " - L"currently dead. This should result in a new window with " - L"the given name."); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowByNameHelper); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - p1->WindowName(L"one"); - p2->WindowName(L"two"); - - VERIFY_ARE_EQUAL(0, p1->GetID()); - VERIFY_ARE_EQUAL(0, p2->GetID()); - - p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { - Log::Comment(L"Commandline dispatched to p1"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); - VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1)); - }); - p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { - Log::Comment(L"Commandline dispatched to p2"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); - VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1)); - }); - - p1->WindowName(L"one"); - p2->WindowName(L"two"); - - m0->AddPeasant(*p1); - m0->AddPeasant(*p2); - - std::vector p1Args{ L"one", L"arg[1]" }; - std::vector p2Args{ L"two", L"this is for p2" }; - - { - Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" }, SW_NORMAL, L"" }; - auto result = m0->ProposeCommandline(eventArgs); - VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); - VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - - { - Log::Comment(L"Send a commandline to \"two\", which should be p2"); - Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" }, SW_NORMAL, L"" }; - auto result = m0->ProposeCommandline(eventArgs); - VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); - VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value - VERIFY_ARE_EQUAL(L"", result.WindowName()); - } - - Log::Comment(L"Kill peasant 2."); - RemotingTests::_killPeasant(m0, p2->GetID()); - - { - Log::Comment(L"Send a commandline to \"two\", who is now dead."); - Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" }, SW_NORMAL, L"" }; - auto result = m0->ProposeCommandline(eventArgs); - VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); - VERIFY_ARE_EQUAL(false, (bool)result.Id()); // Casting to (bool) checks if the reference has a value - VERIFY_ARE_EQUAL(L"two", result.WindowName()); - } - } - - void RemotingTests::TestRenameWindowSuccessfully() - { - Log::Comment(L"Attempt to rename a window. This should succeed."); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - Remoting::RenameRequestArgs eventArgs{ L"foo" }; - p1->RequestRename(eventArgs); - - VERIFY_IS_TRUE(eventArgs.Succeeded()); - VERIFY_ARE_EQUAL(L"foo", p1->WindowName()); - - VERIFY_ARE_EQUAL(0, m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"foo")); - } - - void RemotingTests::TestRenameSameNameAsAnother() - { - Log::Comment(L"Try renaming a window to a name used by another peasant." - L" This should fail."); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - Remoting::RenameRequestArgs eventArgs{ L"two" }; - p1->RequestRename(eventArgs); - - VERIFY_IS_FALSE(eventArgs.Succeeded()); - VERIFY_ARE_EQUAL(L"one", p1->WindowName()); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - } - void RemotingTests::TestRenameSameNameAsADeadPeasant() - { - Log::Comment(L"We'll try renaming a window to the name of a window that" - L" has died. This should succeed, without crashing."); - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - Remoting::RenameRequestArgs eventArgs{ L"two" }; - p1->RequestRename(eventArgs); - - VERIFY_IS_FALSE(eventArgs.Succeeded()); - VERIFY_ARE_EQUAL(L"one", p1->WindowName()); - - VERIFY_ARE_EQUAL(p1->GetID(), m0->_lookupPeasantIdForName(L"one")); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_lookupPeasantIdForName(L"two")); - - RemotingTests::_killPeasant(m0, p2->GetID()); - - p1->RequestRename(eventArgs); - - VERIFY_IS_TRUE(eventArgs.Succeeded()); - VERIFY_ARE_EQUAL(L"two", p1->WindowName()); - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto 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()); - } - - void RemotingTests::TestSummonOnCurrent() - { - 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}") }; - - constexpr auto monarch0PID = 12345u; - constexpr auto peasant1PID = 23456u; - constexpr auto peasant2PID = 34567u; - constexpr auto peasant3PID = 45678u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - auto p3 = make_private(peasant3PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto p2ExpectedToBeSummoned = false; - auto 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"); - auto manager = winrt::make_self(); - m0->_desktopManager = manager.try_as(); - - auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT { - Log::Comment(L"firstCallback: Checking if window is on desktop 1"); - - const auto 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 auto 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 auto 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 auto 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 auto 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() - { - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - auto p3 = make_private(peasant3PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto p2ExpectedToBeSummoned = false; - auto 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"); - auto manager = winrt::make_self(); - m0->_desktopManager = manager.try_as(); - - auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT { - Log::Comment(L"firstCallback: Checking if window is on desktop 1"); - - const auto 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() - { - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - auto p3 = make_private(peasant3PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto p2ExpectedToBeSummoned = false; - auto 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"); - auto manager = winrt::make_self(); - m0->_desktopManager = manager.try_as(); - - auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT { - Log::Comment(L"firstCallback: Checking if window is on desktop 1"); - - const auto 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()); - } - - void RemotingTests::TestSummonMostRecentIsQuake() - { - Log::Comment(L"When a window is named _quake, it shouldn't participate " - L"in window glomming via the MRU window, but it should be able to be summoned"); - - const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - - const auto monarch0PID = 12345u; - const auto peasant1PID = 23456u; - const auto peasant2PID = 34567u; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - - p1->WindowName(L"one"); - p2->WindowName(L"_quake"); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto 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); - }); - - auto p1ExpectedCommandline = false; - auto p2ExpectedCommandline = false; - p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& /*cmdlineArgs*/) { - Log::Comment(L"Commandline dispatched to p1"); - VERIFY_IS_TRUE(p1ExpectedCommandline); - }); - p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& /*cmdlineArgs*/) { - Log::Comment(L"Commandline dispatched to p2"); - VERIFY_IS_TRUE(p2ExpectedCommandline); - }); - m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); - - { - 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 _quake peasant, first desktop"); - Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), - guid1, - winrt::clock().now() }; - p2->ActivateWindow(activatedArgs); - } - - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); - - std::vector commandlineArgs{ L"0", L"arg[1]" }; - Remoting::CommandlineArgs eventArgs{ { commandlineArgs }, { L"" }, SW_NORMAL, L"" }; - - Log::Comment(L"When we attempt to send a commandline to the MRU window," - L" we should find peasant 1 (who's name is \"one\"), not 2" - L" (who's name is \"_quake\")"); - p1ExpectedCommandline = true; - p2ExpectedCommandline = false; - auto result = m0->ProposeCommandline(eventArgs); - VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); - VERIFY_ARE_EQUAL(false, (bool)result.Id()); - - { - Log::Comment(L"When we summon the MRU window, we'll still summon the _quake window"); - p1ExpectedToBeSummoned = false; - p2ExpectedToBeSummoned = true; - Remoting::SummonWindowSelectionArgs args; - args.OnCurrentDesktop(false); - m0->SummonWindow(args); - VERIFY_IS_TRUE(args.FoundMatch()); - } - - { - Log::Comment(L"rename p2 to \"two\""); - Remoting::RenameRequestArgs renameArgs{ L"two" }; - p2->RequestRename(renameArgs); - VERIFY_IS_TRUE(renameArgs.Succeeded()); - } - VERIFY_ARE_EQUAL(L"two", p2->WindowName()); - - Log::Comment(L"Now, the MRU window will correctly be p2, and we can glom to it"); - p1ExpectedCommandline = false; - p2ExpectedCommandline = true; - result = m0->ProposeCommandline(eventArgs); - VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); - VERIFY_ARE_EQUAL(false, (bool)result.Id()); - - { - Log::Comment(L"When we summon the MRU window, we'll still summon the window 2"); - p1ExpectedToBeSummoned = false; - p2ExpectedToBeSummoned = true; - Remoting::SummonWindowSelectionArgs args; - args.OnCurrentDesktop(false); - m0->SummonWindow(args); - VERIFY_IS_TRUE(args.FoundMatch()); - } - } - - void RemotingTests::TestSummonAfterWindowClose() - { - Log::Comment(L"Test that we can summon a window on the current desktop," - L" when the MRU window on that desktop closes normally."); - - 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; - - auto m0 = make_private(monarch0PID); - auto p1 = make_private(peasant1PID); - auto p2 = make_private(peasant2PID); - auto p3 = make_private(peasant3PID); - - 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()); - - auto p1ExpectedToBeSummoned = false; - auto p2ExpectedToBeSummoned = false; - auto 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"); - auto manager = winrt::make_self(); - m0->_desktopManager = manager.try_as(); - - auto firstCallback = [&](HWND h, BOOL* result) -> HRESULT { - Log::Comment(L"firstCallback: Checking if window is on desktop 1"); - - const auto 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"Close window 3. Window 1 is now the MRU on desktop 1."); - RemotingTests::_closePeasant(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()); - } - - void RemotingTests::TestProposeCommandlineWithDeadMonarch() - { - Log::Comment(L"MSFT:32279047 - It's possible for a window to start " - L"right as the monarch is exiting. In that case, " - L"WindowManager relies on getting a " - L"RPC_E_SERVER_UNAVAILABLE when the process 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; - - auto m0 = make_private(monarch0PID); - - { - Remoting::CommandlineArgs args{ { L"wt.exe" }, { L"-Embedding" }, SW_NORMAL, L"" }; - const auto result = m0->ProposeCommandline(args); - auto shouldCreateWindow = result.ShouldCreateWindow(); - VERIFY_IS_TRUE(shouldCreateWindow); - } - - auto m1 = make_self(); - { - Remoting::CommandlineArgs args{ { L"wt.exe" }, { L"-Embedding" }, SW_NORMAL, L"" }; - - try - { - const auto result = m1->ProposeCommandline(args); - } - catch (const winrt::hresult_error& e) - { - // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare here. - constexpr auto RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); - constexpr auto RPC_CALL_FAILED_HR = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED); - - // This is the same check in WindowManager::_proposeToMonarch. - VERIFY_IS_TRUE(e.code() == RPC_SERVER_UNAVAILABLE_HR || e.code() == RPC_CALL_FAILED_HR); - return; - } - - VERIFY_FAIL(L"This should have thrown"); - } - } - -} diff --git a/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def b/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def deleted file mode 100644 index ba15818ddb1..00000000000 --- a/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS -DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE -DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/cascadia/UnitTests_Remoting/pch.cpp b/src/cascadia/UnitTests_Remoting/pch.cpp deleted file mode 100644 index 398a99f6653..00000000000 --- a/src/cascadia/UnitTests_Remoting/pch.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" diff --git a/src/cascadia/UnitTests_Remoting/pch.h b/src/cascadia/UnitTests_Remoting/pch.h deleted file mode 100644 index 523f6305c3b..00000000000 --- a/src/cascadia/UnitTests_Remoting/pch.h +++ /dev/null @@ -1,52 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. ---*/ - -#pragma once - -// Block minwindef.h min/max macros to prevent conflict -#define NOMINMAX - -#define WIN32_LEAN_AND_MEAN -#define NOMCX -#define NOHELP -#define NOCOMM - -#include -#include - -// 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 "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/conattrs.hpp" -#include "../../types/inc/utils.hpp" -#include "../../inc/DefaultSettings.h" - -#include -#include diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 42c033f3b0c..75d322c7720 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -3,18 +3,15 @@ #include "pch.h" #include "AppHost.h" -#include "../types/inc/Viewport.hpp" -#include "../types/inc/utils.hpp" -#include "../types/inc/User32Utils.hpp" -#include "../WinRTUtils/inc/WtExeUtils.h" -#include "resource.h" -#include "VirtualDesktopUtils.h" -#include "icon.h" +#include #include - #include +#include "VirtualDesktopUtils.h" +#include "WindowEmperor.h" +#include "../types/inc/utils.hpp" + using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Composition; using namespace winrt::Windows::UI::Xaml; @@ -23,50 +20,36 @@ 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; using namespace std::chrono_literals; // This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx // "If the high-order bit is 1, the key is down; otherwise, it is up." static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; +static constexpr auto FrameUpdateInterval = std::chrono::milliseconds(16); -constexpr const auto FrameUpdateInterval = std::chrono::milliseconds(16); +static winrt::com_ptr s_desktopManager; -AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic, - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - const Remoting::WindowManager& manager, - const Remoting::Peasant& peasant, - std::unique_ptr window) noexcept : +AppHost::AppHost(WindowEmperor* manager, const winrt::TerminalApp::AppLogic& logic, winrt::TerminalApp::WindowRequestedArgs args) noexcept : _appLogic{ logic }, - _windowLogic{ nullptr }, // don't make one, we're going to take a ref on app's - _windowManager{ manager }, - _peasant{ peasant }, - _desktopManager{ winrt::try_create_instance(__uuidof(VirtualDesktopManager)) } + _windowManager{ manager } { - _started = std::chrono::high_resolution_clock::now(); + if (!s_desktopManager) + { + s_desktopManager = winrt::try_create_instance(__uuidof(VirtualDesktopManager)); + } _HandleCommandlineArgs(args); - _HandleSessionRestore(!args.Content().empty()); - // _HandleCommandlineArgs will create a _windowLogic _useNonClientArea = _windowLogic.GetShowTabsInTitlebar(); - const bool isWarmStart = window != nullptr; - if (isWarmStart) + if (_useNonClientArea) { - _window = std::move(window); + _window = std::make_unique(_windowLogic.GetRequestedTheme()); } else { - if (_useNonClientArea) - { - _window = std::make_unique(_windowLogic.GetRequestedTheme()); - } - else - { - _window = std::make_unique(); - } + _window = std::make_unique(); } // Update our own internal state tracking if we're in quake mode or not. @@ -113,19 +96,6 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& } } -void AppHost::s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& result) -{ - const auto displayHelp = result.ExitCode == 0; - const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE; - const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR; - // TODO:GH#4134: polish this dialog more, to make the text more - // like msiexec /? - MessageBoxW(nullptr, - result.Message.data(), - GetStringResource(messageTitle).data(), - MB_OK | messageIcon); -} - // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to // the WindowManager, to ask if we should become a window process. @@ -141,126 +111,39 @@ void AppHost::s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResu // - // Return Value: // - -void AppHost::_HandleCommandlineArgs(const Remoting::WindowRequestedArgs& windowArgs) +void AppHost::_HandleCommandlineArgs(const winrt::TerminalApp::WindowRequestedArgs& windowArgs) { // We did want to make a window, so let's instantiate it here. // We don't have XAML yet, but we do have other stuff. _windowLogic = _appLogic.CreateNewWindow(); - if (_peasant) + if (const auto content = windowArgs.Content(); !content.empty()) { - const auto& args{ _peasant.InitialArgs() }; - const bool startedForContent = !windowArgs.Content().empty(); - if (startedForContent) - { - _windowLogic.SetStartupContent(windowArgs.Content(), windowArgs.InitialBounds()); - } - else if (args) - { - const auto result = _windowLogic.SetStartupCommandline(args.Commandline(), args.CurrentDirectory(), args.CurrentEnvironment()); - const auto message = _windowLogic.ParseCommandlineMessage(); - if (!message.empty()) - { - AppHost::s_DisplayMessageBox({ message, result }); - - if (_windowLogic.ShouldExitEarly()) - { - ExitThread(result); - } - } - } - - _launchShowWindowCommand = windowArgs.ShowWindowCommand(); - - // This is a fix for GH#12190 and hopefully GH#12169. - // - // If the commandline we were provided is going to result in us only - // opening elevated terminal instances, then we need to not even create - // the window at all here. In that case, we're going through this - // special escape hatch to dispatch all the calls to elevate-shim, and - // then we're going to exit immediately. - if (_windowLogic.ShouldImmediatelyHandoffToElevated()) - { - _windowLogic.HandoffToElevated(); - return; - } - - // After handling the initial args, hookup the callback for handling - // future commandline invocations. When our peasant is told to execute a - // commandline (in the future), it'll trigger this callback, that we'll - // use to send the actions to the app. - // - // MORE EVENT HANDLERS, same rules as the ones above. - _revokers.peasantExecuteCommandlineRequested = _peasant.ExecuteCommandlineRequested(winrt::auto_revoke, { this, &AppHost::_DispatchCommandline }); - _revokers.peasantSummonRequested = _peasant.SummonRequested(winrt::auto_revoke, { this, &AppHost::_HandleSummon }); - _revokers.peasantDisplayWindowIdRequested = _peasant.DisplayWindowIdRequested(winrt::auto_revoke, { this, &AppHost::_DisplayWindowId }); - _revokers.peasantQuitRequested = _peasant.QuitRequested(winrt::auto_revoke, { this, &AppHost::_QuitRequested }); - - _windowLogic.WindowName(_peasant.WindowName()); - _windowLogic.WindowId(_peasant.GetID()); - - _revokers.AttachRequested = _peasant.AttachRequested(winrt::auto_revoke, { this, &AppHost::_handleAttach }); + _windowLogic.SetStartupContent(content, windowArgs.InitialBounds()); + _launchShowWindowCommand = SW_NORMAL; } -} - -void AppHost::_HandleSessionRestore(const bool startedForContent) -{ - const auto& args{ _peasant.InitialArgs() }; - - // This is logic that almost seems like it belongs on the WindowEmperor. - // It probably does. However, it needs to muck with our own window so - // much, that there was no reasonable way of moving this. Moving it also - // seemed to reorder bits of init so much that everything broke. So - // we'll leave it here. - const auto numPeasants = _windowManager.GetNumberOfPeasants(); - // Don't attempt to session restore if we're just making a window for tear-out - if (startedForContent || numPeasants != 1 || !_appLogic.ShouldUsePersistedLayout()) + else { - return; + const auto args = windowArgs.Command(); + _windowLogic.SetStartupCommandline(args); + _launchShowWindowCommand = args.ShowWindowCommand(); } - const auto state = ApplicationState::SharedInstance(); - const auto layouts = state.PersistedWindowLayouts(); - - if (layouts && layouts.Size() > 0) + // This is a fix for GH#12190 and hopefully GH#12169. + // + // If the commandline we were provided is going to result in us only + // opening elevated terminal instances, then we need to not even create + // the window at all here. In that case, we're going through this + // special escape hatch to dispatch all the calls to elevate-shim, and + // then we're going to exit immediately. + if (_windowLogic.ShouldImmediatelyHandoffToElevated()) { - uint32_t startIdx = 0; - // We want to create a window for every saved layout. - // If we are the only window, and no commandline arguments were provided - // then we should just use the current window to load the first layout. - // Otherwise create this window normally with its commandline, and create - // a new window using the first saved layout information. - // The 2nd+ layout will always get a new window. - if (!_windowLogic.HasCommandlineArguments() && - !_appLogic.HasSettingsStartupActions()) - { - _windowLogic.SetPersistedLayoutIdx(startIdx); - startIdx += 1; - } - - // Create new windows for each of the other saved layouts. - for (const auto size = layouts.Size(); startIdx < size; startIdx += 1) - { - auto newWindowArgs = fmt::format(FMT_COMPILE(L"{} -w new -s {}"), args.Commandline()[0], startIdx); - - STARTUPINFO si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - wil::unique_process_information pi; - - LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr, - newWindowArgs.data(), - nullptr, // lpProcessAttributes - nullptr, // lpThreadAttributes - false, // bInheritHandles - DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT, // doCreationFlags - nullptr, // lpEnvironment - nullptr, // lpStartingDirectory - &si, // lpStartupInfo - &pi // lpProcessInformation - )); - } + _windowLogic.HandoffToElevated(); + return; } + + _windowLogic.WindowName(windowArgs.WindowName()); + _windowLogic.WindowId(windowArgs.Id()); } // Method Description: @@ -330,8 +213,6 @@ void AppHost::Initialize() _windowCallbacks.WindowVisibilityChanged = _window->WindowVisibilityChanged([this](bool showOrHide) { _windowLogic.WindowVisibilityChanged(showOrHide); }); - _windowCallbacks.UpdateSettingsRequested = _window->UpdateSettingsRequested({ this, &AppHost::_requestUpdateSettings }); - _revokers.Initialized = _windowLogic.Initialized(winrt::auto_revoke, { this, &AppHost::_WindowInitializedHandler }); _revokers.RequestedThemeChanged = _windowLogic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme }); _revokers.FullscreenChanged = _windowLogic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged }); @@ -349,32 +230,6 @@ void AppHost::Initialize() } }); - _windowCallbacks.AutomaticShutdownRequested = _window->AutomaticShutdownRequested([this]() { - // This is the WM_ENDSESSION handler. - // The event is raised when the user is logged out, because the system is rebooting, etc. - // Due to the design of WM_ENDSESSION, returning from the message indicates to the OS that it's fine to - // terminate us at any time. Luckily Windows has never heavily relied on message passing or asynchronous - // eventing in any of its UI frameworks. It also was clearly impossible to use WaitForMultipleObjects with - // bWaitAll=TRUE and a timeout to wait for all applications to exit cleanly. - // As such we attempt to synchronously shut down the app here. Otherwise, it could just call _quit(). - - const auto state = ApplicationState::SharedInstance(); - - state.PersistedWindowLayouts(nullptr); - - // A duplicate of AppHost::_QuitRequested(). - if (_appLogic && _windowLogic && _appLogic.ShouldUsePersistedLayout()) - { - _windowLogic.PersistState(); - } - - _windowManager.SignalClose(_peasant); - _windowManager.QuitAll(); - - // Ensure to write the state.json before we get TerminateProcess()d by the OS (Thanks!). - state.Flush(); - }); - // Load bearing: make sure the PropertyChanged handler is added before we // call Create, so that when the app sets up the titlebar brush, we're // already prepared to listen for the change notification @@ -383,11 +238,10 @@ void AppHost::Initialize() _appLogic.Create(); _windowLogic.Create(); - _revokers.TitleChanged = _windowLogic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged }); + _revokers.TitleChanged = _windowLogic.TitleChanged(winrt::auto_revoke, { this, &AppHost::_AppTitleChanged }); _revokers.CloseWindowRequested = _windowLogic.CloseWindowRequested(winrt::auto_revoke, { this, &AppHost::_CloseRequested }); _revokers.SetTaskbarProgress = _windowLogic.SetTaskbarProgress(winrt::auto_revoke, { this, &AppHost::SetTaskbarProgress }); _revokers.IdentifyWindowsRequested = _windowLogic.IdentifyWindowsRequested(winrt::auto_revoke, { this, &AppHost::_IdentifyWindowsRequested }); - _revokers.RenameWindowRequested = _windowLogic.RenameWindowRequested(winrt::auto_revoke, { this, &AppHost::_RenameWindowRequested }); _revokers.WindowSizeChanged = _windowLogic.WindowSizeChanged(winrt::auto_revoke, { this, &AppHost::_WindowSizeChanged }); // A note: make sure to listen to our _window_'s settings changed, not the @@ -403,7 +257,6 @@ void AppHost::Initialize() _revokers.ShowWindowChanged = _windowLogic.ShowWindowChanged(winrt::auto_revoke, { this, &AppHost::_ShowWindowChanged }); _revokers.RequestMoveContent = _windowLogic.RequestMoveContent(winrt::auto_revoke, { this, &AppHost::_handleMoveContent }); _revokers.RequestReceiveContent = _windowLogic.RequestReceiveContent(winrt::auto_revoke, { this, &AppHost::_handleReceiveContent }); - _revokers.SendContentRequested = _peasant.SendContentRequested(winrt::auto_revoke, { this, &AppHost::_handleSendContent }); // BODGY // On certain builds of Windows, when Terminal is set as the default @@ -456,14 +309,56 @@ void AppHost::Close() } } -safe_void_coroutine AppHost::_quit() +int64_t AppHost::GetLastActivatedTime() const noexcept { - const auto peasant = _peasant; + return _lastActivatedTime.QuadPart; +} + +// Lazily gets the virtual desktop ID for this window. +winrt::Windows::Foundation::IAsyncOperation AppHost::GetVirtualDesktopId() +{ + static constexpr winrt::guid null_guid{}; + + if (_virtualDesktopId != null_guid) + { + co_return _virtualDesktopId; + } + const auto dispatcher = _windowLogic.GetRoot().Dispatcher(); + const auto hwnd = _window->GetHandle(); + const auto weakThis = weak_from_this(); + + if (!hwnd || !s_desktopManager) + { + co_return null_guid; + } + + // The amazing IVirtualDesktopManager API is cross-process COM into explorer.exe, + // so we can't call it on the UI thread (= slow & reentrant = bugs/freezes). + // Fun fact: GetWindowDesktopId() is O(n) over all HWNDs. :) co_await winrt::resume_background(); - ApplicationState::SharedInstance().PersistedWindowLayouts(nullptr); - _windowManager.QuitAll(); + GUID id; + if (FAILED_LOG(s_desktopManager->GetWindowDesktopId(hwnd, &id))) + { + co_return null_guid; + } + + co_await wil::resume_foreground(dispatcher); + + const auto strongThis = weakThis.lock(); + if (!strongThis) + { + co_return null_guid; + } + + _virtualDesktopId = winrt::guid{ id }; + co_return _virtualDesktopId; +} + +IslandWindow* AppHost::GetWindow() const noexcept +{ + return _window.get(); } void AppHost::_revokeWindowCallbacks() @@ -478,36 +373,7 @@ void AppHost::_revokeWindowCallbacks() _window->WindowCloseButtonClicked(_windowCallbacks.WindowCloseButtonClicked); _window->DragRegionClicked(_windowCallbacks.DragRegionClicked); _window->WindowVisibilityChanged(_windowCallbacks.WindowVisibilityChanged); - _window->UpdateSettingsRequested(_windowCallbacks.UpdateSettingsRequested); _window->MaximizeChanged(_windowCallbacks.MaximizeChanged); - _window->AutomaticShutdownRequested(_windowCallbacks.AutomaticShutdownRequested); -} - -// revoke our callbacks, discard our XAML content (TerminalWindow & -// TerminalPage), and hand back our IslandWindow. This does _not_ close the XAML -// island for this thread. We should not be re-used after this, and our caller -// can destruct us like they normally would during a close. The returned -// IslandWindow will retain ownership of the DesktopWindowXamlSource, for later -// reuse. -[[nodiscard]] std::unique_ptr AppHost::Refrigerate() -{ - // After calling _window->Close() we should avoid creating more WinUI related actions. - // I suspect WinUI wouldn't like that very much. As such unregister all event handlers first. - _revokers = {}; - _showHideWindowThrottler.reset(); - _stopFrameTimer(); - _revokeWindowCallbacks(); - - // DO NOT CLOSE THE WINDOW - _window->Refrigerate(); - - if (_windowLogic) - { - _windowLogic.DismissDialog(); - _windowLogic = nullptr; - } - - return std::move(_window); } // Method Description: @@ -519,13 +385,12 @@ void AppHost::_revokeWindowCallbacks() // - newTitle: the string to use as the new window title // Return Value: // - -void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle) +void AppHost::_AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle) { if (_windowLogic.GetShowTitleInTitlebar()) { _window->UpdateTitle(newTitle); } - _windowManager.UpdateActiveTabTitle(newTitle, _peasant); } // The terminal page is responsible for persisting it's own state, but it does @@ -922,78 +787,43 @@ void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, // - args: the bundle of a commandline and working directory to use for this invocation. // Return Value: // - -void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, - Remoting::CommandlineArgs args) +void AppHost::DispatchCommandline(winrt::TerminalApp::CommandlineArgs args) { - const Remoting::SummonWindowBehavior summonArgs{}; + winrt::TerminalApp::SummonWindowBehavior summonArgs{}; summonArgs.MoveToCurrentDesktop(false); summonArgs.DropdownDuration(0); - summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace); + summonArgs.ToMonitor(winrt::TerminalApp::MonitorBehavior::InPlace); summonArgs.ToggleVisibility(false); // Do not toggle, just make visible. // Summon the window whenever we dispatch a commandline to it. This will // make it obvious when a new tab/pane is created in a window. - _HandleSummon(sender, summonArgs); - _windowLogic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory(), args.CurrentEnvironment()); + HandleSummon(std::move(summonArgs)); + _windowLogic.ExecuteCommandline(std::move(args)); } void AppHost::_WindowActivated(bool activated) { _windowLogic.WindowActivated(activated); - if (activated && _isWindowInitialized) + if (activated && _isWindowInitialized != WindowInitializedState::NotInitialized) { - _peasantNotifyActivateWindow(); + QueryPerformanceCounter(&_lastActivatedTime); + _virtualDesktopId = {}; } } -safe_void_coroutine AppHost::_peasantNotifyActivateWindow() -{ - const auto desktopManager = _desktopManager; - const auto peasant = _peasant; - const auto hwnd = _window->GetHandle(); - - auto weakThis{ weak_from_this() }; - - co_await winrt::resume_background(); - - // If we're gone on the other side of this co_await, well, that's fine. Just bail. - const auto strongThis = weakThis.lock(); - if (!strongThis) - { - co_return; - } - - GUID currentDesktopGuid{}; - if (FAILED_LOG(desktopManager->GetWindowDesktopId(hwnd, ¤tDesktopGuid))) - { - co_return; - } - - // 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. - peasant.ActivateWindow({ - peasant.GetID(), - reinterpret_cast(hwnd), - currentDesktopGuid, - winrt::clock().now(), - }); -} - -void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const Remoting::SummonWindowBehavior& args) +void AppHost::HandleSummon(const winrt::TerminalApp::SummonWindowBehavior args) const { _window->SummonWindow(args); if (args != nullptr && args.MoveToCurrentDesktop()) { - if (_desktopManager) + if (s_desktopManager) { // First thing - make sure that we're not on the current desktop. If // we are, then don't call MoveWindowToDesktop. This is to mitigate // MSFT:33035972 BOOL onCurrentDesktop{ false }; - if (SUCCEEDED(_desktopManager->IsWindowOnCurrentVirtualDesktop(_window->GetHandle(), &onCurrentDesktop)) && onCurrentDesktop) + if (SUCCEEDED(s_desktopManager->IsWindowOnCurrentVirtualDesktop(_window->GetHandle(), &onCurrentDesktop)) && onCurrentDesktop) { // If we succeeded, and the window was on the current desktop, then do nothing. } @@ -1006,7 +836,7 @@ void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*se GUID currentlyActiveDesktop{ 0 }; if (VirtualDesktopUtils::GetCurrentVirtualDesktopId(¤tlyActiveDesktop)) { - LOG_IF_FAILED(_desktopManager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); + LOG_IF_FAILED(s_desktopManager->MoveWindowToDesktop(_window->GetHandle(), currentlyActiveDesktop)); } // If GetCurrentVirtualDesktopId failed, then just leave the window // where it is. Nothing else to be done :/ @@ -1023,26 +853,10 @@ void AppHost::_HandleSummon(const winrt::Windows::Foundation::IInspectable& /*se // - // Return Value: // - -safe_void_coroutine AppHost::_IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable /*sender*/, - const winrt::Windows::Foundation::IInspectable /*args*/) +void AppHost::_IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable /*sender*/, + const winrt::Windows::Foundation::IInspectable /*args*/) { - auto weakThis{ weak_from_this() }; - - // We'll be raising an event that may result in a RPC call to the monarch - - // make sure we're on the background thread, or this will silently fail - co_await winrt::resume_background(); - - // If we're gone on the other side of this co_await, well, that's fine. Just bail. - const auto strongThis = weakThis.lock(); - if (!strongThis) - { - co_return; - } - - if (_peasant) - { - _peasant.RequestIdentifyWindows(); - } + PostMessageW(_windowManager->GetMainWindow(), WindowEmperor::WM_IDENTIFY_ALL_WINDOWS, 0, 0); } // Method Description: @@ -1058,30 +872,6 @@ void AppHost::_DisplayWindowId(const winrt::Windows::Foundation::IInspectable& / _windowLogic.IdentifyWindow(); } -safe_void_coroutine AppHost::_RenameWindowRequested(const winrt::Windows::Foundation::IInspectable /*sender*/, - const winrt::TerminalApp::RenameWindowRequestedArgs args) -{ - // Switch to the BG thread - anything x-proc must happen on a BG thread - co_await winrt::resume_background(); - - if (_peasant) - { - Remoting::RenameRequestArgs requestArgs{ args.ProposedName() }; - - _peasant.RequestRename(requestArgs); - - if (requestArgs.Succeeded()) - { - co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher()); - _windowLogic.WindowName(args.ProposedName()); - } - else - { - _windowLogic.RenameFailed(); - } - } -} - static double _opacityFromBrush(const winrt::Windows::UI::Xaml::Media::Brush& brush) { if (auto acrylic = brush.try_as()) @@ -1122,7 +912,7 @@ void _frameColorHelper(const HWND h, const COLORREF color) void AppHost::_updateTheme() { - auto theme = _appLogic.Theme(); + auto theme = _appLogic.Settings().GlobalSettings().CurrentTheme(); _window->OnApplicationThemeChanged(theme.RequestedTheme()); @@ -1193,14 +983,14 @@ void AppHost::_stopFrameTimer() // is called as the `_frameTimer` Tick callback, roughly 60 times per second. void AppHost::_updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - // - Convert the time delta between when we were started and now, to a hue. This will cycle us through all the colors. - // - Convert that hue to an RGB value. - // - Set the frame's color to that RGB color. - const auto now = std::chrono::high_resolution_clock::now(); - const std::chrono::duration delta{ now - _started }; - const auto seconds = delta.count() / 4; // divide by four, to make the effect slower. Otherwise it flashes way to fast. - float ignored; - const auto color = til::color::from_hue(modf(seconds, &ignored)); + LARGE_INTEGER freq, counter; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&counter); + + const auto period = freq.QuadPart * 4; + const auto mod = counter.QuadPart % period; + const auto hue = static_cast(mod) / static_cast(period); + const auto color = til::color::from_hue(hue); _frameColorHelper(_window->GetHandle(), color); } @@ -1221,52 +1011,11 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab _window->IsQuakeWindow(_windowLogic.IsQuakeWindow()); } -// Raised from our Peasant. We handle by propagating the call to our terminal window. -void AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) -{ - const auto root = _windowLogic.GetRoot(); - if (!root) - { - return; - } - - const auto dispatcher = root.Dispatcher(); - if (!dispatcher) - { - return; - } - - // We process the shutdown synchronously here, because otherwise the - // AutomaticShutdownRequested() logic wouldn't run synchronously either. - til::latch latch{ 1 }; - - dispatcher.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [&latch, weakThis = weak_from_this()]() { - const auto countDownOnExit = wil::scope_exit([&latch] { - latch.count_down(); - }); - - const auto self = weakThis.lock(); - if (!self) - { - return; - } - - if (self->_appLogic && self->_windowLogic && self->_appLogic.ShouldUsePersistedLayout()) - { - self->_windowLogic.PersistState(); - } - - PostQuitMessage(0); - }); - - latch.wait(); -} - // Raised from TerminalWindow. We handle by bubbling the request to the window manager. void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - _quit(); + PostQuitMessage(0); } void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&, @@ -1285,15 +1034,15 @@ void AppHost::_WindowSizeChanged(const winrt::Windows::Foundation::IInspectable& _resizeWindow(_window->GetHandle(), { args.Width(), args.Height() }); } -void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, +void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - const Remoting::SummonWindowBehavior summonArgs{}; + const winrt::TerminalApp::SummonWindowBehavior summonArgs{}; summonArgs.MoveToCurrentDesktop(false); summonArgs.DropdownDuration(0); - summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace); + summonArgs.ToMonitor(winrt::TerminalApp::MonitorBehavior::InPlace); summonArgs.ToggleVisibility(false); // Do not toggle, just make visible. - _HandleSummon(sender, summonArgs); + HandleSummon(summonArgs); } void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&, @@ -1372,25 +1121,7 @@ void AppHost::_WindowMoved() void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { - if (_windowManager.GetNumberOfPeasants() <= 1) - { - _quit(); - return; - } - - // Remove ourself from the list of peasants so that we aren't included in - // any future requests. This will also mean we block until any existing - // event handler finishes. - _windowManager.SignalClose(_peasant); - - if (Utils::IsWindows11()) - { - PostQuitMessage(0); - } - else - { - PostMessageW(_window->GetInteropHandle(), WM_REFRIGERATE, 0, 0); - } + PostMessageW(_windowManager->GetMainWindow(), WindowEmperor::WM_CLOSE_TERMINAL_WINDOW, 0, reinterpret_cast(this)); } void AppHost::_PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/, @@ -1431,14 +1162,9 @@ safe_void_coroutine AppHost::_WindowInitializedHandler(const winrt::Windows::Fou nCmdShow = SW_MAXIMIZE; } + // Delay ShowWindow() until after XAML's initial layout pass is complete. auto weakThis{ weak_from_this() }; - // For inexplicable reasons, again, hop to the BG thread, then back to the - // UI thread. This is shockingly load bearing - without this, then - // sometimes, we'll _still_ show the HWND before the XAML island actually - // paints. co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher(), winrt::Windows::UI::Core::CoreDispatcherPriority::Low); - - // If we're gone on the other side of this co_await, well, that's fine. Just bail. const auto strongThis = weakThis.lock(); if (!strongThis || _window == nullptr) { @@ -1459,7 +1185,6 @@ safe_void_coroutine AppHost::_WindowInitializedHandler(const winrt::Windows::Fou if (!noForeground) { SetForegroundWindow(_window->GetHandle()); - _peasantNotifyActivateWindow(); } // Don't set our state to Initialized until after the call to ShowWindow. @@ -1549,13 +1274,31 @@ void AppHost::_handleMoveContent(const winrt::Windows::Foundation::IInspectable& }; } - _windowManager.RequestMoveContent(args.Window(), args.Content(), args.TabIndex(), windowBoundsReference); -} + const auto windowName = args.Window(); + winrt::hstring sanitizedWindowName; + AppHost* target = nullptr; -void AppHost::_handleAttach(const winrt::Windows::Foundation::IInspectable& /*sender*/, - winrt::Microsoft::Terminal::Remoting::AttachRequest args) -{ - _windowLogic.AttachContent(args.Content(), args.TabIndex()); + if (const auto id = til::parse_signed(windowName)) + { + if (*id > 0) + { + target = _windowManager->GetWindowById(*id); + } + } + else if (windowName != L"new") + { + target = _windowManager->GetWindowByName(windowName); + sanitizedWindowName = windowName; + } + + if (target) + { + target->_windowLogic.AttachContent(args.Content(), args.TabIndex()); + } + else + { + _windowManager->CreateNewWindow(winrt::TerminalApp::WindowRequestedArgs{ sanitizedWindowName, args.Content(), windowBoundsReference }); + } } // Page -> us -> manager -> monarch @@ -1564,21 +1307,8 @@ void AppHost::_handleAttach(const winrt::Windows::Foundation::IInspectable& /*se void AppHost::_handleReceiveContent(const winrt::Windows::Foundation::IInspectable& /* sender */, winrt::TerminalApp::RequestReceiveContentArgs args) { - _windowManager.RequestSendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow(), args.TabIndex() }); -} - -// monarch -> Peasant -> us -> Page -// The Monarch was told to tell us to send our dragged content to someone else. -void AppHost::_handleSendContent(const winrt::Windows::Foundation::IInspectable& /* sender */, - winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args) -{ - _windowLogic.SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow(), args.TabIndex() }); -} - -// Bubble the update settings request up to the emperor. We're being called on -// the Window thread, but the Emperor needs to update the settings on the _main_ -// thread. -void AppHost::_requestUpdateSettings() -{ - UpdateSettingsRequested.raise(); + if (const auto target = _windowManager->GetWindowById(args.SourceWindow())) + { + target->_windowLogic.SendContentToOther(winrt::TerminalApp::RequestReceiveContentArgs{ args.SourceWindow(), args.TargetWindow(), args.TabIndex() }); + } } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 470052069c4..1f1c393b8e3 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -5,73 +5,56 @@ #include "pch.h" #include "NonClientIslandWindow.h" -#include "NotificationIcon.h" #include +class WindowEmperor; + class AppHost : public std::enable_shared_from_this { public: - static constexpr DWORD WM_REFRIGERATE = WM_APP + 0; - - AppHost(const winrt::TerminalApp::AppLogic& logic, - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - const winrt::Microsoft::Terminal::Remoting::WindowManager& manager, - const winrt::Microsoft::Terminal::Remoting::Peasant& peasant, - std::unique_ptr window = nullptr) noexcept; + AppHost(WindowEmperor* manager, const winrt::TerminalApp::AppLogic& logic, winrt::TerminalApp::WindowRequestedArgs args) noexcept; - void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); void Initialize(); void Close(); - [[nodiscard]] std::unique_ptr Refrigerate(); - - 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); - + int64_t GetLastActivatedTime() const noexcept; + winrt::Windows::Foundation::IAsyncOperation GetVirtualDesktopId(); + IslandWindow* GetWindow() const noexcept; winrt::TerminalApp::TerminalWindow Logic(); - static void s_DisplayMessageBox(const winrt::TerminalApp::ParseCommandlineResult& message); - - til::event> UpdateSettingsRequested; + bool OnDirectKeyEvent(uint32_t vkey, uint8_t scanCode, bool down); + void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void HandleSummon(winrt::TerminalApp::SummonWindowBehavior args) const; + void DispatchCommandline(winrt::TerminalApp::CommandlineArgs args); private: - std::unique_ptr _window; - - winrt::TerminalApp::AppLogic _appLogic; - winrt::TerminalApp::TerminalWindow _windowLogic; - - winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; - winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; - - winrt::com_ptr _desktopManager{ nullptr }; - - enum WindowInitializedState : uint32_t + enum class WindowInitializedState : uint32_t { NotInitialized = 0, Initializing = 1, Initialized = 2, }; - WindowInitializedState _isWindowInitialized{ WindowInitializedState::NotInitialized }; - bool _useNonClientArea{ false }; - winrt::Microsoft::Terminal::Settings::Model::LaunchMode _launchMode{}; - + WindowEmperor* _windowManager = nullptr; + std::unique_ptr _window; + winrt::TerminalApp::AppLogic _appLogic{ nullptr }; + winrt::TerminalApp::TerminalWindow _windowLogic{ nullptr }; std::shared_ptr> _showHideWindowThrottler; - - std::chrono::time_point _started; SafeDispatcherTimer _frameTimer; - + LARGE_INTEGER _lastActivatedTime{}; + winrt::guid _virtualDesktopId{}; + WindowInitializedState _isWindowInitialized{ WindowInitializedState::NotInitialized }; + winrt::Microsoft::Terminal::Settings::Model::LaunchMode _launchMode{}; uint32_t _launchShowWindowCommand{ SW_NORMAL }; + bool _useNonClientArea{ false }; - safe_void_coroutine _quit(); void _revokeWindowCallbacks(); - void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); - void _HandleSessionRestore(const bool startedForContent); + void _HandleCommandlineArgs(const winrt::TerminalApp::WindowRequestedArgs& args); winrt::Microsoft::Terminal::Settings::Model::LaunchPosition _GetWindowLaunchPosition(); - void _HandleCreateWindow(const HWND hwnd, const til::rect& proposedRect); + void _HandleCreateWindow(HWND hwnd, const til::rect& proposedRect); void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::UIElement& arg); @@ -92,21 +75,12 @@ class AppHost : public std::enable_shared_from_this const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta); void _WindowActivated(bool activated); - safe_void_coroutine _peasantNotifyActivateWindow(); void _WindowMoved(); - void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, - winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); - - void _HandleSummon(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior& args); - - safe_void_coroutine _IdentifyWindowsRequested(const winrt::Windows::Foundation::IInspectable sender, - const winrt::Windows::Foundation::IInspectable args); + void _IdentifyWindowsRequested(winrt::Windows::Foundation::IInspectable sender, + winrt::Windows::Foundation::IInspectable args); void _DisplayWindowId(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); - safe_void_coroutine _RenameWindowRequested(const winrt::Windows::Foundation::IInspectable sender, - const winrt::TerminalApp::RenameWindowRequestedArgs args); void _HandleSettingsChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SettingsLoadEventArgs& args); @@ -123,9 +97,6 @@ class AppHost : public std::enable_shared_from_this void _SystemMenuChangeRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::SystemMenuChangeArgs& args); - void _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); - void _RequestQuitAll(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); void _CloseRequested(const winrt::Windows::Foundation::IInspectable& sender, @@ -142,29 +113,21 @@ class AppHost : public std::enable_shared_from_this void _PropertyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); - void _initialResizeAndRepositionWindow(const HWND hwnd, til::rect proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); + void _initialResizeAndRepositionWindow(HWND hwnd, til::rect proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode); - void _resizeWindow(const HWND hwnd, til::size newSize); + void _resizeWindow(HWND hwnd, til::size newSize); void _handleMoveContent(const winrt::Windows::Foundation::IInspectable& sender, winrt::TerminalApp::RequestMoveContentArgs args); - void _handleAttach(const winrt::Windows::Foundation::IInspectable& sender, - winrt::Microsoft::Terminal::Remoting::AttachRequest args); - void _requestUpdateSettings(); - - // Page -> us -> monarch void _handleReceiveContent(const winrt::Windows::Foundation::IInspectable& sender, winrt::TerminalApp::RequestReceiveContentArgs args); - // monarch -> us -> Page - void _handleSendContent(const winrt::Windows::Foundation::IInspectable& sender, - winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs args); - void _startFrameTimer(); void _stopFrameTimer(); void _updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); + void _AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); void _HandleRequestLaunchPosition(const winrt::Windows::Foundation::IInspectable& sender, winrt::TerminalApp::LaunchPositionRequest args); @@ -174,14 +137,6 @@ class AppHost : public std::enable_shared_from_this // the members as a part of their own dtors. struct Revokers { - // Event handlers to revoke in ~AppHost, before calling App.Close - winrt::Microsoft::Terminal::Remoting::Peasant::ExecuteCommandlineRequested_revoker peasantExecuteCommandlineRequested; - winrt::Microsoft::Terminal::Remoting::Peasant::SummonRequested_revoker peasantSummonRequested; - winrt::Microsoft::Terminal::Remoting::Peasant::DisplayWindowIdRequested_revoker peasantDisplayWindowIdRequested; - winrt::Microsoft::Terminal::Remoting::Peasant::QuitRequested_revoker peasantQuitRequested; - - winrt::Microsoft::Terminal::Remoting::Peasant::AttachRequested_revoker AttachRequested; - winrt::TerminalApp::TerminalWindow::Initialized_revoker Initialized; winrt::TerminalApp::TerminalWindow::CloseRequested_revoker CloseRequested; winrt::TerminalApp::TerminalWindow::RequestedThemeChanged_revoker RequestedThemeChanged; @@ -195,7 +150,6 @@ class AppHost : public std::enable_shared_from_this winrt::TerminalApp::TerminalWindow::CloseWindowRequested_revoker CloseWindowRequested; winrt::TerminalApp::TerminalWindow::SetTaskbarProgress_revoker SetTaskbarProgress; winrt::TerminalApp::TerminalWindow::IdentifyWindowsRequested_revoker IdentifyWindowsRequested; - winrt::TerminalApp::TerminalWindow::RenameWindowRequested_revoker RenameWindowRequested; winrt::TerminalApp::TerminalWindow::IsQuakeWindowChanged_revoker IsQuakeWindowChanged; winrt::TerminalApp::TerminalWindow::SummonWindowRequested_revoker SummonWindowRequested; winrt::TerminalApp::TerminalWindow::OpenSystemMenu_revoker OpenSystemMenu; @@ -207,8 +161,6 @@ class AppHost : public std::enable_shared_from_this winrt::TerminalApp::TerminalWindow::PropertyChanged_revoker PropertyChanged; winrt::TerminalApp::TerminalWindow::SettingsChanged_revoker SettingsChanged; winrt::TerminalApp::TerminalWindow::WindowSizeChanged_revoker WindowSizeChanged; - - winrt::Microsoft::Terminal::Remoting::Peasant::SendContentRequested_revoker SendContentRequested; } _revokers{}; // our IslandWindow is not a WinRT type. It can't make auto_revokers like @@ -224,9 +176,7 @@ class AppHost : public std::enable_shared_from_this winrt::event_token WindowCloseButtonClicked; winrt::event_token DragRegionClicked; winrt::event_token WindowVisibilityChanged; - winrt::event_token UpdateSettingsRequested; winrt::event_token MaximizeChanged; - winrt::event_token AutomaticShutdownRequested; // LOAD BEARING!! //If you add events here, make sure they're revoked in AppHost::_revokeWindowCallbacks } _windowCallbacks{}; diff --git a/src/cascadia/WindowsTerminal/BaseWindow.h b/src/cascadia/WindowsTerminal/BaseWindow.h index 36c4eabb80e..2b1f57c5123 100644 --- a/src/cascadia/WindowsTerminal/BaseWindow.h +++ b/src/cascadia/WindowsTerminal/BaseWindow.h @@ -3,13 +3,12 @@ #pragma once -#include "CustomWindowMessages.h" -#include - template class BaseWindow { public: + static constexpr UINT CM_UPDATE_TITLE = WM_USER + 0; + virtual ~BaseWindow() = 0; static T* GetThisFromHandle(HWND const window) noexcept { @@ -46,13 +45,6 @@ class BaseWindow { return HandleDpiChange(_window.get(), wparam, lparam); } - - case WM_DESTROY: - { - PostQuitMessage(0); - return 0; - } - case WM_SIZE: { UINT width = LOWORD(lparam); diff --git a/src/cascadia/WindowsTerminal/CustomWindowMessages.h b/src/cascadia/WindowsTerminal/CustomWindowMessages.h deleted file mode 100644 index a1428eeabe3..00000000000 --- a/src/cascadia/WindowsTerminal/CustomWindowMessages.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#pragma once - -// Custom window messages -#define CM_UPDATE_TITLE (WM_USER) -#define CM_NOTIFY_FROM_NOTIFICATION_AREA (WM_USER + 1) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index a604ed53b80..b21ce2046cd 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -6,7 +6,6 @@ #include "../types/inc/Viewport.hpp" #include "resource.h" #include "icon.h" -#include "NotificationIcon.h" #include #include #include @@ -59,29 +58,6 @@ void IslandWindow::Close() } } -// Clear out any state that might be associated with this app instance, so that -// we can later re-use this HWND for another instance. -// -// This doesn't actually close out our HWND or DesktopWindowXamlSource, but it -// will remove all our content, and SW_HIDE the window, so it isn't accessible. -void IslandWindow::Refrigerate() noexcept -{ - // Similar to in Close - unset our HWND's user data. We'll re-set this when - // we get re-heated, so that while we're refrigerated, we won't have - // unexpected callbacks into us while we don't have content. - // - // This pointer will get re-set in _warmInitialize - SetWindowLongPtr(_window.get(), GWLP_USERDATA, 0); - - _resetSystemMenu(); - - _pfnCreateCallback = nullptr; - _pfnSnapDimensionCallback = nullptr; - - _rootGrid.Children().Clear(); - ShowWindow(_window.get(), SW_HIDE); -} - HWND IslandWindow::GetInteropHandle() const { return _interopWindowHandle; @@ -201,8 +177,6 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce UpdateWindow(_window.get()); UpdateWindowIconForActiveMetrics(_window.get()); - - _currentSystemThemeIsDark = Theme::IsSystemInDarkTheme(); } // Method Description: @@ -340,30 +314,12 @@ LRESULT IslandWindow::_OnMoving(const WPARAM /*wParam*/, const LPARAM lParam) return false; } -// return true if this was a "cold" initialize, that didn't start XAML before. -bool IslandWindow::Initialize() -{ - if (!_source) - { - _coldInitialize(); - return true; - } - else - { - // This was a "warm" initialize - we've already got an HWND, but we need - // to move it to the new correct place, new size, and reset any leftover - // runtime state. - _warmInitialize(); - return false; - } -} - // Method Description: // - Start this window for the first time. This will instantiate our XAML // island, set up our root grid, and initialize some other members that only // need to be initialized once. // - This should only be called once. -void IslandWindow::_coldInitialize() +void IslandWindow::Initialize() { _source = DesktopWindowXamlSource{}; @@ -396,25 +352,6 @@ void IslandWindow::_coldInitialize() // We don't really care if this failed or not. TerminalTrySetTransparentBackground(true); } -void IslandWindow::_warmInitialize() -{ - // re-add the pointer to us to our HWND's user data, so that we can start - // getting window proc callbacks again. - _setupUserData(); - - // Manually ask how we want to be created. - if (_pfnCreateCallback) - { - til::rect rc{ GetWindowRect() }; - _pfnCreateCallback(_window.get(), rc); - } - - // Don't call IslandWindow::OnSize - that will set the Width/Height members - // of the _rootGrid. However, NonClientIslandWindow doesn't use those! If you set them, here, - // the contents of the window will never resize. - UpdateWindow(_window.get()); - ForceResize(); -} void IslandWindow::OnSize(const UINT width, const UINT height) { @@ -583,23 +520,6 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam } } - // BODGY This is a fix for the upstream: - // - // https://github.com/microsoft/microsoft-ui-xaml/issues/3577 - // - // ContentDialogs don't resize themselves when the XAML island resizes. - // However, if we manually resize our CoreWindow, that'll actually - // trigger a resize of the ContentDialog. - if (const auto& coreWindow{ winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread() }) - { - if (const auto& interop{ coreWindow.as() }) - { - HWND coreWindowInterop; - interop->get_WindowHandle(&coreWindowInterop); - PostMessage(coreWindowInterop, message, wparam, lparam); - } - } - break; } case WM_MOVING: @@ -735,70 +655,6 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam } break; } - case WM_SETTINGCHANGE: - { - // Currently, we only support checking when the OS theme changes. In - // that case, wParam is 0. Re-evaluate when we decide to reload env vars - // (GH#1125) - if (wparam == 0 && lparam != 0) - { - const std::wstring param{ (wchar_t*)lparam }; - // ImmersiveColorSet seems to be the notification that the OS theme - // changed. If that happens, let the app know, so it can hot-reload - // themes, color schemes that might depend on the OS theme - if (param == L"ImmersiveColorSet") - { - // GH#15732: Don't update the settings, unless the theme - // _actually_ changed. ImmersiveColorSet gets sent more often - // than just on a theme change. It notably gets sent when the PC - // is locked, or the UAC prompt opens. - auto isCurrentlyDark = Theme::IsSystemInDarkTheme(); - if (isCurrentlyDark != _currentSystemThemeIsDark) - { - _currentSystemThemeIsDark = isCurrentlyDark; - UpdateSettingsRequested.raise(); - } - } - } - break; - } - case WM_ENDSESSION: - { - // For WM_QUERYENDSESSION and WM_ENDSESSION, refer to: - // - // https://docs.microsoft.com/en-us/windows/win32/rstmgr/guidelines-for-applications - // - // The OS will send us a WM_QUERYENDSESSION when it's preparing an - // update for our app. It will then send us a WM_ENDSESSION, which gives - // us a small timeout (~30s) to actually shut down gracefully. After - // that timeout, it will send us a WM_CLOSE. If we still don't close - // after the WM_CLOSE, it'll force-kill us (causing a crash which will be - // bucketed to MoAppHang). - // - // If we need to do anything to prepare for being told to shutdown, - // start it in WM_QUERYENDSESSION. If (in the future) we need to prevent - // logoff, we can return false there. (DefWindowProc returns true) - // - // The OS is going to shut us down here. We will manually start a quit, - // so that we can persist the state. If we refuse to gracefully shut - // down here, the OS will crash us to forcefully terminate us. We choose - // to quit here, rather than just close, to skip over any warning - // dialogs (e.g. "Are you sure you want to close all tabs?") which might - // prevent a WM_CLOSE from cleanly closing the window. - // - // This will cause a appHost._RequestQuitAll, which will notify the - // monarch to collect up all the window state and save it. - - TraceLoggingWrite( - g_hWindowsTerminalProvider, - "EndSession", - TraceLoggingDescription("Emitted when the OS has sent a WM_ENDSESSION"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - AutomaticShutdownRequested.raise(); - return true; - } } // TODO: handle messages here... @@ -1323,18 +1179,7 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) // - toggleVisibility: controls how we should behave when already in the foreground. // Return Value: // - -safe_void_coroutine IslandWindow::SummonWindow(Remoting::SummonWindowBehavior args) -{ - // On the foreground thread: - co_await wil::resume_foreground(_rootGrid.Dispatcher()); - _summonWindowRoutineBody(args); -} - -// Method Description: -// - As above. -// BODGY: ARM64 BUILD FAILED WITH fatal error C1001: Internal compiler error -// when this was part of the coroutine body. -void IslandWindow::_summonWindowRoutineBody(Remoting::SummonWindowBehavior args) +void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args) { auto actualDropdownDuration = args.DropdownDuration(); // If the user requested an animation, let's check if animations are enabled in the OS. @@ -1370,7 +1215,7 @@ void IslandWindow::_summonWindowRoutineBody(Remoting::SummonWindowBehavior args) // They want to toggle the window when it is the FG window, and we are // the FG window. However, if we're on a different monitor than the // mouse, then we should move to that monitor instead of dismissing. - if (args.ToMonitor() == Remoting::MonitorBehavior::ToMouse) + if (args.ToMonitor() == winrt::TerminalApp::MonitorBehavior::ToMouse) { const til::rect cursorMonitorRect{ _getMonitorForCursor().rcMonitor }; const til::rect currentMonitorRect{ _getMonitorForWindow(GetHandle()).rcMonitor }; @@ -1447,7 +1292,7 @@ void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool } void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration, - const Remoting::MonitorBehavior toMonitor) + const winrt::TerminalApp::MonitorBehavior toMonitor) { // First, get the window that's currently in the foreground. We'll need // _this_ window to be able to appear on top of. If we just use @@ -1504,7 +1349,7 @@ void IslandWindow::_slideUpWindow(const uint32_t dropdownDuration) // Return Value: // - void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration, - const Remoting::MonitorBehavior toMonitor) + const winrt::TerminalApp::MonitorBehavior toMonitor) { // First, get the window that's currently in the foreground. We'll need // _this_ window to be able to appear on top of. If we just use @@ -1636,13 +1481,13 @@ MONITORINFO IslandWindow::_getMonitorForWindow(HWND foregroundWindow) // - toMonitor: Controls which monitor we should move to. // Return Value: // - -void IslandWindow::_moveToMonitor(HWND oldForegroundWindow, Remoting::MonitorBehavior toMonitor) +void IslandWindow::_moveToMonitor(HWND oldForegroundWindow, winrt::TerminalApp::MonitorBehavior toMonitor) { - if (toMonitor == Remoting::MonitorBehavior::ToCurrent) + if (toMonitor == winrt::TerminalApp::MonitorBehavior::ToCurrent) { _moveToMonitorOf(oldForegroundWindow); } - else if (toMonitor == Remoting::MonitorBehavior::ToMouse) + else if (toMonitor == winrt::TerminalApp::MonitorBehavior::ToMouse) { _moveToMonitorOfMouse(); } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index aac53006677..faed1270384 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -#include "pch.h" +#pragma once #include "BaseWindow.h" -#include void SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept; @@ -23,8 +22,6 @@ class IslandWindow : virtual void MakeWindow() noexcept; virtual void Close(); - virtual void Refrigerate() noexcept; - virtual void OnSize(const UINT width, const UINT height); HWND GetInteropHandle() const; @@ -41,7 +38,7 @@ class IslandWindow : virtual til::rect GetNonClientFrame(const UINT dpi) const noexcept; virtual til::size GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept; - virtual bool Initialize(); + virtual void Initialize(); void SetCreateCallback(std::function pfn) noexcept; @@ -55,7 +52,7 @@ class IslandWindow : void FlashTaskbar(); void SetTaskbarProgress(const size_t state, const size_t progress); - safe_void_coroutine SummonWindow(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args); + void SummonWindow(winrt::TerminalApp::SummonWindowBehavior args); bool IsQuakeWindow() const noexcept; void IsQuakeWindow(bool isQuakeWindow) noexcept; @@ -82,11 +79,9 @@ class IslandWindow : til::event> NotifyReAddNotificationIcon; til::event> ShouldExitFullscreen; til::event> MaximizeChanged; - til::event> AutomaticShutdownRequested; til::event> WindowMoved; til::event> WindowVisibilityChanged; - til::event> UpdateSettingsRequested; protected: void ForceResize() @@ -116,10 +111,6 @@ class IslandWindow : RECT _rcWindowBeforeFullscreen{}; RECT _rcWorkBeforeFullscreen{}; UINT _dpiBeforeFullscreen{ 96 }; - bool _currentSystemThemeIsDark{ true }; - - void _coldInitialize(); - void _warmInitialize(); virtual void _SetIsBorderless(const bool borderlessEnabled); virtual void _SetIsFullscreen(const bool fullscreenEnabled); @@ -131,16 +122,16 @@ class IslandWindow : void _OnGetMinMaxInfo(const WPARAM wParam, const LPARAM lParam); void _globalActivateWindow(const uint32_t dropdownDuration, - const winrt::Microsoft::Terminal::Remoting::MonitorBehavior toMonitor); + const winrt::TerminalApp::MonitorBehavior toMonitor); void _dropdownWindow(const uint32_t dropdownDuration, - const winrt::Microsoft::Terminal::Remoting::MonitorBehavior toMonitor); + const winrt::TerminalApp::MonitorBehavior toMonitor); void _slideUpWindow(const uint32_t dropdownDuration); void _doSlideAnimation(const uint32_t dropdownDuration, const bool down); void _globalDismissWindow(const uint32_t dropdownDuration); static MONITORINFO _getMonitorForCursor(); static MONITORINFO _getMonitorForWindow(HWND foregroundWindow); - void _moveToMonitor(HWND foregroundWindow, const winrt::Microsoft::Terminal::Remoting::MonitorBehavior toMonitor); + void _moveToMonitor(HWND foregroundWindow, const winrt::TerminalApp::MonitorBehavior toMonitor); void _moveToMonitorOfMouse(); void _moveToMonitorOf(HWND foregroundWindow); void _moveToMonitor(const MONITORINFO activeMonitor); @@ -151,8 +142,6 @@ class IslandWindow : void _enterQuakeMode(); til::rect _getQuakeModeSize(HMONITOR hmon); - void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args); - bool _minimizeToNotificationArea{ false }; std::unordered_map _systemMenuItems; diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index 8d4982269bb..3c66105885c 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -5,8 +5,11 @@ ********************************************************/ #include "pch.h" #include "NonClientIslandWindow.h" + +#include +#include + #include "../types/inc/utils.hpp" -#include "TerminalThemeHelpers.h" using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Composition; @@ -14,7 +17,6 @@ using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Hosting; using namespace winrt::Windows::Foundation::Numerics; using namespace ::Microsoft::Console; -using namespace ::Microsoft::Console::Types; static constexpr int AutohideTaskbarSize = 2; @@ -346,17 +348,9 @@ void NonClientIslandWindow::OnAppInitialized() IslandWindow::OnAppInitialized(); } -void NonClientIslandWindow::Refrigerate() noexcept +void NonClientIslandWindow::Initialize() { - IslandWindow::Refrigerate(); - - // Revoke all our XAML callbacks. - _callbacks = {}; -} - -bool NonClientIslandWindow::Initialize() -{ - const bool coldInit = IslandWindow::Initialize(); + IslandWindow::Initialize(); _UpdateFrameMargins(); @@ -393,8 +387,6 @@ bool NonClientIslandWindow::Initialize() // (i.e. re-using an existing window), we need to manually update the // island's position to fill the new window bounds. _ResizeDragBarWindow(); - - return coldInit; } // Method Description: diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index bf9c815237c..76e093a2340 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -17,11 +17,8 @@ Author(s): Mike Griese (migrie) April-2019 --*/ -#include "pch.h" +#pragma once #include "IslandWindow.h" -#include "../../types/inc/Viewport.hpp" -#include -#include class NonClientIslandWindow : public IslandWindow { @@ -32,8 +29,6 @@ class NonClientIslandWindow : public IslandWindow NonClientIslandWindow(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) noexcept; ~NonClientIslandWindow() override; - void Refrigerate() noexcept override; - virtual void Close() override; void MakeWindow() noexcept override; virtual void OnSize(const UINT width, const UINT height) override; @@ -43,7 +38,7 @@ class NonClientIslandWindow : public IslandWindow virtual til::rect GetNonClientFrame(UINT dpi) const noexcept override; virtual til::size GetTotalNonClientExclusiveSize(UINT dpi) const noexcept override; - bool Initialize() override; + void Initialize() override; void OnAppInitialized() override; void SetContent(winrt::Windows::UI::Xaml::UIElement content) override; diff --git a/src/cascadia/WindowsTerminal/NotificationIcon.cpp b/src/cascadia/WindowsTerminal/NotificationIcon.cpp deleted file mode 100644 index 25c945f12e2..00000000000 --- a/src/cascadia/WindowsTerminal/NotificationIcon.cpp +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "icon.h" -#include "NotificationIcon.h" -#include "CustomWindowMessages.h" - -#include -#include - -using namespace winrt::Windows::Foundation::Collections; -using namespace winrt::Microsoft::Terminal; - -NotificationIcon::NotificationIcon(const HWND owningHwnd) : - _owningHwnd{ owningHwnd } -{ - CreateNotificationIcon(); -} - -NotificationIcon::~NotificationIcon() -{ - RemoveIconFromNotificationArea(); -} - -void NotificationIcon::_CreateWindow() -{ - WNDCLASSW wc{}; - wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hInstance = wil::GetModuleInstanceHandle(); - wc.lpszClassName = L"NOTIFICATION_ICON_HOSTING_WINDOW_CLASS"; - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = DefWindowProcW; - wc.hIcon = static_cast(GetActiveAppIconHandle(true)); - RegisterClass(&wc); - - _notificationIconHwnd = wil::unique_hwnd(CreateWindowW(wc.lpszClassName, - wc.lpszClassName, - WS_DISABLED, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - HWND_MESSAGE, - nullptr, - wc.hInstance, - nullptr)); - - WINRT_VERIFY(_notificationIconHwnd); -} - -// Method Description: -// - Creates and adds an icon to the notification area. -// If an icon already exists, update the HWND associated -// to the icon with this window's HWND. -// Arguments: -// - -// Return Value: -// - -void NotificationIcon::CreateNotificationIcon() -{ - if (!_notificationIconHwnd) - { - // Creating a disabled, non visible window just so we can set it - // as the foreground window when showing the context menu. - // This is done so that the context menu can be dismissed - // when clicking outside of it. - _CreateWindow(); - } - - NOTIFYICONDATA nid{}; - nid.cbSize = sizeof(NOTIFYICONDATA); - - // This HWND will receive the callbacks sent by the notification icon. - nid.hWnd = _owningHwnd; - - // App-defined identifier of the icon. The HWND and ID are used - // to identify which icon to operate on when calling Shell_NotifyIcon. - // Multiple icons can be associated with one HWND, but here we're only - // going to be showing one so the ID doesn't really matter. - nid.uID = 1; - - nid.uCallbackMessage = CM_NOTIFY_FROM_NOTIFICATION_AREA; - - // AppName happens to be in the ContextMenu's Resources, see GH#12264 - ScopedResourceLoader loader{ L"TerminalApp/ContextMenu" }; - const auto appNameLoc = loader.GetLocalizedString(L"AppName"); - - nid.hIcon = static_cast(GetActiveAppIconHandle(true)); - StringCchCopy(nid.szTip, ARRAYSIZE(nid.szTip), appNameLoc.c_str()); - nid.uFlags = NIF_MESSAGE | NIF_SHOWTIP | NIF_TIP | NIF_ICON; - Shell_NotifyIcon(NIM_ADD, &nid); - - // For whatever reason, the NIM_ADD call doesn't seem to set the version - // properly, resulting in us being unable to receive the expected notification - // events. We actually have to make a separate NIM_SETVERSION call for it to - // work properly. - nid.uVersion = NOTIFYICON_VERSION_4; - Shell_NotifyIcon(NIM_SETVERSION, &nid); - - _notificationIconData = nid; -} - -// Method Description: -// - This creates our context menu and displays it at the given -// screen coordinates. -// Arguments: -// - coord: The coordinates where we should be showing the context menu. -// - peasants: The map of all peasants that should be available in the context menu. -// Return Value: -// - -void NotificationIcon::ShowContextMenu(const til::point coord, - const IVectorView& peasants) -{ - if (const auto hMenu = _CreateContextMenu(peasants)) - { - // We'll need to set our window to the foreground before calling - // TrackPopupMenuEx or else the menu won't dismiss when clicking away. - SetForegroundWindow(_notificationIconHwnd.get()); - - // User can select menu items with the left and right buttons. - UINT uFlags = TPM_RIGHTBUTTON; - - // Nonzero if drop-down menus are right-aligned with the corresponding menu-bar item - // 0 if the menus are left-aligned. - if (GetSystemMetrics(SM_MENUDROPALIGNMENT) != 0) - { - uFlags |= TPM_RIGHTALIGN; - } - else - { - uFlags |= TPM_LEFTALIGN; - } - - TrackPopupMenuEx(hMenu, uFlags, coord.x, coord.y, _owningHwnd, NULL); - } -} - -// Method Description: -// - This creates the context menu for our notification icon. -// Arguments: -// - peasants: A map of all peasants' ID to their window name. -// Return Value: -// - The handle to the newly created context menu. -HMENU NotificationIcon::_CreateContextMenu(const IVectorView& peasants) -{ - auto hMenu = CreatePopupMenu(); - if (hMenu) - { - MENUINFO mi{}; - mi.cbSize = sizeof(MENUINFO); - mi.fMask = MIM_STYLE | MIM_APPLYTOSUBMENUS | MIM_MENUDATA; - mi.dwStyle = MNS_NOTIFYBYPOS; - mi.dwMenuData = NULL; - SetMenuInfo(hMenu, &mi); - - // Focus Current Terminal Window - AppendMenu(hMenu, MF_STRING, gsl::narrow(NotificationIconMenuItemAction::FocusTerminal), RS_(L"NotificationIconFocusTerminal").c_str()); - AppendMenu(hMenu, MF_SEPARATOR, 0, L""); - - // Submenu for Windows - if (auto submenu = CreatePopupMenu()) - { - for (const auto& p : peasants) - { - std::wstringstream displayText; - displayText << L"#" << p.Id; - - if (!p.TabTitle.empty()) - { - displayText << L": " << std::wstring_view{ p.TabTitle }; - } - - if (!p.Name.empty()) - { - displayText << L" [" << std::wstring_view{ p.Name } << L"]"; - } - - AppendMenu(submenu, MF_STRING, gsl::narrow(p.Id), displayText.str().c_str()); - } - - MENUINFO submenuInfo{}; - submenuInfo.cbSize = sizeof(MENUINFO); - submenuInfo.fMask = MIM_MENUDATA; - submenuInfo.dwStyle = MNS_NOTIFYBYPOS; - submenuInfo.dwMenuData = (UINT_PTR)NotificationIconMenuItemAction::SummonWindow; - SetMenuInfo(submenu, &submenuInfo); - - AppendMenu(hMenu, MF_POPUP, (UINT_PTR)submenu, RS_(L"NotificationIconWindowSubmenu").c_str()); - } - } - return hMenu; -} - -// Method Description: -// - This is the handler for when one of the menu items are selected within -// the notification icon's context menu. -// Arguments: -// - menu: The handle to the menu that holds the menu item that was selected. -// - menuItemIndex: The index of the menu item within the given menu. -// Return Value: -// - -void NotificationIcon::MenuItemSelected(const HMENU menu, const UINT menuItemIndex) -{ - // Check the menu's data for a specific action. - MENUINFO mi{}; - mi.cbSize = sizeof(MENUINFO); - mi.fMask = MIM_MENUDATA; - GetMenuInfo(menu, &mi); - if (mi.dwMenuData) - { - if (gsl::narrow(mi.dwMenuData) == NotificationIconMenuItemAction::SummonWindow) - { - winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{}; - args.WindowID(GetMenuItemID(menu, menuItemIndex)); - args.SummonBehavior().ToggleVisibility(false); - args.SummonBehavior().MoveToCurrentDesktop(false); - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); - SummonWindowRequested.raise(args); - return; - } - } - - // Now check the menu item itself for an action. - const auto action = gsl::narrow(GetMenuItemID(menu, menuItemIndex)); - switch (action) - { - case NotificationIconMenuItemAction::FocusTerminal: - { - winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{}; - args.SummonBehavior().ToggleVisibility(false); - args.SummonBehavior().MoveToCurrentDesktop(false); - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); - SummonWindowRequested.raise(args); - break; - } - } -} - -// Method Description: -// - This is the handler for when the notification icon itself is left-clicked. -// Arguments: -// - -// Return Value: -// - -void NotificationIcon::NotificationIconPressed() -{ - // No name in the args means summon the mru window. - winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs args{}; - args.SummonBehavior().MoveToCurrentDesktop(false); - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); - args.SummonBehavior().ToggleVisibility(false); - SummonWindowRequested.raise(args); -} - -// Method Description: -// - Re-add a notification icon using our currently saved notification icon data. -// Arguments: -// - -// Return Value: -// - -void NotificationIcon::ReAddNotificationIcon() -{ - Shell_NotifyIcon(NIM_ADD, &_notificationIconData); - Shell_NotifyIcon(NIM_SETVERSION, &_notificationIconData); -} - -// Method Description: -// - Deletes our notification icon. -// Arguments: -// - -// Return Value: -// - -void NotificationIcon::RemoveIconFromNotificationArea() -{ - Shell_NotifyIcon(NIM_DELETE, &_notificationIconData); -} diff --git a/src/cascadia/WindowsTerminal/NotificationIcon.h b/src/cascadia/WindowsTerminal/NotificationIcon.h deleted file mode 100644 index 53a0e23d1e2..00000000000 --- a/src/cascadia/WindowsTerminal/NotificationIcon.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" - -#pragma once -// This enumerates all the possible actions -// that our notification icon context menu could do. -enum class NotificationIconMenuItemAction -{ - FocusTerminal, // Focus the MRU terminal. - SummonWindow -}; - -class NotificationIcon -{ -public: - NotificationIcon() = delete; - NotificationIcon(const HWND owningHwnd); - ~NotificationIcon(); - - void CreateNotificationIcon(); - void RemoveIconFromNotificationArea(); - void ReAddNotificationIcon(); - - void NotificationIconPressed(); - void ShowContextMenu(const til::point coord, const winrt::Windows::Foundation::Collections::IVectorView& peasants); - void MenuItemSelected(const HMENU menu, const UINT menuItemIndex); - - til::event> SummonWindowRequested; - -private: - void _CreateWindow(); - HMENU _CreateContextMenu(const winrt::Windows::Foundation::Collections::IVectorView& peasants); - - wil::unique_hwnd _notificationIconHwnd; - HWND _owningHwnd; - NOTIFYICONDATA _notificationIconData; -}; diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index ff22b8e53e8..2564ba0d472 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -4,11 +4,23 @@ #include "pch.h" #include "WindowEmperor.h" -#include "../inc/WindowingBehavior.h" -#include "../../types/inc/utils.hpp" -#include "../WinRTUtils/inc/WtExeUtils.h" +#include +#include +#include +#include +#include + +#include "AppHost.h" #include "resource.h" -#include "NotificationIcon.h" +#include "VirtualDesktopUtils.h" +#include "../../types/inc/User32Utils.hpp" +#include "../../types/inc/utils.hpp" + +enum class NotificationIconMenuItemAction +{ + FocusTerminal, // Focus the MRU terminal. + SummonWindow +}; using namespace winrt; using namespace winrt::Microsoft::Terminal; @@ -18,371 +30,623 @@ using namespace ::Microsoft::Console; using namespace std::chrono_literals; using VirtualKeyModifiers = winrt::Windows::System::VirtualKeyModifiers; -#define TERMINAL_MESSAGE_CLASS_NAME L"TERMINAL_MESSAGE_CLASS" +#ifdef _WIN64 +static constexpr ULONG_PTR TERMINAL_HANDOFF_MAGIC = 0x4c414e494d524554; // 'TERMINAL' +#else +static constexpr ULONG_PTR TERMINAL_HANDOFF_MAGIC = 0x4d524554; // 'TERM' +#endif + extern "C" IMAGE_DOS_HEADER __ImageBase; -WindowEmperor::WindowEmperor() noexcept : - _app{} +// A convenience function around CommandLineToArgv. +static std::vector commandlineToArgArray(const wchar_t* commandLine) { - _manager.FindTargetWindowRequested([this](const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& findWindowArgs) { - { - const auto targetWindow = _app.Logic().FindTargetWindow(findWindowArgs.Args().Commandline()); - findWindowArgs.ResultTargetWindow(targetWindow.WindowId()); - findWindowArgs.ResultTargetWindowName(targetWindow.WindowName()); - } - }); + int argc = 0; + const wil::unique_hlocal_ptr argv{ CommandLineToArgvW(commandLine, &argc) }; + argc = std::max(argc, 0); - _dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread(); + std::vector args; + args.reserve(argc); + for (int i = 0; i < argc; i++) + { + args.emplace_back(argv.get()[i]); + } + return args; } -void _buildArgsFromCommandline(std::vector& args) +// Returns the length of a double-null encoded string *excluding* the trailing double-null character. +static std::wstring_view stringFromDoubleNullTerminated(const wchar_t* beg) { - if (auto commandline{ GetCommandLineW() }) - { - auto argc = 0; + auto end = beg; - // 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); - } - } + for (; *end; end += wcsnlen(end, SIZE_T_MAX) + 1) + { } - if (args.empty()) + + return { beg, end }; +} + +// Appends an uint32_t to a byte vector. +static void serializeUint32(std::vector& out, uint32_t value) +{ + out.insert(out.end(), reinterpret_cast(&value), reinterpret_cast(&value) + sizeof(value)); +} + +// Parses an uint32_t from the input iterator. Performs bounds-checks. +// Returns an iterator that points past it. +static const uint8_t* deserializeUint32(const uint8_t* it, const uint8_t* end, uint32_t& val) +{ + if (static_cast(end - it) < sizeof(uint32_t)) { - args.emplace_back(L"wt.exe"); + throw std::out_of_range("Not enough data for uint32_t"); } + val = *reinterpret_cast(it); + return it + sizeof(uint32_t); } -void WindowEmperor::HandleCommandlineArgs(int nCmdShow) +// Writes an uint32_t length prefix, followed by the string data, to the output vector. +static void serializeString(std::vector& out, std::wstring_view str) { - std::vector args; - _buildArgsFromCommandline(args); - const auto cwd{ wil::GetCurrentDirectoryW() }; + const auto ptr = reinterpret_cast(str.data()); + const auto len = gsl::narrow(str.size()); + serializeUint32(out, len); + out.insert(out.end(), ptr, ptr + len * sizeof(wchar_t)); +} + +// Parses the next string from the input iterator. Performs bounds-checks. +// Returns an iterator that points past it. +static const uint8_t* deserializeString(const uint8_t* it, const uint8_t* end, std::wstring_view& str) +{ + uint32_t len; + it = deserializeUint32(it, end, len); + if (static_cast(end - it) < len * sizeof(wchar_t)) { - // ALWAYS change the _real_ CWD of the Terminal to system32, so that we - // don't lock the directory we were spawned in. - std::wstring system32{}; - if (SUCCEEDED_LOG(wil::GetSystemDirectoryW(system32))) - { - LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectoryW(system32.c_str())); - } + throw std::out_of_range("Not enough data for string content"); } - // GetEnvironmentStringsW() returns a double-null terminated string. - // The hstring(wchar_t*) constructor however only works for regular null-terminated strings. - // Due to that we need to manually search for the terminator. - winrt::hstring env; + str = { reinterpret_cast(it), len }; + return it + len * sizeof(wchar_t); +} + +struct Handoff +{ + std::wstring_view args; + std::wstring_view env; + std::wstring_view cwd; + uint32_t show; +}; + +// Serializes all relevant parameters to a byte blob for a WM_COPYDATA message. +// This allows us to hand off an invocation to an existing instance of the Terminal. +static std::vector serializeHandoffPayload(int nCmdShow) +{ + const auto args = GetCommandLineW(); + const wil::unique_environstrings_ptr envMem{ GetEnvironmentStringsW() }; + const auto env = stringFromDoubleNullTerminated(envMem.get()); + const auto cwd = wil::GetCurrentDirectoryW(); + + std::vector out; + serializeString(out, args); + serializeString(out, env); + serializeString(out, cwd); + serializeUint32(out, static_cast(nCmdShow)); + return out; +} + +// Counterpart to serializeHandoffPayload. +static Handoff deserializeHandoffPayload(const uint8_t* beg, const uint8_t* end) +{ + Handoff result{}; + auto it = beg; + it = deserializeString(it, end, result.args); + it = deserializeString(it, end, result.env); + it = deserializeString(it, end, result.cwd); + it = deserializeUint32(it, end, result.show); + return result; +} + +// Either acquires unique ownership over the given `className` mutex, +// or attempts to pass the commandline to the existing instance. +static wil::unique_mutex acquireMutexOrAttemptHandoff(const wchar_t* className, int nCmdShow) +{ + // If the process that owns the mutex has not finished creating the window yet, + // FindWindowW will return nullptr. We'll retry a few times just in case. + // At the 1.5x growth rate, this will retry up to ~30s in total. + for (DWORD sleep = 50; sleep < 10000; sleep += sleep / 2) { - const wil::unique_environstrings_ptr strings{ GetEnvironmentStringsW() }; - const auto beg = strings.get(); - auto end = beg; + wil::unique_mutex mutex{ CreateMutexW(nullptr, TRUE, className) }; + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + mutex.reset(); + } + if (mutex) + { + return mutex; + } - for (; *end; end += wcsnlen(end, SIZE_T_MAX) + 1) + // I found that FindWindow() with no other filters is substantially faster than + // using FindWindowEx() and restricting the search to just HWND_MESSAGE windows. + // In both cases it's quite fast though at ~1us/op vs ~3us/op (good old Win32 <3). + if (const auto hwnd = FindWindowW(className, nullptr)) { + auto payload = serializeHandoffPayload(nCmdShow); + const COPYDATASTRUCT cds{ + .dwData = TERMINAL_HANDOFF_MAGIC, + .cbData = gsl::narrow(payload.size()), + .lpData = payload.data(), + }; + if (SendMessageTimeoutW(hwnd, WM_COPYDATA, 0, reinterpret_cast(&cds), SMTO_ABORTIFHUNG | SMTO_ERRORONEXIT, 5000, nullptr)) + { + return {}; + } } - env = winrt::hstring{ beg, gsl::narrow(end - beg) }; + Sleep(sleep); } - const Remoting::CommandlineArgs eventArgs{ args, cwd, gsl::narrow_cast(nCmdShow), std::move(env) }; - const auto isolatedMode{ _app.Logic().IsolatedMode() }; - const auto result = _manager.ProposeCommandline(eventArgs, isolatedMode); - int exitCode = 0; + return {}; +} + +HWND WindowEmperor::GetMainWindow() const noexcept +{ + _assertIsMainThread(); + return _window.get(); +} - if (result.ShouldCreateWindow()) +AppHost* WindowEmperor::GetWindowById(uint64_t id) const noexcept +{ + _assertIsMainThread(); + + for (const auto& window : _windows) { - _createNewWindowThread(Remoting::WindowRequestedArgs{ result, eventArgs }); - _becomeMonarch(); - WaitForWindows(); + if (window->Logic().WindowProperties().WindowId() == id) + { + return window.get(); + } } - else + return nullptr; +} + +AppHost* WindowEmperor::GetWindowByName(std::wstring_view name) const noexcept +{ + _assertIsMainThread(); + + for (const auto& window : _windows) { - const auto res = _app.Logic().GetParseCommandlineMessage(eventArgs.Commandline()); - if (!res.Message.empty()) + if (window->Logic().WindowProperties().WindowName() == name) { - AppHost::s_DisplayMessageBox(res); + return window.get(); } - exitCode = res.ExitCode; } - - // There's a mysterious crash in XAML on Windows 10 if you just let _app get destroyed (GH#15410). - // We also need to ensure that all UI threads exit before WindowEmperor leaves the scope on the main thread (MSFT:46744208). - // Both problems can be solved and the shutdown accelerated by using TerminateProcess. - // std::exit(), etc., cannot be used here, because those use ExitProcess for unpackaged applications. - TerminateProcess(GetCurrentProcess(), gsl::narrow_cast(exitCode)); - __assume(false); + return nullptr; } -void WindowEmperor::WaitForWindows() +void WindowEmperor::CreateNewWindow(winrt::TerminalApp::WindowRequestedArgs args) { - MSG message{}; - while (GetMessageW(&message, nullptr, 0, 0)) + _assertIsMainThread(); + + uint64_t id = args.Id(); + bool needsNewId = id == 0; + uint64_t newId = 0; + + for (const auto& host : _windows) { - TranslateMessage(&message); - DispatchMessage(&message); + const auto existingId = host->Logic().WindowProperties().WindowId(); + newId = std::max(newId, existingId); + needsNewId |= existingId == id; } - _finalizeSessionPersistence(); - TerminateProcess(GetCurrentProcess(), 0); + if (needsNewId) + { + args.Id(newId + 1); + } + + auto host = std::make_shared(this, _app.Logic(), std::move(args)); + host->Initialize(); + _windows.emplace_back(std::move(host)); } -void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& args) +AppHost* WindowEmperor::_mostRecentWindow() const noexcept { - Remoting::Peasant peasant{ _manager.CreatePeasant(args) }; - std::shared_ptr window{ nullptr }; + int64_t max = INT64_MIN; + AppHost* mostRecent = nullptr; - // FIRST: Attempt to reheat an existing window that we refrigerated for - // later. If we have an existing unused window, then we don't need to create - // a new WindowThread & HWND for this request. - { // Add a scope to minimize lock duration. - auto fridge{ _oldThreads.lock() }; - if (!fridge->empty()) + for (const auto& w : _windows) + { + const auto lastActivatedTime = w->GetLastActivatedTime(); + if (lastActivatedTime > max) { - // Look at that, a refrigerated thread ready to be used. Let's use that! - window = std::move(fridge->back()); - fridge->pop_back(); + max = lastActivatedTime; + mostRecent = w.get(); } } - // Did we find one? - if (window) + return mostRecent; +} + +void WindowEmperor::HandleCommandlineArgs(int nCmdShow) +{ + std::wstring windowClassName; + windowClassName.reserve(47); // "Windows Terminal Preview Admin 0123456789012345" +#if defined(WT_BRANDING_RELEASE) + windowClassName.append(L"Windows Terminal"); +#elif defined(WT_BRANDING_PREVIEW) + windowClassName.append(L"Windows Terminal Preview"); +#elif defined(WT_BRANDING_CANARY) + windowClassName.append(L"Windows Terminal Canary"); +#else + windowClassName.append(L"Windows Terminal Dev"); +#endif + if (Utils::IsRunningElevated()) + { + windowClassName.append(L" Admin"); + } + if (!IsPackaged()) { - // Cool! Let's increment the number of active windows, and re-heat it. - _windowThreadInstances.fetch_add(1, std::memory_order_relaxed); + const auto path = wil::GetModuleFileNameW(nullptr); + const auto hash = til::hash(path); +#ifdef _WIN64 + fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:016x}"), hash); +#else + fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:08x}"), hash); +#endif + } - window->Microwave(args, peasant); - // This will unblock the event we're waiting on in KeepWarm, and the - // window thread (started below) will continue through it's loop - return; + // Windows Terminal is a single-instance application. Either acquire ownership + // over the mutex, or hand off the command line to the existing instance. + const auto mutex = acquireMutexOrAttemptHandoff(windowClassName.c_str(), nCmdShow); + if (!mutex) + { + // The command line has been handed off. We can now exit. + // We do it with TerminateProcess() primarily to avoid WinUI shutdown issues. + TerminateProcess(GetCurrentProcess(), gsl::narrow_cast(0)); + __assume(false); } - // At this point, there weren't any pending refrigerated threads we could - // just use. That's fine. Let's just go create a new one. + _app = winrt::TerminalApp::App{}; + _app.Logic().ReloadSettings(); - window = std::make_shared(_app.Logic(), args, _manager, peasant); + _createMessageWindow(windowClassName.c_str()); + _setupGlobalHotkeys(); + _checkWindowsForNotificationIcon(); - std::weak_ptr weakThis{ weak_from_this() }; + // When the settings change, we'll want to update our global hotkeys + // and our notification icon based on the new settings. + _app.Logic().SettingsChanged([this](auto&&, const TerminalApp::SettingsLoadEventArgs& args) { + if (SUCCEEDED(args.Result())) + { + _assertIsMainThread(); + _setupGlobalHotkeys(); + _checkWindowsForNotificationIcon(); + } + }); - // Increment our count of window instances _now_, immediately. We're - // starting a window now, we shouldn't exit (due to having 0 windows) till - // this window has a chance to actually start. - // * We can't just put the window immediately into _windows right now, - // because there are multiple async places where we iterate over all - // _windows assuming that they have initialized and we can call methods - // that might hit the TerminalPage. - // * If we don't somehow track this window now, before it has been actually - // started, there's a possible race. As an example, it would be possible - // to drag a tab out of the single window, which would create a new - // window, but have the original window exit before the new window has - // started, causing the app to exit. - // Hence: increment the number of total windows now. - _windowThreadInstances.fetch_add(1, std::memory_order_relaxed); + { + const wil::unique_environstrings_ptr envMem{ GetEnvironmentStringsW() }; + const auto env = stringFromDoubleNullTerminated(envMem.get()); + const auto cwd = wil::GetCurrentDirectoryW(); + const auto showCmd = gsl::narrow_cast(nCmdShow); - std::thread t([weakThis, window]() { - try + // Restore persisted windows. + const auto state = ApplicationState::SharedInstance(); + const auto layouts = state.PersistedWindowLayouts(); + if (layouts && layouts.Size() > 0) { - window->CreateHost(); + _needsPersistenceCleanup = true; - if (auto self{ weakThis.lock() }) + uint32_t startIdx = 0; + for (const auto layout : layouts) { - self->_windowStartedHandlerPostXAML(window); + hstring args[] = { L"wt", L"-w", L"new", L"-s", winrt::to_hstring(startIdx) }; + _dispatchCommandline({ args, cwd, showCmd, std::move(env) }); + startIdx += 1; } - while (window->KeepWarm()) - { - // Now that the window is ready to go, we can add it to our list of windows, - // because we know it will be well behaved. - // - // Be sure to only modify the list of windows under lock. + } - if (auto self{ weakThis.lock() }) - { - auto lockedWindows{ self->_windows.lock() }; - lockedWindows->push_back(window); - } - auto removeWindow = wil::scope_exit([&]() { - if (auto self{ weakThis.lock() }) - { - self->_removeWindow(window->PeasantID()); - } - }); + // Create another window if needed: There aren't any yet, or we got an explicit command line. + const auto args = commandlineToArgArray(GetCommandLineW()); + if (_windows.empty() || args.size() != 1) + { + _dispatchCommandline({ args, cwd, showCmd, std::move(env) }); + } - auto decrementWindowCount = wil::scope_exit([&]() { - if (auto self{ weakThis.lock() }) - { - self->_decrementWindowCount(); - } - }); + // If we created no windows, e.g. because the args are "/?" we can just exit now. + _postQuitMessageIfNeeded(); + } - window->RunMessagePump(); + // ALWAYS change the _real_ CWD of the Terminal to system32, + // so that we don't lock the directory we were spawned in. + if (std::wstring system32; SUCCEEDED_LOG(wil::GetSystemDirectoryW(system32))) + { + LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectoryW(system32.c_str())); + } - // Manually trigger the cleanup callback. This will ensure that we - // remove the window from our list of windows, before we release the - // AppHost (and subsequently, the host's Logic() member that we use - // elsewhere). - removeWindow.reset(); + // The first CoreWindow is created implicitly by XAML and parented to the + // first XAML island. We parent it to our message-only window for 2 reasons: + // * On Windows 10 the CoreWindow will show up as a visible window on the + // taskbar due to a WinUI bug, and this will hide it. + // * When we DestroyWindow() the island it will destroy the CoreWindow, + // and it's not possible to recreate it. That's also a WinUI bug. + if (const auto coreWindow = winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread()) + { + if (const auto interop = coreWindow.try_as()) + { + HWND coreHandle = nullptr; + if (SUCCEEDED(interop->get_WindowHandle(&coreHandle)) && coreHandle) + { + SetParent(coreHandle, _window.get()); + } + } + } - // On Windows 11, we DONT want to refrigerate the window. There, - // we can just close it like normal. Break out of the loop, so - // we don't try to put this window in the fridge. - if (Utils::IsWindows11()) - { - decrementWindowCount.reset(); - break; - } - else - { - window->Refrigerate(); - decrementWindowCount.reset(); + // Main message loop. It pumps all windows. + bool loggedInteraction = false; + MSG msg{}; + while (GetMessageW(&msg, nullptr, 0, 0)) + { + if (!loggedInteraction && (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)) + { + TraceLoggingWrite( + g_hWindowsTerminalProvider, + "SessionBecameInteractive", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + loggedInteraction = true; + } - if (auto self{ weakThis.lock() }) - { - auto fridge{ self->_oldThreads.lock() }; - fridge->push_back(window); - } - } + // GH#638 (Pressing F7 brings up both the history AND a caret browsing message) + // The Xaml input stack doesn't allow an application to suppress the "caret browsing" + // dialog experience triggered when you press F7. Official recommendation from the Xaml + // team is to catch F7 before we hand it off. + // AppLogic contains an ad-hoc implementation of event bubbling for a runtime classes + // implementing a custom IF7Listener interface. + // If the recipient of IF7Listener::OnF7Pressed suggests that the F7 press has, in fact, + // been handled we can discard the message before we even translate it. + if ((msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) && msg.wParam == VK_F7) + { + if (const auto w = _mostRecentWindow(); w && w->OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(msg.lParam)), true)) + { + // The application consumed the F7. Don't let Xaml get it. + continue; + } + } + + // GH#6421 - System XAML will never send an Alt KeyUp event. So, similar + // to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp + // here, and plumb it through. + if ((msg.message == WM_KEYUP || msg.message == WM_SYSKEYUP) && msg.wParam == VK_MENU) + { + // Let's pass to the application + if (const auto w = _mostRecentWindow(); w && w->OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(msg.lParam)), false)) + { + // The application consumed the Alt. Don't let Xaml get it. + continue; } + } - // Now that we no longer care about this thread's window, let it - // release it's app host and flush the rest of the XAML queue. - window->RundownForExit(); + // GH#7125 = System XAML will show a system dialog on Alt Space. We want to + // explicitly prevent that because we handle that ourselves. So similar to + // above, we steal the event and hand it off to the host. + if (msg.message == WM_SYSKEYDOWN && msg.wParam == VK_SPACE) + { + if (const auto w = _mostRecentWindow()) + { + w->OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(msg.lParam)), true); + continue; + } } - CATCH_LOG() - }); - LOG_IF_FAILED(SetThreadDescription(t.native_handle(), L"Window Thread")); - t.detach(); -} + TranslateMessage(&msg); + DispatchMessageW(&msg); + } -// Handler for a WindowThread's Started event, which it raises once the window -// thread starts and XAML is ready to go on that thread. Set up some callbacks -// now that we know this window is set up and ready to go. -// Q: Why isn't adding these callbacks just a part of _createNewWindowThread? -// A: Until the thread actually starts, the AppHost (and its Logic()) haven't -// been ctor'd or initialized, so trying to add callbacks immediately will A/V -void WindowEmperor::_windowStartedHandlerPostXAML(const std::shared_ptr& sender) -{ - // Add a callback to the window's logic to let us know when the window's - // quake mode state changes. We'll use this to check if we need to add - // or remove the notification icon. - sender->Logic().IsQuakeWindowChanged({ this, &WindowEmperor::_windowIsQuakeWindowChanged }); - sender->UpdateSettingsRequested({ this, &WindowEmperor::_windowRequestUpdateSettings }); - - // DON'T Summon the window to the foreground, since we might not _currently_ be in - // the foreground, but we should act like the new window is. - // - // If you summon here, the resulting code will call ShowWindow(SW_SHOW) on - // the Terminal window, making it visible BEFORE the XAML island is actually - // ready to be drawn. We want to wait till the app's Initialized event - // before we make the window visible. -} + _finalizeSessionPersistence(); -void WindowEmperor::_removeWindow(uint64_t senderID) -{ - auto lockedWindows{ _windows.lock() }; + if (_notificationIconShown) + { + Shell_NotifyIconW(NIM_DELETE, &_notificationIcon); + } - // find the window in _windows who's peasant's Id matches the peasant's Id - // and remove it - std::erase_if(*lockedWindows, [&](const auto& w) { - return w->PeasantID() == senderID; - }); + // There's a mysterious crash in XAML on Windows 10 if you just let _app get destroyed (GH#15410). + // We also need to ensure that all UI threads exit before WindowEmperor leaves the scope on the main thread (MSFT:46744208). + // Both problems can be solved and the shutdown accelerated by using TerminateProcess. + // std::exit(), etc., cannot be used here, because those use ExitProcess for unpackaged applications. + TerminateProcess(GetCurrentProcess(), gsl::narrow_cast(0)); + __assume(false); } -void WindowEmperor::_decrementWindowCount() +void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs args) { - // When we run out of windows, exit our process if and only if: - // * We're not allowed to run headless OR - // * we've explicitly been told to "quit", which should fully exit the Terminal. - const bool quitWhenLastWindowExits{ !_app.Logic().AllowHeadless() }; - const bool noMoreWindows{ _windowThreadInstances.fetch_sub(1, std::memory_order_relaxed) == 1 }; - if (noMoreWindows && - (_quitting || quitWhenLastWindowExits)) + const auto exitCode = args.ExitCode(); + + if (const auto msg = args.ExitMessage(); !msg.empty()) { - _close(); + _showMessageBox(msg, exitCode != 0); + return; } -} -// Method Description: -// - Set up all sorts of handlers now that we've determined that we're a process -// that will end up hosting the windows. These include: -// - Setting up a message window to handle hotkeys and notification icon -// invokes. -// - Setting up the global hotkeys. -// - Setting up the notification icon. -// - Setting up callbacks for when the settings change. -// - Setting up callbacks for when the number of windows changes. -// - Setting up the throttled func for layout persistence. Arguments: -// - -void WindowEmperor::_becomeMonarch() -{ - // Add a callback to the window manager so that when the Monarch wants a new - // window made, they come to us - _manager.RequestNewWindow([this](auto&&, const Remoting::WindowRequestedArgs& args) { - _createNewWindowThread(args); - }); + if (exitCode != 0) + { + return; + } - _createMessageWindow(); + const auto parsedTarget = args.TargetWindow(); + WindowingMode windowingBehavior = WindowingMode::UseNew; + uint64_t windowId = 0; + winrt::hstring windowName; - _setupGlobalHotkeys(); + // Figure out the windowing behavior the caller wants + // and get the sanitized window ID (if any) and window name (if any). + if (parsedTarget.empty()) + { + windowingBehavior = _app.Logic().Settings().GlobalSettings().WindowingBehavior(); + } + else if (const auto opt = til::parse_signed(parsedTarget, 10)) + { + // Negative window IDs map to WindowingMode::UseNew. + if (*opt > 0) + { + windowId = gsl::narrow_cast(*opt); + } + else if (*opt == 0) + { + windowingBehavior = WindowingMode::UseExisting; + } + } + else if (parsedTarget == L"last") + { + windowingBehavior = WindowingMode::UseExisting; + } + // A window name of "new" maps to WindowingMode::UseNew. + else if (parsedTarget != L"new") + { + windowName = parsedTarget; + } - // When the settings change, we'll want to update our global hotkeys and our - // notification icon based on the new settings. - _app.Logic().SettingsChanged([this](auto&&, const TerminalApp::SettingsLoadEventArgs& args) { - if (SUCCEEDED(args.Result())) + AppHost* window = nullptr; + + // Map from the windowing behavior, ID, and name to a window. + if (windowId) + { + window = GetWindowById(windowId); + } + else if (!windowName.empty()) + { + window = GetWindowByName(windowName); + } + else + { + switch (windowingBehavior) { - _setupGlobalHotkeys(); - _checkWindowsForNotificationIcon(); + case WindowingMode::UseAnyExisting: + window = _mostRecentWindow(); + break; + case WindowingMode::UseExisting: + _dispatchCommandlineCurrentDesktop(std::move(args)); + return; + default: + break; } - }); + } - // On startup, immediately check if we need to show the notification icon. - _checkWindowsForNotificationIcon(); + if (window) + { + window->DispatchCommandline(std::move(args)); + } + else + { + winrt::TerminalApp::WindowRequestedArgs request{ windowId, std::move(args) }; + request.WindowName(std::move(windowName)); + CreateNewWindow(std::move(request)); + } +} + +// This is an implementation-detail of _dispatchCommandline(). +safe_void_coroutine WindowEmperor::_dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args) +{ + std::weak_ptr mostRecentWeak; + + if (winrt::guid currentDesktop; VirtualDesktopUtils::GetCurrentVirtualDesktopId(reinterpret_cast(¤tDesktop))) + { + int64_t max = INT64_MIN; + for (const auto& w : _windows) + { + const auto lastActivatedTime = w->GetLastActivatedTime(); + const auto desktopId = co_await w->GetVirtualDesktopId(); + if (desktopId == currentDesktop && lastActivatedTime > max) + { + max = lastActivatedTime; + mostRecentWeak = w; + } + } + } - // Set the number of open windows (so we know if we are the last window) - // and subscribe for updates if there are any changes to that number. + // GetVirtualDesktopId(), as current implemented, should always return on the main thread. + _assertIsMainThread(); - _revokers.WindowCreated = _manager.WindowCreated(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged }); - _revokers.WindowClosed = _manager.WindowClosed(winrt::auto_revoke, { this, &WindowEmperor::_numberOfWindowsChanged }); + auto window = mostRecentWeak.lock().get(); + if (!window) + { + window = _mostRecentWindow(); + } - // If a previous session of Windows Terminal stored buffer_*.txt files, then we need to clean all those up on exit - // that aren't needed anymore, even if the user disabled the ShouldUsePersistedLayout() setting in the meantime. + if (window) { - const auto state = ApplicationState::SharedInstance(); - const auto layouts = state.PersistedWindowLayouts(); - _requiresPersistenceCleanupOnExit = layouts && layouts.Size() > 0; + window->DispatchCommandline(std::move(args)); + } + else + { + CreateNewWindow(winrt::TerminalApp::WindowRequestedArgs{ 0, std::move(args) }); } } -// sender and args are always nullptr -void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, - const winrt::Windows::Foundation::IInspectable&) +bool WindowEmperor::_summonWindow(const SummonWindowSelectionArgs& args) const { - // If we closed out the quake window, and don't otherwise need the tray - // icon, let's get rid of it. - _checkWindowsForNotificationIcon(); + AppHost* window = nullptr; + + if (args.WindowID) + { + for (const auto& w : _windows) + { + if (w->Logic().WindowProperties().WindowId() == args.WindowID) + { + window = w.get(); + break; + } + } + } + else if (!args.WindowName.empty()) + { + for (const auto& w : _windows) + { + if (w->Logic().WindowProperties().WindowName() == args.WindowName) + { + window = w.get(); + break; + } + } + } + else + { + window = _mostRecentWindow(); + } + + if (!window) + { + return false; + } + + window->HandleSummon(args.SummonBehavior); + return true; } -#pragma endregion +void WindowEmperor::_summonAllWindows() const +{ + TerminalApp::SummonWindowBehavior args; + args.ToggleVisibility(false); + + for (const auto& window : _windows) + { + window->HandleSummon(args); + } +} #pragma region WindowProc static WindowEmperor* GetThisFromHandle(HWND const window) noexcept { - const auto data = GetWindowLongPtr(window, GWLP_USERDATA); + const auto data = GetWindowLongPtrW(window, GWLP_USERDATA); return reinterpret_cast(data); } + [[nodiscard]] LRESULT __stdcall WindowEmperor::_wndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept { WINRT_ASSERT(window); if (WM_NCCREATE == message) { - auto cs = reinterpret_cast(lparam); + const auto cs = reinterpret_cast(lparam); WindowEmperor* that = static_cast(cs->lpCreateParams); WINRT_ASSERT(that); WINRT_ASSERT(!that->_window); @@ -391,227 +655,399 @@ static WindowEmperor* GetThisFromHandle(HWND const window) noexcept } else if (WindowEmperor* that = GetThisFromHandle(window)) { - return that->_messageHandler(message, wparam, lparam); + return that->_messageHandler(window, message, wparam, lparam); } - return DefWindowProc(window, message, wparam, lparam); + return DefWindowProcW(window, message, wparam, lparam); +} + +void WindowEmperor::_createMessageWindow(const wchar_t* className) +{ + const auto instance = reinterpret_cast(&__ImageBase); + const auto icon = LoadIconW(instance, MAKEINTRESOURCEW(IDI_APPICON)); + + const WNDCLASS wc{ + .lpfnWndProc = &_wndProc, + .hInstance = instance, + .hIcon = icon, + .lpszClassName = className, + }; + RegisterClassW(&wc); + + WINRT_VERIFY(CreateWindowExW( + /* dwExStyle */ 0, + /* lpClassName */ className, + /* lpWindowName */ L"Windows Terminal", + /* dwStyle */ 0, + /* X */ 0, + /* Y */ 0, + /* nWidth */ 0, + /* nHeight */ 0, + /* hWndParent */ HWND_MESSAGE, + /* hMenu */ nullptr, + /* hInstance */ instance, + /* lpParam */ this)); + + _notificationIcon.cbSize = sizeof(NOTIFYICONDATA); + _notificationIcon.hWnd = _window.get(); + _notificationIcon.uID = 1; + _notificationIcon.uFlags = NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP | NIF_ICON; + _notificationIcon.uCallbackMessage = WM_NOTIFY_FROM_NOTIFICATION_AREA; + _notificationIcon.hIcon = icon; + _notificationIcon.uVersion = NOTIFYICON_VERSION_4; + + // AppName happens to be in the ContextMenu's Resources, see GH#12264 + const ScopedResourceLoader loader{ L"TerminalApp/ContextMenu" }; + const auto appNameLoc = loader.GetLocalizedString(L"AppName"); + StringCchCopy(_notificationIcon.szTip, ARRAYSIZE(_notificationIcon.szTip), appNameLoc.c_str()); +} + +void WindowEmperor::_postQuitMessageIfNeeded() const +{ + if (_windows.empty() && !_app.Logic().Settings().GlobalSettings().AllowHeadless()) + { + PostQuitMessage(0); + } } -void WindowEmperor::_createMessageWindow() -{ - WNDCLASS wc{}; - wc.hCursor = LoadCursor(nullptr, IDC_ARROW); - wc.hInstance = reinterpret_cast(&__ImageBase); - wc.lpszClassName = TERMINAL_MESSAGE_CLASS_NAME; - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = WindowEmperor::_wndProc; - wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON)); - RegisterClass(&wc); - WINRT_ASSERT(!_window); - - WINRT_VERIFY(CreateWindow(wc.lpszClassName, - L"Windows Terminal", - 0, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - HWND_MESSAGE, - nullptr, - wc.hInstance, - this)); + +safe_void_coroutine WindowEmperor::_showMessageBox(winrt::hstring message, bool error) const +{ + const auto hwnd = _window.get(); + + co_await winrt::resume_background(); + + const auto messageTitle = error ? IDS_ERROR_DIALOG_TITLE : IDS_HELP_DIALOG_TITLE; + const auto messageIcon = error ? MB_ICONERROR : MB_ICONWARNING; + // TODO:GH#4134: polish this dialog more, to make the text more like msiexec /? + MessageBoxW(hwnd, message.c_str(), GetStringResource(messageTitle).data(), MB_OK | messageIcon); } -LRESULT WindowEmperor::_messageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept +LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept { // use C++11 magic statics to make sure we only do this once. // This won't change over the lifetime of the application static const UINT WM_TASKBARCREATED = []() { return RegisterWindowMessageW(L"TaskbarCreated"); }(); - switch (message) - { - case WM_HOTKEY: + try { - _hotkeyPressed(static_cast(wParam)); - return 0; - } - case CM_NOTIFY_FROM_NOTIFICATION_AREA: - { - switch (LOWORD(lParam)) + switch (message) { - case NIN_SELECT: - case NIN_KEYSELECT: + case WM_CLOSE_TERMINAL_WINDOW: { - _notificationIcon->NotificationIconPressed(); + const auto host = reinterpret_cast(lParam); + auto it = _windows.begin(); + const auto end = _windows.end(); + + for (; it != end; ++it) + { + if (host == it->get()) + { + host->Close(); + _windows.erase(it); + break; + } + } + + _postQuitMessageIfNeeded(); return 0; } - case WM_CONTEXTMENU: + case WM_IDENTIFY_ALL_WINDOWS: + for (const auto& host : _windows) + { + host->Logic().IdentifyWindow(); + } + return 0; + case WM_NOTIFY_FROM_NOTIFICATION_AREA: + switch (LOWORD(lParam)) + { + case NIN_SELECT: + case NIN_KEYSELECT: + { + SummonWindowSelectionArgs args; + args.SummonBehavior.MoveToCurrentDesktop(false); + args.SummonBehavior.ToMonitor(winrt::TerminalApp::MonitorBehavior::InPlace); + args.SummonBehavior.ToggleVisibility(false); + std::ignore = _summonWindow(args); + break; + } + case WM_CONTEXTMENU: + if (const auto menu = CreatePopupMenu()) + { + MENUINFO mi{}; + mi.cbSize = sizeof(MENUINFO); + mi.fMask = MIM_STYLE | MIM_APPLYTOSUBMENUS | MIM_MENUDATA; + mi.dwStyle = MNS_NOTIFYBYPOS; + mi.dwMenuData = NULL; + SetMenuInfo(menu, &mi); + + // Focus Current Terminal Window + AppendMenuW(menu, MF_STRING, static_cast(NotificationIconMenuItemAction::FocusTerminal), RS_(L"NotificationIconFocusTerminal").c_str()); + AppendMenuW(menu, MF_SEPARATOR, 0, L""); + + // Submenu for Windows + if (const auto submenu = CreatePopupMenu()) + { + for (const auto& host : _windows) + { + const auto logic = host->Logic(); + const auto props = logic.WindowProperties(); + + std::wstring displayText; + displayText.reserve(64); + + const auto id = props.WindowId(); + fmt::format_to(std::back_inserter(displayText), L"#{}", id); + + if (const auto title = logic.Title(); !title.empty()) + { + fmt::format_to(std::back_inserter(displayText), L": {}", title); + } + + if (const auto name = props.WindowName(); !name.empty()) + { + fmt::format_to(std::back_inserter(displayText), L" [{}]", name); + } + + AppendMenuW(submenu, MF_STRING, gsl::narrow_cast(id), displayText.c_str()); + } + + static constexpr MENUINFO submenuInfo{ + .cbSize = sizeof(MENUINFO), + .fMask = MIM_MENUDATA, + .dwStyle = MNS_NOTIFYBYPOS, + .dwMenuData = static_cast(NotificationIconMenuItemAction::SummonWindow), + }; + SetMenuInfo(submenu, &submenuInfo); + AppendMenuW(menu, MF_POPUP, reinterpret_cast(submenu), RS_(L"NotificationIconWindowSubmenu").c_str()); + } + + // We'll need to set our window to the foreground before calling + // TrackPopupMenuEx or else the menu won't dismiss when clicking away. + SetForegroundWindow(window); + + // User can select menu items with the left and right buttons. + const auto rightAlign = GetSystemMetrics(SM_MENUDROPALIGNMENT) != 0; + const UINT uFlags = TPM_RIGHTBUTTON | (rightAlign ? TPM_RIGHTALIGN : TPM_LEFTALIGN); + TrackPopupMenuEx(menu, uFlags, GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam), window, nullptr); + + if (_currentWindowMenu) + { + DestroyMenu(_currentWindowMenu); + } + _currentWindowMenu = menu; + } + break; + default: + break; + } + return 0; + case WM_MENUCOMMAND: { - const til::point eventPoint{ GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam) }; - _notificationIcon->ShowContextMenu(eventPoint, _manager.GetPeasantInfos()); + const auto menu = (HMENU)lParam; + const auto menuItemIndex = LOWORD(wParam); + + MENUINFO mi{}; + mi.cbSize = sizeof(MENUINFO); + mi.fMask = MIM_MENUDATA; + GetMenuInfo(menu, &mi); + + if (mi.dwMenuData) + { + if (gsl::narrow_cast(mi.dwMenuData) == NotificationIconMenuItemAction::SummonWindow) + { + SummonWindowSelectionArgs args; + args.WindowID = GetMenuItemID(menu, menuItemIndex); + args.SummonBehavior.ToggleVisibility(false); + args.SummonBehavior.MoveToCurrentDesktop(false); + args.SummonBehavior.ToMonitor(winrt::TerminalApp::MonitorBehavior::InPlace); + std::ignore = _summonWindow(args); + return 0; + } + } + + // Now check the menu item itself for an action. + const auto action = gsl::narrow_cast(GetMenuItemID(menu, menuItemIndex)); + switch (action) + { + case NotificationIconMenuItemAction::FocusTerminal: + { + SummonWindowSelectionArgs args; + args.SummonBehavior.ToggleVisibility(false); + args.SummonBehavior.MoveToCurrentDesktop(false); + args.SummonBehavior.ToMonitor(winrt::TerminalApp::MonitorBehavior::InPlace); + std::ignore = _summonWindow(args); + break; + } + default: + break; + } return 0; } - } - break; - } - case WM_MENUCOMMAND: - { - _notificationIcon->MenuItemSelected((HMENU)lParam, (UINT)wParam); - return 0; - } - default: - { - // We'll want to receive this message when explorer.exe restarts - // so that we can re-add our icon to the notification area. - // This unfortunately isn't a switch case because we register the - // message at runtime. - if (message == WM_TASKBARCREATED) - { - _notificationIcon->ReAddNotificationIcon(); + case WM_SETTINGCHANGE: + // Currently, we only support checking when the OS theme changes. In + // that case, wParam is 0. Re-evaluate when we decide to reload env vars + // (GH#1125) + if (wParam == 0 && lParam != 0) + { + // ImmersiveColorSet seems to be the notification that the OS theme + // changed. If that happens, let the app know, so it can hot-reload + // themes, color schemes that might depend on the OS theme + if (wcscmp(reinterpret_cast(lParam), L"ImmersiveColorSet") == 0) + { + // GH#15732: Don't update the settings, unless the theme + // _actually_ changed. ImmersiveColorSet gets sent more often + // than just on a theme change. It notably gets sent when the PC + // is locked, or the UAC prompt opens. + const auto isCurrentlyDark = Theme::IsSystemInDarkTheme(); + if (isCurrentlyDark != _currentSystemThemeIsDark) + { + _currentSystemThemeIsDark = isCurrentlyDark; + _app.Logic().ReloadSettings(); + } + } + } + return 0; + case WM_COPYDATA: + if (const auto cds = reinterpret_cast(lParam); cds->dwData == TERMINAL_HANDOFF_MAGIC) + { + const auto handoff = deserializeHandoffPayload(static_cast(cds->lpData), static_cast(cds->lpData) + cds->cbData); + const winrt::hstring args{ handoff.args }; + const winrt::hstring env{ handoff.env }; + const winrt::hstring cwd{ handoff.cwd }; + const auto argv = commandlineToArgArray(args.c_str()); + _dispatchCommandline({ argv, cwd, gsl::narrow_cast(handoff.show), env }); + } return 0; + case WM_HOTKEY: + _hotkeyPressed(static_cast(wParam)); + return 0; + case WM_QUERYENDSESSION: + // For WM_QUERYENDSESSION and WM_ENDSESSION, refer to: + // https://docs.microsoft.com/en-us/windows/win32/rstmgr/guidelines-for-applications + if (lParam == ENDSESSION_CLOSEAPP) + { + // ENDSESSION_CLOSEAPP: The application is using a file that must be replaced, + // the system is being serviced, or system resources are exhausted. + RegisterApplicationRestart(nullptr, RESTART_NO_CRASH | RESTART_NO_HANG); + return TRUE; + } + return FALSE; + case WM_ENDSESSION: + _forcePersistence = true; + PostQuitMessage(0); + return 0; + default: + // We'll want to receive this message when explorer.exe restarts + // so that we can re-add our icon to the notification area. + // This unfortunately isn't a switch case because we register the + // message at runtime. + if (message == WM_TASKBARCREATED) + { + _checkWindowsForNotificationIcon(); + return 0; + } } } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); } - return DefWindowProc(_window.get(), message, wParam, lParam); -} -// Close the Terminal application. This will exit the main thread for the -// emperor itself. We should probably only ever be called when we have no -// windows left, and we don't want to keep running anymore. This will discard -// all our refrigerated windows. If we try to use XAML on Windows 10 after this, -// we'll undoubtedly crash. -safe_void_coroutine WindowEmperor::_close() -{ - // Important! Switch back to the main thread for the emperor. That way, the - // quit will go to the emperor's message pump. - co_await wil::resume_foreground(_dispatcher); - PostQuitMessage(0); + return DefWindowProcW(window, message, wParam, lParam); } void WindowEmperor::_finalizeSessionPersistence() const { const auto state = ApplicationState::SharedInstance(); - // Ensure to write the state.json before we TerminateProcess() - state.Flush(); - - if (!_requiresPersistenceCleanupOnExit) + if (_forcePersistence || _app.Logic().Settings().GlobalSettings().ShouldUsePersistedLayout()) { - return; + state.PersistedWindowLayouts(nullptr); + for (const auto& w : _windows) + { + w->Logic().PersistState(); + } } - // Get the "buffer_{guid}.txt" files that we expect to be there - std::unordered_set sessionIds; - if (const auto layouts = state.PersistedWindowLayouts()) + // Ensure to write the state.json before we TerminateProcess() + state.Flush(); + + if (_needsPersistenceCleanup) { - for (const auto& windowLayout : layouts) + // Get the "buffer_{guid}.txt" files that we expect to be there + std::unordered_set sessionIds; + if (const auto layouts = state.PersistedWindowLayouts()) { - for (const auto& actionAndArgs : windowLayout.TabLayout()) + for (const auto& windowLayout : layouts) { - const auto args = actionAndArgs.Args(); - NewTerminalArgs terminalArgs{ nullptr }; - - if (const auto tabArgs = args.try_as()) - { - terminalArgs = tabArgs.ContentArgs().try_as(); - } - else if (const auto paneArgs = args.try_as()) + for (const auto& actionAndArgs : windowLayout.TabLayout()) { - terminalArgs = paneArgs.ContentArgs().try_as(); - } + const auto args = actionAndArgs.Args(); + NewTerminalArgs terminalArgs{ nullptr }; - if (terminalArgs) - { - sessionIds.emplace(terminalArgs.SessionId()); + if (const auto tabArgs = args.try_as()) + { + terminalArgs = tabArgs.ContentArgs().try_as(); + } + else if (const auto paneArgs = args.try_as()) + { + terminalArgs = paneArgs.ContentArgs().try_as(); + } + + if (terminalArgs) + { + sessionIds.emplace(terminalArgs.SessionId()); + } } } } - } - // Remove the "buffer_{guid}.txt" files that shouldn't be there - // e.g. "buffer_FD40D746-163E-444C-B9B2-6A3EA2B26722.txt" - { - const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } }; - const auto filter = settingsDirectory / L"buffer_*"; - WIN32_FIND_DATAW ffd; - - // This could also use std::filesystem::directory_iterator. - // I was just slightly bothered by how it doesn't have a O(1) .filename() - // function, even though the underlying Win32 APIs provide it for free. - // Both work fine. - const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) }; - if (!handle) + // Remove the "buffer_{guid}.txt" files that shouldn't be there + // e.g. "buffer_FD40D746-163E-444C-B9B2-6A3EA2B26722.txt" { - return; - } - - do - { - const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName)); - const std::wstring_view name{ &ffd.cFileName[0], nameLen }; - - if (nameLen != 47) + const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } }; + const auto filter = settingsDirectory / L"buffer_*"; + WIN32_FIND_DATAW ffd; + + // This could also use std::filesystem::directory_iterator. + // I was just slightly bothered by how it doesn't have a O(1) .filename() + // function, even though the underlying Win32 APIs provide it for free. + // Both work fine. + const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) }; + if (!handle) { - continue; + return; } - wchar_t guidStr[39]; - guidStr[0] = L'{'; - memcpy(&guidStr[1], name.data() + 7, 36 * sizeof(wchar_t)); - guidStr[37] = L'}'; - guidStr[38] = L'\0'; - - const auto id = Utils::GuidFromString(&guidStr[0]); - if (!sessionIds.contains(id)) + do { - std::filesystem::remove(settingsDirectory / name); - } - } while (FindNextFileW(handle.get(), &ffd)); + const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName)); + const std::wstring_view name{ &ffd.cFileName[0], nameLen }; + + if (nameLen != 47) + { + continue; + } + + wchar_t guidStr[39]; + guidStr[0] = L'{'; + memcpy(&guidStr[1], name.data() + 7, 36 * sizeof(wchar_t)); + guidStr[37] = L'}'; + guidStr[38] = L'\0'; + + const auto id = Utils::GuidFromString(&guidStr[0]); + if (!sessionIds.contains(id)) + { + std::filesystem::remove(settingsDirectory / name); + } + } while (FindNextFileW(handle.get(), &ffd)); + } } } #pragma endregion #pragma region GlobalHotkeys -// Method Description: -// - Called when the monarch failed to summon a window for a given set of -// SummonWindowSelectionArgs. In this case, we should create the specified -// window ourselves. -// - This is to support the scenario like `globalSummon(Name="_quake")` being -// used to summon the window if it already exists, or create it if it doesn't. -// Arguments: -// - args: Contains information on how we should name the window -// Return Value: -// - -static safe_void_coroutine _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(FMT_COMPILE(L"-w {}"), - args.Name().empty() ? L"new" : - args.Name()) - }; - - SHELLEXECUTEINFOW seInfo{ 0 }; - seInfo.cbSize = sizeof(seInfo); - seInfo.fMask = SEE_MASK_NOASYNC; - seInfo.lpVerb = L"open"; - seInfo.lpFile = exePath.c_str(); - seInfo.lpParameters = cmdline.c_str(); - seInfo.nShow = SW_SHOWNORMAL; - LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo)); - - co_return; -} - void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) { if (hotkeyIndex < 0 || static_cast(hotkeyIndex) > _hotkeys.size()) @@ -620,42 +1056,48 @@ void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) } const auto& summonArgs = til::at(_hotkeys, hotkeyIndex); - Remoting::SummonWindowSelectionArgs args{ summonArgs.Name() }; // desktop:any - MoveToCurrentDesktop=false, OnCurrentDesktop=false // desktop:toCurrent - MoveToCurrentDesktop=true, OnCurrentDesktop=false // desktop:onCurrent - MoveToCurrentDesktop=false, OnCurrentDesktop=true - args.OnCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::OnCurrent); - args.SummonBehavior().MoveToCurrentDesktop(summonArgs.Desktop() == Settings::Model::DesktopBehavior::ToCurrent); - args.SummonBehavior().ToggleVisibility(summonArgs.ToggleVisibility()); - args.SummonBehavior().DropdownDuration(summonArgs.DropdownDuration()); + SummonWindowSelectionArgs args; + args.WindowName = summonArgs.Name(); + args.OnCurrentDesktop = summonArgs.Desktop() == DesktopBehavior::OnCurrent; + args.SummonBehavior.MoveToCurrentDesktop(summonArgs.Desktop() == DesktopBehavior::ToCurrent); + args.SummonBehavior.ToggleVisibility(summonArgs.ToggleVisibility()); + args.SummonBehavior.DropdownDuration(summonArgs.DropdownDuration()); switch (summonArgs.Monitor()) { - case Settings::Model::MonitorBehavior::Any: - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::InPlace); + case MonitorBehavior::Any: + args.SummonBehavior.ToMonitor(TerminalApp::MonitorBehavior::InPlace); break; - case Settings::Model::MonitorBehavior::ToCurrent: - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToCurrent); + case MonitorBehavior::ToCurrent: + args.SummonBehavior.ToMonitor(TerminalApp::MonitorBehavior::ToCurrent); break; - case Settings::Model::MonitorBehavior::ToMouse: - args.SummonBehavior().ToMonitor(Remoting::MonitorBehavior::ToMouse); + case MonitorBehavior::ToMouse: + args.SummonBehavior.ToMonitor(TerminalApp::MonitorBehavior::ToMouse); break; } - _manager.SummonWindow(args); - if (args.FoundMatch()) + if (_summonWindow(args)) { - // Excellent, the window was found. We have nothing else to do here. + return; } - else + + hstring argv[] = { L"wt", L"-w", summonArgs.Name() }; + if (argv[2].empty()) { - // We should make the window ourselves. - _createNewTerminalWindow(summonArgs); + argv[2] = L"new"; } + + const wil::unique_environstrings_ptr envMem{ GetEnvironmentStringsW() }; + const auto env = stringFromDoubleNullTerminated(envMem.get()); + const auto cwd = wil::GetCurrentDirectoryW(); + _dispatchCommandline({ argv, cwd, SW_SHOWDEFAULT, std::move(env) }); } -bool WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept +void WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept { const auto vkey = hotkey.Vkey(); auto hotkeyFlags = MOD_NOREPEAT; @@ -669,22 +1111,7 @@ bool WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Ter // TODO GH#8888: We should display a warning of some kind if this fails. // This can fail if something else already bound this hotkey. - const auto result = ::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey); - LOG_LAST_ERROR_IF(!result); - TraceLoggingWrite(g_hWindowsTerminalProvider, - "RegisterHotKey", - TraceLoggingDescription("Emitted when setting hotkeys"), - TraceLoggingInt64(index, "index", "the index of the hotkey to add"), - TraceLoggingUInt64(vkey, "vkey", "the key"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_WIN), "win", "is WIN in the modifiers"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_ALT), "alt", "is ALT in the modifiers"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_CONTROL), "control", "is CONTROL in the modifiers"), - TraceLoggingUInt64(WI_IsFlagSet(hotkeyFlags, MOD_SHIFT), "shift", "is SHIFT in the modifiers"), - TraceLoggingBool(result, "succeeded", "true if we succeeded"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - - return result; + LOG_IF_WIN32_BOOL_FALSE(::RegisterHotKey(_window.get(), index, hotkeyFlags, vkey)); } // Method Description: @@ -693,31 +1120,11 @@ bool WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Ter // - void WindowEmperor::_unregisterHotKey(const int index) noexcept { - TraceLoggingWrite( - g_hWindowsTerminalProvider, - "UnregisterHotKey", - TraceLoggingDescription("Emitted when clearing previously set hotkeys"), - TraceLoggingInt64(index, "index", "the index of the hotkey to remove"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - LOG_IF_WIN32_BOOL_FALSE(::UnregisterHotKey(_window.get(), index)); } -safe_void_coroutine WindowEmperor::_setupGlobalHotkeys() +void WindowEmperor::_setupGlobalHotkeys() { - // The hotkey MUST be registered on the main thread. It will fail otherwise! - co_await wil::resume_foreground(_dispatcher); - - if (!_window) - { - // MSFT:36797001 There's a surprising number of hits of this callback - // getting triggered during teardown. As a best practice, we really - // should make sure _window exists before accessing it on any coroutine. - // We might be getting called back after the app already began getting - // cleaned up. - co_return; - } // Unregister all previously registered hotkeys. // // RegisterHotKey(), will not unregister hotkeys automatically. @@ -736,17 +1143,8 @@ safe_void_coroutine WindowEmperor::_setupGlobalHotkeys() { if (auto summonArgs = cmd.ActionAndArgs().Args().try_as()) { - auto index = gsl::narrow_cast(_hotkeys.size()); - const auto succeeded = _registerHotKey(index, keyChord); - - TraceLoggingWrite(g_hWindowsTerminalProvider, - "AppHost_setupGlobalHotkey", - TraceLoggingDescription("Emitted when setting a single hotkey"), - TraceLoggingInt64(index, "index", "the index of the hotkey to add"), - TraceLoggingWideString(cmd.Name().c_str(), "name", "the name of the command"), - TraceLoggingBoolean(succeeded, "succeeded", "true if we succeeded"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + const auto index = gsl::narrow_cast(_hotkeys.size()); + _registerHotKey(index, keyChord); _hotkeys.emplace_back(summonArgs); } } @@ -755,29 +1153,6 @@ safe_void_coroutine WindowEmperor::_setupGlobalHotkeys() #pragma endregion #pragma region NotificationIcon -// Method Description: -// - Creates a Notification Icon and hooks up its handlers -// Arguments: -// - -// Return Value: -// - -void WindowEmperor::_createNotificationIcon() -{ - _notificationIcon = std::make_unique(_window.get()); - _notificationIcon->SummonWindowRequested([this](auto& args) { _manager.SummonWindow(args); }); -} - -// Method Description: -// - Deletes our notification icon if we have one. -// Arguments: -// - -// Return Value: -// - -void WindowEmperor::_destroyNotificationIcon() -{ - _notificationIcon->RemoveIconFromNotificationArea(); - _notificationIcon = nullptr; -} void WindowEmperor::_checkWindowsForNotificationIcon() { @@ -799,60 +1174,36 @@ void WindowEmperor::_checkWindowsForNotificationIcon() // themselves getting the new settings, only ask the app logic for the // RequestsTrayIcon setting value, and combine that with the result of each // window (which won't change during a settings reload). - bool needsIcon = _app.Logic().RequestsTrayIcon(); + const auto globals = _app.Logic().Settings().GlobalSettings(); + auto needsIcon = globals.AlwaysShowNotificationIcon() || globals.MinimizeToNotificationArea(); + if (!needsIcon) { - auto windows{ _windows.lock_shared() }; - for (const auto& _windowThread : *windows) + for (const auto& host : _windows) { - needsIcon |= _windowThread->Logic().IsQuakeWindow(); + needsIcon |= host->Logic().IsQuakeWindow(); } } - if (needsIcon) + if (_notificationIconShown == needsIcon) { - _showNotificationIconRequested(); - } - else - { - _hideNotificationIconRequested(); + return; } -} -void WindowEmperor::_showNotificationIconRequested() -{ - if (!_notificationIcon) + if (needsIcon) { - _createNotificationIcon(); + Shell_NotifyIconW(NIM_ADD, &_notificationIcon); + Shell_NotifyIconW(NIM_SETVERSION, &_notificationIcon); } -} - -void WindowEmperor::_hideNotificationIconRequested() -{ - // Destroy it only if our settings allow it - if (_notificationIcon) + else { + Shell_NotifyIconW(NIM_DELETE, &_notificationIcon); // If we no longer want the tray icon, but we did have one, then quick // re-summon all our windows, so they don't get lost when the icon // disappears forever. - _manager.SummonAllWindows(); - - _destroyNotificationIcon(); + _summonAllWindows(); } -} -#pragma endregion -// A callback to the window's logic to let us know when the window's -// quake mode state changes. We'll use this to check if we need to add -// or remove the notification icon. -safe_void_coroutine WindowEmperor::_windowIsQuakeWindowChanged(winrt::Windows::Foundation::IInspectable sender, - winrt::Windows::Foundation::IInspectable args) -{ - co_await wil::resume_foreground(this->_dispatcher); - _checkWindowsForNotificationIcon(); -} -safe_void_coroutine WindowEmperor::_windowRequestUpdateSettings() -{ - // We MUST be on the main thread to update the settings. We will crash when trying to enumerate fragment extensions otherwise. - co_await wil::resume_foreground(this->_dispatcher); - _app.Logic().ReloadSettings(); + _notificationIconShown = needsIcon; } + +#pragma endregion diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index 6a7f0a0fda6..190d9e1ec6a 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -16,73 +16,72 @@ Class Name: --*/ #pragma once -#include "pch.h" -#include "WindowThread.h" +class AppHost; -class WindowEmperor : public std::enable_shared_from_this +class WindowEmperor { public: - WindowEmperor() noexcept; - void WaitForWindows(); - + enum UserMessages : UINT + { + WM_CLOSE_TERMINAL_WINDOW = WM_USER, + WM_IDENTIFY_ALL_WINDOWS, + WM_NOTIFY_FROM_NOTIFICATION_AREA, + }; + + HWND GetMainWindow() const noexcept; + AppHost* GetWindowById(uint64_t id) const noexcept; + AppHost* GetWindowByName(std::wstring_view name) const noexcept; + void CreateNewWindow(winrt::TerminalApp::WindowRequestedArgs args); void HandleCommandlineArgs(int nCmdShow); private: - void _createNewWindowThread(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args); - - [[nodiscard]] static LRESULT __stdcall _wndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept; - LRESULT _messageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept; - wil::unique_hwnd _window; - - winrt::TerminalApp::App _app; - winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr }; - winrt::Microsoft::Terminal::Remoting::WindowManager _manager; - - til::shared_mutex>> _windows; - std::atomic _windowThreadInstances; - - til::shared_mutex>> _oldThreads; - - winrt::event_token _WindowCreatedToken; - winrt::event_token _WindowClosedToken; - - std::vector _hotkeys; - - std::unique_ptr _notificationIcon; - - bool _requiresPersistenceCleanupOnExit = false; - bool _quitting{ false }; - - void _windowStartedHandlerPostXAML(const std::shared_ptr& sender); - void _removeWindow(uint64_t senderID); - void _decrementWindowCount(); - - void _becomeMonarch(); - void _numberOfWindowsChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&); - - safe_void_coroutine _windowIsQuakeWindowChanged(winrt::Windows::Foundation::IInspectable sender, winrt::Windows::Foundation::IInspectable args); - safe_void_coroutine _windowRequestUpdateSettings(); - - void _createMessageWindow(); - - void _hotkeyPressed(const long hotkeyIndex); - bool _registerHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept; - void _unregisterHotKey(const int index) noexcept; - safe_void_coroutine _setupGlobalHotkeys(); - - safe_void_coroutine _close(); + struct SummonWindowSelectionArgs + { + uint64_t WindowID = 0; + std::wstring_view WindowName; + bool OnCurrentDesktop = false; + winrt::TerminalApp::SummonWindowBehavior SummonBehavior; + }; + + [[nodiscard]] static LRESULT __stdcall _wndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) noexcept; + + AppHost* _mostRecentWindow() const noexcept; + bool _summonWindow(const SummonWindowSelectionArgs& args) const; + void _summonAllWindows() const; + void _dispatchCommandline(winrt::TerminalApp::CommandlineArgs args); + safe_void_coroutine _dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args); + LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept; + void _createMessageWindow(const wchar_t* className); + void _postQuitMessageIfNeeded() const; + safe_void_coroutine _showMessageBox(winrt::hstring message, bool error) const; + void _hotkeyPressed(long hotkeyIndex); + void _registerHotKey(int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept; + void _unregisterHotKey(int index) noexcept; + void _setupGlobalHotkeys(); void _finalizeSessionPersistence() const; - - void _createNotificationIcon(); - void _destroyNotificationIcon(); void _checkWindowsForNotificationIcon(); - void _showNotificationIconRequested(); - void _hideNotificationIconRequested(); - struct Revokers + wil::unique_hwnd _window; + winrt::TerminalApp::App _app{ nullptr }; + std::vector> _windows; + std::vector _hotkeys; + NOTIFYICONDATA _notificationIcon{}; + HMENU _currentWindowMenu = nullptr; + bool _notificationIconShown = false; + bool _forcePersistence = false; + bool _needsPersistenceCleanup = false; + std::optional _currentSystemThemeIsDark; + +#ifdef NDEBUG + static constexpr void _assertIsMainThread() noexcept + { + } +#else + void _assertIsMainThread() const noexcept { - winrt::Microsoft::Terminal::Remoting::WindowManager::WindowCreated_revoker WindowCreated; - winrt::Microsoft::Terminal::Remoting::WindowManager::WindowClosed_revoker WindowClosed; - } _revokers{}; + assert(_mainThreadId == GetCurrentThreadId()); + } + DWORD _mainThreadId = GetCurrentThreadId(); +#endif }; diff --git a/src/cascadia/WindowsTerminal/WindowThread.cpp b/src/cascadia/WindowsTerminal/WindowThread.cpp deleted file mode 100644 index 8fef9053434..00000000000 --- a/src/cascadia/WindowsTerminal/WindowThread.cpp +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "pch.h" -#include "WindowThread.h" - -using namespace winrt::Microsoft::Terminal::Remoting; - -bool WindowThread::_loggedInteraction = false; - -WindowThread::WindowThread(winrt::TerminalApp::AppLogic logic, - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - winrt::Microsoft::Terminal::Remoting::WindowManager manager, - winrt::Microsoft::Terminal::Remoting::Peasant peasant) : - _peasant{ std::move(peasant) }, - _appLogic{ std::move(logic) }, - _args{ std::move(args) }, - _manager{ std::move(manager) } -{ - // DO NOT start the AppHost here in the ctor, as that will start XAML on the wrong thread! -} - -void WindowThread::CreateHost() -{ - // Calling this while refrigerated won't work. - // * We can't re-initialize our winrt apartment. - // * AppHost::Initialize has to be done on the "UI" thread. - assert(_warmWindow == nullptr); - - // Start the AppHost HERE, on the actual thread we want XAML to run on - _host = std::make_shared<::AppHost>(_appLogic, - _args, - _manager, - _peasant); - - _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { UpdateSettingsRequested.raise(); }); - - winrt::init_apartment(winrt::apartment_type::single_threaded); - - // Initialize the xaml content. This must be called AFTER the - // WindowsXamlManager is initialized. - _host->Initialize(); -} - -int WindowThread::RunMessagePump() -{ - // Enter the main window loop. - const auto exitCode = _messagePump(); - // Here, the main window loop has exited. - return exitCode; -} - -void WindowThread::_pumpRemainingXamlMessages() -{ - MSG msg = {}; - while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) - { - ::DispatchMessageW(&msg); - } -} - -void WindowThread::RundownForExit() -{ - if (_host) - { - _host->UpdateSettingsRequested(_UpdateSettingsRequestedToken); - _host->Close(); - } - if (_warmWindow) - { - // If we have a _warmWindow, we're a refrigerated thread without a - // AppHost in control of the window. Manually close the window - // ourselves, to free the DesktopWindowXamlSource. - _warmWindow->Close(); - } - - // !! LOAD BEARING !! - // - // Make sure to finish pumping all the messages for our thread here. We - // may think we're all done, but we're not quite. XAML needs more time - // to pump the remaining events through, even at the point we're - // exiting. So do that now. If you don't, then the last tab to close - // will never actually destruct the last tab / TermControl / ControlCore - // / renderer. - _pumpRemainingXamlMessages(); -} - -// Method Description: -// - Check if we should keep this window alive, to try it's message loop again. -// If we were refrigerated for later, then this will block the thread on the -// _microwaveBuzzer. We'll sit there like that till the emperor decides if -// they want to re-use this window thread for a new window. -// Return Value: -// - true IFF we should enter this thread's message loop -// INVARIANT: This must be called on our "ui thread", our window thread. -bool WindowThread::KeepWarm() -{ - if (_host != nullptr) - { - // We're currently hot - return true; - } - - // Even when the _host has been destroyed the HWND will continue receiving messages, in particular WM_DISPATCHNOTIFY at least once a second. This is important to Windows as it keeps your room warm. - MSG msg; - for (;;) - { - if (!GetMessageW(&msg, nullptr, 0, 0)) - { - return false; - } - // We're using a single window message (WM_REFRIGERATE) to indicate both - // state transitions. In this case, the window is actually being woken up. - if (msg.message == AppHost::WM_REFRIGERATE) - { - _UpdateSettingsRequestedToken = _host->UpdateSettingsRequested([this]() { UpdateSettingsRequested.raise(); }); - // Re-initialize the host here, on the window thread - _host->Initialize(); - return true; - } - DispatchMessageW(&msg); - } -} - -// Method Description: -// - "Refrigerate" this thread for later reuse. This will refrigerate the window -// itself, and tear down our current app host. We'll save our window for -// later. We'll also pump out the existing message from XAML, before -// returning. After we return, the emperor will add us to the list of threads -// that can be re-used. -void WindowThread::Refrigerate() -{ - _host->UpdateSettingsRequested(_UpdateSettingsRequestedToken); - - // keep a reference to the HWND and DesktopWindowXamlSource alive. - _warmWindow = _host->Refrigerate(); - - // rundown remaining messages before destructing the app host - _pumpRemainingXamlMessages(); - _host = nullptr; -} - -// Method Description: -// - "Reheat" this thread for reuse. We'll build a new AppHost, and pass in the -// existing window to it. We'll then wake up the thread stuck in KeepWarm(). -void WindowThread::Microwave(WindowRequestedArgs args, Peasant peasant) -{ - const auto hwnd = _warmWindow->GetInteropHandle(); - - _peasant = std::move(peasant); - _args = std::move(args); - - _host = std::make_shared(_appLogic, - _args, - _manager, - _peasant, - std::move(_warmWindow)); - - // raise the signal to unblock KeepWarm and start the window message loop again. - PostMessageW(hwnd, AppHost::WM_REFRIGERATE, 0, 0); -} - -winrt::TerminalApp::TerminalWindow WindowThread::Logic() -{ - return _host->Logic(); -} - -static bool _messageIsF7Keypress(const MSG& message) -{ - return (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN) && message.wParam == VK_F7; -} -static bool _messageIsAltKeyup(const MSG& message) -{ - return (message.message == WM_KEYUP || message.message == WM_SYSKEYUP) && message.wParam == VK_MENU; -} -static bool _messageIsAltSpaceKeypress(const MSG& message) -{ - return message.message == WM_SYSKEYDOWN && message.wParam == VK_SPACE; -} - -int WindowThread::_messagePump() -{ - MSG message{}; - - while (GetMessageW(&message, nullptr, 0, 0)) - { - // We're using a single window message (WM_REFRIGERATE) to indicate both - // state transitions. In this case, the window is actually being refrigerated. - // This will break us out of our main message loop we'll eventually start - // the loop in WindowThread::KeepWarm to await a call to Microwave(). - if (message.message == AppHost::WM_REFRIGERATE) - { - break; - } - - if (!_loggedInteraction && (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN)) - { - TraceLoggingWrite( - g_hWindowsTerminalProvider, - "SessionBecameInteractive", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - _loggedInteraction = true; - } - - // GH#638 (Pressing F7 brings up both the history AND a caret browsing message) - // The Xaml input stack doesn't allow an application to suppress the "caret browsing" - // dialog experience triggered when you press F7. Official recommendation from the Xaml - // team is to catch F7 before we hand it off. - // AppLogic contains an ad-hoc implementation of event bubbling for a runtime classes - // implementing a custom IF7Listener interface. - // If the recipient of IF7Listener::OnF7Pressed suggests that the F7 press has, in fact, - // been handled we can discard the message before we even translate it. - if (_messageIsF7Keypress(message)) - { - if (_host->OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true)) - { - // The application consumed the F7. Don't let Xaml get it. - continue; - } - } - - // GH#6421 - System XAML will never send an Alt KeyUp event. So, similar - // to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp - // here, and plumb it through. - if (_messageIsAltKeyup(message)) - { - // Let's pass to the application - if (_host->OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false)) - { - // The application consumed the Alt. Don't let Xaml get it. - continue; - } - } - - // GH#7125 = System XAML will show a system dialog on Alt Space. We want to - // explicitly prevent that because we handle that ourselves. So similar to - // above, we steal the event and hand it off to the host. - if (_messageIsAltSpaceKeypress(message)) - { - _host->OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true); - continue; - } - - TranslateMessage(&message); - DispatchMessage(&message); - } - return 0; -} - -uint64_t WindowThread::PeasantID() -{ - return _peasant.GetID(); -} diff --git a/src/cascadia/WindowsTerminal/WindowThread.h b/src/cascadia/WindowsTerminal/WindowThread.h deleted file mode 100644 index 0104740d859..00000000000 --- a/src/cascadia/WindowsTerminal/WindowThread.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -#pragma once -#include "pch.h" -#include "AppHost.h" - -class WindowThread : public std::enable_shared_from_this -{ -public: - WindowThread(winrt::TerminalApp::AppLogic logic, - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - winrt::Microsoft::Terminal::Remoting::WindowManager manager, - winrt::Microsoft::Terminal::Remoting::Peasant peasant); - - winrt::TerminalApp::TerminalWindow Logic(); - void CreateHost(); - int RunMessagePump(); - void RundownForExit(); - - bool KeepWarm(); - void Refrigerate(); - void Microwave( - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args, - winrt::Microsoft::Terminal::Remoting::Peasant peasant); - - uint64_t PeasantID(); - - til::event> UpdateSettingsRequested; - -private: - winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; - - winrt::TerminalApp::AppLogic _appLogic{ nullptr }; - winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs _args{ nullptr }; - winrt::Microsoft::Terminal::Remoting::WindowManager _manager{ nullptr }; - - // This is a "shared_ptr", but it should be treated as a unique, owning ptr. - // It's shared, because there are edge cases in refrigeration where internal - // co_awaits inside AppHost might resume after we've dtor'd it, and there's - // no other way for us to let the AppHost know it has passed on. - std::shared_ptr<::AppHost> _host{ nullptr }; - winrt::event_token _UpdateSettingsRequestedToken; - - std::unique_ptr<::IslandWindow> _warmWindow{ nullptr }; - static bool _loggedInteraction; - - int _messagePump(); - void _pumpRemainingXamlMessages(); -}; diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index 7f3ab1aed0b..ef966732727 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -51,13 +51,10 @@ - - - @@ -67,10 +64,8 @@ - - @@ -87,12 +82,9 @@ - - - {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} @@ -138,7 +130,7 @@ --> - false + false diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index fae9d5fb50f..07c6732b7cf 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -45,8 +45,6 @@ Module Name: #undef GetCurrentTime #endif -#include - // Needed just for XamlIslands to work at all: #include #include @@ -70,11 +68,10 @@ Module Name: #include #include -#include #include #include -#include +#include #include // Including TraceLogging essentials for the binary diff --git a/src/cascadia/WindowsTerminal_UIATests/Common/Globals.cs b/src/cascadia/WindowsTerminal_UIATests/Common/Globals.cs index dccd0b5618a..265a05e791b 100644 --- a/src/cascadia/WindowsTerminal_UIATests/Common/Globals.cs +++ b/src/cascadia/WindowsTerminal_UIATests/Common/Globals.cs @@ -31,7 +31,6 @@ public static void WaitForLongTimeout() "WindowsTerminal.exe", "OpenConsole.exe", "Microsoft.Terminal.Control.dll", - "Microsoft.Terminal.Remoting.dll", "Microsoft.Terminal.Settings.Editor.dll", "Microsoft.Terminal.Settings.Model.dll", "TerminalApp.dll", diff --git a/src/cascadia/inc/WindowingBehavior.h b/src/cascadia/inc/WindowingBehavior.h deleted file mode 100644 index c00a33e9ce0..00000000000 --- a/src/cascadia/inc/WindowingBehavior.h +++ /dev/null @@ -1,21 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. ---*/ - -#pragma once -inline constexpr int32_t WindowingBehaviorUseCurrent{ 0 }; -inline constexpr int32_t WindowingBehaviorUseNew{ -1 }; -inline constexpr int32_t WindowingBehaviorUseExisting{ -2 }; -inline constexpr int32_t WindowingBehaviorUseAnyExisting{ -3 }; -inline constexpr int32_t WindowingBehaviorUseName{ -4 }; -inline constexpr int32_t WindowingBehaviorUseNone{ -5 }; - -inline constexpr std::wstring_view QuakeWindowName{ L"_quake" }; - -// Magic names for magic windowing behaviors. These are reserved names, in place -// of window names. "new" can also be used in MoveTab / MovePane actions. -// * new: to use a new window, always -// * last: use the most recent window -inline constexpr std::string_view NewWindow{ "new" }; -inline constexpr std::string_view MostRecentlyUsedWindow{ "last" }; diff --git a/src/tools/MonarchPeasantPackage/Images/LockScreenLogo.scale-200.png b/src/tools/MonarchPeasantPackage/Images/LockScreenLogo.scale-200.png deleted file mode 100644 index 735f57adb5d..00000000000 Binary files a/src/tools/MonarchPeasantPackage/Images/LockScreenLogo.scale-200.png and /dev/null differ diff --git a/src/tools/MonarchPeasantPackage/Images/SplashScreen.scale-200.png b/src/tools/MonarchPeasantPackage/Images/SplashScreen.scale-200.png deleted file mode 100644 index 023e7f1feda..00000000000 Binary files a/src/tools/MonarchPeasantPackage/Images/SplashScreen.scale-200.png and /dev/null differ diff --git a/src/tools/MonarchPeasantPackage/Images/Square150x150Logo.scale-200.png b/src/tools/MonarchPeasantPackage/Images/Square150x150Logo.scale-200.png deleted file mode 100644 index af49fec1a54..00000000000 Binary files a/src/tools/MonarchPeasantPackage/Images/Square150x150Logo.scale-200.png and /dev/null differ diff --git a/src/tools/MonarchPeasantPackage/Images/Square44x44Logo.scale-200.png b/src/tools/MonarchPeasantPackage/Images/Square44x44Logo.scale-200.png deleted file mode 100644 index ce342a2ec8a..00000000000 Binary files a/src/tools/MonarchPeasantPackage/Images/Square44x44Logo.scale-200.png and /dev/null differ diff --git a/src/tools/MonarchPeasantPackage/Images/Square44x44Logo.targetsize-24_altform-unplated.png b/src/tools/MonarchPeasantPackage/Images/Square44x44Logo.targetsize-24_altform-unplated.png deleted file mode 100644 index f6c02ce97e0..00000000000 Binary files a/src/tools/MonarchPeasantPackage/Images/Square44x44Logo.targetsize-24_altform-unplated.png and /dev/null differ diff --git a/src/tools/MonarchPeasantPackage/Images/StoreLogo.png b/src/tools/MonarchPeasantPackage/Images/StoreLogo.png deleted file mode 100644 index 7385b56c0e4..00000000000 Binary files a/src/tools/MonarchPeasantPackage/Images/StoreLogo.png and /dev/null differ diff --git a/src/tools/MonarchPeasantPackage/Images/Wide310x150Logo.scale-200.png b/src/tools/MonarchPeasantPackage/Images/Wide310x150Logo.scale-200.png deleted file mode 100644 index 288995b397f..00000000000 Binary files a/src/tools/MonarchPeasantPackage/Images/Wide310x150Logo.scale-200.png and /dev/null differ diff --git a/src/tools/MonarchPeasantPackage/MonarchPeasantPackage.wapproj b/src/tools/MonarchPeasantPackage/MonarchPeasantPackage.wapproj deleted file mode 100644 index 66580b81e64..00000000000 --- a/src/tools/MonarchPeasantPackage/MonarchPeasantPackage.wapproj +++ /dev/null @@ -1,82 +0,0 @@ - - - - 15.0 - - - - Debug - x86 - - - Release - x86 - - - Debug - x64 - - - Release - x64 - - - Debug - ARM64 - - - Release - ARM64 - - - Debug - AnyCPU - - - Release - AnyCPU - - - - - - $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\ - - - - f75e29d0-d288-478b-8d83-2c190f321a3f - 10.0.22621.0 - 10.0.18362.0 - en-US - false - ..\MonarchPeasantSample\MonarchPeasantSample.vcxproj - - - - Designer - - - - - - - - - - - - - - - true - true - - - - diff --git a/src/tools/MonarchPeasantPackage/Package.appxmanifest b/src/tools/MonarchPeasantPackage/Package.appxmanifest deleted file mode 100644 index 24fd511f810..00000000000 --- a/src/tools/MonarchPeasantPackage/Package.appxmanifest +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - MonarchPeasantPackage - migrie - Images\StoreLogo.png - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/tools/MonarchPeasantSample/AppState.cpp b/src/tools/MonarchPeasantSample/AppState.cpp deleted file mode 100644 index 0a4ebfed518..00000000000 --- a/src/tools/MonarchPeasantSample/AppState.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "pch.h" -#include "AppState.h" -#include "../../types/inc/utils.hpp" - -using namespace winrt; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -void AppState::_setupConsole() -{ - hOutput = GetStdHandle(STD_OUTPUT_HANDLE); - hInput = GetStdHandle(STD_INPUT_HANDLE); - DWORD dwMode = 0; - GetConsoleMode(hOutput, &dwMode); - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(hOutput, dwMode); -} - -void AppState::initializeState() -{ - // Initialize the console handles - _setupConsole(); - - // Set up WinRT - init_apartment(); -} - -bool AppState::areWeTheKing(const bool logPIDs) -{ - auto kingPID = monarch.GetPID(); - auto ourPID = GetCurrentProcessId(); - if (logPIDs) - { - if (ourPID == kingPID) - { - printf(fmt::format("We're the\x1b[33m king\x1b[m - our PID is {}\n", ourPID).c_str()); - } - else - { - printf(fmt::format("We're a lowly peasant - the king is {}\n", kingPID).c_str()); - } - } - return (ourPID == kingPID); -} - -void AppState::remindKingWhoTheyAre(const winrt::MonarchPeasantSample::IPeasant& iPeasant) -{ - winrt::com_ptr monarchImpl; - monarchImpl.copy_from(winrt::get_self(monarch)); - if (monarchImpl) - { - auto ourID = iPeasant.GetID(); - monarchImpl->SetSelfID(ourID); - monarchImpl->AddPeasant(iPeasant); - printf("The king is peasant #%lld\n", ourID); - } - else - { - printf("Shoot, we wanted to be able to get the monarchImpl here but couldn't\n"); - } -} - -winrt::MonarchPeasantSample::Monarch AppState::instantiateMonarch() -{ - // 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 - auto monarch = create_instance(Monarch_clsid, - CLSCTX_LOCAL_SERVER); - return monarch; -} - -MonarchPeasantSample::IPeasant AppState::_createOurPeasant() -{ - auto peasant = winrt::make_self(); - auto ourID = monarch.AddPeasant(*peasant); - printf("The monarch assigned us the ID %llu\n", ourID); - - if (areWeTheKing()) - { - remindKingWhoTheyAre(*peasant); - } - - return *peasant; -} - -void AppState::createMonarch() -{ - monarch = AppState::instantiateMonarch(); -} - -// return true to exit early, false if we should continue into the main loop -bool AppState::processCommandline() -{ - const bool isKing = areWeTheKing(false); - // 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 }, { L"placeholder CWD" }); - - if (createNewWindow) - { - peasant = _createOurPeasant(); - peasant.ExecuteCommandline({ args }, { L"placeholder CWD" }); - return false; - } - else - { - printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n"); - } - - return true; -} diff --git a/src/tools/MonarchPeasantSample/AppState.h b/src/tools/MonarchPeasantSample/AppState.h deleted file mode 100644 index fde4a4333fc..00000000000 --- a/src/tools/MonarchPeasantSample/AppState.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "SampleMonarch.h" -#include "SamplePeasant.h" -#include "../../types/inc/utils.hpp" - -class AppState -{ -public: - bool areWeTheKing(const bool logPIDs = false); - void initializeState(); - - static winrt::MonarchPeasantSample::Monarch instantiateMonarch(); - - void createMonarch(); - bool processCommandline(); - void remindKingWhoTheyAre(const winrt::MonarchPeasantSample::IPeasant& peasant); - - HANDLE hInput{ INVALID_HANDLE_VALUE }; - HANDLE hOutput{ INVALID_HANDLE_VALUE }; - winrt::MonarchPeasantSample::IPeasant peasant{ nullptr }; - winrt::MonarchPeasantSample::Monarch monarch{ nullptr }; - std::vector args; - -private: - void _setupConsole(); - int _appLoop(); - - winrt::MonarchPeasantSample::IPeasant _createOurPeasant(); -}; - -bool monarchAppLoop(AppState& state); // Defined in MonarchMain.cpp -bool peasantAppLoop(AppState& state); // Defined in PeasantMain.cpp diff --git a/src/tools/MonarchPeasantSample/MonarchMain.cpp b/src/tools/MonarchPeasantSample/MonarchMain.cpp deleted file mode 100644 index 7c6ced5daee..00000000000 --- a/src/tools/MonarchPeasantSample/MonarchMain.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "pch.h" -#include "AppState.h" -#include "../../types/inc/utils.hpp" - -void printPeasants(const winrt::MonarchPeasantSample::Monarch& /*monarch*/) -{ - printf("This is unimplemented\n"); -} - -bool monarchAppLoop(AppState& state) -{ - bool exitRequested = false; - printf("Press `l` to list peasants, 'm' to change modes `q` to quit\n"); - - winrt::com_ptr monarchImpl; - monarchImpl.copy_from(winrt::get_self(state.monarch)); - - while (!exitRequested) - { - const auto ch = _getch(); - if (ch == 'l') - { - printPeasants(state.monarch); - } - else if (ch == 'q') - { - exitRequested = true; - } - else if (ch == 'm') - { - if (monarchImpl) - { - monarchImpl->ToggleWindowingBehavior(); - } - } - } - return true; -} diff --git a/src/tools/MonarchPeasantSample/MonarchPeasantSample.vcxproj b/src/tools/MonarchPeasantSample/MonarchPeasantSample.vcxproj deleted file mode 100644 index 7385e4abf68..00000000000 --- a/src/tools/MonarchPeasantSample/MonarchPeasantSample.vcxproj +++ /dev/null @@ -1,95 +0,0 @@ - - - - - {21b7ea5e-1ef8-49b6-ac07-11714af0e37d} - Win32Proj - MonarchPeasantSample - MonarchPeasantSample - MonarchPeasantSample - Application - false - Windows Store - Windows - - - - true - - - - - - - - true - true - - - - - - - - SampleMonarch.idl - - - SamplePeasant.idl - - - - - - - - - - Create - - - SampleMonarch.idl - - - SamplePeasant.idl - - - - - - - - - - - - - - - - - - - - WindowsLocalDebugger - - - - - - - - true - - - Console - - - - - - - - diff --git a/src/tools/MonarchPeasantSample/PeasantMain.cpp b/src/tools/MonarchPeasantSample/PeasantMain.cpp deleted file mode 100644 index a6c25da860b..00000000000 --- a/src/tools/MonarchPeasantSample/PeasantMain.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "pch.h" -#include "AppState.h" -#include "../../types/inc/utils.hpp" - -bool peasantReadInput(AppState& state) -{ - DWORD cNumRead, i; - std::array irInBuf; - - if (!ReadConsoleInput(state.hInput, // input buffer handle - irInBuf.data(), // buffer to read into - static_cast(irInBuf.size()), // size of read buffer - &cNumRead)) // number of records read - { - printf("\x1b[31mReadConsoleInput failed\x1b[m\n"); - ExitProcess(0); - } - for (i = 0; i < cNumRead; i++) - { - switch (irInBuf[i].EventType) - { - case KEY_EVENT: // keyboard input - { - auto key = irInBuf[i].Event.KeyEvent; - if (key.bKeyDown == false && - key.uChar.UnicodeChar == L'q') - { - return true; - } - else - { - printf("This window was activated\n"); - winrt::com_ptr peasantImpl; - peasantImpl.copy_from(winrt::get_self(state.peasant)); - if (peasantImpl) - { - peasantImpl->raiseActivatedEvent(); - } - } - break; - } - - case MOUSE_EVENT: - case WINDOW_BUFFER_SIZE_EVENT: - case FOCUS_EVENT: - case MENU_EVENT: - break; - - default: - printf("\x1b[33mUnknown event from ReadConsoleInput - this is probably impossible\x1b[m\n"); - ExitProcess(0); - break; - } - } - - return false; -} - -// Returns true if we want to just exit the application. -// Returns false if the monarch dies, and we need to elect a new one. -bool peasantAppLoop(AppState& state) -{ - wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast(state.monarch.GetPID())) }; - if (hMonarch.get() == nullptr) - { - const auto gle = GetLastError(); - printf("\x1b[31mFailed to open the monarch process, error was %d\x1b[m\n", gle); - return false; - } - - HANDLE handlesToWaitOn[2]{ hMonarch.get(), state.hInput }; - - bool exitRequested = false; - printf("Press `q` to quit\n"); - - while (!exitRequested) - { - auto waitResult = WaitForMultipleObjects(2, handlesToWaitOn, false, INFINITE); - - switch (waitResult) - { - case WAIT_OBJECT_0 + 0: // handlesToWaitOn[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; - - case WAIT_OBJECT_0 + 1: // handlesToWaitOn[1] was signaled - exitRequested = peasantReadInput(state); - break; - - case WAIT_TIMEOUT: - printf("Wait timed out. This should be impossible.\n"); - break; - - // Return value is invalid. - default: - { - auto gle = GetLastError(); - printf("WaitForMultipleObjects returned: %d\n", waitResult); - printf("Wait error: %d\n", gle); - ExitProcess(0); - } - } - } - - printf("Bottom of peasantAppLoop\n"); - return true; -} diff --git a/src/tools/MonarchPeasantSample/PropertySheet.props b/src/tools/MonarchPeasantSample/PropertySheet.props deleted file mode 100644 index 9c841ec83ee..00000000000 --- a/src/tools/MonarchPeasantSample/PropertySheet.props +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/tools/MonarchPeasantSample/README.md b/src/tools/MonarchPeasantSample/README.md deleted file mode 100644 index 166b1e0cbd5..00000000000 --- a/src/tools/MonarchPeasantSample/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Monarch/Peasant Sample - -This directory contains a sample monarch/peasant application. This is a type of -application where a single "Monarch" can coordinate the actions of multiple -other "Peasant" processes, as described by the specs in [#7240] and [#8135]. - -This project is intended to be a standalone sample of how the architecture would -work, without involving the entirety of the Windows Terminal build. Eventually, -this architecture will be incorporated into `wt.exe` itself, to enable scenarios -like: -* Run `wt` in the current window ([#4472]) -* Single Instance Mode ([#2227]) - -For an example of this sample running, see the below GIF: - -![Gif of the MonarchPeasantSample](monarch-peasant-sample-000.gif) - -This sample operates largely by printing to the console, to help the reader -understand how it's working through its logic. - -## Usage - -``` -MonarchPeasantSample.exe [--session,-s session-id] [args...] -``` - -This will run a new instance of the MonarchPeasantSample. - -One of the running `MonarchPeasantSample` processes will be the "Monarch", -responsible for coordinating the creation of windows between processes. It can -be toggled between different `glomToLastWindow` modes by pressing `m`. It can be -exited with `q`. - -The other instances will be "peasants", who can be told to execute commandlines. -`q` will exit the process. Any other keypress will "activate" the peasant -window, informing the monarch that this peasant is the newly-active one. - -If the `session-id` is provided on the commandline, then the monarch will try to -pass the provided `args` to the specified peasant process, accounting for the -current value of `glomToLastWindow`. See the spec in [#8135] for details of how -this argument works. The actual `args...` params are unused. - -## Project layout - -The code is vaguely separated into the following files, with the following -purposes. As this code isn't production-ready code, the layering isn't -particularly well organized nor enforced. - -* `Monarch.idl/.h/.cpp`: Code for the WinRT Monarch object, responsible for - coordinating the Peasants -* `Peasant.idl/.h/.cpp`: Code for the WinRT Peasant object, which represents any - individual application instance. -* `AppState.h/.cpp`: This file contains some common state that's used throughout - the application, in order to help encapsulate it all in one place. -* `MonarchMain.cpp`: This file contains the main loop for the monarch process. - It needs to be able to process console input, and additionally wait on the - peasants, to know when they've died. -* `PeasantMain.cpp`: This file contains the main loop for the peasant process. - It needs to be able to wait on both console input, and on the Monarch process, - to be able to determine who the next monarch should be. - -## Remaining TODOs - -This project represents an _incomplete_ example, but one that covers enough of -the edge cases for the reader to comprehend the architecture. Below is a list of -things that are missing from the sample: - -* [ ] The Monarch should store a stack for the MRU peasant, not just the single - MRU one -* [ ] The Monarch needs to `WaitForMultipleObjects` on Peasants, to remove them - from the map when they die -* [ ] After an "election", the entire MRU window state is lost, because it was - only stored in the current monarch. This needs to be distributed to all the - other Peasants when it changes. -* [ ] Theoretically, `ProposeCommandline` should use the CWD of the process - calling it. That's left unimplemented for brevity. -* [ ] Technically, the Monarch is also a Peasant, and any keypress should also - activate the Monarch window as the MRU one. -* [ ] We're storing a strictly-increasing int for to determine what the next ID - for a peasant should be. This seems silly, and like we could probably just - iterate to find the first gap in the map (I'm sure there's a better way of - doing this that I can't recall, don't tell my CS540 professor) -* [ ] The following steps causes an unexpected crash: - - Create a monarch(#1) & peasant(#2) - - activate the peasant(#2) - - exit the peasant(#2) - - try running `MonarchPeasantSample.exe -s 0` (or `-s 2`) - - THIS WILL FAIL, but it _should_ just run the commandline in the monarch - (in the case of `-s 0`) or in a new window (in the `-s 1` case) - - The reason this fails is because the Monarch tries to call - `Peasant::ExecuteCommandline`, but the Peasant object doesn't actually - exist anymore (the process is dead!). Three fixes will help here: - - wrap all calls to peasants with try/catch's - - Do the "keep a MRU stack" thing (so we know `-s 0` is now the monarch) - - Do the "wait on Peasant processes" thing, to know to pop #2 from the - map and MRU stack. - -## Utilities - -Use the following helper script to open a new Terminal window with 4 splits - 3 -running instances of MonarchPeasantSample.exe, and a fourth which can be used to -issue commands. - -```cmd -pushd %OPENCON%\bin\x64\Debug\MonarchPeasantSample -wt -d . cmd /k MonarchPeasantSample.exe ; sp -d . cmd /k MonarchPeasantSample.exe ; sp -d . cmd /k MonarchPeasantSample.exe ; sp -d . -popd - -``` - -[#2227]: https://github.com/microsoft/terminal/issues/2227 -[#4472]: https://github.com/microsoft/terminal/issues/4472 -[#7240]: https://github.com/microsoft/terminal/pull/7240 -[#8135]: https://github.com/microsoft/terminal/pull/8135 diff --git a/src/tools/MonarchPeasantSample/SampleMonarch.cpp b/src/tools/MonarchPeasantSample/SampleMonarch.cpp deleted file mode 100644 index 7d0961cd6db..00000000000 --- a/src/tools/MonarchPeasantSample/SampleMonarch.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "pch.h" -#include "SampleMonarch.h" - -#include "Monarch.g.cpp" -#include "../../types/inc/utils.hpp" - -using namespace winrt; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -namespace winrt::MonarchPeasantSample::implementation -{ - Monarch::Monarch() - { - printf("Instantiated a Monarch\n"); - } - - Monarch::~Monarch() - { - printf("~Monarch()\n"); - } - - uint64_t Monarch::GetPID() - { - return GetCurrentProcessId(); - } - - uint64_t Monarch::AddPeasant(winrt::MonarchPeasantSample::IPeasant peasant) - { - // 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::MonarchPeasantSample::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 - [MonarchPeasantSample.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/tools/MonarchPeasantSample/SampleMonarch.h b/src/tools/MonarchPeasantSample/SampleMonarch.h deleted file mode 100644 index e423a95219d..00000000000 --- a/src/tools/MonarchPeasantSample/SampleMonarch.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "Monarch.g.h" -#include "SamplePeasant.h" - -// {50dba6cd-2222-4b12-8363-5e06f5d0082c} -constexpr GUID Monarch_clsid{ - 0x50dba6cd, - 0x2222, - 0x4b12, - { 0x83, 0x63, 0x5e, 0x06, 0xf5, 0xd0, 0x08, 0x2c } -}; - -enum class WindowingBehavior : uint64_t -{ - UseNew = 0, - UseExisting = 1, -}; - -namespace winrt::MonarchPeasantSample::implementation -{ - struct Monarch : public MonarchT - { - Monarch(); - ~Monarch(); - - uint64_t GetPID(); - - uint64_t AddPeasant(winrt::MonarchPeasantSample::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::MonarchPeasantSample::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::MonarchPeasantSample::factory_implementation -{ - BASIC_FACTORY(Monarch); -} diff --git a/src/tools/MonarchPeasantSample/SampleMonarch.idl b/src/tools/MonarchPeasantSample/SampleMonarch.idl deleted file mode 100644 index 4b161753401..00000000000 --- a/src/tools/MonarchPeasantSample/SampleMonarch.idl +++ /dev/null @@ -1,12 +0,0 @@ -import "SamplePeasant.idl"; - -namespace MonarchPeasantSample -{ - [default_interface] runtimeclass Monarch { - Monarch(); - - UInt64 GetPID(); - UInt64 AddPeasant(IPeasant peasant); - Boolean ProposeCommandline(String[] args, String cwd); - }; -} diff --git a/src/tools/MonarchPeasantSample/SamplePeasant.cpp b/src/tools/MonarchPeasantSample/SamplePeasant.cpp deleted file mode 100644 index 7cc65b43789..00000000000 --- a/src/tools/MonarchPeasantSample/SamplePeasant.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "pch.h" -#include "SamplePeasant.h" - -#include "Peasant.g.cpp" -#include "../../types/inc/utils.hpp" - -using namespace winrt; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -namespace winrt::MonarchPeasantSample::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() - { - WindowActivated.raise(*this, nullptr); - } - -} diff --git a/src/tools/MonarchPeasantSample/SamplePeasant.h b/src/tools/MonarchPeasantSample/SamplePeasant.h deleted file mode 100644 index 489f7e722e5..00000000000 --- a/src/tools/MonarchPeasantSample/SamplePeasant.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "Peasant.g.h" - -namespace winrt::MonarchPeasantSample::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(); - - til::typed_event<> WindowActivated; - - private: - uint64_t _id{ 0 }; - }; -} - -namespace winrt::MonarchPeasantSample::factory_implementation -{ - BASIC_FACTORY(Peasant); -} diff --git a/src/tools/MonarchPeasantSample/SamplePeasant.idl b/src/tools/MonarchPeasantSample/SamplePeasant.idl deleted file mode 100644 index f0432518391..00000000000 --- a/src/tools/MonarchPeasantSample/SamplePeasant.idl +++ /dev/null @@ -1,18 +0,0 @@ - - -namespace MonarchPeasantSample -{ - 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/tools/MonarchPeasantSample/main.cpp b/src/tools/MonarchPeasantSample/main.cpp deleted file mode 100644 index 045dd93c8f5..00000000000 --- a/src/tools/MonarchPeasantSample/main.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include "pch.h" -#include "AppState.h" -#include "../../types/inc/utils.hpp" - -using namespace winrt; -using namespace winrt::Windows::Foundation; -using namespace ::Microsoft::Console; - -//////////////////////////////////////////////////////////////////////////////// -// 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 : implements -{ - MonarchFactory() = default; - - HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final - { - *result = nullptr; - if (outer) - { - return CLASS_E_NOAGGREGATION; - } - - if (!g_weak) - { - // Create a new Monarch instance - auto strong = 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 final - { - return S_OK; - } -}; -//////////////////////////////////////////////////////////////////////////////// - -// Function Description: -// - Register the Monarch object with COM. This allows other processes to create -// Monarch's in our process space with CoCreateInstance and the Monarch_clsid. -DWORD registerAsMonarch() -{ - DWORD registrationHostClass{}; - check_hresult(CoRegisterClassObject(Monarch_clsid, - make().get(), - CLSCTX_LOCAL_SERVER, - REGCLS_MULTIPLEUSE, - ®istrationHostClass)); - return registrationHostClass; -} - -// Function Description: -// - Called when the old monarch dies. Create a new connection to the new -// monarch. This might be us! If we're the new monarch, then update the -// Monarch to know which Peasant it came from. Otherwise, tell the new monarch -// that we exist. -void electNewMonarch(AppState& state) -{ - state.monarch = AppState::instantiateMonarch(); - bool isMonarch = state.areWeTheKing(true); - - printf("LONG LIVE THE %sKING\x1b[m\n", isMonarch ? "\x1b[33m" : ""); - - if (isMonarch) - { - state.remindKingWhoTheyAre(state.peasant); - } - else - { - // Add us to the new monarch - state.monarch.AddPeasant(state.peasant); - } -} - -void appLoop(AppState& state) -{ - auto dwRegistration = registerAsMonarch(); - // 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! - auto cleanup = wil::scope_exit([&]() { - check_hresult(CoRevokeClassObject(dwRegistration)); - }); - - // Tricky - first, we have to ask the monarch to handle the commandline. - // They will tell us if we need to create a peasant. - state.createMonarch(); - // processCommandline will return true if we should exit early. - if (state.processCommandline()) - { - return; - } - - bool isMonarch = state.areWeTheKing(true); - bool exitRequested = false; - - // (monarch|peasant)AppLoop will return when they've run to completion. If - // they return true, then just exit the application (the user might have - // pressed 'q' to exit). If the peasant returns false, then it detected the - // monarch died. Attempt to elect a new one. - while (!exitRequested) - { - if (isMonarch) - { - exitRequested = monarchAppLoop(state); - } - else - { - exitRequested = peasantAppLoop(state); - if (!exitRequested) - { - electNewMonarch(state); - isMonarch = state.areWeTheKing(false); - } - } - } -} - -int main(int argc, char** argv) -{ - AppState state; - state.initializeState(); - - // Collect up all the commandline arguments - printf("args:["); - for (auto& elem : wil::make_range(argv, argc)) - { - printf("%s, ", elem); - // This is obviously a bad way of converting args to a vector of - // hstrings, but it'll do for now. - state.args.emplace_back(winrt::to_hstring(elem)); - } - printf("]\n"); - - try - { - appLoop(state); - } - catch (const hresult_error& e) - { - printf("Error: %ls\n", e.message().c_str()); - } - - printf("We've left the app. Press any key to close.\n"); - const auto ch = _getch(); - ch; - printf("Exiting client\n"); -} diff --git a/src/tools/MonarchPeasantSample/monarch-peasant-sample-000.gif b/src/tools/MonarchPeasantSample/monarch-peasant-sample-000.gif deleted file mode 100644 index 77d4a9afa71..00000000000 Binary files a/src/tools/MonarchPeasantSample/monarch-peasant-sample-000.gif and /dev/null differ diff --git a/src/tools/MonarchPeasantSample/pch.cpp b/src/tools/MonarchPeasantSample/pch.cpp deleted file mode 100644 index 1d9f38c57d6..00000000000 --- a/src/tools/MonarchPeasantSample/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" diff --git a/src/tools/MonarchPeasantSample/pch.h b/src/tools/MonarchPeasantSample/pch.h deleted file mode 100644 index 29e037ece78..00000000000 --- a/src/tools/MonarchPeasantSample/pch.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#define WIN32_LEAN_AND_MEAN -// Manually include til after we include Windows.Foundation to give it winrt superpowers -#define BLOCK_TIL -#include -#undef max -#undef min -#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 - -// Manually include til after we include Windows.Foundation to give it winrt superpowers -#include "til.h" -#include "til/winrt.h" - -#include - -#include diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index 07ab54f3b86..05fc5af4c29 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -170,7 +170,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'unitSettingsModel', 'unitRemoting', 'unitControl', 'winconpty')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'unitSettingsModel', 'unitControl', 'winconpty')] [string]$Test, [parameter(Mandatory=$false)] diff --git a/tools/runut.cmd b/tools/runut.cmd index 62614d15d94..6bcca5a17fd 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -23,7 +23,6 @@ if "%PLATFORM%" == "Win32" ( %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%\UnitTests_Remoting\Remoting.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_Control\Control.Unit.Tests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ %* diff --git a/tools/tests.xml b/tools/tests.xml index 9ec3585de6b..7150fbfcde5 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -6,7 +6,6 @@ -