mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-10 08:11:06 +00:00
Compare commits
37 Commits
dev/miniks
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b05df6cb3 | ||
|
|
43e838de9a | ||
|
|
7c3378a835 | ||
|
|
4eeaddc583 | ||
|
|
8f3ad5dcd9 | ||
|
|
982d30467b | ||
|
|
bb2920ec01 | ||
|
|
ecf99fac23 | ||
|
|
c86038926d | ||
|
|
52b05e065e | ||
|
|
81a80257c9 | ||
|
|
1ea9fc26c8 | ||
|
|
e1d15105d7 | ||
|
|
d321ec084c | ||
|
|
f33c69d8b4 | ||
|
|
30b8335479 | ||
|
|
93b79fb23c | ||
|
|
7b8806b1fe | ||
|
|
8c5041b2ae | ||
|
|
a511ab0bc7 | ||
|
|
501c47adb2 | ||
|
|
72121721f3 | ||
|
|
3e80b6a466 | ||
|
|
a80e1d3f73 | ||
|
|
f45df9a717 | ||
|
|
b37f69826e | ||
|
|
b2e76c1812 | ||
|
|
2179e16e3d | ||
|
|
fafc0e1316 | ||
|
|
ca6dff9f20 | ||
|
|
186ff8f638 | ||
|
|
37810aac71 | ||
|
|
33bc88b225 | ||
|
|
d56137876e | ||
|
|
f928d41917 | ||
|
|
e4cc3104ab | ||
|
|
0c10b4b265 |
100
OpenConsole.sln
100
OpenConsole.sln
@@ -313,6 +313,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalTestNetCore", "s
|
||||
{84848BFA-931D-42CE-9ADF-01EE54DE7890} = {84848BFA-931D-42CE-9ADF-01EE54DE7890}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchWinRTServer", "src\tools\ScratchWinRTServer\ScratchWinRTServer.vcxproj", "{D46D9547-F085-4645-B8F7-E8CD21559AB4}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
|
||||
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
|
||||
{48D21369-3D7B-4431-9967-24E81292CF62} = {48D21369-3D7B-4431-9967-24E81292CF62}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchIsland", "src\tools\ScratchIsland\ScratchIsland.vcxproj", "{23A1F736-CD19-4196-980F-84BCD50CF783}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}"
|
||||
@@ -1985,6 +1998,90 @@ Global
|
||||
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|DotNet_x86Test.Build.0 = Release|x86
|
||||
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|Any CPU.ActiveCfg = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|Any CPU.Build.0 = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|ARM64.ActiveCfg = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|ARM64.Build.0 = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x64.Build.0 = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x86.Build.0 = Release|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x64.Build.0 = Debug|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x86.Build.0 = Debug|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|ARM64.ActiveCfg = Release|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x64.ActiveCfg = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x64.Build.0 = Release|x64
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x86.ActiveCfg = Release|Win32
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x86.Build.0 = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|Any CPU.ActiveCfg = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|Any CPU.Build.0 = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|ARM64.ActiveCfg = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|ARM64.Build.0 = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x64.Build.0 = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x86.Build.0 = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x64.Build.0 = Debug|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x86.Build.0 = Debug|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|ARM64.ActiveCfg = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x64.ActiveCfg = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x64.Build.0 = Release|x64
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x86.ActiveCfg = Release|Win32
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x86.Build.0 = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|Any CPU.ActiveCfg = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|Any CPU.Build.0 = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|ARM64.ActiveCfg = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|ARM64.Build.0 = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x64.Build.0 = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x86.Build.0 = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x64.Build.0 = Debug|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x86.Build.0 = Debug|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|ARM64.ActiveCfg = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x64.ActiveCfg = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x64.Build.0 = Release|x64
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x86.ActiveCfg = Release|Win32
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x86.Build.0 = Release|Win32
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
|
||||
@@ -2169,6 +2266,9 @@ Global
|
||||
{067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
|
||||
{1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
{23A1F736-CD19-4196-980F-84BCD50CF783} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
{CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "../TerminalApp/MinMaxCloseControl.h"
|
||||
#include "../TerminalApp/TabRowControl.h"
|
||||
#include "../TerminalApp/ShortcutActionDispatch.h"
|
||||
#include "../TerminalApp/Tab.h"
|
||||
#include "../TerminalApp/TerminalTab.h"
|
||||
#include "../CppWinrtTailored.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
@@ -250,8 +250,8 @@ namespace TerminalAppLocalTests
|
||||
// In the real app, this isn't a problem, but doesn't happen
|
||||
// reliably in the unit tests.
|
||||
Log::Comment(L"Ensure we set the first tab as the selected one.");
|
||||
auto tab{ page->_GetStrongTabImpl(0) };
|
||||
page->_tabView.SelectedItem(tab->GetTabViewItem());
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
page->_tabView.SelectedItem(tab->TabViewItem());
|
||||
page->_UpdatedSelectedTab(0);
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -453,7 +453,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
result = RunOnUIThread([&page]() {
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetStrongTabImpl(0);
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(1, tab->GetLeafPaneCount());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -463,7 +463,7 @@ namespace TerminalAppLocalTests
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetStrongTabImpl(0);
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(2, tab->GetLeafPaneCount());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -481,7 +481,7 @@ namespace TerminalAppLocalTests
|
||||
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
|
||||
auto tab = page->_GetStrongTabImpl(0);
|
||||
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
|
||||
VERIFY_ARE_EQUAL(2,
|
||||
tab->GetLeafPaneCount(),
|
||||
L"We should gracefully do nothing here - the profile no longer exists.");
|
||||
@@ -562,7 +562,7 @@ namespace TerminalAppLocalTests
|
||||
ActionEventArgs eventArgs{ args };
|
||||
// eventArgs.Args(args);
|
||||
page->_HandleSplitPane(nullptr, eventArgs);
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
@@ -573,7 +573,7 @@ namespace TerminalAppLocalTests
|
||||
result = RunOnUIThread([&page]() {
|
||||
ActionEventArgs eventArgs{};
|
||||
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_TRUE(firstTab->IsZoomed());
|
||||
});
|
||||
@@ -583,7 +583,7 @@ namespace TerminalAppLocalTests
|
||||
result = RunOnUIThread([&page]() {
|
||||
ActionEventArgs eventArgs{};
|
||||
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
});
|
||||
@@ -600,7 +600,7 @@ namespace TerminalAppLocalTests
|
||||
SplitPaneArgs args{ SplitType::Duplicate };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
page->_HandleSplitPane(nullptr, eventArgs);
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
@@ -614,7 +614,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
||||
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_TRUE(firstTab->IsZoomed());
|
||||
});
|
||||
@@ -628,7 +628,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
page->_HandleMoveFocus(nullptr, eventArgs);
|
||||
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
});
|
||||
@@ -645,7 +645,7 @@ namespace TerminalAppLocalTests
|
||||
SplitPaneArgs args{ SplitType::Duplicate };
|
||||
ActionEventArgs eventArgs{ args };
|
||||
page->_HandleSplitPane(nullptr, eventArgs);
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
@@ -659,7 +659,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
page->_HandleTogglePaneZoom(nullptr, eventArgs);
|
||||
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_TRUE(firstTab->IsZoomed());
|
||||
});
|
||||
@@ -672,7 +672,7 @@ namespace TerminalAppLocalTests
|
||||
|
||||
page->_HandleClosePane(nullptr, eventArgs);
|
||||
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@@ -683,7 +683,7 @@ namespace TerminalAppLocalTests
|
||||
Log::Comment(L"Check to ensure there's only one pane left.");
|
||||
|
||||
result = RunOnUIThread([&page]() {
|
||||
auto firstTab = page->_GetStrongTabImpl(0);
|
||||
auto firstTab = page->_GetTerminalTabImpl(0);
|
||||
VERIFY_ARE_EQUAL(1, firstTab->GetLeafPaneCount());
|
||||
VERIFY_IS_FALSE(firstTab->IsZoomed());
|
||||
});
|
||||
|
||||
@@ -130,21 +130,26 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
auto activeTab = _GetFocusedTab();
|
||||
|
||||
// Don't do anything if there's only one pane. It's already zoomed.
|
||||
if (activeTab && activeTab->GetLeafPaneCount() > 1)
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
{
|
||||
// First thing's first, remove the current content from the UI
|
||||
// tree. This is important, because we might be leaving zoom, and if
|
||||
// a pane is zoomed, then it's currently in the UI tree, and should
|
||||
// be removed before it's re-added in Pane::Restore
|
||||
_tabContent.Children().Clear();
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
// Don't do anything if there's only one pane. It's already zoomed.
|
||||
if (activeTab && activeTab->GetLeafPaneCount() > 1)
|
||||
{
|
||||
// First thing's first, remove the current content from the UI
|
||||
// tree. This is important, because we might be leaving zoom, and if
|
||||
// a pane is zoomed, then it's currently in the UI tree, and should
|
||||
// be removed before it's re-added in Pane::Restore
|
||||
_tabContent.Children().Clear();
|
||||
|
||||
// Togging the zoom on the tab will cause the tab to inform us of
|
||||
// the new root Content for this tab.
|
||||
activeTab->ToggleZoom();
|
||||
// Togging the zoom on the tab will cause the tab to inform us of
|
||||
// the new root Content for this tab.
|
||||
activeTab->ToggleZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -323,16 +328,19 @@ namespace winrt::TerminalApp::implementation
|
||||
args.Handled(false);
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<SetColorSchemeArgs>())
|
||||
{
|
||||
if (auto activeTab = _GetFocusedTab())
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
{
|
||||
if (auto activeControl = activeTab->GetActiveTerminalControl())
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
|
||||
if (auto activeControl = activeTab->GetActiveTerminalControl())
|
||||
{
|
||||
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
controlSettings->ApplyColorScheme(scheme);
|
||||
activeControl.UpdateSettings(*controlSettings);
|
||||
args.Handled(true);
|
||||
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
|
||||
{
|
||||
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
|
||||
controlSettings->ApplyColorScheme(scheme);
|
||||
activeControl.UpdateSettings(*controlSettings);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,16 +360,18 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
auto activeTab = _GetFocusedTab();
|
||||
if (activeTab)
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
{
|
||||
if (tabColor.has_value())
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
activeTab->SetRuntimeTabColor(tabColor.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetRuntimeTabColor();
|
||||
if (tabColor.has_value())
|
||||
{
|
||||
activeTab->SetRuntimeTabColor(tabColor.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetRuntimeTabColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
@@ -370,10 +380,12 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
auto activeTab = _GetFocusedTab();
|
||||
if (activeTab)
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
{
|
||||
activeTab->ActivateColorPicker();
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
activeTab->ActivateColorPicker();
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
@@ -388,16 +400,18 @@ namespace winrt::TerminalApp::implementation
|
||||
title = realArgs.Title();
|
||||
}
|
||||
|
||||
auto activeTab = _GetFocusedTab();
|
||||
if (activeTab)
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
{
|
||||
if (title.has_value())
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
activeTab->SetTabText(title.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetTabText();
|
||||
if (title.has_value())
|
||||
{
|
||||
activeTab->SetTabText(title.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTab->ResetTabText();
|
||||
}
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
@@ -406,10 +420,12 @@ namespace winrt::TerminalApp::implementation
|
||||
void TerminalPage::_HandleOpenTabRenamer(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
auto activeTab = _GetFocusedTab();
|
||||
if (activeTab)
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
{
|
||||
activeTab->ActivateTabRenamer();
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
activeTab->ActivateTabRenamer();
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppLogic.g.h"
|
||||
|
||||
#include "Tab.h"
|
||||
#include "TerminalPage.h"
|
||||
#include "Jumplist.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
import "ShortcutActionDispatch.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass Tab : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
String Title { get; };
|
||||
Windows.UI.Xaml.Controls.IconSource IconSource { get; };
|
||||
Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand { get; };
|
||||
UInt32 TabViewIndex { get; };
|
||||
|
||||
Windows.UI.Xaml.UIElement Content { get; };
|
||||
|
||||
void SetDispatch(ShortcutActionDispatch dispatch);
|
||||
}
|
||||
}
|
||||
148
src/cascadia/TerminalApp/TabBase.cpp
Normal file
148
src/cascadia/TerminalApp/TabBase.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include <LibraryResources.h>
|
||||
#include "TabBase.h"
|
||||
#include "TabBase.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalControl;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Windows::System;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
WUX::FocusState TabBase::FocusState() const noexcept
|
||||
{
|
||||
return _focusState;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Prepares this tab for being removed from the UI hierarchy
|
||||
void TabBase::Shutdown()
|
||||
{
|
||||
Content(nullptr);
|
||||
_ClosedHandlers(nullptr, nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a context menu attached to the tab.
|
||||
// Currently contains elements allowing the user to close the selected tab
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TabBase::_CreateContextMenu()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
// Close
|
||||
Controls::MenuFlyoutItem closeTabMenuItem;
|
||||
Controls::FontIcon closeSymbol;
|
||||
closeSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
|
||||
closeSymbol.Glyph(L"\xE8BB");
|
||||
|
||||
closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_ClosedHandlers(nullptr, nullptr);
|
||||
}
|
||||
});
|
||||
closeTabMenuItem.Text(RS_(L"TabClose"));
|
||||
closeTabMenuItem.Icon(closeSymbol);
|
||||
|
||||
// Build the menu
|
||||
Controls::MenuFlyout newTabFlyout;
|
||||
newTabFlyout.Items().Append(_CreateCloseSubMenu());
|
||||
newTabFlyout.Items().Append(closeTabMenuItem);
|
||||
TabViewItem().ContextFlyout(newTabFlyout);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a sub-menu containing menu items to close multiple tabs
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the created MenuFlyoutSubItem
|
||||
Controls::MenuFlyoutSubItem TabBase::_CreateCloseSubMenu()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
// Close tabs after
|
||||
_closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_CloseTabsAfter();
|
||||
}
|
||||
});
|
||||
_closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
|
||||
|
||||
// Close other tabs
|
||||
_closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_CloseOtherTabs();
|
||||
}
|
||||
});
|
||||
_closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
|
||||
|
||||
Controls::MenuFlyoutSubItem closeSubMenu;
|
||||
closeSubMenu.Text(RS_(L"TabCloseSubMenu"));
|
||||
closeSubMenu.Items().Append(_closeTabsAfterMenuItem);
|
||||
closeSubMenu.Items().Append(_closeOtherTabsMenuItem);
|
||||
|
||||
return closeSubMenu;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enable the Close menu items based on tab index and total number of tabs
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TabBase::_EnableCloseMenuItems()
|
||||
{
|
||||
// close other tabs is enabled only if there are other tabs
|
||||
_closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
|
||||
// close tabs after is enabled only if there are other tabs on the right
|
||||
_closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);
|
||||
}
|
||||
|
||||
void TabBase::_CloseTabsAfter()
|
||||
{
|
||||
CloseTabsAfterArgs args{ _TabViewIndex };
|
||||
ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args };
|
||||
|
||||
_dispatch.DoAction(closeTabsAfter);
|
||||
}
|
||||
|
||||
void TabBase::_CloseOtherTabs()
|
||||
{
|
||||
CloseOtherTabsArgs args{ _TabViewIndex };
|
||||
ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args };
|
||||
|
||||
_dispatch.DoAction(closeOtherTabs);
|
||||
}
|
||||
|
||||
void TabBase::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs)
|
||||
{
|
||||
TabViewIndex(idx);
|
||||
TabViewNumTabs(numTabs);
|
||||
_EnableCloseMenuItems();
|
||||
SwitchToTabCommand().Action().Args().as<SwitchToTabArgs>().TabIndex(idx);
|
||||
}
|
||||
|
||||
void TabBase::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
|
||||
{
|
||||
_dispatch = dispatch;
|
||||
}
|
||||
}
|
||||
57
src/cascadia/TerminalApp/TabBase.h
Normal file
57
src/cascadia/TerminalApp/TabBase.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
#include "inc/cppwinrt_utils.h"
|
||||
#include "TabBase.g.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class TabTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct TabBase : TabBaseT<TabBase>
|
||||
{
|
||||
public:
|
||||
virtual void Focus(winrt::Windows::UI::Xaml::FocusState focusState) = 0;
|
||||
winrt::Windows::UI::Xaml::FocusState FocusState() const noexcept;
|
||||
|
||||
virtual void Shutdown();
|
||||
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
|
||||
|
||||
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
|
||||
|
||||
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
// The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector.
|
||||
// This is needed since Tab is going to be managing its own SwitchToTab command.
|
||||
GETSET_PROPERTY(uint32_t, TabViewIndex, 0);
|
||||
// The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector.
|
||||
GETSET_PROPERTY(uint32_t, TabViewNumTabs, 0);
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::Command, SwitchToTabCommand, _PropertyChangedHandlers, nullptr);
|
||||
GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
protected:
|
||||
winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused };
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{};
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{};
|
||||
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
|
||||
|
||||
virtual void _CreateContextMenu();
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _CreateCloseSubMenu();
|
||||
void _EnableCloseMenuItems();
|
||||
void _CloseTabsAfter();
|
||||
void _CloseOtherTabs();
|
||||
|
||||
friend class ::TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
}
|
||||
24
src/cascadia/TerminalApp/TabBase.idl
Normal file
24
src/cascadia/TerminalApp/TabBase.idl
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
import "ShortcutActionDispatch.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
unsealed runtimeclass TabBase : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
String Title { get; };
|
||||
String Icon { get; };
|
||||
Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand;
|
||||
Microsoft.UI.Xaml.Controls.TabViewItem TabViewItem { get; };
|
||||
Windows.UI.Xaml.FrameworkElement Content { get; };
|
||||
Windows.UI.Xaml.FocusState FocusState { get; };
|
||||
|
||||
UInt32 TabViewIndex;
|
||||
UInt32 TabViewNumTabs;
|
||||
|
||||
overridable void Focus(Windows.UI.Xaml.FocusState focusState);
|
||||
overridable void Shutdown();
|
||||
|
||||
void SetDispatch(ShortcutActionDispatch dispatch);
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,12 @@
|
||||
<ClInclude Include="MinMaxCloseControl.h">
|
||||
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TabBase.h">
|
||||
<DependentUpon>TabBase.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TerminalTab.h">
|
||||
<DependentUpon>TerminalTab.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TerminalPage.h">
|
||||
<DependentUpon>TerminalPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -95,9 +101,6 @@
|
||||
<ClInclude Include="IconPathConverter.h">
|
||||
<DependentUpon>IconPathConverter.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Tab.h">
|
||||
<DependentUpon>Tab.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pane.h" />
|
||||
<ClInclude Include="ColorHelper.h" />
|
||||
<ClInclude Include="TerminalSettings.h">
|
||||
@@ -127,6 +130,12 @@
|
||||
<ClCompile Include="MinMaxCloseControl.cpp">
|
||||
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TabBase.cpp">
|
||||
<DependentUpon>TabBase.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TerminalTab.cpp">
|
||||
<DependentUpon>TerminalTab.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TerminalPage.cpp">
|
||||
<DependentUpon>TerminalPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -152,9 +161,6 @@
|
||||
<ClCompile Include="IconPathConverter.cpp">
|
||||
<DependentUpon>IconPathConverter.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Tab.cpp">
|
||||
<DependentUpon>Tab.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pane.cpp" />
|
||||
<ClCompile Include="Pane.LayoutSizeNode.cpp" />
|
||||
<ClCompile Include="ColorHelper.cpp" />
|
||||
@@ -197,6 +203,8 @@
|
||||
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="TabBase.idl" />
|
||||
<Midl Include="TerminalTab.idl" />
|
||||
<Midl Include="TerminalPage.idl">
|
||||
<DependentUpon>TerminalPage.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -220,7 +228,6 @@
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
<Midl Include="HasNestedCommandsVisibilityConverter.idl" />
|
||||
<Midl Include="IconPathConverter.idl" />
|
||||
<Midl Include="Tab.idl" />
|
||||
<Midl Include="TerminalSettings.idl" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Misc Files ======================== -->
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
<ClCompile Include="TerminalSettings.cpp">
|
||||
<Filter>settings</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Jumplist.cpp" />
|
||||
<ClCompile Include="Tab.cpp">
|
||||
<Filter>tab</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Utils.h" />
|
||||
@@ -48,6 +52,10 @@
|
||||
<ClInclude Include="TerminalSettings.h">
|
||||
<Filter>settings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Jumplist.h" />
|
||||
<ClInclude Include="Tab.h">
|
||||
<Filter>tab</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="AppLogic.idl">
|
||||
@@ -62,14 +70,13 @@
|
||||
<Midl Include="ShortcutActionDispatch.idl">
|
||||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
<Midl Include="Tab.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
<Midl Include="IDirectKeyListener.idl" />
|
||||
<Midl Include="CommandKeyChordVisibilityConverter.idl" />
|
||||
<Midl Include="TerminalSettings.idl">
|
||||
<Filter>settings</Filter>
|
||||
</Midl>
|
||||
<Midl Include="TerminalTab.idl">
|
||||
<Filter>tab</Filter>
|
||||
</Midl>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
@@ -119,4 +126,4 @@
|
||||
<Filter>app</Filter>
|
||||
</ApplicationDefinition>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -42,7 +42,7 @@ namespace winrt
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalPage::TerminalPage() :
|
||||
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::Tab>() },
|
||||
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
|
||||
_mruTabActions{ winrt::single_threaded_vector<Command>() },
|
||||
_startupActions{ winrt::single_threaded_vector<ActionAndArgs>() }
|
||||
{
|
||||
@@ -668,8 +668,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
TermControl term{ settings, connection };
|
||||
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(profileGuid, term);
|
||||
_MakeSwitchToTabCommand(*newTabImpl, _tabs.Size());
|
||||
|
||||
// Add the new tab to the list of our tabs.
|
||||
auto newTabImpl = winrt::make_self<Tab>(profileGuid, term);
|
||||
_tabs.Append(*newTabImpl);
|
||||
_mruTabActions.Append(newTabImpl->SwitchToTabCommand());
|
||||
|
||||
@@ -686,7 +688,8 @@ namespace winrt::TerminalApp::implementation
|
||||
auto weakTab = make_weak(newTabImpl);
|
||||
|
||||
// When the tab's active pane changes, we'll want to lookup a new icon
|
||||
// for it, and possibly propagate the title up to the window.
|
||||
// for it. The Title change will be propagated upwards through the tab's
|
||||
// PropertyChanged event handler.
|
||||
newTabImpl->ActivePaneChanged([weakTab, weakThis{ get_weak() }]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
@@ -695,24 +698,12 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Possibly update the icon of the tab.
|
||||
page->_UpdateTabIcon(*tab);
|
||||
// Possibly update the title of the tab, window to match the newly
|
||||
// focused pane.
|
||||
page->_UpdateTitle(*tab);
|
||||
}
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->GetTabViewItem();
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
// GH#6570
|
||||
// The TabView does not apply compact sizing to items added after Compact is enabled.
|
||||
// By forcibly reapplying compact sizing every time we add a new tab, we'll make sure
|
||||
// that it works.
|
||||
// Workaround from https://github.com/microsoft/microsoft-ui-xaml/issues/2711
|
||||
if (_tabView.TabWidthMode() == MUX::Controls::TabViewWidthMode::Compact)
|
||||
{
|
||||
_tabView.UpdateLayout();
|
||||
_tabView.TabWidthMode(MUX::Controls::TabViewWidthMode::Compact);
|
||||
}
|
||||
_ReapplyCompactTabSize();
|
||||
|
||||
// Set this tab's icon to the icon from the user's profile
|
||||
const auto profile = _settings.FindProfile(profileGuid);
|
||||
@@ -923,12 +914,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// TitleChanged event.
|
||||
// Arguments:
|
||||
// - tab: the Tab to update the title for.
|
||||
void TerminalPage::_UpdateTitle(const Tab& tab)
|
||||
void TerminalPage::_UpdateTitle(const TerminalTab& tab)
|
||||
{
|
||||
auto newTabTitle = tab.GetActiveTitle();
|
||||
auto newTabTitle = tab.Title();
|
||||
|
||||
if (_settings.GlobalSettings().ShowTitleInTitlebar() &&
|
||||
tab.IsFocused())
|
||||
tab.FocusState() != FocusState::Unfocused)
|
||||
{
|
||||
_titleChangeHandlers(*this, newTabTitle);
|
||||
}
|
||||
@@ -939,7 +930,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// tab's icon to that icon.
|
||||
// Arguments:
|
||||
// - tab: the Tab to update the title for.
|
||||
void TerminalPage::_UpdateTabIcon(Tab& tab)
|
||||
void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
|
||||
{
|
||||
const auto lastFocusedProfileOpt = tab.GetFocusedProfile();
|
||||
if (lastFocusedProfileOpt.has_value())
|
||||
@@ -990,30 +981,32 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
try
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
auto focusedTab = _GetStrongTabImpl(*index);
|
||||
// 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 = focusedTab->GetFocusedProfile();
|
||||
if (profileGuid.has_value())
|
||||
try
|
||||
{
|
||||
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settings);
|
||||
// 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<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
|
||||
_CreateNewTabFromSettings(profileGuid.value(), settings);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1040,8 +1033,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// Removing the tab from the collection should destroy its control and disconnect its connection,
|
||||
// but it doesn't always do so. The UI tree may still be holding the control and preventing its destruction.
|
||||
auto tab{ _GetStrongTabImpl(tabIndex) };
|
||||
tab->Shutdown();
|
||||
auto tab{ _tabs.GetAt(tabIndex) };
|
||||
tab.Shutdown();
|
||||
|
||||
uint32_t mruIndex;
|
||||
if (_mruTabActions.IndexOf(_tabs.GetAt(tabIndex).SwitchToTabCommand(), mruIndex))
|
||||
@@ -1091,8 +1084,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// here. If we don't, then the TabView will technically not have a
|
||||
// selected item at all, which can make things like ClosePane not
|
||||
// work correctly.
|
||||
auto newSelectedTab{ _GetStrongTabImpl(newSelectedIndex) };
|
||||
_tabView.SelectedItem(newSelectedTab->GetTabViewItem());
|
||||
auto newSelectedTab{ _tabs.GetAt(newSelectedIndex) };
|
||||
_tabView.SelectedItem(newSelectedTab.TabViewItem());
|
||||
}
|
||||
|
||||
// GH#5559 - If we were in the middle of a drag/drop, end it by clearing
|
||||
@@ -1114,7 +1107,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Arguments:
|
||||
// - term: The newly created TermControl to connect the events for
|
||||
// - hostingTab: The Tab that's hosting this TermControl instance
|
||||
void TerminalPage::_RegisterTerminalEvents(TermControl term, Tab& hostingTab)
|
||||
void TerminalPage::_RegisterTerminalEvents(TermControl term, TerminalTab& hostingTab)
|
||||
{
|
||||
// Add an event handler when the terminal's selection wants to be copied.
|
||||
// When the text buffer data is retrieved, we'll copy the data into the Clipboard
|
||||
@@ -1144,12 +1137,12 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (args.PropertyName() == L"Content")
|
||||
{
|
||||
if (tab == page->_GetFocusedTab())
|
||||
if (*tab == page->_GetFocusedTab())
|
||||
{
|
||||
page->_tabContent.Children().Clear();
|
||||
page->_tabContent.Children().Append(tab->Content());
|
||||
|
||||
tab->SetFocused(true);
|
||||
tab->Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1160,7 +1153,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab && tab->IsFocused())
|
||||
if (page && tab && (tab->FocusState() != FocusState::Unfocused))
|
||||
{
|
||||
page->_SetNonClientAreaColors(color);
|
||||
}
|
||||
@@ -1170,7 +1163,7 @@ namespace winrt::TerminalApp::implementation
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab && tab->IsFocused())
|
||||
if (page && tab && (tab->FocusState() != FocusState::Unfocused))
|
||||
{
|
||||
page->_ClearNonClientAreaColors();
|
||||
}
|
||||
@@ -1234,8 +1227,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (_startupState == StartupState::InStartup)
|
||||
{
|
||||
auto tab{ _GetStrongTabImpl(tabIndex) };
|
||||
_tabView.SelectedItem(tab->GetTabViewItem());
|
||||
auto tab{ _tabs.GetAt(tabIndex) };
|
||||
_tabView.SelectedItem(tab.TabViewItem());
|
||||
_UpdatedSelectedTab(tabIndex);
|
||||
}
|
||||
else
|
||||
@@ -1263,16 +1256,21 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalPage::_UnZoomIfNeeded()
|
||||
{
|
||||
auto activeTab = _GetFocusedTab();
|
||||
if (activeTab && activeTab->IsZoomed())
|
||||
if (auto focusedTab = _GetFocusedTab())
|
||||
{
|
||||
// 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();
|
||||
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1288,9 +1286,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
auto focusedTab{ _GetStrongTabImpl(*index) };
|
||||
_UnZoomIfNeeded();
|
||||
focusedTab->NavigateFocus(direction);
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->NavigateFocus(direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1298,13 +1298,12 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
auto focusedTab{ _GetStrongTabImpl(*index) };
|
||||
return focusedTab->GetActiveTerminalControl();
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
return terminalTab->GetActiveTerminalControl();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1327,11 +1326,11 @@ 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::com_ptr<Tab> TerminalPage::_GetFocusedTab()
|
||||
winrt::TerminalApp::TabBase TerminalPage::_GetFocusedTab()
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
return _GetStrongTabImpl(*index);
|
||||
return _tabs.GetAt(*index);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1356,8 +1355,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
auto tab{ _GetStrongTabImpl(tabIndex) };
|
||||
_tabView.SelectedItem(tab->GetTabViewItem());
|
||||
auto tabToFocus = page->_tabs.GetAt(tabIndex);
|
||||
_tabView.SelectedItem(tabToFocus.TabViewItem());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1379,9 +1378,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
auto focusedTab{ _GetStrongTabImpl(*index) };
|
||||
_UnZoomIfNeeded();
|
||||
focusedTab->ClosePane();
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ClosePane();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1422,22 +1423,24 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
auto focusedTab{ _GetStrongTabImpl(*index) };
|
||||
uint32_t realRowsToScroll;
|
||||
if (rowsToScroll == nullptr)
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
|
||||
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
|
||||
focusedTab->GetActiveTerminalControl().GetViewHeight() :
|
||||
_systemRowsToScroll;
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the custom value specified in the command
|
||||
realRowsToScroll = rowsToScroll.Value();
|
||||
}
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll);
|
||||
focusedTab->Scroll(scrollDelta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1470,9 +1473,16 @@ namespace winrt::TerminalApp::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
auto focusedTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt));
|
||||
|
||||
// Do nothing if the focused tab isn't a TerminalTab
|
||||
if (!focusedTab)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto focusedTab = _GetStrongTabImpl(*indexOpt);
|
||||
TerminalApp::TerminalSettings controlSettings;
|
||||
GUID realGuid;
|
||||
bool profileFound = false;
|
||||
@@ -1546,9 +1556,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
auto focusedTab{ _GetStrongTabImpl(*index) };
|
||||
_UnZoomIfNeeded();
|
||||
focusedTab->ResizePane(direction);
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
terminalTab->ResizePane(direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1566,11 +1578,13 @@ namespace winrt::TerminalApp::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
const auto control = _GetActiveControl();
|
||||
const auto termHeight = control.GetViewHeight();
|
||||
auto focusedTab{ _GetStrongTabImpl(*indexOpt) };
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, termHeight);
|
||||
focusedTab->Scroll(scrollDelta);
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt)))
|
||||
{
|
||||
const auto control = _GetActiveControl();
|
||||
const auto termHeight = control.GetViewHeight();
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, termHeight);
|
||||
terminalTab->Scroll(scrollDelta);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1681,8 +1695,10 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
auto focusedTab{ _GetStrongTabImpl(*index) };
|
||||
return focusedTab->CalcSnappedDimension(widthOrHeight, dimension);
|
||||
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
|
||||
{
|
||||
return terminalTab->CalcSnappedDimension(widthOrHeight, dimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dimension;
|
||||
@@ -1960,18 +1976,17 @@ namespace winrt::TerminalApp::implementation
|
||||
// Unfocus all the tabs.
|
||||
for (auto tab : _tabs)
|
||||
{
|
||||
auto tabImpl{ _GetStrongTabImpl(tab) };
|
||||
tabImpl->SetFocused(false);
|
||||
tab.Focus(FocusState::Unfocused);
|
||||
}
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto tab{ _GetStrongTabImpl(index) };
|
||||
auto tab{ _tabs.GetAt(index) };
|
||||
|
||||
_tabContent.Children().Clear();
|
||||
_tabContent.Children().Append(tab->Content());
|
||||
_tabContent.Children().Append(tab.Content());
|
||||
|
||||
// GH#7409: If the tab switcher is open, then we _don't_ want to
|
||||
// automatically focus the new tab here. The tab switcher wants
|
||||
@@ -1985,12 +2000,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// need to worry about focus getting lost.
|
||||
if (CommandPalette().Visibility() != Visibility::Visible)
|
||||
{
|
||||
tab->SetFocused(true);
|
||||
tab.Focus(FocusState::Programmatic);
|
||||
_UpdateMRUTab(index);
|
||||
}
|
||||
|
||||
// Raise an event that our title changed
|
||||
_titleChangeHandlers(*this, tab->GetActiveTitle());
|
||||
_titleChangeHandlers(*this, tab.Title());
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@@ -2025,8 +2040,10 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto newSize = e.NewSize();
|
||||
for (auto tab : _tabs)
|
||||
{
|
||||
auto tabImpl{ _GetStrongTabImpl(tab) };
|
||||
tabImpl->ResizeContent(newSize);
|
||||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
{
|
||||
terminalTab->ResizeContent(newSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2097,9 +2114,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
for (auto tab : _tabs)
|
||||
{
|
||||
// Attempt to reload the settings of any panes with this profile
|
||||
auto tabImpl{ _GetStrongTabImpl(tab) };
|
||||
tabImpl->UpdateSettings(settings, profileGuid);
|
||||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
{
|
||||
terminalTab->UpdateSettings(settings, profileGuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
@@ -2111,11 +2129,17 @@ namespace winrt::TerminalApp::implementation
|
||||
// anymore, so we can't possibly update its settings.
|
||||
|
||||
// Update the icon of the tab for the currently focused profile in that tab.
|
||||
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
|
||||
// and profiles so the Title and Icon will be set once and only once on init.
|
||||
for (auto tab : _tabs)
|
||||
{
|
||||
auto tabImpl{ _GetStrongTabImpl(tab) };
|
||||
_UpdateTabIcon(*tabImpl);
|
||||
_UpdateTitle(*tabImpl);
|
||||
if (auto terminalTab = _GetTerminalTabImpl(tab))
|
||||
{
|
||||
_UpdateTabIcon(*terminalTab);
|
||||
|
||||
// Force the TerminalTab to re-grab its currently active control's title.
|
||||
terminalTab->UpdateTitle();
|
||||
}
|
||||
}
|
||||
|
||||
auto weakThis{ get_weak() };
|
||||
@@ -2255,10 +2279,9 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
auto tabImpl{ _GetStrongTabImpl(tab) };
|
||||
if (tabImpl->GetTabViewItem().ContextFlyout())
|
||||
if (tab.TabViewItem().ContextFlyout())
|
||||
{
|
||||
tabImpl->GetTabViewItem().ContextFlyout().Hide();
|
||||
tab.TabViewItem().ContextFlyout().Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2317,32 +2340,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_alwaysOnTopChangedHandlers(*this, nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns a com_ptr to the implementation type of the tab at the given index
|
||||
// Arguments:
|
||||
// - index: an unsigned integer index to a tab in _tabs
|
||||
// Return Value:
|
||||
// - a com_ptr to the implementation type of the Tab
|
||||
winrt::com_ptr<Tab> TerminalPage::_GetStrongTabImpl(const uint32_t index) const
|
||||
{
|
||||
winrt::com_ptr<Tab> tabImpl;
|
||||
tabImpl.copy_from(winrt::get_self<Tab>(_tabs.GetAt(index)));
|
||||
return tabImpl;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns a com_ptr to the implementation type of the given projected Tab
|
||||
// Arguments:
|
||||
// - tab: the projected type of a Tab
|
||||
// Return Value:
|
||||
// - a com_ptr to the implementation type of the Tab
|
||||
winrt::com_ptr<Tab> TerminalPage::_GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const
|
||||
{
|
||||
winrt::com_ptr<Tab> tabImpl;
|
||||
tabImpl.copy_from(winrt::get_self<Tab>(tab));
|
||||
return tabImpl;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets the tab split button color when a new tab color is selected
|
||||
// Arguments:
|
||||
@@ -2544,7 +2541,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Return focus to the active control
|
||||
if (auto index{ _GetFocusedTabIndex() })
|
||||
{
|
||||
_GetStrongTabImpl(index.value())->SetFocused(true);
|
||||
_tabs.GetAt(*index).Focus(FocusState::Programmatic);
|
||||
_UpdateMRUTab(index.value());
|
||||
}
|
||||
}
|
||||
@@ -2583,10 +2580,75 @@ namespace winrt::TerminalApp::implementation
|
||||
const uint32_t size = _tabs.Size();
|
||||
for (uint32_t i = 0; i < size; ++i)
|
||||
{
|
||||
_GetStrongTabImpl(i)->UpdateTabViewIndex(i, size);
|
||||
auto tab{ _tabs.GetAt(i) };
|
||||
auto tabImpl{ winrt::get_self<TabBase>(tab) };
|
||||
tabImpl->UpdateTabViewIndex(i, size);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns a com_ptr to the implementation type of the given tab if it's a TerminalTab.
|
||||
// If the tab is not a TerminalTab, returns nullptr.
|
||||
// Arguments:
|
||||
// - tab: the projected type of a Tab
|
||||
// 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<TerminalTab> TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) const
|
||||
{
|
||||
if (auto terminalTab = tab.try_as<TerminalApp::TerminalTab>())
|
||||
{
|
||||
winrt::com_ptr<TerminalTab> tabImpl;
|
||||
tabImpl.copy_from(winrt::get_self<TerminalTab>(terminalTab));
|
||||
return tabImpl;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - The TabView does not apply compact sizing to items added after Compact is enabled.
|
||||
// By forcibly reapplying compact sizing every time we add a new tab, we'll make sure
|
||||
// that it works.
|
||||
// Workaround from https://github.com/microsoft/microsoft-ui-xaml/issues/2711
|
||||
// TODO: Remove this function and its calls when ingesting the above changes.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_ReapplyCompactTabSize()
|
||||
{
|
||||
if (_tabView.TabWidthMode() == MUX::Controls::TabViewWidthMode::Compact)
|
||||
{
|
||||
_tabView.UpdateLayout();
|
||||
_tabView.TabWidthMode(MUX::Controls::TabViewWidthMode::Compact);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Initializes a SwitchToTab command object for this Tab instance.
|
||||
// This should be done before the tab is added to the _tabs vector so that
|
||||
// controls like the CmdPal that observe the vector changes can always expect
|
||||
// a SwitchToTab command to be available.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPage::_MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index)
|
||||
{
|
||||
SwitchToTabArgs args{ index };
|
||||
ActionAndArgs focusTabAction{ ShortcutAction::SwitchToTab, args };
|
||||
|
||||
Command command;
|
||||
command.Action(focusTabAction);
|
||||
command.Name(tab.Title());
|
||||
command.Icon(tab.Icon());
|
||||
|
||||
tab.SwitchToTabCommand(command);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Computes the delta for scrolling the tab's viewport.
|
||||
// Arguments:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "TerminalPage.g.h"
|
||||
#include "Tab.h"
|
||||
#include "TerminalTab.h"
|
||||
#include "AppKeyBindings.h"
|
||||
#include "TerminalSettings.h"
|
||||
|
||||
@@ -96,10 +96,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<TerminalApp::Tab> _tabs;
|
||||
Windows::Foundation::Collections::IObservableVector<TerminalApp::TabBase> _tabs;
|
||||
Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> _mruTabActions;
|
||||
winrt::com_ptr<Tab> _GetStrongTabImpl(const uint32_t index) const;
|
||||
winrt::com_ptr<Tab> _GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const;
|
||||
winrt::com_ptr<TerminalTab> _GetTerminalTabImpl(const TerminalApp::TabBase& tab) const;
|
||||
|
||||
void _UpdateTabIndices();
|
||||
|
||||
bool _isInFocusMode{ false };
|
||||
@@ -146,8 +146,8 @@ namespace winrt::TerminalApp::implementation
|
||||
void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap) noexcept;
|
||||
void _RegisterActionCallbacks();
|
||||
|
||||
void _UpdateTitle(const Tab& tab);
|
||||
void _UpdateTabIcon(Tab& tab);
|
||||
void _UpdateTitle(const TerminalTab& tab);
|
||||
void _UpdateTabIcon(TerminalTab& tab);
|
||||
void _UpdateTabView();
|
||||
void _UpdateTabWidthMode();
|
||||
void _UpdateCommandsForPalette();
|
||||
@@ -159,7 +159,7 @@ namespace winrt::TerminalApp::implementation
|
||||
void _RemoveTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem);
|
||||
void _RemoveTabViewItemByIndex(uint32_t tabIndex);
|
||||
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, Tab& hostingTab);
|
||||
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, TerminalTab& hostingTab);
|
||||
|
||||
void _SelectNextTab(const bool bMoveRight);
|
||||
bool _SelectTab(const uint32_t tabIndex);
|
||||
@@ -167,7 +167,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetActiveControl();
|
||||
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;
|
||||
winrt::com_ptr<Tab> _GetFocusedTab();
|
||||
TerminalApp::TabBase _GetFocusedTab();
|
||||
winrt::fire_and_forget _SetFocusedTabIndex(const uint32_t tabIndex);
|
||||
void _CloseFocusedTab();
|
||||
void _CloseFocusedPane();
|
||||
@@ -218,6 +218,12 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _UnZoomIfNeeded();
|
||||
|
||||
void _OpenSettingsUI();
|
||||
|
||||
void _ReapplyCompactTabSize();
|
||||
|
||||
void _MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index);
|
||||
|
||||
static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll);
|
||||
static uint32_t _ReadSystemRowsToScroll();
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "pch.h"
|
||||
#include <LibraryResources.h>
|
||||
#include "ColorPickupFlyout.h"
|
||||
#include "Tab.h"
|
||||
#include "Tab.g.cpp"
|
||||
#include "TerminalTab.h"
|
||||
#include "TerminalTab.g.cpp"
|
||||
#include "Utils.h"
|
||||
#include "ColorHelper.h"
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace winrt
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
Tab::Tab(const GUID& profile, const TermControl& control)
|
||||
TerminalTab::TerminalTab(const GUID& profile, const TermControl& control)
|
||||
{
|
||||
_rootPane = std::make_shared<Pane>(profile, control, true);
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace winrt::TerminalApp::implementation
|
||||
Content(_rootPane->GetRootElement());
|
||||
|
||||
_MakeTabViewItem();
|
||||
_MakeSwitchToTabCommand();
|
||||
_CreateContextMenu();
|
||||
}
|
||||
|
||||
@@ -46,18 +45,18 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_MakeTabViewItem()
|
||||
void TerminalTab::_MakeTabViewItem()
|
||||
{
|
||||
_tabViewItem = ::winrt::MUX::Controls::TabViewItem{};
|
||||
TabViewItem(::winrt::MUX::Controls::TabViewItem{});
|
||||
|
||||
_tabViewItem.DoubleTapped([weakThis = get_weak()](auto&& /*s*/, auto&& /*e*/) {
|
||||
TabViewItem().DoubleTapped([weakThis = get_weak()](auto&& /*s*/, auto&& /*e*/) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->ActivateTabRenamer();
|
||||
}
|
||||
});
|
||||
|
||||
_UpdateTitle();
|
||||
UpdateTitle();
|
||||
_RecalculateAndApplyTabColor();
|
||||
}
|
||||
|
||||
@@ -72,22 +71,11 @@ namespace winrt::TerminalApp::implementation
|
||||
// Return Value:
|
||||
// - nullptr if no children were marked `_lastFocused`, else the TermControl
|
||||
// that was last focused.
|
||||
TermControl Tab::GetActiveTerminalControl() const
|
||||
TermControl TerminalTab::GetActiveTerminalControl() const
|
||||
{
|
||||
return _activePane->GetTerminalControl();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the TabViewItem that represents this Tab
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The TabViewItem that represents this Tab
|
||||
winrt::MUX::Controls::TabViewItem Tab::GetTabViewItem()
|
||||
{
|
||||
return _tabViewItem;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called after construction of a Tab object to bind event handlers to its
|
||||
// associated Pane and TermControl object
|
||||
@@ -95,39 +83,29 @@ namespace winrt::TerminalApp::implementation
|
||||
// - control: reference to the TermControl object to bind event to
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::Initialize(const TermControl& control)
|
||||
void TerminalTab::Initialize(const TermControl& control)
|
||||
{
|
||||
_BindEventHandlers(control);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if this is the currently focused tab. For any set of tabs,
|
||||
// there should only be one tab that is marked as focused, though each tab has
|
||||
// no control over the other tabs in the set.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - true iff this tab is focused.
|
||||
bool Tab::IsFocused() const noexcept
|
||||
{
|
||||
return _focused;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates our focus state. If we're gaining focus, make sure to transfer
|
||||
// focus to the last focused terminal control in our tree of controls.
|
||||
// Arguments:
|
||||
// - focused: our new focus state. If true, we should be focused. If false, we
|
||||
// should be unfocused.
|
||||
// - focused: our new focus state
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::SetFocused(const bool focused)
|
||||
void TerminalTab::Focus(WUX::FocusState focusState)
|
||||
{
|
||||
_focused = focused;
|
||||
_focusState = focusState;
|
||||
|
||||
if (_focused)
|
||||
if (_focusState != FocusState::Unfocused)
|
||||
{
|
||||
_Focus();
|
||||
auto lastFocusedControl = GetActiveTerminalControl();
|
||||
if (lastFocusedControl)
|
||||
{
|
||||
lastFocusedControl.Focus(_focusState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +118,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Return Value:
|
||||
// - nullopt if no children of this tab were the last control to be
|
||||
// focused, else the GUID of the profile of the last control to be focused
|
||||
std::optional<GUID> Tab::GetFocusedProfile() const noexcept
|
||||
std::optional<GUID> TerminalTab::GetFocusedProfile() const noexcept
|
||||
{
|
||||
return _activePane->GetFocusedProfile();
|
||||
}
|
||||
@@ -152,7 +130,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - control: reference to the TermControl object to bind event to
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_BindEventHandlers(const TermControl& control) noexcept
|
||||
void TerminalTab::_BindEventHandlers(const TermControl& control) noexcept
|
||||
{
|
||||
_AttachEventHandlersToPane(_rootPane);
|
||||
_AttachEventHandlersToControl(control);
|
||||
@@ -165,35 +143,18 @@ namespace winrt::TerminalApp::implementation
|
||||
// - profile: The GUID of the profile these settings should apply to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::UpdateSettings(const TerminalSettings& settings, const GUID& profile)
|
||||
void TerminalTab::UpdateSettings(const TerminalSettings& settings, const GUID& profile)
|
||||
{
|
||||
_rootPane->UpdateSettings(settings, profile);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Focus the last focused control in our tree of panes.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_Focus()
|
||||
{
|
||||
_focused = true;
|
||||
|
||||
auto lastFocusedControl = GetActiveTerminalControl();
|
||||
if (lastFocusedControl)
|
||||
{
|
||||
lastFocusedControl.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Set the icon on the TabViewItem for this tab.
|
||||
// Arguments:
|
||||
// - iconPath: The new path string to use as the IconPath for our TabViewItem
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget Tab::UpdateIcon(const winrt::hstring iconPath)
|
||||
winrt::fire_and_forget TerminalTab::UpdateIcon(const winrt::hstring iconPath)
|
||||
{
|
||||
// Don't reload our icon if it hasn't changed.
|
||||
if (iconPath == _lastIconPath)
|
||||
@@ -205,13 +166,13 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
co_await winrt::resume_foreground(_tabViewItem.Dispatcher());
|
||||
co_await winrt::resume_foreground(TabViewItem().Dispatcher());
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
|
||||
IconSource(IconPathConverter::IconSourceWUX(_lastIconPath));
|
||||
_tabViewItem.IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
Icon(_lastIconPath);
|
||||
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
|
||||
|
||||
// Update SwitchToTab command's icon
|
||||
SwitchToTabCommand().Icon(_lastIconPath);
|
||||
@@ -225,7 +186,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the title string of the last focused terminal control in our tree.
|
||||
winrt::hstring Tab::GetActiveTitle() const
|
||||
winrt::hstring TerminalTab::_GetActiveTitle() const
|
||||
{
|
||||
if (!_runtimeTabText.empty())
|
||||
{
|
||||
@@ -243,14 +204,14 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget Tab::_UpdateTitle()
|
||||
winrt::fire_and_forget TerminalTab::UpdateTitle()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await winrt::resume_foreground(_tabViewItem.Dispatcher());
|
||||
co_await winrt::resume_foreground(TabViewItem().Dispatcher());
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// Bubble our current tab text to anyone who's listening for changes.
|
||||
Title(GetActiveTitle());
|
||||
Title(_GetActiveTitle());
|
||||
|
||||
// Update SwitchToTab command's name
|
||||
SwitchToTabCommand().Name(Title());
|
||||
@@ -268,7 +229,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - delta: a number of lines to move the viewport relative to the current viewport.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget Tab::Scroll(const int delta)
|
||||
winrt::fire_and_forget TerminalTab::Scroll(const int delta)
|
||||
{
|
||||
auto control = GetActiveTerminalControl();
|
||||
|
||||
@@ -284,7 +245,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - splitType: The type of split we want to create.
|
||||
// Return Value:
|
||||
// - True if the focused pane can be split. False otherwise.
|
||||
bool Tab::CanSplitPane(SplitState splitType)
|
||||
bool TerminalTab::CanSplitPane(SplitState splitType)
|
||||
{
|
||||
return _activePane->CanSplit(splitType);
|
||||
}
|
||||
@@ -298,7 +259,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - control: A TermControl to use in the new pane.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control)
|
||||
void TerminalTab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control)
|
||||
{
|
||||
auto [first, second] = _activePane->Split(splitType, profile, control);
|
||||
_activePane = first;
|
||||
@@ -318,7 +279,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Method Description:
|
||||
// - See Pane::CalcSnappedDimension
|
||||
float Tab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
|
||||
float TerminalTab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
|
||||
{
|
||||
return _rootPane->CalcSnappedDimension(widthOrHeight, dimension);
|
||||
}
|
||||
@@ -330,7 +291,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - newSize: the amount of space that the panes have to fill now.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ResizeContent(const winrt::Windows::Foundation::Size& newSize)
|
||||
void TerminalTab::ResizeContent(const winrt::Windows::Foundation::Size& newSize)
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
@@ -344,7 +305,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - direction: The direction to move the separator in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ResizePane(const Direction& direction)
|
||||
void TerminalTab::ResizePane(const Direction& direction)
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
@@ -358,7 +319,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - direction: The direction to move the focus in.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::NavigateFocus(const Direction& direction)
|
||||
void TerminalTab::NavigateFocus(const Direction& direction)
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
@@ -367,7 +328,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Method Description:
|
||||
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
|
||||
void Tab::Shutdown()
|
||||
void TerminalTab::Shutdown()
|
||||
{
|
||||
_rootPane->Shutdown();
|
||||
}
|
||||
@@ -380,21 +341,21 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ClosePane()
|
||||
void TerminalTab::ClosePane()
|
||||
{
|
||||
_activePane->Close();
|
||||
}
|
||||
|
||||
void Tab::SetTabText(winrt::hstring title)
|
||||
void TerminalTab::SetTabText(winrt::hstring title)
|
||||
{
|
||||
_runtimeTabText = title;
|
||||
_UpdateTitle();
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
void Tab::ResetTabText()
|
||||
void TerminalTab::ResetTabText()
|
||||
{
|
||||
_runtimeTabText = L"";
|
||||
_UpdateTitle();
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -404,7 +365,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ActivateTabRenamer()
|
||||
void TerminalTab::ActivateTabRenamer()
|
||||
{
|
||||
_inRename = true;
|
||||
_receivedKeyDown = false;
|
||||
@@ -421,7 +382,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - control: the TermControl to add events to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_AttachEventHandlersToControl(const TermControl& control)
|
||||
void TerminalTab::_AttachEventHandlersToControl(const TermControl& control)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
@@ -431,7 +392,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// The title of the control changed, but not necessarily the title of the tab.
|
||||
// Set the tab's text to the active panes' text.
|
||||
tab->_UpdateTitle();
|
||||
tab->UpdateTitle();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -468,7 +429,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - pane: a Pane to mark as active.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_UpdateActivePane(std::shared_ptr<Pane> pane)
|
||||
void TerminalTab::_UpdateActivePane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
// Clear the active state of the entire tree, and mark only the pane as active.
|
||||
_rootPane->ClearActive();
|
||||
@@ -476,7 +437,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_activePane->SetActive();
|
||||
|
||||
// Update our own title text to match the newly-active pane.
|
||||
_UpdateTitle();
|
||||
UpdateTitle();
|
||||
|
||||
// Raise our own ActivePaneChanged event.
|
||||
_ActivePaneChangedHandlers();
|
||||
@@ -491,7 +452,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_AttachEventHandlersToPane(std::shared_ptr<Pane> pane)
|
||||
void TerminalTab::_AttachEventHandlersToPane(std::shared_ptr<Pane> pane)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
@@ -531,7 +492,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_CreateContextMenu()
|
||||
void TerminalTab::_CreateContextMenu()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
@@ -605,57 +566,7 @@ namespace winrt::TerminalApp::implementation
|
||||
newTabFlyout.Items().Append(menuSeparator);
|
||||
newTabFlyout.Items().Append(_CreateCloseSubMenu());
|
||||
newTabFlyout.Items().Append(closeTabMenuItem);
|
||||
_tabViewItem.ContextFlyout(newTabFlyout);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a sub-menu containing menu items to close multiple tabs
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - the created MenuFlyoutSubItem
|
||||
Controls::MenuFlyoutSubItem Tab::_CreateCloseSubMenu()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
// Close tabs after
|
||||
_closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_CloseTabsAfter();
|
||||
}
|
||||
});
|
||||
_closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
|
||||
|
||||
// Close other tabs
|
||||
_closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_CloseOtherTabs();
|
||||
}
|
||||
});
|
||||
_closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
|
||||
|
||||
Controls::MenuFlyoutSubItem closeSubMenu;
|
||||
closeSubMenu.Text(RS_(L"TabCloseSubMenu"));
|
||||
closeSubMenu.Items().Append(_closeTabsAfterMenuItem);
|
||||
closeSubMenu.Items().Append(_closeOtherTabsMenuItem);
|
||||
|
||||
return closeSubMenu;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enable the Close menu items based on tab index and total number of tabs
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_EnableCloseMenuItems()
|
||||
{
|
||||
// close other tabs is enabled only if there are other tabs
|
||||
_closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
|
||||
// close tabs after is enabled only if there are other tabs on the right
|
||||
_closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);
|
||||
TabViewItem().ContextFlyout(newTabFlyout);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -670,9 +581,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_UpdateTabHeader()
|
||||
void TerminalTab::_UpdateTabHeader()
|
||||
{
|
||||
winrt::hstring tabText{ GetActiveTitle() };
|
||||
winrt::hstring tabText{ Title() };
|
||||
|
||||
if (!_inRename)
|
||||
{
|
||||
@@ -690,13 +601,13 @@ namespace winrt::TerminalApp::implementation
|
||||
tb.Text(tabText);
|
||||
sp.Children().Append(tb);
|
||||
|
||||
_tabViewItem.Header(sp);
|
||||
TabViewItem().Header(sp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're not currently in the process of renaming the tab,
|
||||
// then just set the tab's text to whatever our active title is.
|
||||
_tabViewItem.Header(winrt::box_value(tabText));
|
||||
TabViewItem().Header(winrt::box_value(tabText));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -713,9 +624,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// - tabText: This should be the text to initialize the rename text box with.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_ConstructTabRenameBox(const winrt::hstring& tabText)
|
||||
void TerminalTab::_ConstructTabRenameBox(const winrt::hstring& tabText)
|
||||
{
|
||||
if (_tabViewItem.Header().try_as<Controls::TextBox>())
|
||||
if (TabViewItem().Header().try_as<Controls::TextBox>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -763,7 +674,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
tab->_runtimeTabText = textBox.Text();
|
||||
tab->_inRename = false;
|
||||
tab->_UpdateTitle();
|
||||
tab->UpdateTitle();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -793,7 +704,7 @@ namespace winrt::TerminalApp::implementation
|
||||
e.Handled(true);
|
||||
textBox.Text(tab->_runtimeTabText);
|
||||
tab->_inRename = false;
|
||||
tab->_UpdateTitle();
|
||||
tab->UpdateTitle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -803,7 +714,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_tabRenameBoxLayoutUpdatedRevoker = tabTextBox.LayoutUpdated(winrt::auto_revoke, [this](auto&&, auto&&) {
|
||||
// Curiously, the sender for this event is null, so we have to
|
||||
// get the TextBox from the Tab's Header().
|
||||
auto textBox{ _tabViewItem.Header().try_as<Controls::TextBox>() };
|
||||
auto textBox{ TabViewItem().Header().try_as<Controls::TextBox>() };
|
||||
if (textBox)
|
||||
{
|
||||
textBox.SelectAll();
|
||||
@@ -813,7 +724,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_tabRenameBoxLayoutUpdatedRevoker.revoke();
|
||||
});
|
||||
|
||||
_tabViewItem.Header(tabTextBox);
|
||||
TabViewItem().Header(tabTextBox);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -822,7 +733,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The tab's color, if any
|
||||
std::optional<winrt::Windows::UI::Color> Tab::GetTabColor()
|
||||
std::optional<winrt::Windows::UI::Color> TerminalTab::GetTabColor()
|
||||
{
|
||||
const auto currControlColor{ GetActiveTerminalControl().TabColor() };
|
||||
std::optional<winrt::Windows::UI::Color> controlTabColor;
|
||||
@@ -859,7 +770,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - color: the color the user picked for their tab
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color)
|
||||
void TerminalTab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color)
|
||||
{
|
||||
_runtimeTabColor.emplace(color);
|
||||
_RecalculateAndApplyTabColor();
|
||||
@@ -874,11 +785,11 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_RecalculateAndApplyTabColor()
|
||||
void TerminalTab::_RecalculateAndApplyTabColor()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
|
||||
TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
|
||||
auto ptrTab = weakThis.get();
|
||||
if (!ptrTab)
|
||||
return;
|
||||
@@ -906,7 +817,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - color: the color the user picked for their tab
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_ApplyTabColor(const winrt::Windows::UI::Color& color)
|
||||
void TerminalTab::_ApplyTabColor(const winrt::Windows::UI::Color& color)
|
||||
{
|
||||
Media::SolidColorBrush selectedTabBrush{};
|
||||
Media::SolidColorBrush deselectedTabBrush{};
|
||||
@@ -935,15 +846,15 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// currently if a tab has a custom color, a deselected state is
|
||||
// signified by using the same color with a bit ot transparency
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
|
||||
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
|
||||
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);
|
||||
|
||||
_RefreshVisualState();
|
||||
|
||||
@@ -958,7 +869,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ResetRuntimeTabColor()
|
||||
void TerminalTab::ResetRuntimeTabColor()
|
||||
{
|
||||
_runtimeTabColor.reset();
|
||||
_RecalculateAndApplyTabColor();
|
||||
@@ -971,7 +882,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_ClearTabBackgroundColor()
|
||||
void TerminalTab::_ClearTabBackgroundColor()
|
||||
{
|
||||
winrt::hstring keys[] = {
|
||||
L"TabViewItemHeaderBackground",
|
||||
@@ -989,9 +900,9 @@ namespace winrt::TerminalApp::implementation
|
||||
for (auto keyString : keys)
|
||||
{
|
||||
auto key = winrt::box_value(keyString);
|
||||
if (_tabViewItem.Resources().HasKey(key))
|
||||
if (TabViewItem().Resources().HasKey(key))
|
||||
{
|
||||
_tabViewItem.Resources().Remove(key);
|
||||
TabViewItem().Resources().Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1005,9 +916,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ActivateColorPicker()
|
||||
void TerminalTab::ActivateColorPicker()
|
||||
{
|
||||
_tabColorPickup.ShowAt(_tabViewItem);
|
||||
_tabColorPickup.ShowAt(TabViewItem());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1017,17 +928,17 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_RefreshVisualState()
|
||||
void TerminalTab::_RefreshVisualState()
|
||||
{
|
||||
if (_focused)
|
||||
if (_focusState != FocusState::Unfocused)
|
||||
{
|
||||
VisualStateManager::GoToState(_tabViewItem, L"Normal", true);
|
||||
VisualStateManager::GoToState(_tabViewItem, L"Selected", true);
|
||||
VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
|
||||
VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager::GoToState(_tabViewItem, L"Selected", true);
|
||||
VisualStateManager::GoToState(_tabViewItem, L"Normal", true);
|
||||
VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
|
||||
VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,7 +948,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - The total number of leaf panes hosted by this tab.
|
||||
int Tab::GetLeafPaneCount() const noexcept
|
||||
int TerminalTab::GetLeafPaneCount() const noexcept
|
||||
{
|
||||
return _rootPane->GetLeafPaneCount();
|
||||
}
|
||||
@@ -1052,12 +963,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// Return Value:
|
||||
// - The SplitState that we should use for an `Automatic` split given
|
||||
// `availableSpace`
|
||||
SplitState Tab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
|
||||
SplitState TerminalTab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
|
||||
{
|
||||
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical);
|
||||
}
|
||||
|
||||
bool Tab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const
|
||||
bool TerminalTab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const
|
||||
{
|
||||
return _rootPane->PreCalculateCanSplit(_activePane, splitType, availableSpace).value_or(false);
|
||||
}
|
||||
@@ -1066,13 +977,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Toggle our zoom state.
|
||||
// * If we're not zoomed, then zoom the active pane, making it take the
|
||||
// full size of the tab. We'll achieve this by changing our response to
|
||||
// Tab::GetRootElement, so that it'll return the zoomed pane only.
|
||||
// Tab::GetTabContent, so that it'll return the zoomed pane only.
|
||||
// * If we're currently zoomed on a pane, un-zoom that pane.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::ToggleZoom()
|
||||
void TerminalTab::ToggleZoom()
|
||||
{
|
||||
if (_zoomedPane)
|
||||
{
|
||||
@@ -1083,7 +994,7 @@ namespace winrt::TerminalApp::implementation
|
||||
EnterZoom();
|
||||
}
|
||||
}
|
||||
void Tab::EnterZoom()
|
||||
void TerminalTab::EnterZoom()
|
||||
{
|
||||
_zoomedPane = _activePane;
|
||||
_rootPane->Maximize(_zoomedPane);
|
||||
@@ -1091,7 +1002,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_UpdateTabHeader();
|
||||
Content(_zoomedPane->GetRootElement());
|
||||
}
|
||||
void Tab::ExitZoom()
|
||||
void TerminalTab::ExitZoom()
|
||||
{
|
||||
_rootPane->Restore(_zoomedPane);
|
||||
_zoomedPane = nullptr;
|
||||
@@ -1100,60 +1011,12 @@ namespace winrt::TerminalApp::implementation
|
||||
Content(_rootPane->GetRootElement());
|
||||
}
|
||||
|
||||
bool Tab::IsZoomed()
|
||||
bool TerminalTab::IsZoomed()
|
||||
{
|
||||
return _zoomedPane != nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Initializes a SwitchToTab command object for this Tab instance.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Tab::_MakeSwitchToTabCommand()
|
||||
{
|
||||
SwitchToTabArgs args{ _TabViewIndex };
|
||||
ActionAndArgs focusTabAction{ ShortcutAction::SwitchToTab, args };
|
||||
|
||||
Command command;
|
||||
command.Action(focusTabAction);
|
||||
command.Name(Title());
|
||||
command.Icon(_lastIconPath);
|
||||
|
||||
SwitchToTabCommand(command);
|
||||
}
|
||||
|
||||
void Tab::_CloseTabsAfter()
|
||||
{
|
||||
CloseTabsAfterArgs args{ _TabViewIndex };
|
||||
ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args };
|
||||
|
||||
_dispatch.DoAction(closeTabsAfter);
|
||||
}
|
||||
|
||||
void Tab::_CloseOtherTabs()
|
||||
{
|
||||
CloseOtherTabsArgs args{ _TabViewIndex };
|
||||
ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args };
|
||||
|
||||
_dispatch.DoAction(closeOtherTabs);
|
||||
}
|
||||
|
||||
void Tab::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs)
|
||||
{
|
||||
TabViewIndex(idx);
|
||||
TabViewNumTabs(numTabs);
|
||||
_EnableCloseMenuItems();
|
||||
SwitchToTabCommand().Action().Args().as<SwitchToTabArgs>().TabIndex(idx);
|
||||
}
|
||||
|
||||
void Tab::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
|
||||
{
|
||||
_dispatch = dispatch;
|
||||
}
|
||||
|
||||
DEFINE_EVENT(Tab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(Tab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DEFINE_EVENT(Tab, ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
#pragma once
|
||||
#include "Pane.h"
|
||||
#include "ColorPickupFlyout.h"
|
||||
#include "Tab.g.h"
|
||||
#include "TabBase.h"
|
||||
#include "TerminalTab.g.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
@@ -14,21 +15,18 @@ namespace TerminalAppLocalTests
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct Tab : public TabT<Tab>
|
||||
struct TerminalTab : TerminalTabT<TerminalTab, TabBase>
|
||||
{
|
||||
public:
|
||||
Tab() = delete;
|
||||
Tab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
TerminalTab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
// Called after construction to perform the necessary setup, which relies on weak_ptr
|
||||
void Initialize(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
|
||||
|
||||
winrt::Microsoft::UI::Xaml::Controls::TabViewItem GetTabViewItem();
|
||||
winrt::Microsoft::Terminal::TerminalControl::TermControl GetActiveTerminalControl() const;
|
||||
std::optional<GUID> GetFocusedProfile() const noexcept;
|
||||
|
||||
bool IsFocused() const noexcept;
|
||||
void SetFocused(const bool focused);
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
|
||||
|
||||
winrt::fire_and_forget Scroll(const int delta);
|
||||
|
||||
@@ -46,9 +44,9 @@ namespace winrt::TerminalApp::implementation
|
||||
void NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::Direction& direction);
|
||||
|
||||
void UpdateSettings(const winrt::TerminalApp::TerminalSettings& settings, const GUID& profile);
|
||||
winrt::hstring GetActiveTitle() const;
|
||||
winrt::fire_and_forget UpdateTitle();
|
||||
|
||||
void Shutdown();
|
||||
void Shutdown() override;
|
||||
void ClosePane();
|
||||
|
||||
void SetTabText(winrt::hstring title);
|
||||
@@ -68,28 +66,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
int GetLeafPaneCount() const noexcept;
|
||||
|
||||
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
|
||||
|
||||
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
|
||||
|
||||
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::Controls::IconSource, IconSource, _PropertyChangedHandlers, nullptr);
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::Command, SwitchToTabCommand, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
// The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector.
|
||||
// This is needed since Tab is going to be managing its own SwitchToTab command.
|
||||
OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewIndex, _PropertyChangedHandlers, 0);
|
||||
// The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector.
|
||||
OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewNumTabs, _PropertyChangedHandlers, 0);
|
||||
|
||||
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::UIElement, Content, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pane> _rootPane{ nullptr };
|
||||
std::shared_ptr<Pane> _activePane{ nullptr };
|
||||
@@ -101,9 +81,7 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{};
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{};
|
||||
|
||||
bool _focused{ false };
|
||||
bool _receivedKeyDown{ false };
|
||||
winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr };
|
||||
|
||||
winrt::hstring _runtimeTabText{};
|
||||
bool _inRename{ false };
|
||||
@@ -112,11 +90,8 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
|
||||
|
||||
void _MakeTabViewItem();
|
||||
void _Focus();
|
||||
|
||||
void _CreateContextMenu();
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _CreateCloseSubMenu();
|
||||
void _EnableCloseMenuItems();
|
||||
void _CreateContextMenu() override;
|
||||
|
||||
void _RefreshVisualState();
|
||||
|
||||
@@ -127,19 +102,14 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _UpdateActivePane(std::shared_ptr<Pane> pane);
|
||||
|
||||
winrt::hstring _GetActiveTitle() const;
|
||||
void _UpdateTabHeader();
|
||||
winrt::fire_and_forget _UpdateTitle();
|
||||
void _ConstructTabRenameBox(const winrt::hstring& tabText);
|
||||
|
||||
void _RecalculateAndApplyTabColor();
|
||||
void _ApplyTabColor(const winrt::Windows::UI::Color& color);
|
||||
void _ClearTabBackgroundColor();
|
||||
|
||||
void _MakeSwitchToTabCommand();
|
||||
|
||||
void _CloseTabsAfter();
|
||||
void _CloseOtherTabs();
|
||||
|
||||
friend class ::TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
}
|
||||
11
src/cascadia/TerminalApp/TerminalTab.idl
Normal file
11
src/cascadia/TerminalApp/TerminalTab.idl
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "TabBase.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TerminalTab : TabBase
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,8 @@
|
||||
<ClInclude Include="../TitlebarControl.h" />
|
||||
<ClInclude Include="../TabRowControl.h" />
|
||||
<ClInclude Include="../App.h" />
|
||||
<ClInclude Include="../Tab.h" />
|
||||
<ClInclude Include="../TerminalTab.h" />
|
||||
<ClInclude Include="../SettingsTab.h" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
||||
@@ -587,21 +587,21 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// This event is only registered during terminal initialization,
|
||||
// so we don't need to check _initializedTerminal.
|
||||
// We also don't lock for things that come back from the renderer.
|
||||
auto chain = _renderEngine->GetSwapChain();
|
||||
auto chainHandle = _renderEngine->GetSwapChainHandle();
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
co_await winrt::resume_foreground(Dispatcher());
|
||||
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
_AttachDxgiSwapChainToXaml(chain.Get());
|
||||
_AttachDxgiSwapChainToXaml(chainHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void TermControl::_AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain)
|
||||
void TermControl::_AttachDxgiSwapChainToXaml(HANDLE swapChainHandle)
|
||||
{
|
||||
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
|
||||
nativePanel->SetSwapChain(swapChain);
|
||||
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative2>();
|
||||
nativePanel->SetSwapChainHandle(swapChainHandle);
|
||||
}
|
||||
|
||||
bool TermControl::_InitializeTerminal()
|
||||
@@ -705,7 +705,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
THROW_IF_FAILED(dxEngine->Enable());
|
||||
_renderEngine = std::move(dxEngine);
|
||||
|
||||
_AttachDxgiSwapChainToXaml(_renderEngine->GetSwapChain().Get());
|
||||
_AttachDxgiSwapChainToXaml(_renderEngine->GetSwapChainHandle());
|
||||
|
||||
// Tell the DX Engine to notify us when the swap chain changes.
|
||||
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems)
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void ToggleRetroEffect();
|
||||
|
||||
winrt::fire_and_forget RenderEngineSwapChainChanged();
|
||||
void _AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain);
|
||||
void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle);
|
||||
winrt::fire_and_forget _RendererEnteredErrorState();
|
||||
void _RenderRetryButton_Click(IInspectable const& button, IInspectable const& args);
|
||||
|
||||
|
||||
@@ -109,62 +109,63 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
||||
// Make sure to call this so we get WM_POINTER messages.
|
||||
EnableMouseInPointer(true);
|
||||
|
||||
// !!! LOAD BEARING !!!
|
||||
// We must initialize the main thread as a single-threaded apartment before
|
||||
// constructing any Xaml objects. Failing to do so will cause some issues
|
||||
// in accessibility somewhere down the line when a UIAutomation object will
|
||||
// be queried on the wrong thread at the wrong time.
|
||||
// We used to initialize as STA only _after_ initializing the application
|
||||
// host, which loaded the settings. The settings needed to be loaded in MTA
|
||||
// because we were using the Windows.Storage APIs. Since we're no longer
|
||||
// doing that, we can safely init as STA before any WinRT dispatches.
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
auto mainLoop = []() {
|
||||
// !!! LOAD BEARING !!!
|
||||
// We must initialize the main thread as a single-threaded apartment before
|
||||
// constructing any Xaml objects. Failing to do so will cause some issues
|
||||
// in accessibility somewhere down the line when a UIAutomation object will
|
||||
// be queried on the wrong thread at the wrong time.
|
||||
// We used to initialize as STA only _after_ initializing the application
|
||||
// host, which loaded the settings. The settings needed to be loaded in MTA
|
||||
// because we were using the Windows.Storage APIs. Since we're no longer
|
||||
// doing that, we can safely init as STA before any WinRT dispatches.
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
|
||||
// Create the AppHost object, which will create both the window and the
|
||||
// Terminal App. This MUST BE constructed before the Xaml manager as TermApp
|
||||
// provides an implementation of Windows.UI.Xaml.Application.
|
||||
AppHost host;
|
||||
// Create the AppHost object, which will create both the window and the
|
||||
// Terminal App. This MUST BE constructed before the Xaml manager as TermApp
|
||||
// provides an implementation of Windows.UI.Xaml.Application.
|
||||
AppHost host;
|
||||
|
||||
// Initialize the xaml content. This must be called AFTER the
|
||||
// WindowsXamlManager is initialized.
|
||||
host.Initialize();
|
||||
// Initialize the xaml content. This must be called AFTER the
|
||||
// WindowsXamlManager is initialized.
|
||||
host.Initialize();
|
||||
|
||||
MSG message;
|
||||
MSG message;
|
||||
|
||||
while (GetMessage(&message, nullptr, 0, 0))
|
||||
{
|
||||
// GH#638 (Pressing F7 brings up both the history AND a caret browsing message)
|
||||
// The Xaml input stack doesn't allow an application to suppress the "caret browsing"
|
||||
// dialog experience triggered when you press F7. Official recommendation from the Xaml
|
||||
// team is to catch F7 before we hand it off.
|
||||
// AppLogic contains an ad-hoc implementation of event bubbling for a runtime classes
|
||||
// implementing a custom IF7Listener interface.
|
||||
// If the recipient of IF7Listener::OnF7Pressed suggests that the F7 press has, in fact,
|
||||
// been handled we can discard the message before we even translate it.
|
||||
if (_messageIsF7Keypress(message))
|
||||
while (GetMessage(&message, nullptr, 0, 0))
|
||||
{
|
||||
if (host.OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true))
|
||||
{
|
||||
// The application consumed the F7. Don't let Xaml get it.
|
||||
continue;
|
||||
if (host.OnDirectKeyEvent(VK_F7, true))
|
||||
{
|
||||
// The application consumed the F7. Don't let Xaml get it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GH#6421 - System XAML will never send an Alt KeyUp event. So, similar
|
||||
// to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp
|
||||
// here, and plumb it through.
|
||||
if (_messageIsAltKeyup(message))
|
||||
{
|
||||
// Let's pass <Alt> to the application
|
||||
if (host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false))
|
||||
// GH#6421 - System XAML will never send an Alt KeyUp event. So, similar
|
||||
// to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp
|
||||
// here, and plumb it through.
|
||||
if (_messageIsAltKeyup(message))
|
||||
{
|
||||
// The application consumed the Alt. Don't let Xaml get it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Let's pass <Alt> to the application
|
||||
if (host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false))
|
||||
{
|
||||
// Let's pass <Alt> to the application
|
||||
if (host.OnDirectKeyEvent(VK_MENU, false))
|
||||
{
|
||||
// The application consumed the Alt. Don't let Xaml get it.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
TranslateMessage(&message);
|
||||
DispatchMessage(&message);
|
||||
TranslateMessage(&message);
|
||||
DispatchMessage(&message);
|
||||
}
|
||||
};
|
||||
|
||||
std::thread t{ mainLoop };
|
||||
mainLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
winrt::event_token name(args const& handler) { return _##name##Handlers.add(handler); } \
|
||||
void name(winrt::event_token const& token) { _##name##Handlers.remove(token); } \
|
||||
\
|
||||
private: \
|
||||
protected: \
|
||||
winrt::event<args> _##name##Handlers;
|
||||
|
||||
// This is a helper macro for both declaring the signature and body of an event
|
||||
@@ -128,7 +128,7 @@ private:
|
||||
// (like when the class is being initialized).
|
||||
#define OBSERVABLE_GETSET_PROPERTY(type, name, event, ...) \
|
||||
public: \
|
||||
type name() { return _##name; }; \
|
||||
type name() const noexcept { return _##name; }; \
|
||||
void name(const type& value) \
|
||||
{ \
|
||||
if (_##name != value) \
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>onecoreuap_apiset.lib;d3dcompiler.lib;dwmapi.lib;uxtheme.lib;shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>onecoreuap_apiset.lib;d3dcompiler.lib;dwmapi.lib;uxtheme.lib;shlwapi.lib;ntdll.lib;dcomp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
||||
@@ -1141,7 +1141,7 @@ size_t Alias::s_ReplaceMacros(std::wstring& str,
|
||||
// - If we found a matching alias, this will be the processed data
|
||||
// and lineCount is updated to the new number of lines.
|
||||
// - If we didn't match and process an alias, return an empty string.
|
||||
std::wstring Alias::s_MatchAndCopyAlias(const std::wstring_view sourceText,
|
||||
std::wstring Alias::s_MatchAndCopyAlias(const std::wstring& sourceText,
|
||||
const std::wstring& exeName,
|
||||
size_t& lineCount)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
const std::wstring& exeName,
|
||||
DWORD& lines);
|
||||
|
||||
static std::wstring s_MatchAndCopyAlias(const std::wstring_view sourceText,
|
||||
static std::wstring s_MatchAndCopyAlias(const std::wstring& sourceText,
|
||||
const std::wstring& exeName,
|
||||
size_t& lineCount);
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include <future>
|
||||
|
||||
// This particular block is necessary so we can include both a UNICODE and non-UNICODE version
|
||||
// of our test supporting function so we can accurately portray and measure both types of text
|
||||
// and test both versions of the console API.
|
||||
@@ -49,24 +47,6 @@ class AliasTests
|
||||
TEST_METHOD_PROPERTY(L"Data:bUnicode", L"{FALSE, TRUE}")
|
||||
TEST_METHOD_PROPERTY(L"Data:bSetFirst", L"{FALSE, TRUE}")
|
||||
END_TEST_METHOD()
|
||||
|
||||
BEGIN_TEST_METHOD(TestCookedAliasProcessing)
|
||||
TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
|
||||
END_TEST_METHOD()
|
||||
|
||||
BEGIN_TEST_METHOD(TestCookedTextEntry)
|
||||
TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
|
||||
END_TEST_METHOD()
|
||||
|
||||
BEGIN_TEST_METHOD(TestCookedAlphaPermutations)
|
||||
//TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
|
||||
TEST_METHOD_PROPERTY(L"Data:inputcp", L"{437, 932}")
|
||||
TEST_METHOD_PROPERTY(L"Data:outputcp", L"{437, 932}")
|
||||
TEST_METHOD_PROPERTY(L"Data:inputmode", L"{487, 481}") // 487 is 0x1e7, 485 is 0x1e1 (ENABLE_LINE_INPUT on/off)
|
||||
TEST_METHOD_PROPERTY(L"Data:outputmode", L"{7}")
|
||||
TEST_METHOD_PROPERTY(L"Data:font", L"{Consolas, MS Gothic}")
|
||||
END_TEST_METHOD()
|
||||
|
||||
};
|
||||
|
||||
// Caller must free ppsz if not null.
|
||||
@@ -163,284 +143,3 @@ void AliasTests::TestGetConsoleAlias()
|
||||
bSetFirst);
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<INPUT_RECORD> _stringToInputs(std::wstring_view wstr)
|
||||
{
|
||||
std::vector<INPUT_RECORD> result;
|
||||
for (const auto& wch : wstr)
|
||||
{
|
||||
INPUT_RECORD ir = { 0 };
|
||||
ir.EventType = KEY_EVENT;
|
||||
ir.Event.KeyEvent.bKeyDown = TRUE;
|
||||
ir.Event.KeyEvent.dwControlKeyState = 0;
|
||||
ir.Event.KeyEvent.uChar.UnicodeChar = wch;
|
||||
ir.Event.KeyEvent.wRepeatCount = 1;
|
||||
ir.Event.KeyEvent.wVirtualKeyCode = VkKeyScanW(wch);
|
||||
ir.Event.KeyEvent.wVirtualScanCode = gsl::narrow<WORD>(MapVirtualKeyW(ir.Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC));
|
||||
|
||||
result.emplace_back(ir);
|
||||
|
||||
ir.Event.KeyEvent.bKeyDown = FALSE;
|
||||
|
||||
result.emplace_back(ir);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AliasTests::TestCookedAliasProcessing()
|
||||
{
|
||||
const auto in = GetStdInputHandle();
|
||||
|
||||
DWORD originalInMode = 0;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(in, &originalInMode));
|
||||
|
||||
DWORD originalCodepage = GetConsoleCP();
|
||||
|
||||
auto restoreInModeOnExit = wil::scope_exit([&]
|
||||
{
|
||||
SetConsoleMode(in, originalInMode);
|
||||
SetConsoleCP(originalCodepage);
|
||||
});
|
||||
|
||||
const DWORD testInMode = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(in, testInMode));
|
||||
|
||||
auto modulePath = wil::GetModuleFileNameW<std::wstring>(nullptr);
|
||||
std::filesystem::path path{ modulePath };
|
||||
auto fileName = path.filename();
|
||||
auto exeName = fileName.wstring();
|
||||
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(AddConsoleAliasW(L"foo", L"echo bar$Techo baz$Techo bam", exeName.data()));
|
||||
|
||||
std::wstring commandWritten = L"foo\r\n";
|
||||
std::queue<std::string> commandExpected;
|
||||
commandExpected.push("echo bar\r");
|
||||
commandExpected.push("echo baz\r");
|
||||
commandExpected.push("echo bam\r");
|
||||
|
||||
auto inputs = _stringToInputs(commandWritten);
|
||||
|
||||
DWORD written = 0;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleInputW(in, inputs.data(), gsl::narrow<DWORD>(inputs.size()), &written));
|
||||
|
||||
std::string buf;
|
||||
buf.resize(500);
|
||||
|
||||
while (!commandExpected.empty())
|
||||
{
|
||||
DWORD read = 0;
|
||||
|
||||
auto tryRead = std::async(std::launch::async, [&] {
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleA(in, buf.data(), gsl::narrow<DWORD>(buf.size()), &read, nullptr));
|
||||
});
|
||||
|
||||
if (std::future_status::ready != tryRead.wait_for(std::chrono::seconds{ 5 }))
|
||||
{
|
||||
// Shove something into the input to unstick it then fail.
|
||||
auto events = _stringToInputs(L"a\r\n");
|
||||
WriteConsoleInputW(in, events.data(), gsl::narrow<DWORD>(events.size()), &written);
|
||||
VERIFY_FAILED(HRESULT_FROM_NT(STATUS_TIMEOUT));
|
||||
|
||||
// If somehow this still isn't enough to unstick the thread, the whole test timeout is 1 min
|
||||
// set in the parameters/metadata at the top.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto actual = std::string{ buf.data(), read };
|
||||
|
||||
auto expected = commandExpected.front();
|
||||
commandExpected.pop();
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
void AliasTests::TestCookedTextEntry()
|
||||
{
|
||||
const auto in = GetStdInputHandle();
|
||||
|
||||
DWORD originalInMode = 0;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(in, &originalInMode));
|
||||
|
||||
DWORD originalCodepage = GetConsoleCP();
|
||||
|
||||
auto restoreInModeOnExit = wil::scope_exit([&] {
|
||||
SetConsoleMode(in, originalInMode);
|
||||
SetConsoleCP(originalCodepage);
|
||||
});
|
||||
|
||||
const DWORD testInMode = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(in, testInMode));
|
||||
|
||||
auto modulePath = wil::GetModuleFileNameW<std::wstring>(nullptr);
|
||||
std::filesystem::path path{ modulePath };
|
||||
auto fileName = path.filename();
|
||||
auto exeName = fileName.wstring();
|
||||
|
||||
|
||||
std::wstring commandWritten = L"foo\r\n";
|
||||
std::queue<std::string> commandExpected;
|
||||
commandExpected.push("foo\r\n");
|
||||
|
||||
auto inputs = _stringToInputs(commandWritten);
|
||||
|
||||
DWORD written = 0;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleInputW(in, inputs.data(), gsl::narrow<DWORD>(inputs.size()), &written));
|
||||
|
||||
std::string buf;
|
||||
buf.resize(500);
|
||||
|
||||
while (!commandExpected.empty())
|
||||
{
|
||||
DWORD read = 0;
|
||||
|
||||
auto tryRead = std::async(std::launch::async, [&] {
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleA(in, buf.data(), gsl::narrow<DWORD>(buf.size()), &read, nullptr));
|
||||
});
|
||||
|
||||
if (std::future_status::ready != tryRead.wait_for(std::chrono::seconds{ 5 }))
|
||||
{
|
||||
// Shove something into the input to unstick it then fail.
|
||||
auto events = _stringToInputs(L"a\r\n");
|
||||
WriteConsoleInputW(in, events.data(), gsl::narrow<DWORD>(events.size()), &written);
|
||||
VERIFY_FAILED(HRESULT_FROM_NT(STATUS_TIMEOUT));
|
||||
|
||||
// If somehow this still isn't enough to unstick the thread, the whole test timeout is 1 min
|
||||
// set in the parameters/metadata at the top.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto actual = std::string{ buf.data(), read };
|
||||
|
||||
auto expected = commandExpected.front();
|
||||
commandExpected.pop();
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
// tests todo:
|
||||
// - test alpha in/out in 437/932 permutations
|
||||
// - test leftover leadbyte/trailbyte when buffer too small
|
||||
void AliasTests::TestCookedAlphaPermutations()
|
||||
{
|
||||
DWORD inputcp, outputcp, inputmode, outputmode;
|
||||
String font;
|
||||
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"inputcp", inputcp), L"Get input cp");
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"outputcp", outputcp), L"Get output cp");
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"inputmode", inputmode), L"Get input mode");
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"outputmode", outputmode), L"Get output mode");
|
||||
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"font", font), L"Get font");
|
||||
|
||||
std::wstring wstrFont{ font };
|
||||
if (wstrFont == L"MS Gothic")
|
||||
{
|
||||
// MS Gothic... but in full width characters and the katakana representation...
|
||||
// MS GOSHIKKU romanized...
|
||||
wstrFont = L"\xff2d\xff33\x0020\x30b4\x30b7\x30c3\x30af";
|
||||
}
|
||||
|
||||
const auto in = GetStdInputHandle();
|
||||
const auto out = GetStdOutputHandle();
|
||||
|
||||
Log::Comment(L"Backup original modes and codepages and font.");
|
||||
|
||||
DWORD originalInMode, originalOutMode, originalInputCP, originalOutputCP;
|
||||
CONSOLE_FONT_INFOEX originalFont = { 0 };
|
||||
originalFont.cbSize = sizeof(originalFont);
|
||||
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(in, &originalInMode));
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(out, &originalOutMode));
|
||||
originalInputCP = GetConsoleCP();
|
||||
originalOutputCP = GetConsoleOutputCP();
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(GetCurrentConsoleFontEx(out, FALSE, &originalFont));
|
||||
|
||||
auto restoreModesOnExit = wil::scope_exit([&]
|
||||
{
|
||||
SetConsoleMode(in, originalInMode);
|
||||
SetConsoleMode(out, originalOutMode);
|
||||
SetConsoleCP(originalInputCP);
|
||||
SetConsoleOutputCP(originalOutputCP);
|
||||
SetCurrentConsoleFontEx(out, FALSE, &originalFont);
|
||||
});
|
||||
|
||||
Log::Comment(L"Apply our modes and codepages and font.");
|
||||
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(in, inputmode));
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(out, outputmode));
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleCP(inputcp));
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleOutputCP(outputcp));
|
||||
|
||||
auto ourFont = originalFont;
|
||||
wmemcpy_s(ourFont.FaceName, ARRAYSIZE(ourFont.FaceName), wstrFont.data(), wstrFont.size());
|
||||
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(SetCurrentConsoleFontEx(out, FALSE, &ourFont));
|
||||
|
||||
const wchar_t alpha = L'\u03b1';
|
||||
const std::string alpha437 = "\xe0";
|
||||
const std::string alpha932 = "\x83\xbf";
|
||||
|
||||
std::string expected = inputcp == 932 ? alpha932 : alpha437;
|
||||
|
||||
std::wstring sendInput;
|
||||
sendInput.append(&alpha, 1);
|
||||
|
||||
// If we're in line input, we have to send a newline and we'll get one back.
|
||||
if (WI_IsFlagSet(inputmode, ENABLE_LINE_INPUT))
|
||||
{
|
||||
expected.append("\r\n");
|
||||
sendInput.append(L"\r\n");
|
||||
}
|
||||
|
||||
Log::Comment(L"send the string");
|
||||
auto sendInputRecords = _stringToInputs(sendInput);
|
||||
DWORD written;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(WriteConsoleInputW(in, sendInputRecords.data(), gsl::narrow<DWORD>(sendInputRecords.size()), &written));
|
||||
|
||||
Log::Comment(L"receive the string");
|
||||
std::string recvInput;
|
||||
recvInput.resize(500); // excessively big
|
||||
DWORD read;
|
||||
VERIFY_WIN32_BOOL_SUCCEEDED(ReadConsoleA(in, recvInput.data(), gsl::narrow<DWORD>(recvInput.size()), &read, nullptr));
|
||||
|
||||
recvInput.resize(read);
|
||||
|
||||
// corruption magic
|
||||
// In MS Gothic, alpha is full width (2 columns)
|
||||
// In Consolas, alpha is half width (1 column)
|
||||
// Alpha itself is an ambiguous character, meaning the console finds the width
|
||||
// by asking the font.
|
||||
// Unfortunately, there's some code mixed up in the cooked read for a long time where
|
||||
// the width is used as a predictor of how many bytes it will consume.
|
||||
// In this specific combination of using a font where the ambiguous alpha is half width,
|
||||
// the output code page doesn't support double bytes, and the input code page does...
|
||||
// The result is stomped with a null as the conversion fails thinking it doesn't have enough space.
|
||||
if (wstrFont == L"Consolas" && inputcp == 932 && outputcp == 437)
|
||||
{
|
||||
VERIFY_IS_GREATER_THAN_OR_EQUAL(recvInput.size(), 1);
|
||||
|
||||
VERIFY_ARE_EQUAL('\x00', recvInput[0]);
|
||||
|
||||
if (WI_IsFlagSet(inputmode, ENABLE_LINE_INPUT))
|
||||
{
|
||||
VERIFY_IS_GREATER_THAN_OR_EQUAL(recvInput.size(), 3);
|
||||
VERIFY_ARE_EQUAL('\r', recvInput[1]);
|
||||
VERIFY_ARE_EQUAL('\n', recvInput[2]);
|
||||
}
|
||||
}
|
||||
// end corruption magic
|
||||
else
|
||||
{
|
||||
VERIFY_ARE_EQUAL(expected, recvInput);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO tests:
|
||||
// - leaving behind a lead/trail byte
|
||||
// - leaving behind a lead/trail byte and having more data
|
||||
// -- doing it in a loop/continuously.
|
||||
// - read it char by char
|
||||
// - change the codepage in the middle of reading and/or between commands
|
||||
|
||||
@@ -1002,267 +1002,198 @@ void COOKED_READ_DATA::SavePendingInput(const size_t index, const bool multiline
|
||||
// - Status code that indicates success, out of memory, etc.
|
||||
[[nodiscard]] NTSTATUS COOKED_READ_DATA::_handlePostCharInputLoop(const bool isUnicode, size_t& numBytes, ULONG& controlKeyState) noexcept
|
||||
{
|
||||
std::wstring inputLine{ _backupLimit, _bytesRead / sizeof(WCHAR) };
|
||||
|
||||
size_t lineCount = 1;
|
||||
DWORD LineCount = 1;
|
||||
|
||||
if (_echoInput)
|
||||
{
|
||||
const auto foundPos = inputLine.find(UNICODE_CARRIAGERETURN);
|
||||
|
||||
// If we find a carriage return...
|
||||
if (decltype(inputLine)::npos != foundPos)
|
||||
// Figure out where real string ends (at carriage return or end of buffer).
|
||||
PWCHAR StringPtr = _backupLimit;
|
||||
size_t StringLength = _bytesRead;
|
||||
bool FoundCR = false;
|
||||
for (size_t i = 0; i < (_bytesRead / sizeof(WCHAR)); i++)
|
||||
{
|
||||
if (*StringPtr++ == UNICODE_CARRIAGERETURN)
|
||||
{
|
||||
StringLength = i * sizeof(WCHAR);
|
||||
FoundCR = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FoundCR)
|
||||
{
|
||||
// ...and we have command history, save the command.
|
||||
if (_commandHistory)
|
||||
{
|
||||
const auto command = inputLine.substr(0, foundPos); // Save only the command up to the \r\n, not the \r\n itself.
|
||||
// add to command line recall list if we have a history list.
|
||||
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
LOG_IF_FAILED(_commandHistory->Add( command, WI_IsFlagSet(gci.Flags, CONSOLE_HISTORY_NODUP)));
|
||||
LOG_IF_FAILED(_commandHistory->Add({ _backupLimit, StringLength / sizeof(wchar_t) },
|
||||
WI_IsFlagSet(gci.Flags, CONSOLE_HISTORY_NODUP)));
|
||||
}
|
||||
|
||||
// Translate the alias if we find one that matches and replace our input line.
|
||||
const auto aliasedLine = Alias::s_MatchAndCopyAlias(inputLine, _exeName, lineCount);
|
||||
|
||||
if (!aliasedLine.empty())
|
||||
{
|
||||
inputLine = aliasedLine;
|
||||
}
|
||||
// check for alias
|
||||
ProcessAliases(LineCount);
|
||||
}
|
||||
}
|
||||
|
||||
gsl::span<byte> returnData;
|
||||
std::string translation;
|
||||
std::optional<DWORD> translationCP;
|
||||
|
||||
if (!isUnicode)
|
||||
bool fAddDbcsLead = false;
|
||||
size_t NumBytes = 0;
|
||||
// at this point, a->NumBytes contains the number of bytes in
|
||||
// the UNICODE string read. UserBufferSize contains the converted
|
||||
// size of the app's buffer.
|
||||
if (_bytesRead > _userBufferSize || LineCount > 1)
|
||||
{
|
||||
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
translationCP = gci.CP;
|
||||
translation = ConvertToA(*translationCP, inputLine);
|
||||
returnData = gsl::span<byte>{ reinterpret_cast<byte*>(translation.data()), translation.size() };
|
||||
if (LineCount > 1)
|
||||
{
|
||||
PWSTR Tmp;
|
||||
if (!isUnicode)
|
||||
{
|
||||
if (_pInputBuffer->IsReadPartialByteSequenceAvailable())
|
||||
{
|
||||
fAddDbcsLead = true;
|
||||
std::unique_ptr<IInputEvent> event = GetInputBuffer()->FetchReadPartialByteSequence(false);
|
||||
const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(event.get());
|
||||
*_userBuffer = static_cast<char>(pKeyEvent->GetCharData());
|
||||
_userBuffer++;
|
||||
_userBufferSize -= sizeof(wchar_t);
|
||||
}
|
||||
|
||||
NumBytes = 0;
|
||||
for (Tmp = _backupLimit;
|
||||
*Tmp != UNICODE_LINEFEED && _userBufferSize / sizeof(WCHAR) > NumBytes;
|
||||
Tmp++)
|
||||
{
|
||||
NumBytes += IsGlyphFullWidth(*Tmp) ? 2 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#pragma prefast(suppress: __WARNING_BUFFER_OVERFLOW, "LineCount > 1 means there's a UNICODE_LINEFEED")
|
||||
// clang-format on
|
||||
for (Tmp = _backupLimit; *Tmp != UNICODE_LINEFEED; Tmp++)
|
||||
{
|
||||
FAIL_FAST_IF(!(Tmp < (_backupLimit + _bytesRead)));
|
||||
}
|
||||
|
||||
numBytes = (ULONG)(Tmp - _backupLimit + 1) * sizeof(*Tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isUnicode)
|
||||
{
|
||||
PWSTR Tmp;
|
||||
|
||||
if (_pInputBuffer->IsReadPartialByteSequenceAvailable())
|
||||
{
|
||||
fAddDbcsLead = true;
|
||||
std::unique_ptr<IInputEvent> event = GetInputBuffer()->FetchReadPartialByteSequence(false);
|
||||
const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(event.get());
|
||||
*_userBuffer = static_cast<char>(pKeyEvent->GetCharData());
|
||||
_userBuffer++;
|
||||
_userBufferSize -= sizeof(wchar_t);
|
||||
}
|
||||
NumBytes = 0;
|
||||
size_t NumToWrite = _bytesRead;
|
||||
for (Tmp = _backupLimit;
|
||||
NumToWrite && _userBufferSize / sizeof(WCHAR) > NumBytes;
|
||||
Tmp++, NumToWrite -= sizeof(WCHAR))
|
||||
{
|
||||
NumBytes += IsGlyphFullWidth(*Tmp) ? 2 : 1;
|
||||
}
|
||||
}
|
||||
numBytes = _userBufferSize;
|
||||
}
|
||||
|
||||
__analysis_assume(numBytes <= _userBufferSize);
|
||||
memmove(_userBuffer, _backupLimit, numBytes);
|
||||
|
||||
INPUT_READ_HANDLE_DATA* const pInputReadHandleData = GetInputReadHandleData();
|
||||
const std::wstring_view pending{ _backupLimit + (numBytes / sizeof(wchar_t)), (_bytesRead - numBytes) / sizeof(wchar_t) };
|
||||
if (LineCount > 1)
|
||||
{
|
||||
pInputReadHandleData->SaveMultilinePendingInput(pending);
|
||||
}
|
||||
else
|
||||
{
|
||||
pInputReadHandleData->SavePendingInput(pending);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnData = gsl::span<byte>{ reinterpret_cast<byte*>(inputLine.data()), inputLine.size() * sizeof(wchar_t) };
|
||||
if (!isUnicode)
|
||||
{
|
||||
PWSTR Tmp;
|
||||
|
||||
if (_pInputBuffer->IsReadPartialByteSequenceAvailable())
|
||||
{
|
||||
fAddDbcsLead = true;
|
||||
std::unique_ptr<IInputEvent> event = GetInputBuffer()->FetchReadPartialByteSequence(false);
|
||||
const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(event.get());
|
||||
*_userBuffer = static_cast<char>(pKeyEvent->GetCharData());
|
||||
_userBuffer++;
|
||||
_userBufferSize -= sizeof(wchar_t);
|
||||
|
||||
if (_userBufferSize == 0)
|
||||
{
|
||||
numBytes = 1;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
NumBytes = 0;
|
||||
size_t NumToWrite = _bytesRead;
|
||||
for (Tmp = _backupLimit;
|
||||
NumToWrite && _userBufferSize / sizeof(WCHAR) > NumBytes;
|
||||
Tmp++, NumToWrite -= sizeof(WCHAR))
|
||||
{
|
||||
NumBytes += IsGlyphFullWidth(*Tmp) ? 2 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
numBytes = _bytesRead;
|
||||
|
||||
if (numBytes > _userBufferSize)
|
||||
{
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
memmove(_userBuffer, _backupLimit, numBytes);
|
||||
}
|
||||
|
||||
gsl::span<byte> userBuffer{ reinterpret_cast<byte*>(_userBuffer), _userBufferSize };
|
||||
|
||||
const auto maxFit = std::min(returnData.size(), userBuffer.size());
|
||||
std::copy_n(returnData.begin(), maxFit, userBuffer.begin());
|
||||
|
||||
|
||||
|
||||
numBytes = maxFit;
|
||||
controlKeyState = _controlKeyState;
|
||||
|
||||
// TODO: limit translation to only return one line at a time (for multiline)
|
||||
// TODO: store any full characters we couldn't return as pending input
|
||||
// TODO: store any partial characters (trailing bytes) in the inputbuffer partial sequence
|
||||
// TODO: retrieve leftover partials into the front of the return data
|
||||
// TODO: store the original data, translation, its codepage, and the bytes we used of it
|
||||
// so subsequent calls without changing the codepage can keep walking..
|
||||
// ...and calls that do change the codepage can walk WC2MB to find out how much of the WC they
|
||||
// already used and retranslate it. (this is slow, but changing modes in the middle is not advised)
|
||||
if (!isUnicode)
|
||||
{
|
||||
// if ansi, translate string.
|
||||
std::unique_ptr<char[]> tempBuffer;
|
||||
try
|
||||
{
|
||||
tempBuffer = std::make_unique<char[]>(NumBytes);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
std::unique_ptr<IInputEvent> partialEvent;
|
||||
numBytes = TranslateUnicodeToOem(_userBuffer,
|
||||
gsl::narrow<ULONG>(numBytes / sizeof(wchar_t)),
|
||||
tempBuffer.get(),
|
||||
gsl::narrow<ULONG>(NumBytes),
|
||||
partialEvent);
|
||||
|
||||
if (partialEvent.get())
|
||||
{
|
||||
GetInputBuffer()->StoreReadPartialByteSequence(std::move(partialEvent));
|
||||
}
|
||||
|
||||
if (numBytes > _userBufferSize)
|
||||
{
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
memmove(_userBuffer, tempBuffer.get(), numBytes);
|
||||
if (fAddDbcsLead)
|
||||
{
|
||||
numBytes++;
|
||||
}
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
//
|
||||
// // ---
|
||||
//
|
||||
// DWORD LineCount = 1;
|
||||
//
|
||||
// if (_echoInput)
|
||||
// {
|
||||
// // Figure out where real string ends (at carriage return or end of buffer).
|
||||
// PWCHAR StringPtr = _backupLimit;
|
||||
// size_t StringLength = _bytesRead;
|
||||
// bool FoundCR = false;
|
||||
// for (size_t i = 0; i < (_bytesRead / sizeof(WCHAR)); i++)
|
||||
// {
|
||||
// if (*StringPtr++ == UNICODE_CARRIAGERETURN)
|
||||
// {
|
||||
// StringLength = i * sizeof(WCHAR);
|
||||
// FoundCR = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (FoundCR)
|
||||
// {
|
||||
// if (_commandHistory)
|
||||
// {
|
||||
// // add to command line recall list if we have a history list.
|
||||
// CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
// LOG_IF_FAILED(_commandHistory->Add({ _backupLimit, StringLength / sizeof(wchar_t) },
|
||||
// WI_IsFlagSet(gci.Flags, CONSOLE_HISTORY_NODUP)));
|
||||
// }
|
||||
//
|
||||
// // check for alias
|
||||
// ProcessAliases(LineCount);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// bool fAddDbcsLead = false;
|
||||
// size_t NumBytes = 0;
|
||||
// // at this point, a->NumBytes contains the number of bytes in
|
||||
// // the UNICODE string read. UserBufferSize contains the converted
|
||||
// // size of the app's buffer.
|
||||
// if (_bytesRead > _userBufferSize || LineCount > 1)
|
||||
// {
|
||||
// if (LineCount > 1)
|
||||
// {
|
||||
// PWSTR Tmp;
|
||||
// if (!isUnicode)
|
||||
// {
|
||||
// if (_pInputBuffer->IsReadPartialByteSequenceAvailable())
|
||||
// {
|
||||
// fAddDbcsLead = true;
|
||||
// std::unique_ptr<IInputEvent> event = GetInputBuffer()->FetchReadPartialByteSequence(false);
|
||||
// const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(event.get());
|
||||
// *_userBuffer = static_cast<char>(pKeyEvent->GetCharData());
|
||||
// _userBuffer++;
|
||||
// _userBufferSize -= sizeof(wchar_t);
|
||||
// }
|
||||
//
|
||||
// NumBytes = 0;
|
||||
// for (Tmp = _backupLimit;
|
||||
// *Tmp != UNICODE_LINEFEED && _userBufferSize / sizeof(WCHAR) > NumBytes;
|
||||
// Tmp++)
|
||||
// {
|
||||
// NumBytes += IsGlyphFullWidth(*Tmp) ? 2 : 1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // clang-format off
|
||||
//#pragma prefast(suppress: __WARNING_BUFFER_OVERFLOW, "LineCount > 1 means there's a UNICODE_LINEFEED")
|
||||
// // clang-format on
|
||||
// for (Tmp = _backupLimit; *Tmp != UNICODE_LINEFEED; Tmp++)
|
||||
// {
|
||||
// FAIL_FAST_IF(!(Tmp < (_backupLimit + _bytesRead)));
|
||||
// }
|
||||
//
|
||||
// numBytes = (ULONG)(Tmp - _backupLimit + 1) * sizeof(*Tmp);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (!isUnicode)
|
||||
// {
|
||||
// PWSTR Tmp;
|
||||
//
|
||||
// if (_pInputBuffer->IsReadPartialByteSequenceAvailable())
|
||||
// {
|
||||
// fAddDbcsLead = true;
|
||||
// std::unique_ptr<IInputEvent> event = GetInputBuffer()->FetchReadPartialByteSequence(false);
|
||||
// const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(event.get());
|
||||
// *_userBuffer = static_cast<char>(pKeyEvent->GetCharData());
|
||||
// _userBuffer++;
|
||||
// _userBufferSize -= sizeof(wchar_t);
|
||||
// }
|
||||
// NumBytes = 0;
|
||||
// size_t NumToWrite = _bytesRead;
|
||||
// for (Tmp = _backupLimit;
|
||||
// NumToWrite && _userBufferSize / sizeof(WCHAR) > NumBytes;
|
||||
// Tmp++, NumToWrite -= sizeof(WCHAR))
|
||||
// {
|
||||
// NumBytes += IsGlyphFullWidth(*Tmp) ? 2 : 1;
|
||||
// }
|
||||
// }
|
||||
// numBytes = _userBufferSize;
|
||||
// }
|
||||
//
|
||||
// __analysis_assume(numBytes <= _userBufferSize);
|
||||
// memmove(_userBuffer, _backupLimit, numBytes);
|
||||
//
|
||||
// INPUT_READ_HANDLE_DATA* const pInputReadHandleData = GetInputReadHandleData();
|
||||
// const std::wstring_view pending{ _backupLimit + (numBytes / sizeof(wchar_t)), (_bytesRead - numBytes) / sizeof(wchar_t) };
|
||||
// if (LineCount > 1)
|
||||
// {
|
||||
// pInputReadHandleData->SaveMultilinePendingInput(pending);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// pInputReadHandleData->SavePendingInput(pending);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (!isUnicode)
|
||||
// {
|
||||
// PWSTR Tmp;
|
||||
//
|
||||
// if (_pInputBuffer->IsReadPartialByteSequenceAvailable())
|
||||
// {
|
||||
// fAddDbcsLead = true;
|
||||
// std::unique_ptr<IInputEvent> event = GetInputBuffer()->FetchReadPartialByteSequence(false);
|
||||
// const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(event.get());
|
||||
// *_userBuffer = static_cast<char>(pKeyEvent->GetCharData());
|
||||
// _userBuffer++;
|
||||
// _userBufferSize -= sizeof(wchar_t);
|
||||
//
|
||||
// if (_userBufferSize == 0)
|
||||
// {
|
||||
// numBytes = 1;
|
||||
// return STATUS_SUCCESS;
|
||||
// }
|
||||
// }
|
||||
// NumBytes = 0;
|
||||
// size_t NumToWrite = _bytesRead;
|
||||
// for (Tmp = _backupLimit;
|
||||
// NumToWrite && _userBufferSize / sizeof(WCHAR) > NumBytes;
|
||||
// Tmp++, NumToWrite -= sizeof(WCHAR))
|
||||
// {
|
||||
// NumBytes += IsGlyphFullWidth(*Tmp) ? 2 : 1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// numBytes = _bytesRead;
|
||||
//
|
||||
// if (numBytes > _userBufferSize)
|
||||
// {
|
||||
// return STATUS_BUFFER_OVERFLOW;
|
||||
// }
|
||||
//
|
||||
// memmove(_userBuffer, _backupLimit, numBytes);
|
||||
// }
|
||||
// controlKeyState = _controlKeyState;
|
||||
//
|
||||
// if (!isUnicode)
|
||||
// {
|
||||
// // if ansi, translate string.
|
||||
// std::unique_ptr<char[]> tempBuffer;
|
||||
// try
|
||||
// {
|
||||
// tempBuffer = std::make_unique<char[]>(NumBytes);
|
||||
// }
|
||||
// catch (...)
|
||||
// {
|
||||
// return STATUS_NO_MEMORY;
|
||||
// }
|
||||
//
|
||||
// std::unique_ptr<IInputEvent> partialEvent;
|
||||
// numBytes = TranslateUnicodeToOem(_userBuffer,
|
||||
// gsl::narrow<ULONG>(numBytes / sizeof(wchar_t)),
|
||||
// tempBuffer.get(),
|
||||
// gsl::narrow<ULONG>(NumBytes),
|
||||
// partialEvent);
|
||||
//
|
||||
// if (partialEvent.get())
|
||||
// {
|
||||
// GetInputBuffer()->StoreReadPartialByteSequence(std::move(partialEvent));
|
||||
// }
|
||||
//
|
||||
// if (numBytes > _userBufferSize)
|
||||
// {
|
||||
// return STATUS_BUFFER_OVERFLOW;
|
||||
// }
|
||||
//
|
||||
// memmove(_userBuffer, tempBuffer.get(), numBytes);
|
||||
// if (fAddDbcsLead)
|
||||
// {
|
||||
// numBytes++;
|
||||
// }
|
||||
// }
|
||||
// return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ DxEngine::DxEngine() :
|
||||
_glyphCell{},
|
||||
_boxDrawingEffect{},
|
||||
_haveDeviceResources{ false },
|
||||
_swapChainHandle{ INVALID_HANDLE_VALUE },
|
||||
_swapChainDesc{ 0 },
|
||||
_swapChainFrameLatencyWaitableObject{ INVALID_HANDLE_VALUE },
|
||||
_recreateDeviceRequested{ false },
|
||||
@@ -488,6 +489,13 @@ try
|
||||
}
|
||||
case SwapChainMode::ForComposition:
|
||||
{
|
||||
if (!_swapChainHandle)
|
||||
{
|
||||
RETURN_IF_FAILED(DCompositionCreateSurfaceHandle(GENERIC_ALL, nullptr, &_swapChainHandle));
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(_dxgiFactory2.As(&_dxgiFactoryMedia));
|
||||
|
||||
// Use the given target size for compositions.
|
||||
_swapChainDesc.Width = _displaySizePixels.width<UINT>();
|
||||
_swapChainDesc.Height = _displaySizePixels.height<UINT>();
|
||||
@@ -497,10 +505,11 @@ try
|
||||
// It's 100% required to use scaling mode stretch for composition. There is no other choice.
|
||||
_swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
|
||||
|
||||
RETURN_IF_FAILED(_dxgiFactory2->CreateSwapChainForComposition(_d3dDevice.Get(),
|
||||
&_swapChainDesc,
|
||||
nullptr,
|
||||
&_dxgiSwapChain));
|
||||
RETURN_IF_FAILED(_dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(_d3dDevice.Get(),
|
||||
_swapChainHandle.get(),
|
||||
&_swapChainDesc,
|
||||
nullptr,
|
||||
&_dxgiSwapChain));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -842,14 +851,14 @@ try
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
|
||||
HANDLE DxEngine::GetSwapChainHandle()
|
||||
{
|
||||
if (_dxgiSwapChain.Get() == nullptr)
|
||||
if (!_swapChainHandle)
|
||||
{
|
||||
THROW_IF_FAILED(_CreateDeviceResources(true));
|
||||
}
|
||||
|
||||
return _dxgiSwapChain;
|
||||
return _swapChainHandle.get();
|
||||
}
|
||||
|
||||
void DxEngine::_InvalidateRectangle(const til::rectangle& rc)
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
void SetSoftwareRendering(bool enable) noexcept;
|
||||
|
||||
::Microsoft::WRL::ComPtr<IDXGISwapChain1> GetSwapChain();
|
||||
HANDLE GetSwapChainHandle();
|
||||
|
||||
// IRenderEngine Members
|
||||
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override;
|
||||
@@ -119,6 +119,8 @@ namespace Microsoft::Console::Render
|
||||
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept;
|
||||
void SetDefaultTextBackgroundOpacity(const float opacity) noexcept;
|
||||
|
||||
wil::unique_handle _swapChainHandle;
|
||||
|
||||
void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept;
|
||||
|
||||
protected:
|
||||
@@ -212,6 +214,7 @@ namespace Microsoft::Console::Render
|
||||
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushBackground;
|
||||
|
||||
::Microsoft::WRL::ComPtr<IDXGIFactory2> _dxgiFactory2;
|
||||
::Microsoft::WRL::ComPtr<IDXGIFactoryMedia> _dxgiFactoryMedia;
|
||||
::Microsoft::WRL::ComPtr<IDXGIDevice> _dxgiDevice;
|
||||
::Microsoft::WRL::ComPtr<IDXGISurface> _dxgiSurface;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <typeinfo>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <dcomp.h>
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
281
src/tools/ScratchIsland/AppHost.cpp
Normal file
281
src/tools/ScratchIsland/AppHost.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppHost.h"
|
||||
#include "../types/inc/Viewport.hpp"
|
||||
#include "../types/inc/utils.hpp"
|
||||
#include "../types/inc/User32Utils.hpp"
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Composition;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Hosting;
|
||||
using namespace winrt::Windows::Foundation::Numerics;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
|
||||
AppHost::AppHost() noexcept :
|
||||
_window{ nullptr }
|
||||
{
|
||||
_window = std::make_unique<IslandWindow>();
|
||||
|
||||
// Tell the window to callback to us when it's about to handle a WM_CREATE
|
||||
auto pfn = std::bind(&AppHost::_HandleCreateWindow,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
_window->SetCreateCallback(pfn);
|
||||
|
||||
_window->MakeWindow();
|
||||
}
|
||||
|
||||
AppHost::~AppHost()
|
||||
{
|
||||
// destruction order is important for proper teardown here
|
||||
_window = nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Initializes the XAML island, creates the terminal app, and sets the
|
||||
// island's content to that of the terminal app's content. Also registers some
|
||||
// callbacks with TermApp.
|
||||
// !!! IMPORTANT!!!
|
||||
// This must be called *AFTER* WindowsXamlManager::InitializeForCurrentThread.
|
||||
// If it isn't, then we won't be able to create the XAML island.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::Initialize()
|
||||
{
|
||||
_window->Initialize();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Initialize the UI
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
_rootGrid = Grid();
|
||||
_swapchainsGrid = Grid();
|
||||
_swp0 = SwapChainPanel();
|
||||
_swp1 = SwapChainPanel();
|
||||
_swp2 = SwapChainPanel();
|
||||
_swp3 = SwapChainPanel();
|
||||
// Set up the content of the application. If the app has a custom titlebar,
|
||||
// set that content as well.
|
||||
{
|
||||
auto firstRowDef = Controls::RowDefinition();
|
||||
firstRowDef.Height(GridLengthHelper::FromValueAndType(1, GridUnitType::Star));
|
||||
|
||||
auto secondRowDef = Controls::RowDefinition();
|
||||
secondRowDef.Height(GridLengthHelper::FromValueAndType(9, GridUnitType::Star));
|
||||
|
||||
_rootGrid.RowDefinitions().Append(firstRowDef);
|
||||
_rootGrid.RowDefinitions().Append(secondRowDef);
|
||||
}
|
||||
{
|
||||
auto firstRowDef = Controls::RowDefinition();
|
||||
firstRowDef.Height(GridLengthHelper::FromValueAndType(1, GridUnitType::Star));
|
||||
|
||||
auto secondRowDef = Controls::RowDefinition();
|
||||
secondRowDef.Height(GridLengthHelper::FromValueAndType(1, GridUnitType::Star));
|
||||
|
||||
_swapchainsGrid.RowDefinitions().Append(firstRowDef);
|
||||
_swapchainsGrid.RowDefinitions().Append(secondRowDef);
|
||||
|
||||
auto firstColDef = Controls::ColumnDefinition();
|
||||
firstColDef.Width(GridLengthHelper::FromValueAndType(1, GridUnitType::Star));
|
||||
|
||||
auto secondColDef = Controls::ColumnDefinition();
|
||||
secondColDef.Width(GridLengthHelper::FromValueAndType(1, GridUnitType::Star));
|
||||
|
||||
_swapchainsGrid.ColumnDefinitions().Append(firstColDef);
|
||||
_swapchainsGrid.ColumnDefinitions().Append(secondColDef);
|
||||
}
|
||||
|
||||
_rootGrid.Children().Append(_swapchainsGrid);
|
||||
Controls::Grid::SetRow(_swapchainsGrid, 1);
|
||||
|
||||
{
|
||||
Media::SolidColorBrush solidColor{};
|
||||
til::color newBgColor{ 0xFFFF0000 };
|
||||
solidColor.Color(newBgColor);
|
||||
_rootGrid.Background(solidColor);
|
||||
}
|
||||
{
|
||||
Media::SolidColorBrush solidColor{};
|
||||
til::color newBgColor{ 0xFF00FF00 };
|
||||
solidColor.Color(newBgColor);
|
||||
_swapchainsGrid.Background(solidColor);
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Thickness newMargin = ThicknessHelper::FromUniformLength(4);
|
||||
_swp0.Margin(newMargin);
|
||||
_swp1.Margin(newMargin);
|
||||
_swp2.Margin(newMargin);
|
||||
_swp3.Margin(newMargin);
|
||||
|
||||
_rootGrid.Children().Append(_swp0);
|
||||
_rootGrid.Children().Append(_swp1);
|
||||
_rootGrid.Children().Append(_swp2);
|
||||
_rootGrid.Children().Append(_swp3);
|
||||
|
||||
Controls::Grid::SetRow(_swp0, 0);
|
||||
Controls::Grid::SetColumn(_swp0, 0);
|
||||
|
||||
Controls::Grid::SetRow(_swp1, 0);
|
||||
Controls::Grid::SetColumn(_swp1, 1);
|
||||
|
||||
Controls::Grid::SetRow(_swp2, 1);
|
||||
Controls::Grid::SetColumn(_swp2, 0);
|
||||
|
||||
Controls::Grid::SetRow(_swp3, 1);
|
||||
Controls::Grid::SetColumn(_swp3, 1);
|
||||
_window->SetContent(_rootGrid);
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
_createHost();
|
||||
|
||||
_window->OnAppInitialized();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget AppHost::_createHost()
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
|
||||
auto host0 = _manager.CreateHost();
|
||||
|
||||
co_await winrt::resume_foreground(_swp0.Dispatcher());
|
||||
|
||||
host0.CreateSwapChain(_swp0);
|
||||
|
||||
_swp0_layoutUpdatedRevoker = _swp0.LayoutUpdated(winrt::auto_revoke, [this, host0](auto /*s*/, auto /*e*/) {
|
||||
// This event fires every time the layout changes, but it is always the last one to fire
|
||||
// in any layout change chain. That gives us great flexibility in finding the right point
|
||||
// at which to initialize our renderer (and our terminal).
|
||||
// Any earlier than the last layout update and we may not know the terminal's starting size.
|
||||
|
||||
host0.Host().BeginRendering();
|
||||
|
||||
// if (_InitializeTerminal())
|
||||
// {
|
||||
// Only let this succeed once.
|
||||
_swp0_layoutUpdatedRevoker.revoke();
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Resize the window we're about to create to the appropriate dimensions, as
|
||||
// specified in the settings. This will be called during the handling of
|
||||
// WM_CREATE. We'll load the settings for the app, then get the proposed size
|
||||
// of the terminal from the app. Using that proposed size, we'll resize the
|
||||
// window we're creating, so that it'll match the values in the settings.
|
||||
// Arguments:
|
||||
// - hwnd: The HWND of the window we're about to create.
|
||||
// - proposedRect: The location and size of the window that we're about to
|
||||
// create. We'll use this rect to determine which monitor the window is about
|
||||
// to appear on.
|
||||
// - launchMode: A LaunchMode enum reference that indicates the launch mode
|
||||
// Return Value:
|
||||
// - None
|
||||
void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect)
|
||||
{
|
||||
// Acquire the actual initial position
|
||||
winrt::Windows::Foundation::Point initialPosition{ (float)proposedRect.left, (float)proposedRect.top };
|
||||
proposedRect.left = gsl::narrow_cast<long>(initialPosition.X);
|
||||
proposedRect.top = gsl::narrow_cast<long>(initialPosition.Y);
|
||||
|
||||
long adjustedHeight = 0;
|
||||
long adjustedWidth = 0;
|
||||
|
||||
// Find nearest monitor.
|
||||
HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
// Get nearest monitor information
|
||||
MONITORINFO monitorInfo;
|
||||
monitorInfo.cbSize = sizeof(MONITORINFO);
|
||||
GetMonitorInfo(hmon, &monitorInfo);
|
||||
|
||||
// This API guarantees that dpix and dpiy will be equal, but neither is an
|
||||
// optional parameter so give two UINTs.
|
||||
UINT dpix = USER_DEFAULT_SCREEN_DPI;
|
||||
UINT dpiy = USER_DEFAULT_SCREEN_DPI;
|
||||
// If this fails, we'll use the default of 96.
|
||||
GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
|
||||
|
||||
// We need to check if the top left point of the titlebar of the window is within any screen
|
||||
RECT offScreenTestRect;
|
||||
offScreenTestRect.left = proposedRect.left;
|
||||
offScreenTestRect.top = proposedRect.top;
|
||||
offScreenTestRect.right = offScreenTestRect.left + 1;
|
||||
offScreenTestRect.bottom = offScreenTestRect.top + 1;
|
||||
|
||||
bool isTitlebarIntersectWithMonitors = false;
|
||||
EnumDisplayMonitors(
|
||||
nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL {
|
||||
auto intersectWithMonitor = reinterpret_cast<bool*>(lParam);
|
||||
*intersectWithMonitor = true;
|
||||
// Continue the enumeration
|
||||
return FALSE;
|
||||
},
|
||||
reinterpret_cast<LPARAM>(&isTitlebarIntersectWithMonitors));
|
||||
|
||||
if (!isTitlebarIntersectWithMonitors)
|
||||
{
|
||||
// If the title bar is out-of-screen, we set the initial position to
|
||||
// the top left corner of the nearest monitor
|
||||
proposedRect.left = monitorInfo.rcWork.left;
|
||||
proposedRect.top = monitorInfo.rcWork.top;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Size initialSize{ 800, 600 };
|
||||
|
||||
const short islandWidth = Utils::ClampToShortMax(
|
||||
static_cast<long>(ceil(initialSize.Width)), 1);
|
||||
const short islandHeight = Utils::ClampToShortMax(
|
||||
static_cast<long>(ceil(initialSize.Height)), 1);
|
||||
|
||||
// Get the size of a window we'd need to host that client rect. This will
|
||||
// add the titlebar space.
|
||||
const auto nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix);
|
||||
adjustedWidth = islandWidth + nonClientSize.cx;
|
||||
adjustedHeight = islandHeight + nonClientSize.cy;
|
||||
|
||||
const COORD origin{ gsl::narrow<short>(proposedRect.left),
|
||||
gsl::narrow<short>(proposedRect.top) };
|
||||
const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1),
|
||||
Utils::ClampToShortMax(adjustedHeight, 1) };
|
||||
|
||||
const auto newPos = Viewport::FromDimensions(origin, dimensions);
|
||||
bool succeeded = SetWindowPos(hwnd,
|
||||
nullptr,
|
||||
newPos.Left(),
|
||||
newPos.Top(),
|
||||
newPos.Width(),
|
||||
newPos.Height(),
|
||||
SWP_NOACTIVATE | SWP_NOZORDER);
|
||||
|
||||
// Refresh the dpi of HWND because the dpi where the window will launch may be different
|
||||
// at this time
|
||||
_window->RefreshCurrentDPI();
|
||||
|
||||
// If we can't resize the window, that's really okay. We can just go on with
|
||||
// the originally proposed window size.
|
||||
LOG_LAST_ERROR_IF(!succeeded);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the app wants to change its theme. We'll forward this to the
|
||||
// IslandWindow, so it can update the root UI element of the entire XAML tree.
|
||||
// Arguments:
|
||||
// - sender: unused
|
||||
// - arg: the ElementTheme to use as the new theme for the UI
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& arg)
|
||||
{
|
||||
_window->OnApplicationThemeChanged(arg);
|
||||
}
|
||||
38
src/tools/ScratchIsland/AppHost.h
Normal file
38
src/tools/ScratchIsland/AppHost.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "IslandWindow.h"
|
||||
#include "HostManager.h"
|
||||
|
||||
class AppHost
|
||||
{
|
||||
public:
|
||||
AppHost() noexcept;
|
||||
virtual ~AppHost();
|
||||
|
||||
void Initialize();
|
||||
|
||||
private:
|
||||
bool _useNonClientArea{ false };
|
||||
|
||||
std::unique_ptr<IslandWindow> _window;
|
||||
winrt::ScratchIsland::HostManager _manager;
|
||||
|
||||
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect);
|
||||
void _UpdateTheme(const winrt::Windows::Foundation::IInspectable&,
|
||||
const winrt::Windows::UI::Xaml::ElementTheme& arg);
|
||||
|
||||
winrt::fire_and_forget _createHost();
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _rootGrid{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _swapchainsGrid{ nullptr };
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel _swp0{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel _swp1{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel _swp2{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel _swp3{ nullptr };
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _swp0_layoutUpdatedRevoker;
|
||||
};
|
||||
228
src/tools/ScratchIsland/BaseWindow.h
Normal file
228
src/tools/ScratchIsland/BaseWindow.h
Normal file
@@ -0,0 +1,228 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Custom window messages
|
||||
#define CM_UPDATE_TITLE (WM_USER)
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
||||
template<typename T>
|
||||
class BaseWindow
|
||||
{
|
||||
public:
|
||||
virtual ~BaseWindow() = 0;
|
||||
static T* GetThisFromHandle(HWND const window) noexcept
|
||||
{
|
||||
return reinterpret_cast<T*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
[[nodiscard]] static LRESULT __stdcall WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
|
||||
{
|
||||
WINRT_ASSERT(window);
|
||||
|
||||
if (WM_NCCREATE == message)
|
||||
{
|
||||
auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||||
T* that = static_cast<T*>(cs->lpCreateParams);
|
||||
WINRT_ASSERT(that);
|
||||
WINRT_ASSERT(!that->_window);
|
||||
that->_window = wil::unique_hwnd(window);
|
||||
|
||||
return that->_OnNcCreate(wparam, lparam);
|
||||
}
|
||||
else if (T* that = GetThisFromHandle(window))
|
||||
{
|
||||
return that->MessageHandler(message, wparam, lparam);
|
||||
}
|
||||
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_DPICHANGED:
|
||||
{
|
||||
return HandleDpiChange(_window.get(), wparam, lparam);
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_SIZE:
|
||||
{
|
||||
UINT width = LOWORD(lparam);
|
||||
UINT height = HIWORD(lparam);
|
||||
|
||||
switch (wparam)
|
||||
{
|
||||
case SIZE_MAXIMIZED:
|
||||
[[fallthrough]];
|
||||
case SIZE_RESTORED:
|
||||
if (_minimized)
|
||||
{
|
||||
_minimized = false;
|
||||
OnRestore();
|
||||
}
|
||||
|
||||
// We always need to fire the resize event, even when we're transitioning from minimized.
|
||||
// We might be transitioning directly from minimized to maximized, and we'll need
|
||||
// to trigger any size-related content changes.
|
||||
OnResize(width, height);
|
||||
break;
|
||||
case SIZE_MINIMIZED:
|
||||
if (!_minimized)
|
||||
{
|
||||
_minimized = true;
|
||||
OnMinimize();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// do nothing.
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CM_UPDATE_TITLE:
|
||||
{
|
||||
SetWindowTextW(_window.get(), _title.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(_window.get(), message, wparam, lparam);
|
||||
}
|
||||
|
||||
// DPI Change handler. on WM_DPICHANGE resize the window
|
||||
[[nodiscard]] LRESULT HandleDpiChange(const HWND hWnd, const WPARAM wParam, const LPARAM lParam)
|
||||
{
|
||||
_inDpiChange = true;
|
||||
const HWND hWndStatic = GetWindow(hWnd, GW_CHILD);
|
||||
if (hWndStatic != nullptr)
|
||||
{
|
||||
const UINT uDpi = HIWORD(wParam);
|
||||
|
||||
// Resize the window
|
||||
auto lprcNewScale = reinterpret_cast<RECT*>(lParam);
|
||||
|
||||
SetWindowPos(hWnd, nullptr, lprcNewScale->left, lprcNewScale->top, lprcNewScale->right - lprcNewScale->left, lprcNewScale->bottom - lprcNewScale->top, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
_currentDpi = uDpi;
|
||||
}
|
||||
_inDpiChange = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void OnResize(const UINT width, const UINT height) = 0;
|
||||
virtual void OnMinimize() = 0;
|
||||
virtual void OnRestore() = 0;
|
||||
|
||||
RECT GetWindowRect() const noexcept
|
||||
{
|
||||
RECT rc = { 0 };
|
||||
::GetWindowRect(_window.get(), &rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
HWND GetHandle() const noexcept
|
||||
{
|
||||
return _window.get();
|
||||
}
|
||||
|
||||
float GetCurrentDpiScale() const noexcept
|
||||
{
|
||||
const auto dpi = ::GetDpiForWindow(_window.get());
|
||||
const auto scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
|
||||
return scale;
|
||||
}
|
||||
|
||||
//// Gets the physical size of the client area of the HWND in _window
|
||||
SIZE GetPhysicalSize() const noexcept
|
||||
{
|
||||
RECT rect = {};
|
||||
GetClientRect(_window.get(), &rect);
|
||||
const auto windowsWidth = rect.right - rect.left;
|
||||
const auto windowsHeight = rect.bottom - rect.top;
|
||||
return SIZE{ windowsWidth, windowsHeight };
|
||||
}
|
||||
|
||||
//// Gets the logical (in DIPs) size of a physical size specified by the parameter physicalSize
|
||||
//// Remarks:
|
||||
//// XAML coordinate system is always in Display Independent Pixels (a.k.a DIPs or Logical). However Win32 GDI (because of legacy reasons)
|
||||
//// in DPI mode "Per-Monitor and Per-Monitor (V2) DPI Awareness" is always in physical pixels.
|
||||
//// The formula to transform is:
|
||||
//// logical = (physical / dpi) + 0.5 // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75
|
||||
//// See also:
|
||||
//// https://docs.microsoft.com/en-us/windows/desktop/LearnWin32/dpi-and-device-independent-pixels
|
||||
//// https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows#per-monitor-and-per-monitor-v2-dpi-awareness
|
||||
winrt::Windows::Foundation::Size GetLogicalSize(const SIZE physicalSize) const noexcept
|
||||
{
|
||||
const auto scale = GetCurrentDpiScale();
|
||||
// 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75
|
||||
const auto logicalWidth = (physicalSize.cx / scale) + 0.5f;
|
||||
const auto logicalHeight = (physicalSize.cy / scale) + 0.5f;
|
||||
return winrt::Windows::Foundation::Size(logicalWidth, logicalHeight);
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Size GetLogicalSize() const noexcept
|
||||
{
|
||||
return GetLogicalSize(GetPhysicalSize());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sends a message to our message loop to update the title of the window.
|
||||
// Arguments:
|
||||
// - newTitle: a string to use as the new title of the window.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void UpdateTitle(std::wstring_view newTitle)
|
||||
{
|
||||
_title = newTitle;
|
||||
PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast<LPARAM>(nullptr));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// Reset the current dpi of the window. This method is only called after we change the
|
||||
// initial launch position. This makes sure the dpi is consistent with the monitor on which
|
||||
// the window will launch
|
||||
void RefreshCurrentDPI()
|
||||
{
|
||||
_currentDpi = GetDpiForWindow(_window.get());
|
||||
}
|
||||
|
||||
protected:
|
||||
using base_type = BaseWindow<T>;
|
||||
wil::unique_hwnd _window;
|
||||
|
||||
unsigned int _currentDpi = 0;
|
||||
bool _inDpiChange = false;
|
||||
|
||||
std::wstring _title = L"";
|
||||
|
||||
bool _minimized = false;
|
||||
|
||||
// Method Description:
|
||||
// - This method is called when the window receives the WM_NCCREATE message.
|
||||
// Return Value:
|
||||
// - The value returned from the window proc.
|
||||
virtual [[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept
|
||||
{
|
||||
SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
|
||||
|
||||
EnableNonClientDpiScaling(_window.get());
|
||||
_currentDpi = GetDpiForWindow(_window.get());
|
||||
|
||||
return DefWindowProc(_window.get(), WM_NCCREATE, wParam, lParam);
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline BaseWindow<T>::~BaseWindow()
|
||||
{
|
||||
}
|
||||
50
src/tools/ScratchIsland/HostAndProcess.cpp
Normal file
50
src/tools/ScratchIsland/HostAndProcess.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "pch.h"
|
||||
#include "HostAndProcess.h"
|
||||
|
||||
#include "HostAndProcess.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
namespace winrt::ScratchIsland::implementation
|
||||
{
|
||||
HostAndProcess::HostAndProcess(ScratchWinRTServer::HostClass host,
|
||||
wil::unique_process_information pi /*, wil::unique_handle hSwapchain*/) :
|
||||
_host{ host },
|
||||
_pi{ std::move(pi) } /*,
|
||||
_hOurSwapchain {std::move(hSwapchain)}*/
|
||||
{
|
||||
}
|
||||
|
||||
ScratchWinRTServer::HostClass HostAndProcess::Host() const
|
||||
{
|
||||
return _host;
|
||||
}
|
||||
|
||||
void HostAndProcess::CreateSwapChain(winrt::Windows::UI::Xaml::Controls::SwapChainPanel panel)
|
||||
{
|
||||
// wil::unique_handle _swapChainHandle;
|
||||
DCompositionCreateSurfaceHandle(GENERIC_ALL, nullptr, &_hOurSwapchain);
|
||||
// wil::unique_handle otherProcess = OpenProcess(contentProcessPid);
|
||||
// HANDLE otherGuysHandleId = INVALID_HANDLE_VALUE;
|
||||
BOOL ret = DuplicateHandle(GetCurrentProcess(),
|
||||
_hOurSwapchain.get(),
|
||||
_pi.hProcess,
|
||||
&_hTheirSwapchain,
|
||||
0,
|
||||
FALSE,
|
||||
DUPLICATE_SAME_ACCESS);
|
||||
|
||||
// {
|
||||
auto panelNative = panel.as<ISwapChainPanelNative2>();
|
||||
winrt::check_hresult(panelNative->SetSwapChainHandle(_hOurSwapchain.get()));
|
||||
// }
|
||||
|
||||
_host.ThisIsInsane((uint64_t)_hTheirSwapchain.get());
|
||||
|
||||
ret;
|
||||
}
|
||||
|
||||
}
|
||||
29
src/tools/ScratchIsland/HostAndProcess.h
Normal file
29
src/tools/ScratchIsland/HostAndProcess.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <winrt/ScratchWinRTServer.h>
|
||||
#include "HostAndProcess.g.h"
|
||||
|
||||
namespace winrt::ScratchIsland::implementation
|
||||
{
|
||||
struct HostAndProcess : public HostAndProcessT<HostAndProcess>
|
||||
{
|
||||
HostAndProcess(ScratchWinRTServer::HostClass host, wil::unique_process_information pi);
|
||||
|
||||
ScratchWinRTServer::HostClass Host() const;
|
||||
|
||||
void CreateSwapChain(Windows::UI::Xaml::Controls::SwapChainPanel panel);
|
||||
|
||||
private:
|
||||
ScratchWinRTServer::HostClass _host{ nullptr };
|
||||
wil::unique_process_information _pi;
|
||||
wil::unique_handle _hOurSwapchain{ INVALID_HANDLE_VALUE };
|
||||
wil::unique_handle _hTheirSwapchain{ INVALID_HANDLE_VALUE };
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::ScratchIsland::factory_implementation
|
||||
{
|
||||
// struct HostAndProcess : HostAndProcessT<HostAndProcess, implementation::HostAndProcess>
|
||||
// {
|
||||
// };
|
||||
}
|
||||
9
src/tools/ScratchIsland/HostAndProcess.idl
Normal file
9
src/tools/ScratchIsland/HostAndProcess.idl
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
namespace ScratchIsland
|
||||
{
|
||||
[default_interface] runtimeclass HostAndProcess {
|
||||
ScratchWinRTServer.HostClass Host { get; };
|
||||
void CreateSwapChain(Windows.UI.Xaml.Controls.SwapChainPanel panel);
|
||||
};
|
||||
}
|
||||
81
src/tools/ScratchIsland/HostManager.cpp
Normal file
81
src/tools/ScratchIsland/HostManager.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "pch.h"
|
||||
#include "HostManager.h"
|
||||
|
||||
#include "HostManager.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace ::Microsoft::Console;
|
||||
|
||||
namespace winrt::ScratchIsland::implementation
|
||||
{
|
||||
HostManager::HostManager()
|
||||
{
|
||||
_hosts = winrt::single_threaded_observable_vector<winrt::ScratchIsland::HostAndProcess>();
|
||||
}
|
||||
|
||||
Collections::IObservableVector<winrt::ScratchIsland::HostAndProcess> HostManager::Hosts()
|
||||
{
|
||||
return _hosts;
|
||||
}
|
||||
|
||||
static wil::unique_process_information _createHostClassProcess(const winrt::guid& g)
|
||||
{
|
||||
auto guidStr{ Utils::GuidToString(g) };
|
||||
std::wstring commandline{ fmt::format(L"ScratchWinRTServer.exe {}", guidStr) };
|
||||
STARTUPINFO siOne{ 0 };
|
||||
siOne.cb = sizeof(STARTUPINFOW);
|
||||
wil::unique_process_information piOne;
|
||||
auto succeeded = CreateProcessW(
|
||||
nullptr,
|
||||
commandline.data(),
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
false, // bInheritHandles
|
||||
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
||||
nullptr, // lpEnvironment
|
||||
nullptr, // startingDirectory
|
||||
&siOne, // lpStartupInfo
|
||||
&piOne // lpProcessInformation
|
||||
);
|
||||
THROW_IF_WIN32_BOOL_FALSE(succeeded);
|
||||
// if (!succeeded)
|
||||
// {
|
||||
// printf("Failed to create host process\n");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Ooof this is dumb, but we need a sleep here to make the server starts.
|
||||
// That's _sub par_. Maybe we could use the host's stdout to have them emit
|
||||
// a byte when they're set up?
|
||||
Sleep(2500);
|
||||
|
||||
// TODO MONDAY - It seems like it takes conhost too long to start up to
|
||||
// host the ScratchWinRTServer that even a sleep 100 is too short. However,
|
||||
// any longer, and XAML will just crash, because some frame took too long.
|
||||
// So we _need_ to do the "have the server explicitly tell us it's ready"
|
||||
// thing, and maybe also do it on a bg thread (and signal to the UI thread
|
||||
// that it can attach now)
|
||||
|
||||
return std::move(piOne);
|
||||
}
|
||||
|
||||
winrt::ScratchIsland::HostAndProcess HostManager::CreateHost()
|
||||
{
|
||||
// 1. Generate a GUID.
|
||||
winrt::guid g{ Utils::CreateGuid() };
|
||||
|
||||
// 2. Spawn a Server.exe, with the guid on the commandline
|
||||
auto piContent{ std::move(_createHostClassProcess(g)) };
|
||||
|
||||
auto host = create_instance<winrt::ScratchWinRTServer::HostClass>(g, CLSCTX_LOCAL_SERVER);
|
||||
THROW_IF_NULL_ALLOC(host);
|
||||
|
||||
auto hostAndProcess = winrt::make_self<HostAndProcess>(host, std::move(piContent));
|
||||
|
||||
_hosts.Append(*hostAndProcess);
|
||||
|
||||
return *hostAndProcess;
|
||||
}
|
||||
}
|
||||
66
src/tools/ScratchIsland/HostManager.h
Normal file
66
src/tools/ScratchIsland/HostManager.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "HostManager.g.h"
|
||||
#include "HostAndProcess.h"
|
||||
|
||||
// 50dba6cd-4ddb-4b12-8363-5e06f5d0082c
|
||||
static constexpr GUID HostManager_clsid{
|
||||
0x50dba6cd,
|
||||
0x4ddb,
|
||||
0x4b12,
|
||||
{ 0x83, 0x63, 0x5e, 0x06, 0xf5, 0xd0, 0x08, 0x2c }
|
||||
};
|
||||
|
||||
namespace winrt::ScratchIsland::implementation
|
||||
{
|
||||
struct HostManager : public HostManagerT<HostManager>
|
||||
{
|
||||
HostManager();
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::ScratchIsland::HostAndProcess> Hosts();
|
||||
winrt::ScratchIsland::HostAndProcess CreateHost();
|
||||
|
||||
private:
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::ScratchIsland::HostAndProcess> _hosts{ nullptr };
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::ScratchIsland::factory_implementation
|
||||
{
|
||||
struct HostManager : HostManagerT<HostManager, implementation::HostManager>
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
// I bet all this could be a macro.
|
||||
// I MORE be that this is all done by the factory_implementation stuff, isn't it...
|
||||
struct HostManagerFactory : winrt::implements<HostManagerFactory, IClassFactory>
|
||||
{
|
||||
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
|
||||
{
|
||||
*result = nullptr;
|
||||
if (outer)
|
||||
{
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
|
||||
return winrt::make<winrt::ScratchIsland::implementation::HostManager>().as(iid, result);
|
||||
}
|
||||
|
||||
HRESULT __stdcall LockServer(BOOL) noexcept final
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void RegisterHostManager()
|
||||
{
|
||||
DWORD registrationHostManager{};
|
||||
|
||||
winrt::check_hresult(CoRegisterClassObject(HostManager_clsid,
|
||||
winrt::make<HostManagerFactory>().get(),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
REGCLS_MULTIPLEUSE,
|
||||
®istrationHostManager));
|
||||
printf("registrationHostManager:%d\n", registrationHostManager);
|
||||
}
|
||||
};
|
||||
12
src/tools/ScratchIsland/HostManager.idl
Normal file
12
src/tools/ScratchIsland/HostManager.idl
Normal file
@@ -0,0 +1,12 @@
|
||||
import "HostAndProcess.idl";
|
||||
|
||||
namespace ScratchIsland
|
||||
{
|
||||
[default_interface] runtimeclass HostManager // : IScratchInterface
|
||||
{
|
||||
HostManager();
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<HostAndProcess> Hosts { get; };
|
||||
HostAndProcess CreateHost();
|
||||
};
|
||||
}
|
||||
605
src/tools/ScratchIsland/IslandWindow.cpp
Normal file
605
src/tools/ScratchIsland/IslandWindow.cpp
Normal file
@@ -0,0 +1,605 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "IslandWindow.h"
|
||||
#include "../types/inc/Viewport.hpp"
|
||||
#include "resource.h"
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Composition;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Hosting;
|
||||
using namespace winrt::Windows::Foundation::Numerics;
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
|
||||
#define XAML_HOSTING_WINDOW_CLASS_NAME L"SCRATCH_ISLAND_CLASS"
|
||||
|
||||
IslandWindow::IslandWindow() noexcept :
|
||||
_interopWindowHandle{ nullptr },
|
||||
_rootGrid{ nullptr },
|
||||
_source{ nullptr },
|
||||
_pfnCreateCallback{ nullptr }
|
||||
{
|
||||
}
|
||||
|
||||
IslandWindow::~IslandWindow()
|
||||
{
|
||||
_source.Close();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create the actual window that we'll use for the application.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::MakeWindow() noexcept
|
||||
{
|
||||
WNDCLASS wc{};
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||||
wc.lpszClassName = XAML_HOSTING_WINDOW_CLASS_NAME;
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON));
|
||||
RegisterClass(&wc);
|
||||
WINRT_ASSERT(!_window);
|
||||
|
||||
// Create the window with the default size here - During the creation of the
|
||||
// window, the system will give us a chance to set its size in WM_CREATE.
|
||||
// WM_CREATE will be handled synchronously, before CreateWindow returns.
|
||||
WINRT_VERIFY(CreateWindowEx(_alwaysOnTop ? WS_EX_TOPMOST : 0,
|
||||
wc.lpszClassName,
|
||||
L"Scratch Island",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
nullptr,
|
||||
nullptr,
|
||||
wc.hInstance,
|
||||
this));
|
||||
|
||||
WINRT_ASSERT(_window);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when no tab is remaining to close the window.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::Close()
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Set a callback to be called when we process a WM_CREATE message. This gives
|
||||
// the AppHost a chance to resize the window to the proper size.
|
||||
// Arguments:
|
||||
// - pfn: a function to be called during the handling of WM_CREATE. It takes two
|
||||
// parameters:
|
||||
// * HWND: the HWND of the window that's being created.
|
||||
// * RECT: The position on the screen that the system has proposed for our
|
||||
// window.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept
|
||||
{
|
||||
_pfnCreateCallback = pfn;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handles a WM_CREATE message. Calls our create callback, if one's been set.
|
||||
// Arguments:
|
||||
// - wParam: unused
|
||||
// - lParam: the lParam of a WM_CREATE, which is a pointer to a CREATESTRUCTW
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexcept
|
||||
{
|
||||
// Get proposed window rect from create structure
|
||||
CREATESTRUCTW* pcs = reinterpret_cast<CREATESTRUCTW*>(lParam);
|
||||
RECT rc;
|
||||
rc.left = pcs->x;
|
||||
rc.top = pcs->y;
|
||||
rc.right = rc.left + pcs->cx;
|
||||
rc.bottom = rc.top + pcs->cy;
|
||||
|
||||
if (_pfnCreateCallback)
|
||||
{
|
||||
_pfnCreateCallback(_window.get(), rc);
|
||||
}
|
||||
|
||||
int nCmdShow = SW_SHOW;
|
||||
|
||||
ShowWindow(_window.get(), nCmdShow);
|
||||
|
||||
UpdateWindow(_window.get());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Handles a WM_SIZING message, which occurs when user drags a window border
|
||||
// or corner. It intercepts this resize action and applies 'snapping' i.e.
|
||||
// aligns the terminal's size to its cell grid. We're given the window size,
|
||||
// which we then adjust based on the terminal's properties (like font size).
|
||||
// Arguments:
|
||||
// - wParam: Specifies which edge of the window is being dragged.
|
||||
// - lParam: Pointer to the requested window rectangle (this is, the one that
|
||||
// originates from current drag action). It also acts as the return value
|
||||
// (it's a ref parameter).
|
||||
// Return Value:
|
||||
// - <none>
|
||||
LRESULT IslandWindow::_OnSizing(const WPARAM /*wParam*/, const LPARAM /*lParam*/)
|
||||
{
|
||||
// If we haven't been given the callback that would adjust the dimension,
|
||||
// then we can't do anything, so just bail out.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void IslandWindow::Initialize()
|
||||
{
|
||||
const bool initialized = (_interopWindowHandle != nullptr);
|
||||
|
||||
_source = DesktopWindowXamlSource{};
|
||||
|
||||
auto interop = _source.as<IDesktopWindowXamlSourceNative>();
|
||||
winrt::check_hresult(interop->AttachToWindow(_window.get()));
|
||||
|
||||
// stash the child interop handle so we can resize it when the main hwnd is resized
|
||||
interop->get_WindowHandle(&_interopWindowHandle);
|
||||
|
||||
_rootGrid = winrt::Windows::UI::Xaml::Controls::Grid();
|
||||
_source.Content(_rootGrid);
|
||||
}
|
||||
|
||||
void IslandWindow::OnSize(const UINT width, const UINT height)
|
||||
{
|
||||
// update the interop window size
|
||||
SetWindowPos(_interopWindowHandle, nullptr, 0, 0, width, height, SWP_SHOWWINDOW);
|
||||
|
||||
if (_rootGrid)
|
||||
{
|
||||
const auto size = GetLogicalSize();
|
||||
_rootGrid.Width(size.Width);
|
||||
_rootGrid.Height(size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
{
|
||||
_HandleCreateWindow(wparam, lparam);
|
||||
return 0;
|
||||
}
|
||||
case WM_SETFOCUS:
|
||||
{
|
||||
if (_interopWindowHandle != nullptr)
|
||||
{
|
||||
// send focus to the child window
|
||||
SetFocus(_interopWindowHandle);
|
||||
return 0; // eat the message
|
||||
}
|
||||
}
|
||||
|
||||
case WM_NCLBUTTONDOWN:
|
||||
case WM_NCLBUTTONUP:
|
||||
case WM_NCMBUTTONDOWN:
|
||||
case WM_NCMBUTTONUP:
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCRBUTTONUP:
|
||||
case WM_NCXBUTTONDOWN:
|
||||
case WM_NCXBUTTONUP:
|
||||
{
|
||||
// If we clicked in the titlebar, raise an event so the app host can
|
||||
// dispatch an appropriate event.
|
||||
_DragRegionClickedHandlers();
|
||||
break;
|
||||
}
|
||||
case WM_MENUCHAR:
|
||||
{
|
||||
// GH#891: return this LRESULT here to prevent the app from making a
|
||||
// bell when alt+key is pressed. A menu is active and the user presses a
|
||||
// key that does not correspond to any mnemonic or accelerator key,
|
||||
return MAKELRESULT(0, MNC_CLOSE);
|
||||
}
|
||||
case WM_SIZING:
|
||||
{
|
||||
return _OnSizing(wparam, lparam);
|
||||
}
|
||||
case WM_CLOSE:
|
||||
{
|
||||
// // If the user wants to close the app by clicking 'X' button,
|
||||
// // we hand off the close experience to the app layer. If all the tabs
|
||||
// // are closed, the window will be closed as well.
|
||||
// _windowCloseButtonClickedHandler();
|
||||
// return 0;
|
||||
Close();
|
||||
}
|
||||
case WM_MOUSEWHEEL:
|
||||
try
|
||||
{
|
||||
// This whole handler is a hack for GH#979.
|
||||
//
|
||||
// On some laptops, their trackpads won't scroll inactive windows
|
||||
// _ever_. With our entire window just being one giant XAML Island, the
|
||||
// touchpad driver thinks our entire window is inactive, and won't
|
||||
// scroll the XAML island. On those types of laptops, we'll get a
|
||||
// WM_MOUSEWHEEL here, in our root window, when the trackpad scrolls.
|
||||
// We're going to take that message and manually plumb it through to our
|
||||
// TermControl's, or anything else that implements IMouseWheelListener.
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
|
||||
// Important! Do not use the LOWORD or HIWORD macros to extract the x-
|
||||
// and y- coordinates of the cursor position because these macros return
|
||||
// incorrect results on systems with multiple monitors. Systems with
|
||||
// multiple monitors can have negative x- and y- coordinates, and LOWORD
|
||||
// and HIWORD treat the coordinates as unsigned quantities.
|
||||
const til::point eventPoint{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
|
||||
// This mouse event is relative to the display origin, not the window. Convert here.
|
||||
const til::rectangle windowRect{ GetWindowRect() };
|
||||
const auto origin = windowRect.origin();
|
||||
const auto relative = eventPoint - origin;
|
||||
// Convert to logical scaling before raising the event.
|
||||
const auto real = relative / GetCurrentDpiScale();
|
||||
|
||||
const short wheelDelta = static_cast<short>(HIWORD(wparam));
|
||||
|
||||
// Raise an event, so any listeners can handle the mouse wheel event manually.
|
||||
_MouseScrolledHandlers(real, wheelDelta);
|
||||
return 0;
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// TODO: handle messages here...
|
||||
return base_type::MessageHandler(message, wparam, lparam);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the window has been resized (or maximized)
|
||||
// Arguments:
|
||||
// - width: the new width of the window _in pixels_
|
||||
// - height: the new height of the window _in pixels_
|
||||
void IslandWindow::OnResize(const UINT width, const UINT height)
|
||||
{
|
||||
if (_interopWindowHandle)
|
||||
{
|
||||
OnSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the window is minimized to the taskbar.
|
||||
void IslandWindow::OnMinimize()
|
||||
{
|
||||
// TODO GH#1989 Stop rendering island content when the app is minimized.
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the window is restored from having been minimized.
|
||||
void IslandWindow::OnRestore()
|
||||
{
|
||||
// TODO GH#1989 Stop rendering island content when the app is minimized.
|
||||
}
|
||||
|
||||
void IslandWindow::SetContent(winrt::Windows::UI::Xaml::UIElement content)
|
||||
{
|
||||
_rootGrid.Children().Clear();
|
||||
_rootGrid.Children().Append(content);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets the difference between window and client area size.
|
||||
// Arguments:
|
||||
// - dpi: dpi of a monitor on which the window is placed
|
||||
// Return Value
|
||||
// - The size difference
|
||||
SIZE IslandWindow::GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept
|
||||
{
|
||||
const auto windowStyle = static_cast<DWORD>(GetWindowLong(_window.get(), GWL_STYLE));
|
||||
RECT islandFrame{};
|
||||
|
||||
// If we failed to get the correct window size for whatever reason, log
|
||||
// the error and go on. We'll use whatever the control proposed as the
|
||||
// size of our window, which will be at least close.
|
||||
LOG_IF_WIN32_BOOL_FALSE(AdjustWindowRectExForDpi(&islandFrame, windowStyle, false, 0, dpi));
|
||||
|
||||
return {
|
||||
islandFrame.right - islandFrame.left,
|
||||
islandFrame.bottom - islandFrame.top
|
||||
};
|
||||
}
|
||||
|
||||
void IslandWindow::OnAppInitialized()
|
||||
{
|
||||
// Do a quick resize to force the island to paint
|
||||
const auto size = GetPhysicalSize();
|
||||
OnSize(size.cx, size.cy);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the app wants to change its theme. We'll update the root UI
|
||||
// element of the entire XAML tree, so that all UI elements get the theme
|
||||
// applied.
|
||||
// Arguments:
|
||||
// - arg: the ElementTheme to use as the new theme for the UI
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme)
|
||||
{
|
||||
_rootGrid.RequestedTheme(requestedTheme);
|
||||
// Invalidate the window rect, so that we'll repaint any elements we're
|
||||
// drawing ourselves to match the new theme
|
||||
::InvalidateRect(_window.get(), nullptr, false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates our focus mode state. See _SetIsBorderless for more details.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::FocusModeChanged(const bool focusMode)
|
||||
{
|
||||
// Do nothing if the value was unchanged.
|
||||
if (focusMode == _borderless)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_SetIsBorderless(focusMode);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates our fullscreen state. See _SetIsFullscreen for more details.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::FullscreenChanged(const bool fullscreen)
|
||||
{
|
||||
// Do nothing if the value was unchanged.
|
||||
if (fullscreen == _fullscreen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_SetIsFullscreen(fullscreen);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enter or exit the "always on top" state. Before the window is created, this
|
||||
// value will later be used when we create the window to create the window on
|
||||
// top of all others. After the window is created, it will either enter the
|
||||
// group of topmost windows, or exit the group of topmost windows.
|
||||
// Arguments:
|
||||
// - alwaysOnTop: whether we should be entering or exiting always on top mode.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop)
|
||||
{
|
||||
_alwaysOnTop = alwaysOnTop;
|
||||
|
||||
const auto hwnd = GetHandle();
|
||||
if (hwnd)
|
||||
{
|
||||
SetWindowPos(hwnd,
|
||||
_alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
|
||||
0, // the window dimensions are unused, because we're passing SWP_NOSIZE
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_NOMOVE | SWP_NOSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// From GdiEngine::s_SetWindowLongWHelper
|
||||
void _SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept
|
||||
{
|
||||
// SetWindowLong has strange error handling. On success, it returns the
|
||||
// previous Window Long value and doesn't modify the Last Error state. To
|
||||
// deal with this, we set the last error to 0/S_OK first, call it, and if
|
||||
// the previous long was 0, we check if the error was non-zero before
|
||||
// reporting. Otherwise, we'll get an "Error: The operation has completed
|
||||
// successfully." and there will be another screenshot on the internet
|
||||
// making fun of Windows. See:
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
|
||||
SetLastError(0);
|
||||
LONG const lResult = SetWindowLongW(hWnd, nIndex, dwNewLong);
|
||||
if (0 == lResult)
|
||||
{
|
||||
LOG_LAST_ERROR_IF(0 != GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This is a helper to figure out what the window styles should be, given the
|
||||
// current state of flags like borderless mode and fullscreen mode.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a LONG with the appropriate flags set for our current window mode, to be used with GWL_STYLE
|
||||
LONG IslandWindow::_getDesiredWindowStyle() const
|
||||
{
|
||||
auto windowStyle = GetWindowLongW(GetHandle(), GWL_STYLE);
|
||||
|
||||
// If we're both fullscreen and borderless, fullscreen mode takes precedence.
|
||||
|
||||
if (_fullscreen)
|
||||
{
|
||||
// When moving to fullscreen, remove WS_OVERLAPPEDWINDOW, which specifies
|
||||
// styles for non-fullscreen windows (e.g. caption bar), and add the
|
||||
// WS_POPUP style to allow us to size ourselves to the monitor size.
|
||||
// Do the reverse when restoring from fullscreen.
|
||||
// Doing these modifications to that window will cause a vista-style
|
||||
// window frame to briefly appear when entering and exiting fullscreen.
|
||||
WI_ClearFlag(windowStyle, WS_BORDER);
|
||||
WI_ClearFlag(windowStyle, WS_SIZEBOX);
|
||||
WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW);
|
||||
|
||||
WI_SetFlag(windowStyle, WS_POPUP);
|
||||
return windowStyle;
|
||||
}
|
||||
else if (_borderless)
|
||||
{
|
||||
// When moving to borderless, remove WS_OVERLAPPEDWINDOW, which
|
||||
// specifies styles for non-fullscreen windows (e.g. caption bar), and
|
||||
// add the WS_BORDER and WS_SIZEBOX styles. This allows us to still have
|
||||
// a small resizing frame, but without a full titlebar, nor caption
|
||||
// buttons.
|
||||
|
||||
WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW);
|
||||
WI_ClearFlag(windowStyle, WS_POPUP);
|
||||
|
||||
WI_SetFlag(windowStyle, WS_BORDER);
|
||||
WI_SetFlag(windowStyle, WS_SIZEBOX);
|
||||
return windowStyle;
|
||||
}
|
||||
|
||||
// Here, we're not in either fullscreen or borderless mode. Return to
|
||||
// WS_OVERLAPPEDWINDOW.
|
||||
WI_ClearFlag(windowStyle, WS_POPUP);
|
||||
WI_ClearFlag(windowStyle, WS_BORDER);
|
||||
WI_ClearFlag(windowStyle, WS_SIZEBOX);
|
||||
|
||||
WI_SetAllFlags(windowStyle, WS_OVERLAPPEDWINDOW);
|
||||
|
||||
return windowStyle;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enable or disable focus mode. When entering focus mode, we'll
|
||||
// need to manually hide the entire titlebar.
|
||||
// - When we're entering focus we need to do some additional modification
|
||||
// of our window styles. However, the NonClientIslandWindow very explicitly
|
||||
// _doesn't_ need to do these steps.
|
||||
// Arguments:
|
||||
// - borderlessEnabled: If true, we're entering focus mode. If false, we're leaving.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::_SetIsBorderless(const bool borderlessEnabled)
|
||||
{
|
||||
_borderless = borderlessEnabled;
|
||||
|
||||
HWND const hWnd = GetHandle();
|
||||
|
||||
// First, modify regular window styles as appropriate
|
||||
auto windowStyle = _getDesiredWindowStyle();
|
||||
_SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle);
|
||||
|
||||
// Now modify extended window styles as appropriate
|
||||
// When moving to fullscreen, remove the window edge style to avoid an
|
||||
// ugly border when not focused.
|
||||
auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE);
|
||||
WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen);
|
||||
_SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle);
|
||||
|
||||
// Resize the window, with SWP_FRAMECHANGED, to trigger user32 to
|
||||
// recalculate the non/client areas
|
||||
const til::rectangle windowPos{ GetWindowRect() };
|
||||
SetWindowPos(GetHandle(),
|
||||
HWND_TOP,
|
||||
windowPos.left<int>(),
|
||||
windowPos.top<int>(),
|
||||
windowPos.width<int>(),
|
||||
windowPos.height<int>(),
|
||||
SWP_SHOWWINDOW | SWP_FRAMECHANGED);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Controls setting us into or out of fullscreen mode. Largely taken from
|
||||
// Window::SetIsFullscreen in conhost.
|
||||
// - When entering fullscreen mode, we'll save the current window size and
|
||||
// location, and expand to take the entire monitor size. When leaving, we'll
|
||||
// use that saved size to restore back to.
|
||||
// Arguments:
|
||||
// - fullscreenEnabled true if we should enable fullscreen mode, false to disable.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled)
|
||||
{
|
||||
// It is possible to enter _SetIsFullscreen even if we're already in full
|
||||
// screen. Use the old is in fullscreen flag to gate checks that rely on the
|
||||
// current state.
|
||||
const auto oldIsInFullscreen = _fullscreen;
|
||||
_fullscreen = fullscreenEnabled;
|
||||
|
||||
HWND const hWnd = GetHandle();
|
||||
|
||||
// First, modify regular window styles as appropriate
|
||||
auto windowStyle = _getDesiredWindowStyle();
|
||||
_SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle);
|
||||
|
||||
// Now modify extended window styles as appropriate
|
||||
// When moving to fullscreen, remove the window edge style to avoid an
|
||||
// ugly border when not focused.
|
||||
auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE);
|
||||
WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen);
|
||||
_SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle);
|
||||
|
||||
// When entering/exiting fullscreen mode, we also need to backup/restore the
|
||||
// current window size, and resize the window to match the new state.
|
||||
_BackupWindowSizes(oldIsInFullscreen);
|
||||
_ApplyWindowSize();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Used in entering/exiting fullscreen mode. Saves the current window size,
|
||||
// and the full size of the monitor, for use in _ApplyWindowSize.
|
||||
// - Taken from conhost's Window::_BackupWindowSizes
|
||||
// Arguments:
|
||||
// - fCurrentIsInFullscreen: true if we're currently in fullscreen mode.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::_BackupWindowSizes(const bool fCurrentIsInFullscreen)
|
||||
{
|
||||
if (_fullscreen)
|
||||
{
|
||||
// Note: the current window size depends on the current state of the
|
||||
// window. So don't back it up if we're already in full screen.
|
||||
if (!fCurrentIsInFullscreen)
|
||||
{
|
||||
_nonFullscreenWindowSize = GetWindowRect();
|
||||
}
|
||||
|
||||
// get and back up the current monitor's size
|
||||
HMONITOR const hCurrentMonitor = MonitorFromWindow(GetHandle(), MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO currMonitorInfo;
|
||||
currMonitorInfo.cbSize = sizeof(currMonitorInfo);
|
||||
if (GetMonitorInfo(hCurrentMonitor, &currMonitorInfo))
|
||||
{
|
||||
_fullscreenWindowSize = currMonitorInfo.rcMonitor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Applys the appropriate window size for transitioning to/from fullscreen mode.
|
||||
// - Taken from conhost's Window::_ApplyWindowSize
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::_ApplyWindowSize()
|
||||
{
|
||||
const auto newSize = _fullscreen ? _fullscreenWindowSize : _nonFullscreenWindowSize;
|
||||
LOG_IF_WIN32_BOOL_FALSE(SetWindowPos(GetHandle(),
|
||||
HWND_TOP,
|
||||
newSize.left,
|
||||
newSize.top,
|
||||
newSize.right - newSize.left,
|
||||
newSize.bottom - newSize.top,
|
||||
SWP_FRAMECHANGED));
|
||||
}
|
||||
|
||||
DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
|
||||
77
src/tools/ScratchIsland/IslandWindow.h
Normal file
77
src/tools/ScratchIsland/IslandWindow.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "BaseWindow.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
class IslandWindow :
|
||||
public BaseWindow<IslandWindow>
|
||||
{
|
||||
public:
|
||||
IslandWindow() noexcept;
|
||||
virtual ~IslandWindow() override;
|
||||
|
||||
virtual void MakeWindow() noexcept;
|
||||
void Close();
|
||||
virtual void OnSize(const UINT width, const UINT height);
|
||||
|
||||
[[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override;
|
||||
void OnResize(const UINT width, const UINT height) override;
|
||||
void OnMinimize() override;
|
||||
void OnRestore() override;
|
||||
virtual void OnAppInitialized();
|
||||
virtual void SetContent(winrt::Windows::UI::Xaml::UIElement content);
|
||||
virtual void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
|
||||
virtual SIZE GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept;
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
void SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept;
|
||||
|
||||
void FocusModeChanged(const bool focusMode);
|
||||
void FullscreenChanged(const bool fullscreen);
|
||||
void SetAlwaysOnTop(const bool alwaysOnTop);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
|
||||
DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
|
||||
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
|
||||
|
||||
protected:
|
||||
void ForceResize()
|
||||
{
|
||||
// Do a quick resize to force the island to paint
|
||||
const auto size = GetPhysicalSize();
|
||||
OnSize(size.cx, size.cy);
|
||||
}
|
||||
|
||||
HWND _interopWindowHandle;
|
||||
|
||||
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source;
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _rootGrid;
|
||||
|
||||
std::function<void(const HWND, const RECT)> _pfnCreateCallback;
|
||||
|
||||
void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept;
|
||||
[[nodiscard]] LRESULT _OnSizing(const WPARAM wParam, const LPARAM lParam);
|
||||
|
||||
bool _borderless{ false };
|
||||
bool _fullscreen{ false };
|
||||
bool _alwaysOnTop{ false };
|
||||
RECT _fullscreenWindowSize;
|
||||
RECT _nonFullscreenWindowSize;
|
||||
|
||||
virtual void _SetIsBorderless(const bool borderlessEnabled);
|
||||
virtual void _SetIsFullscreen(const bool fullscreenEnabled);
|
||||
void _BackupWindowSizes(const bool currentIsInFullscreen);
|
||||
void _ApplyWindowSize();
|
||||
|
||||
LONG _getDesiredWindowStyle() const;
|
||||
|
||||
private:
|
||||
// This minimum width allows for width the tabs fit
|
||||
static constexpr long minimumWidth = 460L;
|
||||
};
|
||||
1
src/tools/ScratchIsland/ScratchIsland.def
Normal file
1
src/tools/ScratchIsland/ScratchIsland.def
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
24
src/tools/ScratchIsland/ScratchIsland.manifest
Normal file
24
src/tools/ScratchIsland/ScratchIsland.manifest
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<!-- This file is copied into ut_app/TerminalApp.Unit.Tests.manifest as part
|
||||
of the pre-build step for that project. Changes should only be made to the
|
||||
WindowsTerminal version of the file. -->
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 1903 -->
|
||||
<!-- See https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/xaml-islands -->
|
||||
<!-- "maxversiontested" is CASE SENSITIVE. Do not change this.-->
|
||||
<maxversiontested Id="10.0.18362.0"/>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
94
src/tools/ScratchIsland/ScratchIsland.rc
Normal file
94
src/tools/ScratchIsland/ScratchIsland.rc
Normal file
@@ -0,0 +1,94 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_ERROR_DIALOG_TITLE "Error"
|
||||
IDS_HELP_DIALOG_TITLE "Help"
|
||||
IDS_ERROR_ARCHITECTURE_FORMAT
|
||||
"Windows Terminal is designed to run on your system's native architecture (%s).\nYou are currently using the %s version.\n\nPlease use the version of Windows Terminal that matches your system's native architecture."
|
||||
IDS_X86_ARCHITECTURE "i386"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_AMD64_ARCHITECTURE "AMD64"
|
||||
IDS_ARM64_ARCHITECTURE "ARM64"
|
||||
IDS_ARM_ARCHITECTURE "ARM"
|
||||
IDS_UNKNOWN_ARCHITECTURE "Unknown"
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
175
src/tools/ScratchIsland/ScratchIsland.vcxproj
Normal file
175
src/tools/ScratchIsland/ScratchIsland.vcxproj
Normal file
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||||
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{23a1f736-cd19-4196-980f-84bcd50cf783}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>ScratchIsland</RootNamespace>
|
||||
<ProjectName>ScratchIsland</ProjectName>
|
||||
<TargetName>ScratchIsland</TargetName>
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<OpenConsoleUniversalApp>false</OpenConsoleUniversalApp>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsStoreApp>true</WindowsStoreApp>
|
||||
<WindowsAppContainer>false</WindowsAppContainer>
|
||||
<!-- IMPORTANT! Xaml Islands only works on >= 17709 -->
|
||||
<!-- IMPORTANT! cppwinrt.pre.props specifies 17134 -->
|
||||
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<!-- DON'T REDIRECT OUR OUTPUT -->
|
||||
<NoOutputRedirection>true</NoOutputRedirection>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<ClCompile>
|
||||
<!-- Manually include the generated TerminalCore header's path, because
|
||||
adding a project reference will confuse msbuild, because TerminalCore
|
||||
isn't a dll, it's a lib, and cppwinrt won't include a lib's header -->
|
||||
<AdditionalIncludeDirectories>"$(OpenConsoleDir)src\cascadia\TerminalCore\lib\Generated Files";%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>gdi32.lib;dwmapi.lib;Shcore.lib;UxTheme.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<PropertyGroup>
|
||||
<GenerateManifest>true</GenerateManifest>
|
||||
<EmbedManifest>true</EmbedManifest>
|
||||
</PropertyGroup>
|
||||
<!-- Source Files -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="AppHost.h" />
|
||||
<ClInclude Include="BaseWindow.h" />
|
||||
<ClInclude Include="IslandWindow.h" />
|
||||
<ClInclude Include="HostManager.h" />
|
||||
<ClInclude Include="HostAndProcess.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="AppHost.cpp" />
|
||||
<ClCompile Include="IslandWindow.cpp" />
|
||||
<ClCompile Include="HostManager.cpp" />
|
||||
<ClCompile Include="HostAndProcess.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="HostManager.idl" />
|
||||
<Midl Include="HostAndProcess.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ScratchIsland.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="ScratchIsland.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<!-- Dependencies -->
|
||||
<ItemGroup>
|
||||
<!-- Even though we do have proper recursive dependencies, we want to keep some of these here
|
||||
so that the AppX Manifest contains their activatable classes. -->
|
||||
<!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj" /> -->
|
||||
<!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" /> -->
|
||||
<!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" /> -->
|
||||
|
||||
<!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalApp\TerminalApp.vcxproj" /> -->
|
||||
|
||||
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
|
||||
<ProjectReference Include="..\ScratchWinRTServer\ScratchWinRTServer.vcxproj">
|
||||
<Project>{d46d9547-f085-4645-b8f7-e8cd21559ab4}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
This ItemGroup and the Globals PropertyGroup below it are required in order
|
||||
to enable F5 debugging for the unpackaged application
|
||||
-->
|
||||
<ItemGroup>
|
||||
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_general.xml" />
|
||||
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_local_windows.xml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||||
<Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>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}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!-- Override GetPackagingOutputs to roll up all our dependencies.
|
||||
This ensures that when the WAP packaging project asks what files go into
|
||||
the package, we tell it.
|
||||
This is a heavily stripped version of the one in Microsoft.*.AppxPackage.targets.
|
||||
-->
|
||||
<PropertyGroup>
|
||||
<_ContinueOnError Condition="'$(BuildingProject)' == 'true'">true</_ContinueOnError>
|
||||
<_ContinueOnError Condition="'$(BuildingProject)' != 'true'">false</_ContinueOnError>
|
||||
</PropertyGroup>
|
||||
<Target Name="GetPackagingOutputs" Returns="@(PackagingOutputs)">
|
||||
<MSBuild
|
||||
Projects="@(ProjectReferenceWithConfiguration)"
|
||||
Targets="GetPackagingOutputs"
|
||||
BuildInParallel="$(BuildInParallel)"
|
||||
Properties="%(ProjectReferenceWithConfiguration.SetConfiguration); %(ProjectReferenceWithConfiguration.SetPlatform)"
|
||||
Condition="'@(ProjectReferenceWithConfiguration)' != ''
|
||||
and '%(ProjectReferenceWithConfiguration.BuildReference)' == 'true'
|
||||
and '%(ProjectReferenceWithConfiguration.ReferenceOutputAssembly)' == 'true'"
|
||||
ContinueOnError="$(_ContinueOnError)">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="_PackagingOutputsFromOtherProjects"/>
|
||||
</MSBuild>
|
||||
|
||||
<ItemGroup>
|
||||
<PackagingOutputs Include="@(_PackagingOutputsFromOtherProjects)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- **BEGIN VC LIBS HACK** -->
|
||||
<PropertyGroup>
|
||||
<ReasonablePlatform Condition="'$(Platform)'=='Win32'">x86</ReasonablePlatform>
|
||||
<ReasonablePlatform Condition="'$(ReasonablePlatform)'==''">$(Platform)</ReasonablePlatform>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(ScratchIslandOfficialBuild)'=='true'">
|
||||
<!-- Add all the CRT libs as content; these must be inside a Target as they are wildcards. -->
|
||||
<_OpenConsoleVCLibToCopy Include="$(VCToolsRedistInstallDir)\$(ReasonablePlatform)\Microsoft.VC142.CRT\*.dll" />
|
||||
|
||||
<PackagingOutputs Include="@(_OpenConsoleVCLibToCopy)">
|
||||
<ProjectName>$(ProjectName)</ProjectName>
|
||||
<OutputGroup>BuiltProjectOutputGroup</OutputGroup>
|
||||
<TargetPath>%(Filename)%(Extension)</TargetPath>
|
||||
</PackagingOutputs>
|
||||
</ItemGroup>
|
||||
<!-- **END VC LIBS HACK** -->
|
||||
</Target>
|
||||
|
||||
<!-- <Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" /> -->
|
||||
<Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" />
|
||||
</Project>
|
||||
|
||||
54
src/tools/ScratchIsland/main.cpp
Normal file
54
src/tools/ScratchIsland/main.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppHost.h"
|
||||
#include "resource.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Composition;
|
||||
using namespace winrt::Windows::UI::Xaml::Hosting;
|
||||
using namespace winrt::Windows::Foundation::Numerics;
|
||||
|
||||
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
||||
{
|
||||
// If Terminal is spawned by a shortcut that requests that it run in a new process group
|
||||
// while attached to a console session, that request is nonsense. That request will, however,
|
||||
// cause WT to start with Ctrl-C disabled. This wouldn't matter, because it's a Windows-subsystem
|
||||
// application. Unfortunately, that state is heritable. In short, if you start WT using cmd in
|
||||
// a weird way, ^C stops working _inside_ the terminal. Mad.
|
||||
SetConsoleCtrlHandler(NULL, FALSE);
|
||||
|
||||
// Make sure to call this so we get WM_POINTER messages.
|
||||
EnableMouseInPointer(true);
|
||||
|
||||
// !!! LOAD BEARING !!!
|
||||
// We must initialize the main thread as a single-threaded apartment before
|
||||
// constructing any Xaml objects. Failing to do so will cause some issues
|
||||
// in accessibility somewhere down the line when a UIAutomation object will
|
||||
// be queried on the wrong thread at the wrong time.
|
||||
// We used to initialize as STA only _after_ initializing the application
|
||||
// host, which loaded the settings. The settings needed to be loaded in MTA
|
||||
// because we were using the Windows.Storage APIs. Since we're no longer
|
||||
// doing that, we can safely init as STA before any WinRT dispatches.
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
|
||||
// Create the AppHost object, which will create both the window and the
|
||||
// Terminal App. This MUST BE constructed before the Xaml manager as TermApp
|
||||
// provides an implementation of Windows.UI.Xaml.Application.
|
||||
AppHost host;
|
||||
|
||||
// Initialize the xaml content. This must be called AFTER the
|
||||
// WindowsXamlManager is initialized.
|
||||
host.Initialize();
|
||||
|
||||
MSG message;
|
||||
|
||||
while (GetMessage(&message, nullptr, 0, 0))
|
||||
{
|
||||
TranslateMessage(&message);
|
||||
DispatchMessage(&message);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
8
src/tools/ScratchIsland/packages.config
Normal file
8
src/tools/ScratchIsland/packages.config
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||||
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.0.0" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.200609001" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.1-rc" targetFramework="native" />
|
||||
<package id="Terminal.ThemeHelpers" version="0.2.200324001" targetFramework="native" />
|
||||
</packages>
|
||||
4
src/tools/ScratchIsland/pch.cpp
Normal file
4
src/tools/ScratchIsland/pch.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
92
src/tools/ScratchIsland/pch.h
Normal file
92
src/tools/ScratchIsland/pch.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- pch.h
|
||||
|
||||
Abstract:
|
||||
- Contains external headers to include in the precompile phase of console build process.
|
||||
- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Ignore checked iterators warning from VC compiler.
|
||||
#define _SCL_SECURE_NO_WARNINGS
|
||||
|
||||
// Block minwindef.h min/max macros to prevent <algorithm> conflict
|
||||
#define NOMINMAX
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <unknwn.h>
|
||||
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
|
||||
#include <windows.h>
|
||||
#include <UIAutomation.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <shellscalingapi.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
#include "../inc/LibraryIncludes.h"
|
||||
|
||||
// This is inexplicable, but for whatever reason, cppwinrt conflicts with the
|
||||
// SDK definition of this function, so the only fix is to undef it.
|
||||
// from WinBase.h
|
||||
// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime
|
||||
#ifdef GetCurrentTime
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
#include <wil/cppwinrt.h>
|
||||
|
||||
// Needed just for XamlIslands to work at all:
|
||||
#include <winrt/Windows.system.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/windows.ui.core.h>
|
||||
#include <winrt/Windows.UI.Xaml.Hosting.h>
|
||||
#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
|
||||
|
||||
// Additional headers for various xaml features. We need:
|
||||
// * Controls for grid
|
||||
// * Media for ScaleTransform
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.ui.xaml.media.h>
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
#include <wil/resource.h>
|
||||
#include <wil/win32_helpers.h>
|
||||
|
||||
// Including TraceLogging essentials for the binary
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <winmeta.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider);
|
||||
#include <telemetry\ProjectTelemetry.h>
|
||||
#include <TraceLoggingActivity.h>
|
||||
|
||||
// For commandline argument processing
|
||||
#include <shellapi.h>
|
||||
#include <processenv.h>
|
||||
|
||||
#include <dcomp.h>
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <d2d1.h>
|
||||
#include <d2d1_1.h>
|
||||
#include <d2d1_2.h>
|
||||
#include <d2d1_3.h>
|
||||
#include <d2d1helper.h>
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <dwrite_2.h>
|
||||
#include <dwrite_3.h>
|
||||
|
||||
#include "til.h"
|
||||
24
src/tools/ScratchIsland/resource.h
Normal file
24
src/tools/ScratchIsland/resource.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by WindowsTerminal.rc
|
||||
//
|
||||
#define IDI_APPICON 101
|
||||
#define IDS_ERROR_DIALOG_TITLE 105
|
||||
#define IDS_HELP_DIALOG_TITLE 106
|
||||
#define IDS_ERROR_ARCHITECTURE_FORMAT 110
|
||||
#define IDS_X86_ARCHITECTURE 111
|
||||
#define IDS_AMD64_ARCHITECTURE 112
|
||||
#define IDS_ARM64_ARCHITECTURE 113
|
||||
#define IDS_ARM_ARCHITECTURE 114
|
||||
#define IDS_UNKNOWN_ARCHITECTURE 115
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 104
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
261
src/tools/ScratchWinRTServer/HostClass.cpp
Normal file
261
src/tools/ScratchWinRTServer/HostClass.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
#include "pch.h"
|
||||
#include <argb.h>
|
||||
#include <DefaultSettings.h>
|
||||
#include "HostClass.h"
|
||||
#include "HostClass.g.cpp"
|
||||
|
||||
// #include <wrl.h>
|
||||
extern std::mutex m;
|
||||
extern std::condition_variable cv;
|
||||
extern bool dtored;
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Input;
|
||||
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::ViewManagement;
|
||||
using namespace winrt::Windows::UI::Input;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
// using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
|
||||
namespace winrt::ScratchWinRTServer::implementation
|
||||
{
|
||||
HostClass::HostClass(const winrt::guid& g) :
|
||||
_id{ g },
|
||||
_desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 },
|
||||
_actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false }
|
||||
{
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
_settings = winrt::Microsoft::Terminal::Settings::TerminalSettings();
|
||||
|
||||
auto fontSize = _settings.FontSize();
|
||||
const auto newSize = std::max<short>(gsl::narrow_cast<short>(fontSize), 1);
|
||||
const auto fontFace = _settings.FontFace();
|
||||
const auto fontWeight = _settings.FontWeight();
|
||||
_actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false };
|
||||
_desiredFont = { _actualFont };
|
||||
}
|
||||
|
||||
HostClass::~HostClass()
|
||||
{
|
||||
printf("~HostClass()\n");
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
dtored = true;
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
winrt::guid HostClass::Id()
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
void HostClass::Attach(Windows::UI::Xaml::Controls::SwapChainPanel panel)
|
||||
{
|
||||
printf("Hey dummy, we're not using Attach() anymore\n");
|
||||
}
|
||||
|
||||
void HostClass::BeginRendering()
|
||||
{
|
||||
_InitializeTerminal();
|
||||
}
|
||||
|
||||
void HostClass::RenderEngineSwapChainChanged()
|
||||
{
|
||||
// This event is only registered during terminal initialization,
|
||||
// so we don't need to check _initializedTerminal.
|
||||
// We also don't lock for things that come back from the renderer.
|
||||
auto chainHandle = _renderEngine->GetSwapChainHandle();
|
||||
auto weakThis{ get_weak() };
|
||||
|
||||
// co_await winrt::resume_foreground(Dispatcher());
|
||||
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
_AttachDxgiSwapChainToXaml(chainHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void HostClass::_AttachDxgiSwapChainToXaml(HANDLE swapChainHandle)
|
||||
{
|
||||
// NOPE DONT DO THIS
|
||||
// auto nativePanel = _panel.as<ISwapChainPanelNative2>();
|
||||
// nativePanel->SetSwapChainHandle(swapChainHandle);
|
||||
}
|
||||
|
||||
void HostClass::ThisIsInsane(uint64_t swapchainHandle)
|
||||
{
|
||||
HANDLE foo = (HANDLE)swapchainHandle;
|
||||
_hSwapchain.reset(foo);
|
||||
// _hSwapchain.put((HANDLE)swapchainHandle);
|
||||
// _hSwapchain = (HANDLE)swapchainHandle;
|
||||
}
|
||||
|
||||
bool HostClass::_InitializeTerminal()
|
||||
{
|
||||
{ // scope for terminalLock
|
||||
auto terminalLock = _terminal->LockForWriting();
|
||||
|
||||
if (_initializedTerminal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// const auto actualWidth = _panel.ActualWidth();
|
||||
// const auto actualHeight = _panel.ActualHeight();
|
||||
// const auto windowWidth = actualWidth * _panel.CompositionScaleX(); // Width() and Height() are NaN?
|
||||
// const auto windowHeight = actualHeight * _panel.CompositionScaleY();
|
||||
|
||||
const auto actualWidth = 400; //_panel.ActualWidth();
|
||||
const auto actualHeight = 150; //_panel.ActualHeight();
|
||||
const auto windowWidth = 400.0; // actualWidth * _panel.CompositionScaleX(); // Width() and Height() are NaN?
|
||||
const auto windowHeight = 150.0; // actualHeight * _panel.CompositionScaleY();
|
||||
|
||||
if (windowWidth == 0 || windowHeight == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First create the render thread.
|
||||
// Then stash a local pointer to the render thread so we can initialize it and enable it
|
||||
// to paint itself *after* we hand off its ownership to the renderer.
|
||||
// We split up construction and initialization of the render thread object this way
|
||||
// because the renderer and render thread have circular references to each other.
|
||||
auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
|
||||
auto* const localPointerToThread = renderThread.get();
|
||||
|
||||
// Now create the renderer and initialize the render thread.
|
||||
_renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
|
||||
::Microsoft::Console::Render::IRenderTarget& renderTarget = *_renderer;
|
||||
|
||||
// _renderer->SetRendererEnteredErrorStateCallback([weakThis = get_weak()]() {
|
||||
// if (auto strongThis{ weakThis.get() })
|
||||
// {
|
||||
// strongThis->_RendererEnteredErrorState();
|
||||
// }
|
||||
// });
|
||||
|
||||
THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
|
||||
|
||||
// Set up the DX Engine
|
||||
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
|
||||
dxEngine->_swapChainHandle.swap(_hSwapchain);
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
|
||||
// Initialize our font with the renderer
|
||||
// We don't have to care about DPI. We'll get a change message immediately if it's not 96
|
||||
// and react accordingly.
|
||||
// _UpdateFont(true); // <-- TODO
|
||||
_renderer->TriggerFontChange(96, _desiredFont, _actualFont); // "UpdateFont"
|
||||
|
||||
const COORD windowSize{ static_cast<short>(windowWidth), static_cast<short>(windowHeight) };
|
||||
|
||||
// Fist set up the dx engine with the window size in pixels.
|
||||
// Then, using the font, get the number of characters that can fit.
|
||||
// Resize our terminal connection to match that size, and initialize the terminal with that size.
|
||||
const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
|
||||
THROW_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
|
||||
|
||||
// Update DxEngine's SelectionBackground
|
||||
// dxEngine->SetSelectionBackground(_settings.SelectionBackground());
|
||||
|
||||
const auto vp = dxEngine->GetViewportInCharacters(viewInPixels);
|
||||
const auto width = vp.Width();
|
||||
const auto height = vp.Height();
|
||||
// _connection.Resize(height, width); // <-- TODO
|
||||
|
||||
// Override the default width and height to match the size of the swapChainPanel
|
||||
// _settings.InitialCols(width); // <-- TODO
|
||||
// _settings.InitialRows(height); // <-- TODO
|
||||
_settings.DefaultBackground(til::color{ 255, 0, 255, 255 }); //rgba
|
||||
_settings.DefaultForeground(til::color{ 0, 0, 0, 255 }); //rgba
|
||||
_terminal->CreateFromSettings(_settings, renderTarget); // <-- TODO
|
||||
|
||||
dxEngine->SetRetroTerminalEffects(false);
|
||||
dxEngine->SetForceFullRepaintRendering(false);
|
||||
dxEngine->SetSoftwareRendering(false);
|
||||
|
||||
// // Update DxEngine's AntialiasingMode
|
||||
// switch (_settings.AntialiasingMode())
|
||||
// {
|
||||
// case TextAntialiasingMode::Cleartype:
|
||||
// dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
|
||||
// break;
|
||||
// case TextAntialiasingMode::Aliased:
|
||||
// dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
||||
// break;
|
||||
// case TextAntialiasingMode::Grayscale:
|
||||
// default:
|
||||
// dxEngine->SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// // GH#5098: Inform the engine of the opacity of the default text background.
|
||||
// if (_settings.UseAcrylic())
|
||||
// {
|
||||
// dxEngine->SetDefaultTextBackgroundOpacity(::base::saturated_cast<float>(_settings.TintOpacity()));
|
||||
// }
|
||||
|
||||
THROW_IF_FAILED(dxEngine->Enable());
|
||||
_renderEngine = std::move(dxEngine);
|
||||
|
||||
// _AttachDxgiSwapChainToXaml(_renderEngine->GetSwapChainHandle());
|
||||
|
||||
// Tell the DX Engine to notify us when the swap chain changes.
|
||||
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems)
|
||||
_renderEngine->SetCallback(std::bind(&HostClass::RenderEngineSwapChainChanged, this));
|
||||
|
||||
// auto bottom = _terminal->GetViewport().BottomExclusive();
|
||||
// auto bufferHeight = bottom;
|
||||
|
||||
// ScrollBar().Maximum(bufferHeight - bufferHeight);
|
||||
// ScrollBar().Minimum(0);
|
||||
// ScrollBar().Value(0);
|
||||
// ScrollBar().ViewportSize(bufferHeight);
|
||||
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
// // Set up blinking cursor
|
||||
// int blinkTime = GetCaretBlinkTime();
|
||||
// if (blinkTime != INFINITE)
|
||||
// {
|
||||
// // Create a timer
|
||||
// DispatcherTimer cursorTimer;
|
||||
// cursorTimer.Interval(std::chrono::milliseconds(blinkTime));
|
||||
// cursorTimer.Tick({ get_weak(), &TermControl::_CursorTimerTick });
|
||||
// cursorTimer.Start();
|
||||
// _cursorTimer.emplace(std::move(cursorTimer));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // The user has disabled cursor blinking
|
||||
// _cursorTimer = std::nullopt;
|
||||
// }
|
||||
|
||||
// // import value from WinUser (convert from milli-seconds to micro-seconds)
|
||||
// _multiClickTimer = GetDoubleClickTime() * 1000;
|
||||
|
||||
// // Focus the control here. If we do it during control initialization, then
|
||||
// // focus won't actually get passed to us. I believe this is because
|
||||
// // we're not technically a part of the UI tree yet, so focusing us
|
||||
// // becomes a no-op.
|
||||
// this->Focus(FocusState::Programmatic);
|
||||
|
||||
_initializedTerminal = true;
|
||||
} // scope for TerminalLock
|
||||
|
||||
// Start the connection outside of lock, because it could
|
||||
// start writing output immediately.
|
||||
// _connection.Start(); // <-- TODO
|
||||
|
||||
// Likewise, run the event handlers outside of lock (they could
|
||||
// be reentrant)
|
||||
// _InitializedHandlers(*this, nullptr);
|
||||
|
||||
_terminal->Write(L"Hello world from another process");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
52
src/tools/ScratchWinRTServer/HostClass.h
Normal file
52
src/tools/ScratchWinRTServer/HostClass.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "HostClass.g.h"
|
||||
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
|
||||
#include <winrt/Microsoft.Terminal.Settings.h>
|
||||
#include "../../renderer/base/Renderer.hpp"
|
||||
#include "../../renderer/dx/DxRenderer.hpp"
|
||||
#include "../../renderer/uia/UiaRenderer.hpp"
|
||||
#include "../../cascadia/TerminalCore/Terminal.hpp"
|
||||
|
||||
namespace winrt::ScratchWinRTServer::implementation
|
||||
{
|
||||
struct HostClass : HostClassT<HostClass>
|
||||
{
|
||||
HostClass(const winrt::guid& g);
|
||||
~HostClass();
|
||||
void DoTheThing();
|
||||
|
||||
void Attach(Windows::UI::Xaml::Controls::SwapChainPanel panel);
|
||||
void BeginRendering();
|
||||
|
||||
void RenderEngineSwapChainChanged();
|
||||
|
||||
void ThisIsInsane(uint64_t swapchainHandle);
|
||||
|
||||
private:
|
||||
int _DoCount{ 0 };
|
||||
winrt::guid _id;
|
||||
wil::unique_handle _hSwapchain{ INVALID_HANDLE_VALUE };
|
||||
Windows::UI::Xaml::Controls::SwapChainPanel _panel{ nullptr };
|
||||
|
||||
bool _initializedTerminal{ false };
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _connection;
|
||||
std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal;
|
||||
FontInfoDesired _desiredFont;
|
||||
FontInfo _actualFont;
|
||||
|
||||
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer;
|
||||
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine;
|
||||
winrt::Microsoft::Terminal::Settings::IControlSettings _settings;
|
||||
void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle);
|
||||
|
||||
bool _InitializeTerminal();
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::ScratchWinRTServer::factory_implementation
|
||||
{
|
||||
struct HostClass : HostClassT<HostClass, implementation::HostClass>
|
||||
{
|
||||
};
|
||||
}
|
||||
15
src/tools/ScratchWinRTServer/HostClass.idl
Normal file
15
src/tools/ScratchWinRTServer/HostClass.idl
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
namespace ScratchWinRTServer
|
||||
{
|
||||
[default_interface] runtimeclass HostClass {
|
||||
HostClass(Guid g);
|
||||
|
||||
Guid Id { get; };
|
||||
|
||||
void Attach(Windows.UI.Xaml.Controls.SwapChainPanel panel);
|
||||
void BeginRendering();
|
||||
|
||||
void ThisIsInsane(UInt64 swapchainHandle);
|
||||
};
|
||||
}
|
||||
6
src/tools/ScratchWinRTServer/IMyComInterface.h
Normal file
6
src/tools/ScratchWinRTServer/IMyComInterface.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
struct __declspec(uuid("28578d33-c090-4279-9669-dbeea3f24bb0")) IMyComInterface : ::IUnknown
|
||||
{
|
||||
virtual HRESULT __stdcall Call() = 0;
|
||||
};
|
||||
9
src/tools/ScratchWinRTServer/IMyComInterface.idl
Normal file
9
src/tools/ScratchWinRTServer/IMyComInterface.idl
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
// [
|
||||
// uuid("BB64926F-1A4D-470D-BB8A-3D2CC4B035E4"),
|
||||
// object,
|
||||
// local
|
||||
// ] interface IMyComInterface : IUnknown
|
||||
// {
|
||||
// HRESULT MyMethod();
|
||||
// };
|
||||
11
src/tools/ScratchWinRTServer/IScratchInterface.idl
Normal file
11
src/tools/ScratchWinRTServer/IScratchInterface.idl
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
namespace ScratchWinRTServer
|
||||
{
|
||||
// [default_interface]
|
||||
interface IScratchInterface
|
||||
{
|
||||
String DoTheThing();
|
||||
};
|
||||
|
||||
}
|
||||
16
src/tools/ScratchWinRTServer/PropertySheet.props
Normal file
16
src/tools/ScratchWinRTServer/PropertySheet.props
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<!--
|
||||
To customize common C++/WinRT project properties:
|
||||
* right-click the project node
|
||||
* expand the Common Properties item
|
||||
* select the C++/WinRT property page
|
||||
|
||||
For more advanced scenarios, and complete documentation, please see:
|
||||
https://github.com/Microsoft/cppwinrt/tree/master/nuget
|
||||
-->
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup />
|
||||
</Project>
|
||||
11
src/tools/ScratchWinRTServer/ScratchClass.cpp
Normal file
11
src/tools/ScratchWinRTServer/ScratchClass.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "pch.h"
|
||||
#include "ScratchClass.h"
|
||||
|
||||
#include "ScratchClass.g.cpp"
|
||||
namespace winrt::ScratchWinRTServer::implementation
|
||||
{
|
||||
ScratchClass::ScratchClass()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
21
src/tools/ScratchWinRTServer/ScratchClass.h
Normal file
21
src/tools/ScratchWinRTServer/ScratchClass.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "ScratchClass.g.h"
|
||||
namespace winrt::ScratchWinRTServer::implementation
|
||||
{
|
||||
struct ScratchClass : public ScratchClassT<ScratchClass>
|
||||
{
|
||||
ScratchClass();
|
||||
hstring DoTheThing()
|
||||
{
|
||||
return L"Hello there";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::ScratchWinRTServer::factory_implementation
|
||||
{
|
||||
struct ScratchClass : ScratchClassT<ScratchClass, implementation::ScratchClass>
|
||||
{
|
||||
};
|
||||
}
|
||||
15
src/tools/ScratchWinRTServer/ScratchClass.idl
Normal file
15
src/tools/ScratchWinRTServer/ScratchClass.idl
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
namespace ScratchWinRTServer
|
||||
{
|
||||
[default_interface] runtimeclass ScratchClass // : IScratchInterface
|
||||
{
|
||||
ScratchClass();
|
||||
String DoTheThing();
|
||||
|
||||
// Adding a Windows.UI.Xaml element will crash the server when the
|
||||
// server tries to instantiate the element without a XAML host.
|
||||
//
|
||||
// Windows.UI.Xaml.Controls.Button MyButton { get; };
|
||||
};
|
||||
}
|
||||
27
src/tools/ScratchWinRTServer/ScratchWinRTServer.manifest
Normal file
27
src/tools/ScratchWinRTServer/ScratchWinRTServer.manifest
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 1903 -->
|
||||
<!-- See https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/xaml-islands -->
|
||||
<!-- "maxversiontested" is CASE SENSITIVE. Do not change this.-->
|
||||
<maxversiontested Id="10.0.18362.0"/>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
|
||||
<file name="ScratchWinRTServer.exe" hashalg="SHA1">
|
||||
<activatableClass name="ScratchWinRTServer.ScratchClass" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1"></activatableClass>
|
||||
</file>
|
||||
<file name="TerminalSettings.dll" hashalg="SHA1">
|
||||
<activatableClass name="Microsoft.Terminal.Settings.TerminalSettings" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1"></activatableClass>
|
||||
</file>
|
||||
|
||||
</assembly>
|
||||
179
src/tools/ScratchWinRTServer/ScratchWinRTServer.vcxproj
Normal file
179
src/tools/ScratchWinRTServer/ScratchWinRTServer.vcxproj
Normal file
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{d46d9547-f085-4645-b8f7-e8cd21559ab4}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>ScratchWinRTServer</RootNamespace>
|
||||
<WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="PropertySheet.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<PreprocessorDefinitions>_CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj</AdditionalOptions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ScratchClass.h" />
|
||||
<ClInclude Include="HostClass.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ScratchClass.cpp" />
|
||||
<ClCompile Include="HostClass.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="PropertySheet.props" />
|
||||
<Text Include="readme.txt">
|
||||
<DeploymentContent>false</DeploymentContent>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="IScratchInterface.idl" />
|
||||
<Midl Include="ScratchClass.idl" />
|
||||
<Midl Include="HostClass.idl" />
|
||||
<!-- <Midl Include="IMyComInterface.idl" /> -->
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="ScratchWinRTServer.manifest" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<!-- <ProjectReference Include="..\ScratchWinRTServer\ScratchWinRTServer.vcxproj">
|
||||
<Project>{d46d9547-f085-4645-b8f7-e8cd21559ab4}</Project>
|
||||
</ProjectReference> -->
|
||||
<ProjectReference Include="$(SolutionDir)src\types\lib\types.vcxproj" />
|
||||
|
||||
<ProjectReference Include="$(SolutionDir)src\buffer\out\lib\bufferout.vcxproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\base\lib\base.vcxproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\dx\lib\dx.vcxproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\renderer\uia\lib\uia.vcxproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\terminal\parser\lib\parser.vcxproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\terminal\input\lib\terminalinput.vcxproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj">
|
||||
<Private>false</Private>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)src\cascadia\TerminalCore\lib\TerminalCore-lib.vcxproj" />
|
||||
<ProjectReference Include="$(SolutionDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj">
|
||||
<Private>false</Private>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<!-- ====================== Compiler & Linker Flags ===================== -->
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<!-- <AdditionalIncludeDirectories>$(SolutionDir)\src\inc;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories> -->
|
||||
<!-- Manually disable unreachable code warning, because jconcpp has a ton of that. -->
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>onecoreuap_apiset.lib;d3dcompiler.lib;dwmapi.lib;uxtheme.lib;shlwapi.lib;ntdll.lib;dcomp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>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}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200609.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="readme.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
203
src/tools/ScratchWinRTServer/main.cpp
Normal file
203
src/tools/ScratchWinRTServer/main.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#include "pch.h"
|
||||
#include <winrt/ScratchWinRTServer.h>
|
||||
#include "ScratchClass.h"
|
||||
#include "HostClass.h"
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
struct ScratchStringable : implements<ScratchStringable, IStringable, IClosable, winrt::ScratchWinRTServer::IScratchInterface>
|
||||
{
|
||||
hstring ToString()
|
||||
{
|
||||
return L"Hello from server, ScratchStringable";
|
||||
}
|
||||
hstring DoTheThing()
|
||||
{
|
||||
return L"Zhu Li! Do the thing!";
|
||||
}
|
||||
void Close() { printf("Closed ScratchStringable\n"); }
|
||||
};
|
||||
struct MyStringable : implements<MyStringable, IStringable>
|
||||
{
|
||||
hstring ToString()
|
||||
{
|
||||
return L"Hello from server, MyStringable";
|
||||
}
|
||||
};
|
||||
|
||||
struct MyStringableFactory : implements<MyStringableFactory, IClassFactory>
|
||||
{
|
||||
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
|
||||
{
|
||||
*result = nullptr;
|
||||
if (outer)
|
||||
{
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
printf("Created MyStringable\n");
|
||||
return make<MyStringable>().as(iid, result);
|
||||
}
|
||||
|
||||
HRESULT __stdcall LockServer(BOOL) noexcept final
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
struct ScratchStringableFactory : implements<ScratchStringableFactory, IClassFactory>
|
||||
{
|
||||
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
|
||||
{
|
||||
*result = nullptr;
|
||||
if (outer)
|
||||
{
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
printf("Created ScratchStringable\n");
|
||||
return make<ScratchStringable>().as(iid, result);
|
||||
}
|
||||
|
||||
HRESULT __stdcall LockServer(BOOL) noexcept final
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
struct ScratchClassFactory : implements<ScratchClassFactory, IClassFactory>
|
||||
{
|
||||
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
|
||||
{
|
||||
*result = nullptr;
|
||||
if (outer)
|
||||
{
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
printf("\x1b[90mSERVER: Created ScratchClass\x1b[m\n");
|
||||
return make<ScratchWinRTServer::implementation::ScratchClass>().as(iid, result);
|
||||
}
|
||||
|
||||
HRESULT __stdcall LockServer(BOOL) noexcept final
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
bool dtored = false;
|
||||
winrt::weak_ref<ScratchWinRTServer::implementation::HostClass> g_weak{ nullptr };
|
||||
|
||||
struct HostClassFactory : implements<HostClassFactory, IClassFactory>
|
||||
{
|
||||
HostClassFactory(winrt::guid g) :
|
||||
_guid{ g } {};
|
||||
|
||||
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
|
||||
{
|
||||
*result = nullptr;
|
||||
if (outer)
|
||||
{
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
|
||||
if (!g_weak)
|
||||
{
|
||||
auto strong = make_self<ScratchWinRTServer::implementation::HostClass>(_guid);
|
||||
|
||||
g_weak = (*strong).get_weak();
|
||||
return strong.as(iid, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto strong = g_weak.get();
|
||||
return strong.as(iid, result);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT __stdcall LockServer(BOOL) noexcept final
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
winrt::guid _guid;
|
||||
};
|
||||
|
||||
// DAA16D7F-EF66-4FC9-B6F2-3E5B6D924576
|
||||
static constexpr GUID MyStringable_clsid{
|
||||
0xdaa16d7f,
|
||||
0xef66,
|
||||
0x4fc9,
|
||||
{ 0xb6, 0xf2, 0x3e, 0x5b, 0x6d, 0x92, 0x45, 0x76 }
|
||||
};
|
||||
|
||||
// EAA16D7F-EF66-4FC9-B6F2-3E5B6D924576
|
||||
static constexpr GUID ScratchStringable_clsid{
|
||||
0xeaa16d7f,
|
||||
0xef66,
|
||||
0x4fc9,
|
||||
{ 0xb6, 0xf2, 0x3e, 0x5b, 0x6d, 0x92, 0x45, 0x76 }
|
||||
};
|
||||
// FAA16D7F-EF66-4FC9-B6F2-3E5B6D924576
|
||||
static constexpr GUID ScratchClass_clsid{
|
||||
0xfaa16d7f,
|
||||
0xef66,
|
||||
0x4fc9,
|
||||
{ 0xb6, 0xf2, 0x3e, 0x5b, 0x6d, 0x92, 0x45, 0x76 }
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD dwMode = 0;
|
||||
GetConsoleMode(hOut, &dwMode);
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
SetConsoleMode(hOut, dwMode);
|
||||
|
||||
printf("\x1b[90mSERVER: args:[");
|
||||
if (argc > 0)
|
||||
{
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
printf("%s,", argv[i]);
|
||||
}
|
||||
}
|
||||
printf("]\x1b[m\n");
|
||||
|
||||
winrt::guid guidFromCmdline{};
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
std::string guidString{ argv[1] };
|
||||
auto canConvert = guidString.length() == 38 && guidString.front() == '{' && guidString.back() == '}';
|
||||
if (canConvert)
|
||||
{
|
||||
std::wstring wideGuidStr{ til::u8u16(guidString) };
|
||||
printf("\x1b[90mSERVER: Found GUID:%ls\x1b[m\n", wideGuidStr.c_str());
|
||||
GUID result{};
|
||||
THROW_IF_FAILED(IIDFromString(wideGuidStr.c_str(), &result));
|
||||
guidFromCmdline = result;
|
||||
}
|
||||
}
|
||||
if (guidFromCmdline == winrt::guid{})
|
||||
{
|
||||
printf("did not recieve GUID, early returning.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_apartment();
|
||||
|
||||
DWORD registrationHostClass{};
|
||||
check_hresult(CoRegisterClassObject(guidFromCmdline,
|
||||
make<HostClassFactory>(guidFromCmdline).get(),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
REGCLS_MULTIPLEUSE,
|
||||
®istrationHostClass));
|
||||
printf("%d\n", registrationHostClass);
|
||||
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
cv.wait(lk, [] { return dtored; });
|
||||
|
||||
// printf("\x1b[90mSERVER: Press Enter me when you're done serving.\x1b[m");
|
||||
// getchar();
|
||||
printf("\x1b[90mSERVER: exiting %s\n\x1b[m", argv[1]);
|
||||
}
|
||||
4
src/tools/ScratchWinRTServer/packages.config
Normal file
4
src/tools/ScratchWinRTServer/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200609.3" targetFramework="native" />
|
||||
</packages>
|
||||
1
src/tools/ScratchWinRTServer/pch.cpp
Normal file
1
src/tools/ScratchWinRTServer/pch.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
35
src/tools/ScratchWinRTServer/pch.h
Normal file
35
src/tools/ScratchWinRTServer/pch.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#define BLOCK_TIL
|
||||
#include <wil/cppwinrt.h>
|
||||
#undef max
|
||||
#undef min
|
||||
#include "LibraryIncludes.h"
|
||||
|
||||
#include <Unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.UI.Xaml.Media.h>
|
||||
#include <windows.ui.xaml.media.dxinterop.h>
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <d2d1.h>
|
||||
#include <d2d1_1.h>
|
||||
#include <d2d1_2.h>
|
||||
#include <d2d1_3.h>
|
||||
#include <d2d1helper.h>
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <dwrite_2.h>
|
||||
#include <dwrite_3.h>
|
||||
30
src/tools/ScratchWinRTServer/readme.txt
Normal file
30
src/tools/ScratchWinRTServer/readme.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
========================================================================
|
||||
C++/WinRT ScratchWinRTServer Project Overview
|
||||
========================================================================
|
||||
|
||||
This project demonstrates how to get started consuming Windows Runtime
|
||||
classes directly from standard C++, using platform projection headers
|
||||
generated from Windows SDK metadata files.
|
||||
|
||||
Steps to generate and consume SDK platform projection:
|
||||
1. Build project initally to generate platform projection headers into
|
||||
your Generated Files folder.
|
||||
2. Include a projection namespace header in your pch.h, such as
|
||||
<winrt/Windows.Foundation.h>.
|
||||
3. Consume winrt namespace and any Windows Runtime namespaces, such as
|
||||
winrt::Windows::Foundation, from source code.
|
||||
4. Initialize apartment via init_apartment() and consume winrt classes.
|
||||
|
||||
Steps to generate and consume a projection from third party metadata:
|
||||
1. Add a WinMD reference by right-clicking the References project node
|
||||
and selecting "Add Reference...". In the Add References dialog,
|
||||
browse to the component WinMD you want to consume and add it.
|
||||
2. Build the project once to generate projection headers for the
|
||||
referenced WinMD file under the "Generated Files" subfolder.
|
||||
3. As above, include projection headers in pch or source code
|
||||
to consume projected Windows Runtime classes.
|
||||
|
||||
========================================================================
|
||||
Learn more about C++/WinRT here:
|
||||
http://aka.ms/cppwinrt/
|
||||
========================================================================
|
||||
@@ -2,52 +2,9 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// This wmain exists for help in writing scratch programs while debugging.
|
||||
int __cdecl wmain(int /*argc*/, WCHAR* /*argv[]*/)
|
||||
{
|
||||
unsigned char buf[256];
|
||||
DWORD len;
|
||||
int i;
|
||||
auto in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
auto out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
const wchar_t alpha = L'\u03b1';
|
||||
|
||||
DWORD mode;
|
||||
GetConsoleMode(in, &mode);
|
||||
//mode &= ~ENABLE_LINE_INPUT;
|
||||
SetConsoleMode(in, mode);
|
||||
|
||||
SetConsoleCP(932);
|
||||
SetConsoleOutputCP(437);
|
||||
|
||||
printf("Input CP %d\n", GetConsoleCP());
|
||||
printf("Output CP %d\n", GetConsoleOutputCP());
|
||||
|
||||
GetConsoleMode(in, &mode);
|
||||
printf("Input Mode %02x\n", mode);
|
||||
GetConsoleMode(out, &mode);
|
||||
printf("Output Mode %02x\n", mode);
|
||||
|
||||
CONSOLE_FONT_INFOEX font = { 0 };
|
||||
font.cbSize = sizeof(font);
|
||||
BOOL retval = GetCurrentConsoleFontEx(out, FALSE, &font);
|
||||
DWORD err = GetLastError();
|
||||
|
||||
retval;
|
||||
err;
|
||||
|
||||
//mode &= ~ENABLE_LINE_INPUT;
|
||||
//SetConsoleMode(in, mode);
|
||||
|
||||
while (true)
|
||||
{
|
||||
ReadFile(in, buf, sizeof(buf), &len, NULL);
|
||||
for (i = 0; i < (int)len; i++)
|
||||
printf("%02x ", buf[i]);
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user