diff --git a/.github/actions/spell-check/dictionary/README.md b/.github/actions/spell-check/dictionary/README.md index ad8e772c260..26164527969 100644 --- a/.github/actions/spell-check/dictionary/README.md +++ b/.github/actions/spell-check/dictionary/README.md @@ -1,6 +1,6 @@ # Dictionaries are lists of words to accept unconditionally -While check spelling will complain about a whitelisted word +While check spelling will complain about an expected word which is no longer present, you can include things here even if they are not otherwise present in the repository. diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index 627ddbf0875..9fd188e1ada 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -34,6 +34,7 @@ IExplorer IInheritable IMap IObject +IPeasant IStorage ITab ITaskbar @@ -43,6 +44,7 @@ localtime lround LSHIFT msappx +MULTIPLEUSE NCHITTEST NCLBUTTONDBLCLK NCRBUTTONDBLCLK @@ -56,6 +58,8 @@ otms OUTLINETEXTMETRICW overridable PAGESCROLL +REGCLS +pmr RETURNCMD rfind roundf diff --git a/.github/actions/spell-check/expect/a129ff14ec985d6b7bf09e296a831c955d41040b.txt b/.github/actions/spell-check/expect/a129ff14ec985d6b7bf09e296a831c955d41040b.txt new file mode 100644 index 00000000000..877c72b2e04 --- /dev/null +++ b/.github/actions/spell-check/expect/a129ff14ec985d6b7bf09e296a831c955d41040b.txt @@ -0,0 +1,14 @@ +checkboxes +CSIDL +csv +horiz +IDispatch +inlines +IWeb +Progman +reserialize +SHANDLE +SHGFP +udk +unfocus +WClass diff --git a/.github/actions/spell-check/expect/expect.txt b/.github/actions/spell-check/expect/expect.txt index ad77d910404..5f2703f7366 100644 --- a/.github/actions/spell-check/expect/expect.txt +++ b/.github/actions/spell-check/expect/expect.txt @@ -168,7 +168,6 @@ BOLDFONT BOOLIFY bools boostorg -Bopomofo Borland BOTTOMLEFT BOTTOMRIGHT @@ -248,7 +247,6 @@ charset CHARSETINFO chcp checkbox -Checkboxes chh Childitem chk @@ -384,7 +382,6 @@ CORESYSTEM cotaskmem countof cout -CParams CPG cpinfo CPINFOEX @@ -413,7 +410,6 @@ csbi csbiex csharp CSHORT -cso csproj Csr csrmsg @@ -426,7 +422,6 @@ cstdlib cstr cstring cstyle -CSV CSwitch CText ctime @@ -628,7 +623,6 @@ doskey dotnet doubleclick downlevel -DOWNSCALE dpg dpi DPIAPI @@ -841,7 +835,6 @@ gcy gdi gdip gdirenderer -GENERATEPROJECTPRIFILE geopol GETALIAS GETALIASES @@ -957,7 +950,6 @@ hfont hglobal hh hhh -hhhh hhook hhx HIBYTE @@ -984,7 +976,6 @@ hmod hmodule hmon HMONITOR -Horiz HORZ hostable hostlib @@ -1097,10 +1088,8 @@ INITMENU inkscape inl INLINEPREFIX -Inlines INotify inout -INPATHROOT inproc Inputkeyinfo INPUTPROCESSORPROFILE @@ -1113,7 +1102,6 @@ INTERCEPTCOPYPASTE INTERNALNAME interop interoperability -intersectors inthread intptr intsafe @@ -1212,7 +1200,6 @@ KJ KLF KLMNOPQRST KLMNOPQRSTQQQQQ -Kode KU KVM KX @@ -1344,7 +1331,6 @@ mailto majorly makeappx MAKEINTRESOURCE -MAKEINTRESOURCEA MAKEINTRESOURCEW MAKELANGID MAKELONG @@ -1417,7 +1403,6 @@ monostate MOUSEACTIVATE MOUSEFIRST MOUSEHWHEEL -mousemode MOUSEMOVE mousewheel MOVESTART @@ -1439,7 +1424,6 @@ MSGSELECTMODE msiexec MSIL msix -msixbundle msrc msvcrt MSVS @@ -1842,7 +1826,6 @@ pshn PSHNOTIFY PSHORT pshpack -psin PSINGLE psl psldl @@ -1962,16 +1945,13 @@ Replymessage repositorypath rescap Resequence -Reserialize RESETCONTENT resheader resizable resmimetype -reso restrictedcapabilities resw resx -RETROII retval rfa rfc @@ -1991,7 +1971,6 @@ rgpwsz rgrc rgs rgui -rgus rgw rgwch rhs @@ -2042,14 +2021,11 @@ SBCSDBCS sbi sbiex sbold -sbri -scanbri scancode scanline schemename SCL scm -scol scprintf SCRBUF SCRBUFSIZE @@ -2128,7 +2104,6 @@ sfi SFINAE SFUI sgr -SGRXY SHCo shcore shellapi @@ -2183,9 +2158,6 @@ SOURCESDIRECTORY SPACEBAR spammy spand -spe -sph -spherefunctions splashscreen sprintf sqlproj @@ -2303,7 +2275,6 @@ TCI tcome tcommandline tcommands -tcon TDelegated TDP TEAMPROJECT @@ -2449,8 +2420,6 @@ ucdxml uch UCHAR ucs -UDK -UDKs UDM uer uget @@ -2473,7 +2442,6 @@ UNCPRIORITY undef Unescape unexpand -Unfocus unhighlighting unhosted unicode @@ -2564,7 +2532,6 @@ vga vgaoem viewkind viewports -Viginetting Virt VIRTTERM Virtualizing @@ -2781,7 +2748,6 @@ wwaproj WWith wx wxh -wz xa xact xamarin @@ -2800,14 +2766,12 @@ XColors xcopy XCount xdy -xe XEncoding xes Xes XES xff XFile -xlang XManifest XMath XMFLOAT @@ -2831,9 +2795,7 @@ xutr xvalue XVIRTUALSCREEN XWalk -XWV xy -xyw Xzn yact YAML @@ -2850,7 +2812,6 @@ YVIRTUALSCREEN Yw YWalk yx -yzx Zc ZCmd ZCtrl @@ -2862,7 +2823,6 @@ zu zxcvbnm zy AAAAABBBBBBCCC -AAAAA BBBBBCCC abcd LPMINMAXINFO diff --git a/OpenConsole.sln b/OpenConsole.sln index 364216a7cbf..eaf51110a64 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -171,18 +171,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControl", "src\casc EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj", "{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}" ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} + {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} {CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia\TerminalApp\dll\TerminalApp.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} + {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} EndProjectSection @@ -234,8 +235,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\cascadia\TerminalApp\TerminalAppLib.vcxproj", "{CA5CAD1A-9A12-429C-B551-8562EC954746}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} + {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}" @@ -341,6 +342,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting.Lib", "src\cascadia\Remoting\Microsoft.Terminal.RemotingLib.vcxproj", "{43CE4CE5-0010-4B99-9569-672670D26E26}" +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 + {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2137,6 +2151,85 @@ Global {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.Build.0 = Release|x64 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.Build.0 = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.Build.0 = AuditMode|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.Build.0 = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|ARM64.Build.0 = Debug|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x64.ActiveCfg = Debug|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x64.Build.0 = Debug|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x86.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x86.Build.0 = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|Any CPU.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|ARM64.ActiveCfg = Release|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|ARM64.Build.0 = Release|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x64.Build.0 = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x86.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x86.Build.0 = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x64.ActiveCfg = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x86.Build.0 = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|ARM64.Build.0 = Debug|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x64.ActiveCfg = Debug|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x64.Build.0 = Debug|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x86.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x86.Build.0 = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|Any CPU.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|ARM64.ActiveCfg = Release|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|ARM64.Build.0 = Release|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x64.ActiveCfg = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x64.Build.0 = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x86.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x86.Build.0 = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x86.Build.0 = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|ARM64.Build.0 = Debug|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x64.ActiveCfg = Debug|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x64.Build.0 = Debug|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x86.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x86.Build.0 = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|Any CPU.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|ARM64.ActiveCfg = Release|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|ARM64.Build.0 = Release|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x64.ActiveCfg = Release|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x64.Build.0 = Release|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2221,6 +2314,9 @@ Global {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {43CE4CE5-0010-4B99-9569-672670D26E26} = {59840756-302F-44DF-AA47-441A9D673202} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {59840756-302F-44DF-AA47-441A9D673202} + {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/build/Helix/runtests.cmd b/build/Helix/runtests.cmd index ddf8c2d3d11..b55ee2cf3c8 100644 --- a/build/Helix/runtests.cmd +++ b/build/Helix/runtests.cmd @@ -18,7 +18,7 @@ FOR %%A IN (TestHostApp.exe,te.exe,te.processhost.exe,conhost.exe,OpenConsole.ex echo %TIME% -:: kill dhandler, which is a tool designed to handle unexpected windows appearing. But since our tests are +:: kill dhandler, which is a tool designed to handle unexpected windows appearing. But since our tests are :: expected to show UI we don't want it running. taskkill -f -im dhandler.exe @@ -28,7 +28,7 @@ echo %TIME% powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1 echo %TIME% -set testBinaryCandidates=TerminalApp.LocalTests.dll Conhost.UIA.Tests.dll +set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll set testBinaries= for %%B in (%testBinaryCandidates%) do ( if exist %%B ( @@ -103,4 +103,4 @@ copy /y *_subresults.json %HELIX_WORKITEM_UPLOAD_ROOT% type testResults.xml -echo %TIME% \ No newline at end of file +echo %TIME% diff --git a/build/pipelines/templates/helix-runtests-job.yml b/build/pipelines/templates/helix-runtests-job.yml index 81125f3d988..04169e342aa 100644 --- a/build/pipelines/templates/helix-runtests-job.yml +++ b/build/pipelines/templates/helix-runtests-job.yml @@ -5,14 +5,14 @@ parameters: testSuite: '' # If a Pipeline runs this template more than once, this parameter should be unique per build flavor to differentiate the # the different test runs: - helixType: 'test/devtest' + helixType: 'test/devtest' artifactName: 'drop' maxParallel: 4 rerunPassesRequiredToAvoidFailure: 5 taefQuery: '' # if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline: useBuildOutputFromPipeline: $(System.DefinitionId) - matrix: + matrix: # Release_x86: # buildPlatform: 'x86' # buildConfiguration: 'release' @@ -39,13 +39,13 @@ jobs: taefPath: $(Build.SourcesDirectory)\build\Helix\packages\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$(buildPlatform) helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}' - + steps: - task: CmdLine@1 displayName: 'Display build machine environment variables' inputs: filename: 'set' - + - task: NuGetToolInstaller@0 displayName: 'Use NuGet 5.2.0' inputs: @@ -59,23 +59,23 @@ jobs: nugetConfigPath: nuget.config restoreDirectory: packages - - task: DownloadBuildArtifacts@0 + - task: DownloadBuildArtifacts@0 condition: and(succeeded(),eq(variables['useBuildOutputFromBuildId'],'')) - inputs: - artifactName: ${{ parameters.artifactName }} + inputs: + artifactName: ${{ parameters.artifactName }} downloadPath: '$(artifactsDir)' - - task: DownloadBuildArtifacts@0 + - task: DownloadBuildArtifacts@0 condition: and(succeeded(),ne(variables['useBuildOutputFromBuildId'],'')) - inputs: + inputs: buildType: specific buildVersionToDownload: specific project: $(System.TeamProjectId) pipeline: ${{ parameters.useBuildOutputFromPipeline }} buildId: $(useBuildOutputFromBuildId) - artifactName: ${{ parameters.artifactName }} + artifactName: ${{ parameters.artifactName }} downloadPath: '$(artifactsDir)' - task: CmdLine@1 @@ -90,7 +90,7 @@ jobs: targetType: filePath filePath: build\Helix\PrepareHelixPayload.ps1 arguments: -Platform '$(buildPlatform)' -Configuration '$(buildConfiguration)' -ArtifactName '${{ parameters.artifactName }}' - + - task: CmdLine@1 displayName: 'Display Helix payload contents' inputs: @@ -104,7 +104,16 @@ jobs: outputProjFileName: 'RunTestsInHelix-TerminalAppLocalTests.proj' testSuite: '${{ parameters.testSuite }}' taefQuery: ${{ parameters.taefQuery }} - + + - template: helix-createprojfile-steps.yml + parameters: + condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) + testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\SettingsModel.LocalTests.dll' + outputProjFileName: 'RunTestsInHelix-SettingsModelLocalTests.proj' + testSuite: '${{ parameters.testSuite }}' + taefQuery: ${{ parameters.taefQuery }} + + - template: helix-createprojfile-steps.yml parameters: condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) @@ -118,7 +127,7 @@ jobs: inputs: PathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: ${{ parameters.artifactName }} - + - task: DotNetCoreCLI@2 displayName: 'Run tests in Helix (open queues)' env: diff --git a/src/buffer/out/AttrRow.cpp b/src/buffer/out/AttrRow.cpp index 68f7a715491..8e807ead133 100644 --- a/src/buffer/out/AttrRow.cpp +++ b/src/buffer/out/AttrRow.cpp @@ -184,15 +184,15 @@ size_t ATTR_ROW::FindAttrIndex(const size_t index, size_t* const pApplies) const // Routine Description: // - Finds the hyperlink IDs present in this row and returns them // Return value: -// - An unordered set containing the hyperlink IDs present in this row -std::unordered_set ATTR_ROW::GetHyperlinks() +// - The hyperlink IDs present in this row +std::vector ATTR_ROW::GetHyperlinks() { - std::unordered_set ids; + std::vector ids; for (const auto& run : _list) { if (run.GetAttributes().IsHyperlink()) { - ids.emplace(run.GetAttributes().GetHyperlinkId()); + ids.emplace_back(run.GetAttributes().GetHyperlinkId()); } } return ids; diff --git a/src/buffer/out/AttrRow.hpp b/src/buffer/out/AttrRow.hpp index b9506efbd74..75da3ec1969 100644 --- a/src/buffer/out/AttrRow.hpp +++ b/src/buffer/out/AttrRow.hpp @@ -20,7 +20,6 @@ Revision History: #pragma once -#include "boost/container/small_vector.hpp" #include "TextAttributeRun.hpp" #include "AttrRowIterator.hpp" @@ -51,7 +50,7 @@ class ATTR_ROW final size_t FindAttrIndex(const size_t index, size_t* const pApplies) const; - std::unordered_set GetHyperlinks(); + std::vector GetHyperlinks(); bool SetAttrToEnd(const UINT iStart, const TextAttribute attr); void ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAttribute& replaceWith) noexcept; diff --git a/src/buffer/out/AttrRowIterator.hpp b/src/buffer/out/AttrRowIterator.hpp index 025439b2612..744b02f1042 100644 --- a/src/buffer/out/AttrRowIterator.hpp +++ b/src/buffer/out/AttrRowIterator.hpp @@ -15,7 +15,6 @@ Author(s): #pragma once -#include "boost/container/small_vector.hpp" #include "TextAttribute.hpp" #include "TextAttributeRun.hpp" diff --git a/src/buffer/out/CharRow.hpp b/src/buffer/out/CharRow.hpp index 8fcccaa65fb..b2f7ab41c1c 100644 --- a/src/buffer/out/CharRow.hpp +++ b/src/buffer/out/CharRow.hpp @@ -24,7 +24,6 @@ Revision History: #include "CharRowCellReference.hpp" #include "CharRowCell.hpp" #include "UnicodeStorage.hpp" -#include "boost/container/small_vector.hpp" class ROW; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 8a74a0e3f69..3ba42d89d39 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1230,9 +1230,15 @@ void TextBuffer::_PruneHyperlinks() // If the buffer does not contain the same reference, we can remove that hyperlink from our map // This way, obsolete hyperlink references are cleared from our hyperlink map instead of hanging around // Get all the hyperlink references in the row we're erasing - auto firstRowRefs = _storage.at(_firstRow).GetAttrRow().GetHyperlinks(); - if (!firstRowRefs.empty()) + const auto hyperlinks = _storage.at(_firstRow).GetAttrRow().GetHyperlinks(); + + if (!hyperlinks.empty()) { + // Move to unordered set so we can use hashed lookup of IDs instead of linear search. + // Only make it an unordered set now because set always heap allocates but vector + // doesn't when the set is empty (saving an allocation in the common case of no links.) + std::unordered_set firstRowRefs{ hyperlinks.cbegin(), hyperlinks.cend() }; + const auto total = TotalRowCount(); // Loop through all the rows in the buffer except the first row - // we have found all hyperlink references in the first row and put them in refs, @@ -1254,12 +1260,12 @@ void TextBuffer::_PruneHyperlinks() break; } } - } - // Now delete obsolete references from our map - for (auto hyperlinkReference : firstRowRefs) - { - RemoveHyperlinkFromMap(hyperlinkReference); + // Now delete obsolete references from our map + for (auto hyperlinkReference : firstRowRefs) + { + RemoveHyperlinkFromMap(hyperlinkReference); + } } } diff --git a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest index 173ca496ea7..f4fa79fb4a5 100644 --- a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest @@ -82,14 +82,9 @@ - + + diff --git a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest index 57a2a38c178..62fbb9de1db 100644 --- a/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Pre.appxmanifest @@ -82,15 +82,9 @@ - - diff --git a/src/cascadia/CascadiaPackage/Package.appxmanifest b/src/cascadia/CascadiaPackage/Package.appxmanifest index 3565188a630..fb44274e7f4 100644 --- a/src/cascadia/CascadiaPackage/Package.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package.appxmanifest @@ -83,14 +83,9 @@ - diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp new file mode 100644 index 00000000000..db90f3381fd --- /dev/null +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -0,0 +1,25 @@ +#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::Args(winrt::array_view const& value) + { + _args = { value.begin(), value.end() }; + } + + winrt::com_array CommandlineArgs::Args() + { + return winrt::com_array{ _args.begin(), _args.end() }; + } +} diff --git a/src/cascadia/Remoting/CommandlineArgs.h b/src/cascadia/Remoting/CommandlineArgs.h new file mode 100644 index 00000000000..159071854f2 --- /dev/null +++ b/src/cascadia/Remoting/CommandlineArgs.h @@ -0,0 +1,39 @@ +#pragma once + +#include "CommandlineArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct CommandlineArgs : public CommandlineArgsT + { + public: + CommandlineArgs() : + _args{}, + _cwd{ L"" } + { + } + + CommandlineArgs(const winrt::array_view& args, + winrt::hstring currentDirectory) : + _args{ args.begin(), args.end() }, + _cwd{ currentDirectory } + { + } + + winrt::hstring CurrentDirectory() { return _cwd; }; + + void Args(winrt::array_view const& value); + winrt::com_array Args(); + + private: + winrt::com_array _args; + winrt::hstring _cwd; + }; + +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(CommandlineArgs); +} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj new file mode 100644 index 00000000000..d3f1bf6c7f4 --- /dev/null +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -0,0 +1,97 @@ + + + + {43ce4ce5-0010-4b99-9569-672670d26e26} + Win32Proj + Microsoft.Terminal.Remoting.Lib + Microsoft.Terminal.Remoting + Microsoft.Terminal.Remoting.Lib + 10.0.17763.0 + StaticLibrary + Console + true + + + + + + + + Monarch.idl + + + + + Peasant.idl + + + WindowManager.idl + + + Peasant.idl + + + + + + Monarch.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 new file mode 100644 index 00000000000..ab32df78283 --- /dev/null +++ b/src/cascadia/Remoting/Monarch.cpp @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Monarch.h" +#include "CommandlineArgs.h" + +#include "Monarch.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() } + { + } + + // This is a private constructor to be used in unit tests, where we don't + // want each Monarch to necessarily use the current PID. + Monarch::Monarch(const uint64_t testPID) : + _ourPID{ testPID } + { + } + + Monarch::~Monarch() + { + } + + 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. + // Arguments: + // - peasant: the new Peasant to track. + // Return Value: + // - the ID assigned to the peasant. + uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) + { + // TODO:projects/5 This is terrible. There's gotta be a better way + // 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. + _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; + } + + auto newPeasantsId = peasant.GetID(); + _peasants[newPeasantsId] = peasant; + + // Add an event listener to the peasant's WindowActivated event. + peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + + // TODO:projects/5 Wait on the peasant's PID, and remove them from the + // map if they die. This won't work great in tests though, with fake + // PIDs. + + return newPeasantsId; + } + + // Method Description: + // - Event handler for the Peasant::WindowActivated event. Used as an + // opportunity for us to update our internal stack of the "most recent + // window". + // Arguments: + // - sender: the Peasant that raised this event. This might be out-of-proc! + // Return Value: + // - + void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + // TODO:projects/5 Pass the desktop and timestamp of when the window was + // activated in `args`. + + if (auto peasant{ sender.try_as() }) + { + auto theirID = peasant.GetID(); + _setMostRecentPeasant(theirID); + } + } + + // Method Description: + // - Lookup a peasant by its ID. + // Arguments: + // - peasantID: The ID Of the peasant to find + // Return Value: + // - the peasant if it exists in our map, otherwise null + Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) + { + auto peasantSearch = _peasants.find(peasantID); + return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + } + + void Monarch::_setMostRecentPeasant(const uint64_t peasantID) + { + // TODO:projects/5 Use a heap/priority queue per-desktop to track which + // peasant was the most recent per-desktop. When we want to get the most + // recent of all desktops (WindowingBehavior::UseExisting), then use the + // most recent of all desktops. + _mostRecentPeasant = peasantID; + } + + // 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: + // - + bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& /*args*/) + { + // TODO:projects/5 + // The branch dev/migrie/f/remote-commandlines has a more complete + // version of this function, with a naive implementation. For now, we + // always want to create a new window, so we'll just return true. This + // will tell the caller that we didn't handle the commandline, and they + // should open a new window to deal with it themselves. + return true; + } + +} diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h new file mode 100644 index 00000000000..eeeb9fe7602 --- /dev/null +++ b/src/cascadia/Remoting/Monarch.h @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Monarch.g.h" +#include "Peasant.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +// We sure different GUIDs here depending on whether we're running a Release, +// Preview, or Dev build. This ensures that different installs don't +// accidentally talk to one another. +// +// * Release: {06171993-7eb1-4f3e-85f5-8bdd7386cce3} +// * Preview: {04221993-7eb1-4f3e-85f5-8bdd7386cce3} +// * Dev: {08302020-7eb1-4f3e-85f5-8bdd7386cce3} +constexpr GUID Monarch_clsid +{ +#if defined(WT_BRANDING_RELEASE) + 0x06171993, +#elif defined(WT_BRANDING_PREVIEW) + 0x04221993, +#else + 0x08302020, +#endif + 0x7eb1, + 0x4f3e, + { + 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 + } +}; + +enum class WindowingBehavior : uint64_t +{ + UseNew = 0, + UseExisting = 1, +}; + +namespace RemotingUnitTests +{ + class RemotingTests; +}; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct Monarch : public MonarchT + { + Monarch(); + ~Monarch(); + + uint64_t GetPID(); + + uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); + + bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + + private: + Monarch(const uint64_t testPID); + uint64_t _ourPID; + + uint64_t _nextPeasantID{ 1 }; + uint64_t _thisPeasantID{ 0 }; + uint64_t _mostRecentPeasant{ 0 }; + WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew }; + std::unordered_map _peasants; + + winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); + void _setMostRecentPeasant(const uint64_t peasantID); + + void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + + friend class RemotingUnitTests::RemotingTests; + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(Monarch); +} diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl new file mode 100644 index 00000000000..8dcb7a66a7b --- /dev/null +++ b/src/cascadia/Remoting/Monarch.idl @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Peasant.idl"; + +namespace Microsoft.Terminal.Remoting +{ + [default_interface] runtimeclass Monarch { + Monarch(); + + UInt64 GetPID(); + UInt64 AddPeasant(IPeasant peasant); + Boolean ProposeCommandline(CommandlineArgs args); + }; +} diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h new file mode 100644 index 00000000000..496a6094686 --- /dev/null +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -0,0 +1,49 @@ +// 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 new file mode 100644 index 00000000000..ce3c4e96e97 --- /dev/null +++ b/src/cascadia/Remoting/Peasant.cpp @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Peasant.h" +#include "CommandlineArgs.h" +#include "Peasant.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + Peasant::Peasant() : + _ourPID{ GetCurrentProcessId() } + { + } + + // This is a private constructor to be used in unit tests, where we don't + // want each Peasant to necessarily use the current PID. + Peasant::Peasant(const uint64_t testPID) : + _ourPID{ testPID } + { + } + + 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; + } + + // Raise an event with these args. The AppHost will listen for this + // event to know when to take these args and dispatch them to a + // currently-running window. + _ExecuteCommandlineRequestedHandlers(*this, args); + + return true; + } + + Remoting::CommandlineArgs Peasant::InitialArgs() + { + return _initialArgs; + } + +} diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h new file mode 100644 index 00000000000..b83011e8673 --- /dev/null +++ b/src/cascadia/Remoting/Peasant.h @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Peasant.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace RemotingUnitTests +{ + class RemotingTests; +}; +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + 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); + + winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); + + private: + Peasant(const uint64_t testPID); + uint64_t _ourPID; + + uint64_t _id{ 0 }; + + winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; + + friend class RemotingUnitTests::RemotingTests; + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(Peasant); +} diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl new file mode 100644 index 00000000000..8597b2489dd --- /dev/null +++ b/src/cascadia/Remoting/Peasant.idl @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace Microsoft.Terminal.Remoting +{ + + runtimeclass CommandlineArgs + { + CommandlineArgs(); + CommandlineArgs(String[] args, String cwd); + + String[] Args { get; set; }; + String CurrentDirectory(); + }; + + interface IPeasant + { + CommandlineArgs InitialArgs { get; }; + + void AssignID(UInt64 id); + UInt64 GetID(); + UInt64 GetPID(); + Boolean ExecuteCommandline(CommandlineArgs args); + event Windows.Foundation.TypedEventHandler WindowActivated; + event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; + }; + + [default_interface] runtimeclass Peasant : IPeasant + { + Peasant(); + }; +} diff --git a/src/cascadia/Remoting/Resources/en-US/Resources.resw b/src/cascadia/Remoting/Resources/en-US/Resources.resw new file mode 100644 index 00000000000..f4af46df557 --- /dev/null +++ b/src/cascadia/Remoting/Resources/en-US/Resources.resw @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp new file mode 100644 index 00000000000..f80f8e84cf9 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "WindowManager.h" +#include "MonarchFactory.h" +#include "CommandlineArgs.h" + +#include "WindowManager.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + WindowManager::WindowManager() + { + // Register with COM as a server for the Monarch class + _registerAsMonarch(); + // Instantiate an instance of the Monarch. This may or may not be in-proc! + _createMonarch(); + } + + WindowManager::~WindowManager() + { + // IMPORTANT! Tear down the registration as soon as we exit. If we're not a + // real peasant window (the monarch passed our commandline to someone else), + // then the monarch dies, we don't want our registration becoming the active + // monarch! + CoRevokeClassObject(_registrationHostClass); + _registrationHostClass = 0; + } + + void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) + { + const bool isKing = _areWeTheKing(); + // If we're the king, we _definitely_ want to process the arguments, we were + // launched with them! + // + // Otherwise, the King will tell us if we should make a new window + _shouldCreateWindow = isKing || + _monarch.ProposeCommandline(args); + + if (_shouldCreateWindow) + { + // If we should create a new window, then instantiate our Peasant + // instance, and tell that peasant to handle that commandline. + _createOurPeasant(); + + _peasant.ExecuteCommandline(args); + } + // Otherwise, we'll do _nothing_. + } + + bool WindowManager::ShouldCreateWindow() + { + return _shouldCreateWindow; + } + + void WindowManager::_registerAsMonarch() + { + winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, + winrt::make<::MonarchFactory>().get(), + CLSCTX_LOCAL_SERVER, + REGCLS_MULTIPLEUSE, + &_registrationHostClass)); + } + + void WindowManager::_createMonarch() + { + // Heads up! This only works because we're using + // "metadata-based-marshalling" for our WinRT types. That means the OS is + // using the .winmd file we generate to figure out the proxy/stub + // definitions for our types automatically. This only works in the following + // cases: + // + // * If we're running unpackaged: the .winmd must be a sibling of the .exe + // * If we're running packaged: the .winmd must be in the package root + _monarch = create_instance(Monarch_clsid, + CLSCTX_LOCAL_SERVER); + } + + bool WindowManager::_areWeTheKing() + { + const auto kingPID{ _monarch.GetPID() }; + const auto ourPID{ GetCurrentProcessId() }; + return (ourPID == kingPID); + } + + Remoting::IPeasant WindowManager::_createOurPeasant() + { + auto p = winrt::make_self(); + _peasant = *p; + _monarch.AddPeasant(_peasant); + + // TODO:projects/5 Spawn a thread to wait on the monarch, and handle the election + + return _peasant; + } + + Remoting::Peasant WindowManager::CurrentWindow() + { + return _peasant; + } + +} diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h new file mode 100644 index 00000000000..0d25a4dfb96 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.h @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "WindowManager.g.h" +#include "Peasant.h" +#include "Monarch.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct WindowManager final : public WindowManagerT + { + WindowManager(); + ~WindowManager(); + + void ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + bool ShouldCreateWindow(); + + winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); + + private: + bool _shouldCreateWindow{ false }; + DWORD _registrationHostClass{ 0 }; + winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; + winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; + + void _registerAsMonarch(); + void _createMonarch(); + bool _areWeTheKing(); + winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(WindowManager); +} diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl new file mode 100644 index 00000000000..e9ef1ee345c --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.idl @@ -0,0 +1,13 @@ +import "Peasant.idl"; + + +namespace Microsoft.Terminal.Remoting +{ + [default_interface] runtimeclass WindowManager + { + WindowManager(); + void ProposeCommandline(CommandlineArgs args); + Boolean ShouldCreateWindow { get; }; + IPeasant CurrentWindow(); + }; +} diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def new file mode 100644 index 00000000000..8c1a02932d0 --- /dev/null +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj new file mode 100644 index 00000000000..8b58ce05673 --- /dev/null +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -0,0 +1,74 @@ + + + + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} + Microsoft.Terminal.Remoting + Microsoft.Terminal.Remoting + + + DynamicLibrary + Console + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {18D09A24-8240-42D6-8CB6-236EEE820263} + + + + true + true + + + + + + + $(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); + + + User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) + + /INCLUDE:_DllMain@12 + /INCLUDE:DllMain + + + + false + + + + diff --git a/src/cascadia/Remoting/packages.config b/src/cascadia/Remoting/packages.config new file mode 100644 index 00000000000..8a013cf32b2 --- /dev/null +++ b/src/cascadia/Remoting/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/src/cascadia/Remoting/pch.cpp b/src/cascadia/Remoting/pch.cpp new file mode 100644 index 00000000000..3c27d44d570 --- /dev/null +++ b/src/cascadia/Remoting/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/src/cascadia/Remoting/pch.h b/src/cascadia/Remoting/pch.h new file mode 100644 index 00000000000..165aefe02d8 --- /dev/null +++ b/src/cascadia/Remoting/pch.h @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// pch.h +// Header for platform projection include files +// + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMCX +#define NOHELP +#define NOCOMM + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#define BLOCK_TIL +#include +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#include + +#include + +#include + +#include +#include +#include + +#include + +// Including TraceLogging essentials for the binary +#include +#include +TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider); +#include +#include + +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" diff --git a/src/cascadia/ShellExtension/OpenTerminalHere.cpp b/src/cascadia/ShellExtension/OpenTerminalHere.cpp index d5669e21f63..bd748338d7a 100644 --- a/src/cascadia/ShellExtension/OpenTerminalHere.cpp +++ b/src/cascadia/ShellExtension/OpenTerminalHere.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "OpenTerminalHere.h" +#include // TODO GH#6112: Localize these strings static constexpr std::wstring_view VerbDisplayName{ L"Open in Windows Terminal" }; @@ -117,14 +118,29 @@ static std::wstring _getExePath() HRESULT OpenTerminalHere::Invoke(IShellItemArray* psiItemArray, IBindCtx* /*pBindContext*/) { - DWORD count; - psiItemArray->GetCount(&count); + wil::unique_cotaskmem_string pszName; - winrt::com_ptr psi; - RETURN_IF_FAILED(psiItemArray->GetItemAt(0, psi.put())); + if (psiItemArray == nullptr) + { + // get the current path from explorer.exe + const auto path = this->_GetPathFromExplorer(); - wil::unique_cotaskmem_string pszName; - RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pszName)); + // no go, unable to get a reasonable path + if (path.empty()) + { + return S_FALSE; + } + pszName = wil::make_cotaskmem_string(path.c_str(), path.length()); + } + else + { + DWORD count; + psiItemArray->GetCount(&count); + + winrt::com_ptr psi; + RETURN_IF_FAILED(psiItemArray->GetItemAt(0, psi.put())); + RETURN_IF_FAILED(psi->GetDisplayName(SIGDN_FILESYSPATH, &pszName)); + } { wil::unique_process_information _piClient; @@ -214,3 +230,90 @@ HRESULT OpenTerminalHere::EnumSubCommands(IEnumExplorerCommand** ppEnum) *ppEnum = nullptr; return E_NOTIMPL; } + +std::wstring OpenTerminalHere::_GetPathFromExplorer() const +{ + using namespace std; + using namespace winrt; + + wstring path; + HRESULT hr = NOERROR; + + auto hwnd = ::GetForegroundWindow(); + if (hwnd == nullptr) + { + return path; + } + + TCHAR szName[MAX_PATH] = { 0 }; + ::GetClassName(hwnd, szName, MAX_PATH); + if (0 == StrCmp(szName, L"WorkerW") || + 0 == StrCmp(szName, L"Progman")) + { + //special folder: desktop + hr = ::SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, szName); + if (FAILED(hr)) + { + return path; + } + + path = szName; + return path; + } + + if (0 != StrCmp(szName, L"CabinetWClass")) + { + return path; + } + + auto shell = create_instance(CLSID_ShellWindows); + if (shell == nullptr) + { + return path; + } + + com_ptr disp; + wil::unique_variant variant; + variant.vt = VT_I4; + + com_ptr browser; + // look for correct explorer window + for (variant.intVal = 0; + shell->Item(variant, disp.put()) == S_OK; + variant.intVal++) + { + com_ptr tmp; + if (FAILED(disp->QueryInterface(tmp.put()))) + { + continue; + } + + HWND tmpHWND = NULL; + hr = tmp->get_HWND(reinterpret_cast(&tmpHWND)); + if (hwnd == tmpHWND) + { + browser = tmp; + break; //found + } + } + + if (browser != nullptr) + { + wil::unique_bstr url; + hr = browser->get_LocationURL(&url); + if (FAILED(hr)) + { + return path; + } + + wstring sUrl(url.get(), SysStringLen(url.get())); + DWORD size = MAX_PATH; + hr = ::PathCreateFromUrl(sUrl.c_str(), szName, &size, NULL); + if (SUCCEEDED(hr)) + { + path = szName; + } + } + + return path; +} diff --git a/src/cascadia/ShellExtension/OpenTerminalHere.h b/src/cascadia/ShellExtension/OpenTerminalHere.h index c79fc3424aa..c2dc4098dc4 100644 --- a/src/cascadia/ShellExtension/OpenTerminalHere.h +++ b/src/cascadia/ShellExtension/OpenTerminalHere.h @@ -46,6 +46,9 @@ struct __declspec(uuid("9f156763-7844-4dc4-b2b1-901f640f5155")) STDMETHODIMP GetCanonicalName(GUID* pguidCommandName); STDMETHODIMP EnumSubCommands(IEnumExplorerCommand** ppEnum); #pragma endregion + +private: + std::wstring _GetPathFromExplorer() const; }; CoCreatableClass(OpenTerminalHere); diff --git a/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj b/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj index 681bcad5cf7..e95305528c4 100644 --- a/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj +++ b/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj @@ -11,10 +11,13 @@ true - - + + + User32.lib;%(AdditionalDependencies) + + @@ -40,7 +43,6 @@ - + + Transparent @@ -35,9 +37,11 @@ the MIT License. See LICENSE in the project root for license information. --> + + Transparent @@ -46,9 +50,11 @@ the MIT License. See LICENSE in the project root for license information. --> 3.0 + + @@ -95,8 +101,8 @@ the MIT License. See LICENSE in the project root for license information. --> - - + + diff --git a/src/cascadia/TerminalApp/TabPaletteItem.cpp b/src/cascadia/TerminalApp/TabPaletteItem.cpp index f7f1fd456dc..baf42fc06c0 100644 --- a/src/cascadia/TerminalApp/TabPaletteItem.cpp +++ b/src/cascadia/TerminalApp/TabPaletteItem.cpp @@ -19,7 +19,7 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; namespace winrt::TerminalApp::implementation { TabPaletteItem::TabPaletteItem(winrt::TerminalApp::TabBase const& tab) : - _Tab(tab) + _tab(tab) { Name(tab.Title()); Icon(tab.Icon()); diff --git a/src/cascadia/TerminalApp/TabPaletteItem.h b/src/cascadia/TerminalApp/TabPaletteItem.h index 4a498e6f766..e5c55798974 100644 --- a/src/cascadia/TerminalApp/TabPaletteItem.h +++ b/src/cascadia/TerminalApp/TabPaletteItem.h @@ -14,9 +14,13 @@ namespace winrt::TerminalApp::implementation TabPaletteItem() = default; TabPaletteItem(winrt::TerminalApp::TabBase const& tab); - GETSET_PROPERTY(winrt::TerminalApp::TabBase, Tab, nullptr); + winrt::TerminalApp::TabBase Tab() const noexcept + { + return _tab.get(); + } private: + winrt::weak_ref _tab; Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _tabChangedRevoker; }; } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 3f9b5ee83ff..b30596637b0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4,7 +4,6 @@ #include "pch.h" #include "TerminalPage.h" #include "Utils.h" -#include "AppLogic.h" #include "../../types/inc/utils.hpp" #include @@ -108,7 +107,7 @@ namespace winrt::TerminalApp::implementation if (auto page{ weakThis.get() }) { _UpdateCommandsForPalette(); - CommandPalette().SetKeyBindings(*_bindings); + CommandPalette().SetKeyMap(_settings.KeyMap()); } } @@ -332,7 +331,7 @@ namespace winrt::TerminalApp::implementation } else { - _ProcessStartupActions(_startupActions, true); + ProcessStartupActions(_startupActions, true); } } } @@ -348,8 +347,8 @@ namespace winrt::TerminalApp::implementation // should fire an Initialized event. // Return Value: // - - winrt::fire_and_forget TerminalPage::_ProcessStartupActions(Windows::Foundation::Collections::IVector actions, - const bool initial) + winrt::fire_and_forget TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector actions, + const bool initial) { // If there are no actions left, do nothing. if (actions.Size() == 0) @@ -602,7 +601,9 @@ namespace winrt::TerminalApp::implementation settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick }); newTabFlyout.Items().Append(settingsItem); - auto settingsKeyChord = keyBindings.GetKeyBindingForAction(ShortcutAction::OpenSettings); + Microsoft::Terminal::Settings::Model::OpenSettingsArgs args{ SettingsTarget::SettingsFile }; + Microsoft::Terminal::Settings::Model::ActionAndArgs settingsAction{ ShortcutAction::OpenSettings, args }; + const auto settingsKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(settingsAction) }; if (settingsKeyChord) { _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord); @@ -943,9 +944,7 @@ namespace winrt::TerminalApp::implementation auto const shiftDown = WI_IsFlagSet(CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift), CoreVirtualKeyStates::Down); winrt::Microsoft::Terminal::TerminalControl::KeyChord kc{ ctrlDown, altDown, shiftDown, static_cast(key) }; - auto setting = AppLogic::CurrentAppSettings(); - auto keymap = setting.GlobalSettings().KeyMap(); - const auto actionAndArgs = keymap.TryLookup(kc); + const auto actionAndArgs = _settings.KeyMap().TryLookup(kc); if (actionAndArgs) { if (CommandPalette().Visibility() == Visibility::Visible && actionAndArgs.Action() != ShortcutAction::ToggleCommandPalette) @@ -1095,34 +1094,31 @@ namespace winrt::TerminalApp::implementation // - Duplicates the current focused tab void TerminalPage::_DuplicateTabViewItem() { - if (auto index{ _GetFocusedTabIndex() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) + try { - try + // TODO: GH#5047 - In the future, we should get the Profile of + // the focused pane, and use that to build a new instance of the + // settings so we can duplicate this tab/pane. + // + // Currently, if the profile doesn't exist anymore in our + // settings, we'll silently do nothing. + // + // In the future, it will be preferable to just duplicate the + // current control's settings, but we can't do that currently, + // because we won't be able to create a new instance of the + // connection without keeping an instance of the original Profile + // object around. + + const auto& profileGuid = terminalTab->GetFocusedProfile(); + if (profileGuid.has_value()) { - // TODO: GH#5047 - In the future, we should get the Profile of - // the focused pane, and use that to build a new instance of the - // settings so we can duplicate this tab/pane. - // - // Currently, if the profile doesn't exist anymore in our - // settings, we'll silently do nothing. - // - // In the future, it will be preferable to just duplicate the - // current control's settings, but we can't do that currently, - // because we won't be able to create a new instance of the - // connection without keeping an instance of the original Profile - // object around. - - const auto& profileGuid = terminalTab->GetFocusedProfile(); - if (profileGuid.has_value()) - { - const auto settings{ winrt::make(_settings, profileGuid.value(), *_bindings) }; - _CreateNewTabFromSettings(profileGuid.value(), settings); - } + const auto settings{ winrt::make(_settings, profileGuid.value(), *_bindings) }; + _CreateNewTabFromSettings(profileGuid.value(), settings); } - CATCH_LOG(); } + CATCH_LOG(); } } @@ -1319,14 +1315,6 @@ namespace winrt::TerminalApp::implementation // - Sets focus to the tab to the right or left the currently selected tab. void TerminalPage::_SelectNextTab(const bool bMoveRight) { - if (CommandPalette().Visibility() == Visibility::Visible) - { - // If the tab switcher is currently open, don't change its mode. - // Just select the new tab. - CommandPalette().SelectNextItem(bMoveRight); - return; - } - const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode(); const bool useInOrderTabIndex = tabSwitchMode != TabSwitcherMode::MostRecentlyUsed; @@ -1418,20 +1406,17 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_UnZoomIfNeeded() { - if (auto focusedTab = _GetFocusedTab()) + if (const auto activeTab{ _GetFocusedTabImpl() }) { - if (auto activeTab = _GetTerminalTabImpl(focusedTab)) + if (activeTab->IsZoomed()) { - if (activeTab->IsZoomed()) - { - // Remove the content from the tab first, so Pane::UnZoom can - // re-attach the content to the tree w/in the pane - _tabContent.Children().Clear(); - // In ExitZoom, we'll change the Tab's Content(), triggering the - // content changed event, which will re-attach the tab's new content - // root to the tree. - activeTab->ExitZoom(); - } + // Remove the content from the tab first, so Pane::UnZoom can + // re-attach the content to the tree w/in the pane + _tabContent.Children().Clear(); + // In ExitZoom, we'll change the Tab's Content(), triggering the + // content changed event, which will re-attach the tab's new content + // root to the tree. + activeTab->ExitZoom(); } } } @@ -1446,24 +1431,18 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_MoveFocus(const FocusDirection& direction) { - if (auto index{ _GetFocusedTabIndex() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) - { - _UnZoomIfNeeded(); - terminalTab->NavigateFocus(direction); - } + _UnZoomIfNeeded(); + terminalTab->NavigateFocus(direction); } } TermControl TerminalPage::_GetActiveControl() { - if (auto index{ _GetFocusedTabIndex() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) - { - return terminalTab->GetActiveTerminalControl(); - } + return terminalTab->GetActiveTerminalControl(); } return nullptr; } @@ -1488,7 +1467,7 @@ namespace winrt::TerminalApp::implementation // Method Description: // - returns a com_ptr to the currently focused tab. This might return null, // so make sure to check the result! - winrt::TerminalApp::TabBase TerminalPage::_GetFocusedTab() + winrt::TerminalApp::TabBase TerminalPage::_GetFocusedTab() const noexcept { if (auto index{ _GetFocusedTabIndex() }) { @@ -1497,6 +1476,18 @@ namespace winrt::TerminalApp::implementation return nullptr; } + // Method Description: + // - returns a com_ptr to the currently focused tab implementation. This might return null, + // so make sure to check the result! + winrt::com_ptr TerminalPage::_GetFocusedTabImpl() const noexcept + { + if (auto tab{ _GetFocusedTab() }) + { + return _GetTerminalTabImpl(tab); + } + return nullptr; + } + // Method Description: // - An async method for changing the focused tab on the UI thread. This // method will _only_ set the selected item of the TabView, which will @@ -1538,13 +1529,10 @@ namespace winrt::TerminalApp::implementation // tab's Closed event. void TerminalPage::_CloseFocusedPane() { - if (auto index{ _GetFocusedTabIndex() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) - { - _UnZoomIfNeeded(); - terminalTab->ClosePane(); - } + _UnZoomIfNeeded(); + terminalTab->ClosePane(); } } @@ -1587,26 +1575,23 @@ namespace winrt::TerminalApp::implementation // - rowsToScroll: a number of lines to move the viewport. If not provided we will use a system default. void TerminalPage::_Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference& rowsToScroll) { - if (auto index{ _GetFocusedTabIndex() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) + uint32_t realRowsToScroll; + if (rowsToScroll == nullptr) { - uint32_t realRowsToScroll; - if (rowsToScroll == nullptr) - { - // The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page - realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ? - terminalTab->GetActiveTerminalControl().GetViewHeight() : - _systemRowsToScroll; - } - else - { - // use the custom value specified in the command - realRowsToScroll = rowsToScroll.Value(); - } - auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll); - terminalTab->Scroll(scrollDelta); + // The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page + realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ? + terminalTab->GetActiveTerminalControl().GetViewHeight() : + _systemRowsToScroll; } + else + { + // use the custom value specified in the command + realRowsToScroll = rowsToScroll.Value(); + } + auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll); + terminalTab->Scroll(scrollDelta); } } @@ -1632,17 +1617,9 @@ namespace winrt::TerminalApp::implementation return; } - auto indexOpt = _GetFocusedTabIndex(); + const auto focusedTab{ _GetFocusedTabImpl() }; - // Do nothing if for some reason, there's no tab in focus. We don't want to crash. - if (!indexOpt) - { - return; - } - - auto focusedTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt)); - - // Do nothing if the focused tab isn't a TerminalTab + // Do nothing if no TerminalTab is focused if (!focusedTab) { return; @@ -1721,13 +1698,10 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_ResizePane(const ResizeDirection& direction) { - if (auto index{ _GetFocusedTabIndex() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) - { - _UnZoomIfNeeded(); - terminalTab->ResizePane(direction); - } + _UnZoomIfNeeded(); + terminalTab->ResizePane(direction); } } @@ -1738,14 +1712,8 @@ namespace winrt::TerminalApp::implementation // - scrollDirection: ScrollUp will move the viewport up, ScrollDown will move the viewport down void TerminalPage::_ScrollPage(ScrollDirection scrollDirection) { - auto indexOpt = _GetFocusedTabIndex(); - // Do nothing if for some reason, there's no tab in focus. We don't want to crash. - if (!indexOpt) - { - return; - } - - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt))) + // Do nothing if for some reason, there's no terminal tab in focus. We don't want to crash. + if (const auto terminalTab{ _GetFocusedTabImpl() }) { const auto control = _GetActiveControl(); const auto termHeight = control.GetViewHeight(); @@ -1756,13 +1724,10 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_ScrollToBufferEdge(ScrollDirection scrollDirection) { - if (const auto indexOpt = _GetFocusedTabIndex()) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt))) - { - auto scrollDelta = _ComputeScrollDelta(scrollDirection, INT_MAX); - terminalTab->Scroll(scrollDelta); - } + auto scrollDelta = _ComputeScrollDelta(scrollDirection, INT_MAX); + terminalTab->Scroll(scrollDelta); } } @@ -1872,12 +1837,9 @@ namespace winrt::TerminalApp::implementation { if (_settings && _settings.GlobalSettings().SnapToGridOnResize()) { - if (auto index{ _GetFocusedTabIndex() }) + if (const auto terminalTab{ _GetFocusedTabImpl() }) { - if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) - { - return terminalTab->CalcSnappedDimension(widthOrHeight, dimension); - } + return terminalTab->CalcSnappedDimension(widthOrHeight, dimension); } } return dimension; @@ -2234,7 +2196,7 @@ namespace winrt::TerminalApp::implementation // here, then the user won't be able to navigate the ATS any // longer. // - // When the tab swither is eventually dismissed, the focus will + // When the tab switcher is eventually dismissed, the focus will // get tossed back to the focused terminal control, so we don't // need to worry about focus getting lost. if (CommandPalette().Visibility() != Visibility::Visible) @@ -2883,7 +2845,7 @@ namespace winrt::TerminalApp::implementation // Return Value: // - If the tab is a TerminalTab, a com_ptr to the implementation type. // If the tab is not a TerminalTab, nullptr - winrt::com_ptr TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) const + winrt::com_ptr TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) { if (auto terminalTab = tab.try_as()) { diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index e80549cee5a..58709e86e8d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -81,6 +81,8 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); + winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); + // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); @@ -111,7 +113,7 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Collections::IObservableVector _tabs; Windows::Foundation::Collections::IVector _mruTabs; - winrt::com_ptr _GetTerminalTabImpl(const TerminalApp::TabBase& tab) const; + static winrt::com_ptr _GetTerminalTabImpl(const TerminalApp::TabBase& tab); void _UpdateTabIndices(); @@ -138,7 +140,6 @@ namespace winrt::TerminalApp::implementation StartupState _startupState{ StartupState::NotInitialized }; Windows::Foundation::Collections::IVector _startupActions; - winrt::fire_and_forget _ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); void _ShowAboutDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseWarningDialog(); @@ -183,7 +184,9 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::TerminalControl::TermControl _GetActiveControl(); std::optional _GetFocusedTabIndex() const noexcept; - TerminalApp::TabBase _GetFocusedTab(); + TerminalApp::TabBase _GetFocusedTab() const noexcept; + winrt::com_ptr _GetFocusedTabImpl() const noexcept; + winrt::fire_and_forget _SetFocusedTabIndex(const uint32_t tabIndex); void _CloseFocusedTab(); void _CloseFocusedPane(); @@ -251,12 +254,9 @@ namespace winrt::TerminalApp::implementation void _OpenSettingsUI(); - void _MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index); - static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll); static uint32_t _ReadSystemRowsToScroll(); - void _UpdateTabSwitcherCommands(const bool mru); void _UpdateMRUTab(const uint32_t index); void _TryMoveTab(const uint32_t currentTabIndex, const int32_t suggestedNewTabIndex); diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj index 585d0e8faab..958593eae5e 100644 --- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj +++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj @@ -81,6 +81,5 @@ $(OpenConsoleCommonOutDir)\conptylib.lib;%(AdditionalDependencies) - diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 85f878b2097..62e5f50d6c6 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1344,8 +1344,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto ptr = args.Pointer(); const auto point = args.GetCurrentPoint(*this); + const auto cursorPosition = point.Position(); + const auto terminalPosition = _GetTerminalPosition(cursorPosition); - if (!_focused) + if (!_focused && (_terminal->GetHyperlinkAtPosition(terminalPosition).empty())) { args.Handled(true); return; @@ -1364,8 +1366,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { auto lock = _terminal->LockForWriting(); - const auto cursorPosition = point.Position(); - if (_singleClickTouchdownPos) { // Figure out if the user's moved a quarter of a cell's smaller axis away from the clickdown point @@ -1407,10 +1407,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _TryStopAutoScroll(ptr.PointerId()); } } - const auto terminalPos = _GetTerminalPosition(point.Position()); - if (terminalPos != _lastHoveredCell) + + if (terminalPosition != _lastHoveredCell) { - const auto uri = _terminal->GetHyperlinkAtPosition(terminalPos); + const auto uri = _terminal->GetHyperlinkAtPosition(terminalPosition); if (!uri.empty()) { // Update the tooltip with the URI @@ -1425,7 +1425,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Compute the location of the top left corner of the cell in DIPS const til::size marginsInDips{ til::math::rounding, GetPadding().Left, GetPadding().Top }; - const til::point startPos{ terminalPos.X, terminalPos.Y }; + const til::point startPos{ terminalPosition.X, terminalPosition.Y }; const til::size fontSize{ _actualFont.GetSize() }; const til::point posInPixels{ startPos * fontSize }; const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() }; @@ -1435,10 +1435,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), (locationInDIPs.x() - SwapChainPanel().ActualOffset().x)); OverlayCanvas().SetTop(HyperlinkTooltipBorder(), (locationInDIPs.y() - SwapChainPanel().ActualOffset().y)); } - _lastHoveredCell = terminalPos; + _lastHoveredCell = terminalPosition; - const auto newId = _terminal->GetHyperlinkIdAtPosition(terminalPos); - const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPos); + const auto newId = _terminal->GetHyperlinkIdAtPosition(terminalPosition); + const auto newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPosition); // If the hyperlink ID changed or the interval changed, trigger a redraw all // (so this will happen both when we move onto a link and when we move off a link) if (newId != _lastHoveredId || (newInterval != _lastHoveredInterval)) diff --git a/src/cascadia/TerminalSettingsEditor/CommonResources.xaml b/src/cascadia/TerminalSettingsEditor/CommonResources.xaml index 313a3e8b1e7..25e83dd58df 100644 --- a/src/cascadia/TerminalSettingsEditor/CommonResources.xaml +++ b/src/cascadia/TerminalSettingsEditor/CommonResources.xaml @@ -6,7 +6,10 @@ 15.0 20,0,0,0 0,0,0,20 - 200 + 250 + + 10,0,0,0 + 0,10,0,0 diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp index a417779e8c1..fb6b12a449f 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp +++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp @@ -76,7 +76,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { if (const auto tag{ navViewItem.Tag() }) { - if (tag.try_as()) + if (tag.try_as()) { // hide NavViewItem pointing to a Profile navViewItem.Visibility(Visibility::Collapsed); @@ -104,13 +104,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { if (const auto tag{ selectedItem.as().Tag() }) { - if (const auto profile{ tag.try_as() }) + if (const auto oldProfile{ tag.try_as() }) { // check if the profile still exists - if (_settingsClone.FindProfile(profile.Guid())) + if (const auto profile{ _settingsClone.FindProfile(oldProfile.Guid()) }) { // Navigate to the page with the given profile - contentFrame().Navigate(xaml_typename(), winrt::make(_viewModelForProfile(profile), _settingsClone.GlobalSettings().ColorSchemes(), *this)); + _Navigate(_viewModelForProfile(profile)); return; } } @@ -126,7 +126,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // could not find the page we were on, fallback to first menu item const auto firstItem{ navigationMenu.MenuItems().GetAt(0) }; navigationMenu.SelectedItem(firstItem); - if (const auto tag{ navigationMenu.SelectedItem().as().Tag() }) + if (const auto tag{ navigationMenu.SelectedItem().as().Tag() }) { _Navigate(unbox_value(tag)); } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.xaml b/src/cascadia/TerminalSettingsEditor/Profiles.xaml index 501e237a950..4aeb4dd2169 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles.xaml @@ -34,7 +34,7 @@ the MIT License. See LICENSE in the project root for license information. --> - + @@ -42,16 +42,17 @@ the MIT License. See LICENSE in the project root for license information. --> + Grid.Row="1" + Margin="{StaticResource PivotIndentMargin}"> - + @@ -530,7 +531,7 @@ the MIT License. See LICENSE in the project root for license information. --> - + { OpenSettingsArgs() = default; + OpenSettingsArgs(const SettingsTarget& target) : + _Target{ target } {} GETSET_PROPERTY(SettingsTarget, Target, SettingsTarget::SettingsFile); static constexpr std::string_view TargetKey{ "target" }; @@ -850,4 +852,5 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation BASIC_FACTORY(CloseOtherTabsArgs); BASIC_FACTORY(CloseTabsAfterArgs); BASIC_FACTORY(MoveTabArgs); + BASIC_FACTORY(OpenSettingsArgs); } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 2c539babe3d..e4413fbd452 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -147,6 +147,7 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass OpenSettingsArgs : IActionArgs { + OpenSettingsArgs(SettingsTarget target); SettingsTarget Target { get; }; }; diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index da183b249dc..88ea5ac30ed 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -363,4 +363,7 @@ Break into the debugger + + Open Settings... + diff --git a/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj new file mode 100644 index 00000000000..a151a594027 --- /dev/null +++ b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj @@ -0,0 +1,78 @@ + + + + + + + {68a10cd3-aa64-465b-af5f-ed4e9700543c} + Win32Proj + RemotingUnitTests + UnitTests_Remoting + Remoting.UnitTests + DynamicLibrary + 10.0.18362.0 + 10.0.18362.0 + true + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + ..;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\jsoncpp\json;$(OpenConsoleDir)src\inc;$(OpenConsoleDir)src\inc\test;$(WinRT_IncludePath)\..\cppwinrt\winrt;"$(OpenConsoleDir)\src\cascadia\Remoting\Generated Files";%(AdditionalIncludeDirectories) + pch.h + + + 4702;%(DisableSpecificWarnings) + + + onecoreuap.lib;%(AdditionalDependencies) + + + + + true + true + + + + + + + + diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp new file mode 100644 index 00000000000..396482ecc03 --- /dev/null +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "../Remoting/Monarch.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; + +// These are some gross macros that let us call a private ctor for +// Monarch/Peasant. We can't just use make_self, because that doesn't let us +// call a private ctor. We can use com_ptr::attach, but since we're allocating +// the thing on the stack, we need to make sure to call detach before the object +// is destructed. + +#define MAKE_MONARCH(name, pid) \ + Remoting::implementation::Monarch _local_##name##{ pid }; \ + com_ptr name; \ + name.attach(&_local_##name##); \ + auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); }); + +#define MAKE_PEASANT(name, pid) \ + Remoting::implementation::Peasant _local_##name##{ pid }; \ + com_ptr name; \ + name.attach(&_local_##name##); \ + auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); }); + +namespace RemotingUnitTests +{ + class RemotingTests + { + BEGIN_TEST_CLASS(RemotingTests) + END_TEST_CLASS() + + TEST_METHOD(CreateMonarch); + TEST_METHOD(CreatePeasant); + + TEST_CLASS_SETUP(ClassSetup) + { + return true; + } + }; + + void RemotingTests::CreateMonarch() + { + auto m1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(m1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + m1->GetPID(), + L"A Monarch without an explicit PID should use the current PID"); + + Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); + + auto expectedFakePID = 1234u; + MAKE_MONARCH(m2, expectedFakePID); + + VERIFY_IS_NOT_NULL(m2); + VERIFY_ARE_EQUAL(expectedFakePID, + m2->GetPID(), + L"A Monarch with an explicit PID should use the one we provided"); + } + + void RemotingTests::CreatePeasant() + { + auto p1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(p1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + p1->GetPID(), + L"A Peasant without an explicit PID should use the current PID"); + + Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); + + auto expectedFakePID = 2345u; + MAKE_PEASANT(p2, expectedFakePID); + + VERIFY_IS_NOT_NULL(p2); + VERIFY_ARE_EQUAL(expectedFakePID, + p2->GetPID(), + L"A Peasant with an explicit PID should use the one we provided"); + } + +} diff --git a/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def b/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def new file mode 100644 index 00000000000..8c1a02932d0 --- /dev/null +++ b/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def @@ -0,0 +1,3 @@ +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 new file mode 100644 index 00000000000..3c27d44d570 --- /dev/null +++ b/src/cascadia/UnitTests_Remoting/pch.cpp @@ -0,0 +1,4 @@ +// 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 new file mode 100644 index 00000000000..da79b2d4fd8 --- /dev/null +++ b/src/cascadia/UnitTests_Remoting/pch.h @@ -0,0 +1,39 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ + +#pragma once + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#define BLOCK_TIL +// This includes support libraries from the CRT, STL, WIL, and GSL +#include "LibraryIncludes.h" +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#include +#include +#include + +#include +#include "consoletaeftemplates.hpp" + +#include +#include +#include +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" + +// Common includes for most tests: +#include "../../inc/argb.h" +#include "../../inc/conattrs.hpp" +#include "../../types/inc/utils.hpp" +#include "../../inc/DefaultSettings.h" diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a97ba3f46be..57043b0ed13 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -8,13 +8,12 @@ #include "../types/inc/User32Utils.hpp" #include "resource.h" -#include - using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Composition; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Hosting; using namespace winrt::Windows::Foundation::Numerics; +using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace ::Microsoft::Console; using namespace ::Microsoft::Console::Types; @@ -25,17 +24,24 @@ static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; AppHost::AppHost() noexcept : _app{}, + _windowManager{}, _logic{ nullptr }, // don't make one, we're going to take a ref on app's _window{ nullptr } { _logic = _app.Logic(); // get a ref to app's logic - _useNonClientArea = _logic.GetShowTabsInTitlebar(); - // If there were commandline args to our process, try and process them here. - // Do this before AppLogic::Create, otherwise this will have no effect + // Do this before AppLogic::Create, otherwise this will have no effect. + // + // This will send our commandline to the Monarch, to ask if we should make a + // new window or not. If not, exit immediately. _HandleCommandlineArgs(); + if (!_shouldCreateWindow) + { + return; + } + _useNonClientArea = _logic.GetShowTabsInTitlebar(); if (_useNonClientArea) { _window = std::make_unique(_logic.GetRequestedTheme()); @@ -65,6 +71,7 @@ AppHost::AppHost() noexcept : AppHost::~AppHost() { // destruction order is important for proper teardown here + _window = nullptr; _app.Close(); _app = nullptr; @@ -96,9 +103,35 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& } } +void _buildArgsFromCommandline(std::vector& args) +{ + if (auto commandline{ GetCommandLineW() }) + { + int argc = 0; + + // Get the argv, and turn them into a hstring array to pass to the app. + wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + if (argv) + { + for (auto& elem : wil::make_range(argv.get(), argc)) + { + args.emplace_back(elem); + } + } + } + if (args.empty()) + { + args.emplace_back(L"wt.exe"); + } +} + // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to -// the app logic for processing. +// the WindowManager, to ask if we should become a window process. +// - If we should create a window, then pass the arguments to the app logic for +// processing. +// - If we shouldn't become a window, set _shouldCreateWindow to false and exit +// immediately. // - If the logic determined there's an error while processing that commandline, // display a message box to the user with the text of the error, and exit. // * We display a message box because we're a Win32 application (not a @@ -111,21 +144,24 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& // - void AppHost::_HandleCommandlineArgs() { - if (auto commandline{ GetCommandLineW() }) + std::vector args; + _buildArgsFromCommandline(args); + std::wstring cwd{ wil::GetCurrentDirectoryW() }; + + Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; + _windowManager.ProposeCommandline(eventArgs); + + _shouldCreateWindow = _windowManager.ShouldCreateWindow(); + if (!_shouldCreateWindow) { - int argc = 0; + return; + } - // Get the argv, and turn them into a hstring array to pass to the app. - wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - if (argv) + if (auto peasant{ _windowManager.CurrentWindow() }) + { + if (auto args{ peasant.InitialArgs() }) { - std::vector args; - for (auto& elem : wil::make_range(argv.get(), argc)) - { - args.emplace_back(elem); - } - - const auto result = _logic.SetStartupCommandline({ args }); + const auto result = _logic.SetStartupCommandline(args.Args()); const auto message = _logic.ParseCommandlineMessage(); if (!message.empty()) { @@ -145,7 +181,19 @@ void AppHost::_HandleCommandlineArgs() } } } + + // After handling the initial args, hookup the callback for handling + // future commandline invocations. When our peasant is told to execute a + // commandline (in the future), it'll trigger this callback, that we'll + // use to send the actions to the app. + peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); } + + // TODO:projects/5 if we end up not creating a new window, we crash. I'm + // thinking this is because the XAML host is not happy about being torn + // down before it has a chance to do really anything. Is there some way + // to get the app logic without instantiating the entire app? or at + // least the parts we'll need for remoting? } // Method Description: @@ -462,3 +510,14 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta) } } } + +bool AppHost::HasWindow() +{ + return _shouldCreateWindow; +} + +void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, + winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) +{ + _logic.ExecuteCommandline(args.Args()); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 25de688131b..18c175d1757 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -3,9 +3,6 @@ #include "pch.h" -#include -#include - #include "NonClientIslandWindow.h" class AppHost @@ -20,12 +17,16 @@ class AppHost bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + bool HasWindow(); + private: bool _useNonClientArea; std::unique_ptr _window; winrt::TerminalApp::App _app; winrt::TerminalApp::AppLogic _logic; + bool _shouldCreateWindow{ false }; + winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; void _HandleCommandlineArgs(); @@ -43,4 +44,7 @@ class AppHost void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const til::point coord, const int32_t delta); + + void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, + winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); }; diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index c66d6c80333..aa45f987aba 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -77,6 +77,7 @@ + @@ -98,7 +99,7 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. @@ -106,7 +107,7 @@ - + diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index 726ded30444..91188aaca5d 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -124,6 +124,10 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) // Terminal App. This MUST BE constructed before the Xaml manager as TermApp // provides an implementation of Windows.UI.Xaml.Application. AppHost host; + if (!host.HasWindow()) + { + return 0; + } // Initialize the xaml content. This must be called AFTER the // WindowsXamlManager is initialized. diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index e91aeada6e6..b416ccccc4f 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -61,6 +61,11 @@ Module Name: #include #include +#include +#include +#include +#include + #include #include diff --git a/src/host/precomp.h b/src/host/precomp.h index d1a701d642d..507262a6f8a 100644 --- a/src/host/precomp.h +++ b/src/host/precomp.h @@ -88,8 +88,6 @@ TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider); #include "../inc/operators.hpp" #include "../inc/conattrs.hpp" -#include "boost/container/small_vector.hpp" - // TODO: MSFT 9355094 Find a better way of doing this. http://osgvsowi/9355094 [[nodiscard]] inline NTSTATUS NTSTATUS_FROM_HRESULT(HRESULT hr) { diff --git a/src/host/tracing.cpp b/src/host/tracing.cpp index 3511d31cced..aa3a9973b33 100644 --- a/src/host/tracing.cpp +++ b/src/host/tracing.cpp @@ -210,13 +210,13 @@ void Tracing::s_TraceApi(const CONSOLE_SCREENBUFFERINFO_MSG* const a) static_assert(sizeof(UINT32) == sizeof(*a->ColorTable), "a->ColorTable"); } -void Tracing::s_TraceApi(const CONSOLE_MODE_MSG* const a, const std::wstring& handleType) +void Tracing::s_TraceApi(const CONSOLE_MODE_MSG* const a, const std::wstring_view handleType) { TraceLoggingWrite( g_hConhostV2EventTraceProvider, "API_GetConsoleMode", TraceLoggingHexUInt32(a->Mode, "Mode"), - TraceLoggingWideString(handleType.c_str(), "Handle type"), + TraceLoggingCountedWideString(handleType.data(), gsl::narrow_cast(handleType.size()), "Handle type"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::API)); } diff --git a/src/host/tracing.hpp b/src/host/tracing.hpp index a44476b4acd..40744ec211d 100644 --- a/src/host/tracing.hpp +++ b/src/host/tracing.hpp @@ -50,7 +50,7 @@ class Tracing static void s_TraceApi(_In_ const void* const buffer, const CONSOLE_WRITECONSOLE_MSG* const a); static void s_TraceApi(const CONSOLE_SCREENBUFFERINFO_MSG* const a); - static void s_TraceApi(const CONSOLE_MODE_MSG* const a, const std::wstring& handleType); + static void s_TraceApi(const CONSOLE_MODE_MSG* const a, const std::wstring_view handleType); static void s_TraceApi(const CONSOLE_SETTEXTATTRIBUTE_MSG* const a); static void s_TraceApi(const CONSOLE_WRITECONSOLEOUTPUTSTRING_MSG* const a); diff --git a/src/inc/LibraryIncludes.h b/src/inc/LibraryIncludes.h index 1e2510f0690..3f203682cf2 100644 --- a/src/inc/LibraryIncludes.h +++ b/src/inc/LibraryIncludes.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,9 @@ #include #pragma warning(pop) +// Boost +#include "boost/container/small_vector.hpp" + // IntSafe #define ENABLE_INTSAFE_SIGNED_FUNCTIONS #include diff --git a/src/interactivity/win32/SystemConfigurationProvider.cpp b/src/interactivity/win32/SystemConfigurationProvider.cpp index 175f05a6d83..af37c4c5cd9 100644 --- a/src/interactivity/win32/SystemConfigurationProvider.cpp +++ b/src/interactivity/win32/SystemConfigurationProvider.cpp @@ -86,13 +86,12 @@ void SystemConfigurationProvider::GetSettingsFromLink( CONSOLE_STATE_INFO csi = pLinkSettings->CreateConsoleStateInfo(); csi.LinkTitle = linkNameForCsi; - WCHAR wszShortcutTitle[MAX_PATH]; - BOOL fReadConsoleProperties; + WCHAR wszShortcutTitle[MAX_PATH] = L"\0"; + BOOL fReadConsoleProperties = FALSE; WORD wShowWindow = pLinkSettings->GetShowWindow(); DWORD dwHotKey = pLinkSettings->GetHotKey(); - - int iShowWindow; - WORD wHotKey; + int iShowWindow = 0; + WORD wHotKey = 0; NTSTATUS Status = ShortcutSerialization::s_GetLinkValues(&csi, &fReadConsoleProperties, wszShortcutTitle, @@ -105,19 +104,22 @@ void SystemConfigurationProvider::GetSettingsFromLink( &iShowWindow, &wHotKey); - // Convert results back to appropriate types and set. - if (SUCCEEDED(IntToWord(iShowWindow, &wShowWindow))) + if (NT_SUCCESS(Status)) { - pLinkSettings->SetShowWindow(wShowWindow); - } + // Convert results back to appropriate types and set. + if (SUCCEEDED(IntToWord(iShowWindow, &wShowWindow))) + { + pLinkSettings->SetShowWindow(wShowWindow); + } - dwHotKey = wHotKey; - pLinkSettings->SetHotKey(dwHotKey); + dwHotKey = wHotKey; + pLinkSettings->SetHotKey(dwHotKey); - if (wszLinkTarget[0] != L'\0') - { - // guarantee null termination to make OACR happy. - wszLinkTarget[ARRAYSIZE(wszLinkTarget) - 1] = L'\0'; + if (wszLinkTarget[0] != L'\0') + { + // guarantee null termination to make OACR happy. + wszLinkTarget[ARRAYSIZE(wszLinkTarget) - 1] = L'\0'; + } } // if we got a title, use it. even on overall link value load failure, the title will be correct if diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 0d7f0b36108..3038675efae 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -126,6 +126,13 @@ namespace Microsoft::Console::Render COLORREF _lastBg; bool _lastFontItalic; + // Memory pooling to save alloc/free work to the OS for things + // frequently created and dropped. + // It's important the pool is first so it can be given to the others on construction. + std::pmr::unsynchronized_pool_resource _pool; + std::pmr::vector _polyStrings; + std::pmr::vector> _polyWidths; + [[nodiscard]] HRESULT _InvalidCombine(const RECT* const prc) noexcept; [[nodiscard]] HRESULT _InvalidOffset(const POINT* const ppt) noexcept; [[nodiscard]] HRESULT _InvalidRestrict() noexcept; diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 1e07be9521f..0885b975572 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -308,13 +308,11 @@ using namespace Microsoft::Console::Render; const auto pPolyTextLine = &_pPolyText[_cPolyText]; - auto pwsPoly = std::make_unique(cchLine); - RETURN_IF_NULL_ALLOC(pwsPoly); + auto& polyString = _polyStrings.emplace_back(cchLine, UNICODE_NULL); COORD const coordFontSize = _GetFontSize(); - auto rgdxPoly = std::make_unique(cchLine); - RETURN_IF_NULL_ALLOC(rgdxPoly); + auto& polyWidth = _polyWidths.emplace_back(cchLine, 0); // Sum up the total widths the entire line/run is expected to take while // copying the pixel widths into a structure to direct GDI how many pixels to use per character. @@ -327,9 +325,9 @@ using namespace Microsoft::Console::Render; // Our GDI renderer hasn't and isn't going to handle things above U+FFFF or sequences. // So replace anything complicated with a replacement character for drawing purposes. - pwsPoly[i] = cluster.GetTextAsSingle(); - rgdxPoly[i] = gsl::narrow(cluster.GetColumns()) * coordFontSize.X; - cchCharWidths += rgdxPoly[i]; + polyString[i] = cluster.GetTextAsSingle(); + polyWidth[i] = gsl::narrow(cluster.GetColumns()) * coordFontSize.X; + cchCharWidths += polyWidth[i]; } // Detect and convert for raster font... @@ -338,7 +336,7 @@ using namespace Microsoft::Console::Render; // dispatch conversion into our codepage // Find out the bytes required - int const cbRequired = WideCharToMultiByte(_fontCodepage, 0, pwsPoly.get(), (int)cchLine, nullptr, 0, nullptr, nullptr); + int const cbRequired = WideCharToMultiByte(_fontCodepage, 0, polyString.data(), (int)cchLine, nullptr, 0, nullptr, nullptr); if (cbRequired != 0) { @@ -346,7 +344,7 @@ using namespace Microsoft::Console::Render; auto psConverted = std::make_unique(cbRequired); // Attempt conversion to current codepage - int const cbConverted = WideCharToMultiByte(_fontCodepage, 0, pwsPoly.get(), (int)cchLine, psConverted.get(), cbRequired, nullptr, nullptr); + int const cbConverted = WideCharToMultiByte(_fontCodepage, 0, polyString.data(), (int)cchLine, psConverted.get(), cbRequired, nullptr, nullptr); // If successful... if (cbConverted != 0) @@ -356,22 +354,22 @@ using namespace Microsoft::Console::Render; if (cchRequired != 0) { - auto pwsConvert = std::make_unique(cchRequired); + std::pmr::wstring polyConvert(cchRequired, UNICODE_NULL, &_pool); // Then do the actual conversion. - int const cchConverted = MultiByteToWideChar(CP_ACP, 0, psConverted.get(), cbRequired, pwsConvert.get(), cchRequired); + int const cchConverted = MultiByteToWideChar(CP_ACP, 0, psConverted.get(), cbRequired, polyConvert.data(), cchRequired); if (cchConverted != 0) { // If all successful, use this instead. - pwsPoly.swap(pwsConvert); + polyString.swap(polyConvert); } } } } } - pPolyTextLine->lpstr = pwsPoly.release(); + pPolyTextLine->lpstr = polyString.data(); pPolyTextLine->n = gsl::narrow(clusters.size()); pPolyTextLine->x = ptDraw.x; pPolyTextLine->y = ptDraw.y; @@ -380,7 +378,7 @@ using namespace Microsoft::Console::Render; pPolyTextLine->rcl.top = pPolyTextLine->y; pPolyTextLine->rcl.right = pPolyTextLine->rcl.left + ((SHORT)cchCharWidths * coordFontSize.X); pPolyTextLine->rcl.bottom = pPolyTextLine->rcl.top + coordFontSize.Y; - pPolyTextLine->pdx = rgdxPoly.release(); + pPolyTextLine->pdx = polyWidth.data(); if (trimLeft) { @@ -417,20 +415,10 @@ using namespace Microsoft::Console::Render; hr = E_FAIL; } - for (size_t iPoly = 0; iPoly < _cPolyText; iPoly++) - { - if (nullptr != _pPolyText[iPoly].lpstr) - { - delete[] _pPolyText[iPoly].lpstr; - _pPolyText[iPoly].lpstr = nullptr; - } + _polyStrings.clear(); + _polyWidths.clear(); - if (nullptr != _pPolyText[iPoly].pdx) - { - delete[] _pPolyText[iPoly].pdx; - _pPolyText[iPoly].pdx = nullptr; - } - } + ZeroMemory(_pPolyText, sizeof(_pPolyText)); _cPolyText = 0; } diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index a164cd63e9d..acea2d360cf 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -33,7 +33,10 @@ GdiEngine::GdiEngine() : _fPaintStarted(false), _invalidCharacters{}, _hfont(nullptr), - _hfontItalic(nullptr) + _hfontItalic(nullptr), + _pool{}, // It's important the pool is first so it can be given to the others on construction. + _polyStrings{ &_pool }, + _polyWidths{ &_pool } { ZeroMemory(_pPolyText, sizeof(POLYTEXTW) * s_cPolyTextCache); _rcInvalid = { 0 }; diff --git a/src/server/ApiDispatchers.cpp b/src/server/ApiDispatchers.cpp index db0134d482f..8032bd61fd5 100644 --- a/src/server/ApiDispatchers.cpp +++ b/src/server/ApiDispatchers.cpp @@ -35,7 +35,7 @@ { Telemetry::Instance().LogApiCall(Telemetry::ApiCall::GetConsoleMode); CONSOLE_MODE_MSG* const a = &m->u.consoleMsgL1.GetConsoleMode; - std::wstring handleType = L"unknown"; + std::wstring_view handleType = L"unknown"; TraceLoggingWrite(g_hConhostV2EventTraceProvider, "API_GetConsoleMode", @@ -54,14 +54,14 @@ RETURN_HR_IF_NULL(E_HANDLE, pObjectHandle); if (pObjectHandle->IsInputHandle()) { - handleType = L"input handle"; + handleType = L"input"; InputBuffer* pObj; RETURN_IF_FAILED(pObjectHandle->GetInputBuffer(GENERIC_READ, &pObj)); m->_pApiRoutines->GetConsoleInputModeImpl(*pObj, a->Mode); } else { - handleType = L"output handle"; + handleType = L"output"; SCREEN_INFORMATION* pObj; RETURN_IF_FAILED(pObjectHandle->GetScreenBuffer(GENERIC_READ, &pObj)); m->_pApiRoutines->GetConsoleOutputModeImpl(*pObj, a->Mode); diff --git a/src/server/ApiMessage.cpp b/src/server/ApiMessage.cpp index 88ce1e67bb4..439778656b5 100644 --- a/src/server/ApiMessage.cpp +++ b/src/server/ApiMessage.cpp @@ -9,10 +9,17 @@ #include "DeviceComm.h" _CONSOLE_API_MSG::_CONSOLE_API_MSG() : + Complete{ 0 }, + State{ 0 }, _pDeviceComm(nullptr), - _pApiRoutines(nullptr) + _pApiRoutines(nullptr), + _inputBuffer{}, + _outputBuffer{}, + Descriptor{ 0 }, + CreateObject{ 0 }, + CreateScreenBuffer{ 0 }, + msgHeader{ 0 } { - ZeroMemory(this, sizeof(_CONSOLE_API_MSG)); } ConsoleProcessHandle* _CONSOLE_API_MSG::GetProcessHandle() const @@ -57,6 +64,7 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const // - HRESULT indicating if the input buffer was successfully retrieved. [[nodiscard]] HRESULT _CONSOLE_API_MSG::GetInputBuffer(_Outptr_result_bytebuffer_(*pcbSize) void** const ppvBuffer, _Out_ ULONG* const pcbSize) +try { // Initialize the buffer if it hasn't been initialized yet. if (State.InputBuffer == nullptr) @@ -65,12 +73,11 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const ULONG const cbReadSize = Descriptor.InputSize - State.ReadOffset; - wistd::unique_ptr pPayload = wil::make_unique_nothrow(cbReadSize); - RETURN_IF_NULL_ALLOC(pPayload); + _inputBuffer.resize(cbReadSize); - RETURN_IF_FAILED(ReadMessageInput(0, pPayload.get(), cbReadSize)); + RETURN_IF_FAILED(ReadMessageInput(0, _inputBuffer.data(), cbReadSize)); - State.InputBuffer = pPayload.release(); // TODO: MSFT: 9565140 - don't release, maintain as smart pointer. + State.InputBuffer = _inputBuffer.data(); State.InputBufferSize = cbReadSize; } @@ -80,6 +87,7 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const return S_OK; } +CATCH_RETURN(); // Routine Description: // - This routine retrieves the output buffer associated with this message. It will allocate one if needed. @@ -94,6 +102,7 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const [[nodiscard]] HRESULT _CONSOLE_API_MSG::GetAugmentedOutputBuffer(const ULONG cbFactor, _Outptr_result_bytebuffer_(*pcbSize) PVOID* const ppvBuffer, _Out_ PULONG pcbSize) +try { // Initialize the buffer if it hasn't been initialized yet. if (State.OutputBuffer == nullptr) @@ -103,11 +112,12 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const ULONG cbWriteSize = Descriptor.OutputSize - State.WriteOffset; RETURN_IF_FAILED(ULongMult(cbWriteSize, cbFactor, &cbWriteSize)); - BYTE* pPayload = new (std::nothrow) BYTE[cbWriteSize]; - RETURN_IF_NULL_ALLOC(pPayload); - ZeroMemory(pPayload, sizeof(BYTE) * cbWriteSize); + _outputBuffer.resize(cbWriteSize); - State.OutputBuffer = pPayload; // TODO: MSFT: 9565140 - maintain as smart pointer. + // 0 it out. + std::fill(_outputBuffer.begin(), _outputBuffer.end(), (BYTE)0); + + State.OutputBuffer = _outputBuffer.data(); State.OutputBufferSize = cbWriteSize; } @@ -117,6 +127,7 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const return S_OK; } +CATCH_RETURN(); // Routine Description: // - This routine retrieves the output buffer associated with this message. It will allocate one if needed. @@ -148,8 +159,9 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const if (State.InputBuffer != nullptr) { - delete[] static_cast(State.InputBuffer); + _inputBuffer.clear(); State.InputBuffer = nullptr; + State.InputBufferSize = 0; } if (State.OutputBuffer != nullptr) @@ -165,8 +177,9 @@ ConsoleHandleData* _CONSOLE_API_MSG::GetObjectHandle() const LOG_IF_FAILED(_pDeviceComm->WriteOutput(&IoOperation)); } - delete[] static_cast(State.OutputBuffer); + _outputBuffer.clear(); State.OutputBuffer = nullptr; + State.OutputBufferSize = 0; } return hr; diff --git a/src/server/ApiMessage.h b/src/server/ApiMessage.h index 29613fd41b5..8ff2857f3cb 100644 --- a/src/server/ApiMessage.h +++ b/src/server/ApiMessage.h @@ -35,6 +35,11 @@ typedef struct _CONSOLE_API_MSG IDeviceComm* _pDeviceComm; IApiRoutines* _pApiRoutines; +private: + boost::container::small_vector _inputBuffer; + boost::container::small_vector _outputBuffer; + +public: // From here down is the actual packet data sent/received. CD_IO_DESCRIPTOR Descriptor; union @@ -58,6 +63,10 @@ typedef struct _CONSOLE_API_MSG // End packet data public: + // DO NOT PUT MORE FIELDS DOWN HERE. + // The tail end of this structure will have a console driver packet + // copied over it and it will overwrite any fields declared here. + ConsoleProcessHandle* GetProcessHandle() const; ConsoleHandleData* GetObjectHandle() const; @@ -73,4 +82,8 @@ typedef struct _CONSOLE_API_MSG void SetReplyStatus(const NTSTATUS Status); void SetReplyInformation(const ULONG_PTR pInformation); + // DO NOT PUT MORE FIELDS DOWN HERE. + // The tail end of this structure will have a console driver packet + // copied over it and it will overwrite any fields declared here. + } CONSOLE_API_MSG, *PCONSOLE_API_MSG, * const PCCONSOLE_API_MSG; diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index fa2f86afd98..9ea8ff47c09 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -383,7 +383,10 @@ static constexpr bool _isActionableFromGround(const wchar_t wch) noexcept void StateMachine::_ActionExecute(const wchar_t wch) { _trace.TraceOnExecute(wch); - _engine->ActionExecute(wch); + const bool success = _engine->ActionExecute(wch); + + // Trace the result. + _trace.DispatchSequenceTrace(success); } // Routine Description: @@ -397,7 +400,11 @@ void StateMachine::_ActionExecute(const wchar_t wch) void StateMachine::_ActionExecuteFromEscape(const wchar_t wch) { _trace.TraceOnExecuteFromEscape(wch); - _engine->ActionExecuteFromEscape(wch); + + const bool success = _engine->ActionExecuteFromEscape(wch); + + // Trace the result. + _trace.DispatchSequenceTrace(success); } // Routine Description: @@ -409,7 +416,11 @@ void StateMachine::_ActionExecuteFromEscape(const wchar_t wch) void StateMachine::_ActionPrint(const wchar_t wch) { _trace.TraceOnAction(L"Print"); - _engine->ActionPrint(wch); + + const bool success = _engine->ActionPrint(wch); + + // Trace the result. + _trace.DispatchSequenceTrace(success); } // Routine Description: diff --git a/src/terminal/parser/tracing.cpp b/src/terminal/parser/tracing.cpp index 54f689017ea..727883e9bdf 100644 --- a/src/terminal/parser/tracing.cpp +++ b/src/terminal/parser/tracing.cpp @@ -76,7 +76,11 @@ void ParserTracing::TraceCharInput(const wchar_t wch) void ParserTracing::AddSequenceTrace(const wchar_t wch) { - _sequenceTrace.push_back(wch); + // Don't waste time storing this if no one is listening. + if (TraceLoggingProviderEnabled(g_hConsoleVirtTermParserEventTraceProvider, WINEVENT_LEVEL_VERBOSE, 0)) + { + _sequenceTrace.push_back(wch); + } } void ParserTracing::DispatchSequenceTrace(const bool fSuccess) noexcept diff --git a/src/tsf/TfEditSession.cpp b/src/tsf/TfEditSession.cpp index 0f7be65ae81..d207a9ab509 100644 --- a/src/tsf/TfEditSession.cpp +++ b/src/tsf/TfEditSession.cpp @@ -1016,7 +1016,7 @@ CEditSessionObject::Release() RETURN_HR_IF(E_FAIL, lStartResult > 0); FullTextRange->CompareEnd(ec, InterimRange, TF_ANCHOR_END, &lEndResult); - RETURN_HR_IF(E_FAIL, lEndResult != 1); + RETURN_HR_IF(E_FAIL, lEndResult < 0); if (lStartResult < 0) { diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index 3f628e4235c..fd205c2ba51 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -159,7 +159,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'unitRemoting')] [string]$Test, [parameter(Mandatory=$false)] diff --git a/tools/runut.cmd b/tools/runut.cmd index 6f28b883fb5..7b7571c678b 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -23,6 +23,7 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Types.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\til.unit.tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^ + %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_Remoting\Remoting.UnitTests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ %_TestHostAppPath%\SettingsModel.LocalTests.dll ^ %* diff --git a/tools/tests.xml b/tools/tests.xml index 437145b0517..d1831acfad8 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -5,6 +5,8 @@ + +