mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-10 16:21:06 +00:00
Compare commits
32 Commits
release-1.
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9021900904 | ||
|
|
f6b051945f | ||
|
|
52b05e065e | ||
|
|
81a80257c9 | ||
|
|
1ea9fc26c8 | ||
|
|
e1d15105d7 | ||
|
|
d321ec084c | ||
|
|
f33c69d8b4 | ||
|
|
30b8335479 | ||
|
|
93b79fb23c | ||
|
|
efb1fddb99 | ||
|
|
7bc5de613c | ||
|
|
7b8806b1fe | ||
|
|
8c5041b2ae | ||
|
|
a511ab0bc7 | ||
|
|
501c47adb2 | ||
|
|
72121721f3 | ||
|
|
3e80b6a466 | ||
|
|
a80e1d3f73 | ||
|
|
f45df9a717 | ||
|
|
b37f69826e | ||
|
|
b2e76c1812 | ||
|
|
2179e16e3d | ||
|
|
fafc0e1316 | ||
|
|
ca6dff9f20 | ||
|
|
186ff8f638 | ||
|
|
37810aac71 | ||
|
|
33bc88b225 | ||
|
|
d56137876e | ||
|
|
f928d41917 | ||
|
|
e4cc3104ab | ||
|
|
0c10b4b265 |
105
OpenConsole.sln
105
OpenConsole.sln
@@ -87,8 +87,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Host.Tests.Feature", "src\h
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{18D09A24-8240-42D6-8CB6-236EEE820263} = {18D09A24-8240-42D6-8CB6-236EEE820263}
|
||||
{FC802440-AD6A-4919-8F2C-7701F2B38D79} = {FC802440-AD6A-4919-8F2C-7701F2B38D79}
|
||||
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
|
||||
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}
|
||||
{9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalParser.UnitTests", "src\terminal\parser\ut_parser\Parser.UnitTests.vcxproj", "{12144E07-FE63-4D33-9231-748B8D8C3792}"
|
||||
@@ -314,6 +314,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}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchWinRTClient", "src\tools\ScratchWinRTClient\ScratchWinRTClient.vcxproj", "{06382349-D62A-4C7D-A7D3-9CA817EAE092}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchIsland", "src\tools\ScratchIsland\ScratchIsland.vcxproj", "{23a1f736-cd19-4196-980f-84bcd50cf783}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{06382349-D62A-4C7D-A7D3-9CA817EAE092} = {06382349-D62A-4C7D-A7D3-9CA817EAE092}
|
||||
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}"
|
||||
EndProject
|
||||
Global
|
||||
@@ -1997,6 +2010,62 @@ 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
|
||||
{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
|
||||
@@ -2024,6 +2093,37 @@ Global
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32
|
||||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32
|
||||
|
||||
|
||||
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.ActiveCfg = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.Build.0 = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.ActiveCfg = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.Build.0 = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.ActiveCfg = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.Build.0 = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.ActiveCfg = Release|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.Build.0 = Release|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.Build.0 = Debug|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.Build.0 = Debug|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|ARM64.ActiveCfg = Release|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.ActiveCfg = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.Build.0 = Release|x64
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.ActiveCfg = Release|Win32
|
||||
{23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2104,6 +2204,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}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
||||
@@ -34,7 +34,7 @@ steps:
|
||||
configuration: '$(BuildConfiguration)'
|
||||
msbuildArgs: "${{ parameters.additionalBuildArguments }}"
|
||||
clean: true
|
||||
maximumCpuCount: false
|
||||
maximumCpuCount: true
|
||||
|
||||
- task: PowerShell@2
|
||||
displayName: 'Check MSIX for common regressions'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
|
||||
<XesBaseYearForStoreVersion>2020</XesBaseYearForStoreVersion>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>2</VersionMinor>
|
||||
<VersionMinor>3</VersionMinor>
|
||||
<VersionInfoProductName>Windows Terminal</VersionInfoProductName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -158,7 +158,7 @@ For commands with arguments:
|
||||
| ---- | ---- |
|
||||
| Function and Alphanumeric Keys | `f1-f24`, `a-z`, `0-9` |
|
||||
| Symbols | ``` ` ```, `-`, `=`, `[`, `]`, `\`, `;`, `'`, `,`, `.`, `/` |
|
||||
| Arrow Keys | `down`, `left`, `right`, `up`, `pagedown`, `pageup`, `pgdn`, `pgup`, `end`, `home`, `plus`, `app`, `menu` |
|
||||
| Arrow Keys | `down`, `left`, `right`, `up`, `pagedown`, `pageup`, `pgdn`, `pgup`, `end`, `home`, `plus` |
|
||||
| Action Keys | `tab`, `enter`, `esc`, `escape`, `space`, `backspace`, `delete`, `insert` |
|
||||
| Numpad Keys | `numpad_0-numpad_9`, `numpad0-numpad9`, `numpad_add`, `numpad_plus`, `numpad_decimal`, `numpad_period`, `numpad_divide`, `numpad_minus`, `numpad_subtract`, `numpad_multiply` |
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"title": "Microsoft's Windows Terminal Settings Profile Schema",
|
||||
"definitions": {
|
||||
"KeyChordSegment": {
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)(?:\\+(ctrl|alt|shift)(?<!\\2))?(?:\\+(ctrl|alt|shift)(?<!\\2|\\3))?\\+)?(?<key>[^\\s+]|app|menu|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
"pattern": "^(?<modifier>(ctrl|alt|shift)(?:\\+(ctrl|alt|shift)(?<!\\2))?(?:\\+(ctrl|alt|shift)(?<!\\2|\\3))?\\+)?(?<key>[^\\s+]|backspace|tab|enter|esc|escape|space|pgup|pageup|pgdn|pagedown|end|home|left|up|right|down|insert|delete|(?<!shift.+)(?:numpad_?[0-9]|numpad_(?:period|decimal))|numpad_(?:multiply|plus|add|minus|subtract|divide)|f[1-9]|f1[0-9]|f2[0-4]|plus)$",
|
||||
"type": "string",
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\napp, menu\tMENU key\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
"description": "The string should fit the format \"[ctrl+][alt+][shift+]<keyName>\", where each modifier is optional, separated by + symbols, and keyName is either one of the names listed in the table below, or any single key character. The string should be written in full lowercase.\nbackspace\tBACKSPACE key\ntab\tTAB key\nenter\tENTER key\nesc, escape\tESC key\nspace\tSPACEBAR\npgup, pageup\tPAGE UP key\npgdn, pagedown\tPAGE DOWN key\nend\tEND key\nhome\tHOME key\nleft\tLEFT ARROW key\nup\tUP ARROW key\nright\tRIGHT ARROW key\ndown\tDOWN ARROW key\ninsert\tINS key\ndelete\tDEL key\nnumpad_0-numpad_9, numpad0-numpad9\tNumeric keypad keys 0 to 9. Can't be combined with the shift modifier.\nnumpad_multiply\tNumeric keypad MULTIPLY key (*)\nnumpad_plus, numpad_add\tNumeric keypad ADD key (+)\nnumpad_minus, numpad_subtract\tNumeric keypad SUBTRACT key (-)\nnumpad_period, numpad_decimal\tNumeric keypad DECIMAL key (.). Can't be combined with the shift modifier.\nnumpad_divide\tNumeric keypad DIVIDE key (/)\nf1-f24\tF1 to F24 function keys\nplus\tADD key (+)"
|
||||
},
|
||||
"Color": {
|
||||
"default": "#",
|
||||
@@ -392,16 +392,10 @@
|
||||
"minimum": 1,
|
||||
"type": "integer"
|
||||
},
|
||||
"startOnUserLogin": {
|
||||
"default": false,
|
||||
"description": "When set to true, this enables the launch of Windows Terminal at startup. Setting this to false will disable the startup task entry. If the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"launchMode": {
|
||||
"default": "default",
|
||||
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window.",
|
||||
"description": "Defines whether the Terminal will launch as maximized or not.",
|
||||
"enum": [
|
||||
"fullscreen",
|
||||
"maximized",
|
||||
"default"
|
||||
],
|
||||
|
||||
BIN
res/Cascadia.ttf
BIN
res/Cascadia.ttf
Binary file not shown.
Binary file not shown.
@@ -17,5 +17,5 @@ Please consult the [license](https://raw.githubusercontent.com/microsoft/cascadi
|
||||
|
||||
### Fonts Included
|
||||
|
||||
* Cascadia Code, Cascadia Mono (2008.25)
|
||||
* from microsoft/cascadia-code@678eea921b0c8b921b9fb009bb16d3d2ca5b8112
|
||||
* Cascadia Code, Cascadia Mono (2007.15)
|
||||
* from microsoft/cascadia-code@2a54363b2c867f7ae811b9a034c0024cef67de96
|
||||
|
||||
@@ -35,11 +35,6 @@ void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph)
|
||||
// - erases key and its associated data from the storage
|
||||
// Arguments:
|
||||
// - key - the key to remove
|
||||
// ~~~~~~~~~~~~~~
|
||||
// NOTE: VS 16.7 changes std::map::erase to noexcept, but the build agents are still 16.6.5.
|
||||
// Ignore this audit warning on your dev box until the build starts failing. Then fix it
|
||||
// and remove this comment.
|
||||
// ~~~~~~~~~~~~~
|
||||
void UnicodeStorage::Erase(const key_type key)
|
||||
{
|
||||
_map.erase(key);
|
||||
|
||||
@@ -147,10 +147,8 @@ namespace TerminalAppLocalTests
|
||||
{ "name": "command0", "command": { "action": "splitPane", "split": null } },
|
||||
{ "name": "command1", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "name": "command2", "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "name": "command3", "command": { "action": "splitPane", "split": "none" } },
|
||||
{ "name": "command4", "command": { "action": "splitPane" } },
|
||||
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } },
|
||||
{ "name": "command6", "command": { "action": "splitPane", "split": "foo" } }
|
||||
{ "name": "command5", "command": { "action": "splitPane", "split": "auto" } }
|
||||
])" };
|
||||
|
||||
const auto commands0Json = VerifyParseSucceeded(commands0String);
|
||||
@@ -159,7 +157,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_ARE_EQUAL(0u, commands.size());
|
||||
auto warnings = implementation::Command::LayerJson(commands, commands0Json);
|
||||
VERIFY_ARE_EQUAL(0u, warnings.size());
|
||||
VERIFY_ARE_EQUAL(7u, commands.size());
|
||||
VERIFY_ARE_EQUAL(5u, commands.size());
|
||||
|
||||
{
|
||||
auto command = commands.at(L"command0");
|
||||
@@ -191,16 +189,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.at(L"command3");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.at(L"command4");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
@@ -221,16 +209,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
auto command = commands.at(L"command6");
|
||||
VERIFY_IS_NOT_NULL(command);
|
||||
VERIFY_IS_NOT_NULL(command.Action());
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action());
|
||||
const auto& realArgs = command.Action().Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
void CommandTests::TestResourceKeyName()
|
||||
{
|
||||
|
||||
@@ -323,10 +323,8 @@ namespace TerminalAppLocalTests
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "splitPane", "split": null } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "splitPane", "split": "horizontal" } },
|
||||
{ "keys": ["ctrl+f"], "command": { "action": "splitPane", "split": "none" } },
|
||||
{ "keys": ["ctrl+g"], "command": { "action": "splitPane" } },
|
||||
{ "keys": ["ctrl+h"], "command": { "action": "splitPane", "split": "auto" } },
|
||||
{ "keys": ["ctrl+i"], "command": { "action": "splitPane", "split": "foo" } }
|
||||
{ "keys": ["ctrl+h"], "command": { "action": "splitPane", "split": "auto" } }
|
||||
])" };
|
||||
|
||||
const auto bindings0Json = VerifyParseSucceeded(bindings0String);
|
||||
@@ -335,7 +333,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(7u, appKeyBindings->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(5u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
@@ -364,15 +362,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Horizontal, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('G') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
@@ -391,15 +380,6 @@ namespace TerminalAppLocalTests
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('I') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SplitPaneArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_ARE_EQUAL(winrt::TerminalApp::SplitState::Automatic, realArgs.SplitStyle());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingsTests::TestSetTabColorArgs()
|
||||
@@ -407,7 +387,6 @@ namespace TerminalAppLocalTests
|
||||
const std::string bindings0String{ R"([
|
||||
{ "keys": ["ctrl+c"], "command": { "action": "setTabColor", "color": null } },
|
||||
{ "keys": ["ctrl+d"], "command": { "action": "setTabColor", "color": "#123456" } },
|
||||
{ "keys": ["ctrl+e"], "command": { "action": "setTabColor", "color": "thisStringObviouslyWontWork" } },
|
||||
{ "keys": ["ctrl+f"], "command": "setTabColor" },
|
||||
])" };
|
||||
|
||||
@@ -417,7 +396,7 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_NOT_NULL(appKeyBindings);
|
||||
VERIFY_ARE_EQUAL(0u, appKeyBindings->_keyShortcuts.size());
|
||||
appKeyBindings->LayerJson(bindings0Json);
|
||||
VERIFY_ARE_EQUAL(4u, appKeyBindings->_keyShortcuts.size());
|
||||
VERIFY_ARE_EQUAL(3u, appKeyBindings->_keyShortcuts.size());
|
||||
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('C') };
|
||||
@@ -439,15 +418,6 @@ namespace TerminalAppLocalTests
|
||||
// Remember that COLORREFs are actually BBGGRR order, while the string is in #RRGGBB order
|
||||
VERIFY_ARE_EQUAL(static_cast<uint32_t>(til::color(0x563412)), realArgs.TabColor().Value());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('E') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
VERIFY_ARE_EQUAL(ShortcutAction::SetTabColor, actionAndArgs.Action());
|
||||
const auto& realArgs = actionAndArgs.Args().try_as<SetTabColorArgs>();
|
||||
VERIFY_IS_NOT_NULL(realArgs);
|
||||
// Verify the args have the expected value
|
||||
VERIFY_IS_NULL(realArgs.TabColor());
|
||||
}
|
||||
{
|
||||
KeyChord kc{ true, false, false, static_cast<int32_t>('F') };
|
||||
auto actionAndArgs = TestUtils::GetActionAndArgs(*appKeyBindings, kc);
|
||||
|
||||
@@ -1431,10 +1431,6 @@ namespace TerminalAppLocalTests
|
||||
{
|
||||
"name": "profile3",
|
||||
"closeOnExit": null
|
||||
},
|
||||
{
|
||||
"name": "profile4",
|
||||
"closeOnExit": { "clearly": "not a string" }
|
||||
}
|
||||
]
|
||||
})" };
|
||||
@@ -1449,7 +1445,6 @@ namespace TerminalAppLocalTests
|
||||
|
||||
// Unknown modes parse as "Graceful"
|
||||
VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings._profiles[3].GetCloseOnExitMode());
|
||||
VERIFY_ARE_EQUAL(CloseOnExitMode::Graceful, settings._profiles[4].GetCloseOnExitMode());
|
||||
}
|
||||
void SettingsTests::TestCloseOnExitCompatibilityShim()
|
||||
{
|
||||
@@ -2354,7 +2349,7 @@ namespace TerminalAppLocalTests
|
||||
"commandline": "wsl.exe"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
"bindings": [
|
||||
{ "keys": "ctrl+a", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "name": "ctrl+b", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
{ "keys": "ctrl+c", "name": "ctrl+c", "command": { "action": "splitPane", "split": "vertical" } },
|
||||
@@ -2528,4 +2523,5 @@ namespace TerminalAppLocalTests
|
||||
VERIFY_IS_TRUE(realArgs.TerminalArgs().Profile().empty());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,35 +16,12 @@ using namespace ::Microsoft::Terminal::Core;
|
||||
|
||||
static LPCWSTR term_window_class = L"HwndTerminalClass";
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
static constexpr bool _IsMouseMessage(UINT uMsg)
|
||||
{
|
||||
return uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP || uMsg == WM_LBUTTONDBLCLK ||
|
||||
uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP || uMsg == WM_MBUTTONDBLCLK ||
|
||||
uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP || uMsg == WM_RBUTTONDBLCLK ||
|
||||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL;
|
||||
}
|
||||
|
||||
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
|
||||
// See microsoft/terminal#2066 for more info.
|
||||
static bool _IsGlyphWideForceNarrowFallback(const std::wstring_view /* glyph */) noexcept
|
||||
{
|
||||
return false; // glyph is not wide.
|
||||
}
|
||||
|
||||
static bool _EnsureStaticInitialization()
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static bool initialized = []() {
|
||||
// *** THIS IS A SINGLETON ***
|
||||
SetGlyphWidthFallback(_IsGlyphWideForceNarrowFallback);
|
||||
|
||||
return true;
|
||||
}();
|
||||
return initialized;
|
||||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
|
||||
@@ -59,31 +36,10 @@ try
|
||||
|
||||
if (terminal)
|
||||
{
|
||||
if (_IsMouseMessage(uMsg))
|
||||
if (_IsMouseMessage(uMsg) && terminal->_CanSendVTMouseInput())
|
||||
{
|
||||
if (terminal->_CanSendVTMouseInput() && terminal->_SendMouseEvent(uMsg, wParam, lParam))
|
||||
if (terminal->_SendMouseEvent(uMsg, wParam, lParam))
|
||||
{
|
||||
// GH#6401: Capturing the mouse ensures that we get drag/release events
|
||||
// even if the user moves outside the window.
|
||||
// _SendMouseEvent returns false if the terminal's not in VT mode, so we'll
|
||||
// fall through to release the capture.
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
SetCapture(hwnd);
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Suppress all mouse events that made it into the terminal.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -101,10 +57,6 @@ try
|
||||
return 0;
|
||||
case WM_LBUTTONUP:
|
||||
terminal->_singleClickTouchdownPos = std::nullopt;
|
||||
[[fallthrough]];
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
if (WI_IsFlagSet(wParam, MK_LBUTTON))
|
||||
@@ -120,7 +72,7 @@ try
|
||||
{
|
||||
const auto bufferData = terminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
LOG_IF_FAILED(terminal->_CopyTextToSystemClipboard(bufferData, true));
|
||||
TerminalClearSelection(terminal);
|
||||
terminal->_terminal->ClearSelection();
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
@@ -129,13 +81,6 @@ try
|
||||
terminal->_PasteTextFromClipboard();
|
||||
}
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
// Release Terminal's hwnd so Teardown doesn't try to destroy it again
|
||||
terminal->_hwnd.release();
|
||||
terminal->Teardown();
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
@@ -169,16 +114,14 @@ static bool RegisterTermClass(HINSTANCE hInstance) noexcept
|
||||
}
|
||||
|
||||
HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
_desiredFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, DEFAULT_FONT_WEIGHT, { 0, 14 }, CP_UTF8, false },
|
||||
_desiredFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8 },
|
||||
_actualFont{ L"Consolas", 0, 10, { 0, 14 }, CP_UTF8, false },
|
||||
_uiaProvider{ nullptr },
|
||||
_uiaProviderInitialized{ false },
|
||||
_currentDpi{ USER_DEFAULT_SCREEN_DPI },
|
||||
_pfnWriteCallback{ nullptr },
|
||||
_multiClickTime{ 500 } // this will be overwritten by the windows system double-click time
|
||||
{
|
||||
_EnsureStaticInitialization();
|
||||
|
||||
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
|
||||
|
||||
if (RegisterTermClass(hInstance))
|
||||
@@ -205,11 +148,6 @@ HwndTerminal::HwndTerminal(HWND parentHwnd) :
|
||||
}
|
||||
}
|
||||
|
||||
HwndTerminal::~HwndTerminal()
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
|
||||
HRESULT HwndTerminal::Initialize()
|
||||
{
|
||||
_terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
|
||||
@@ -224,6 +162,9 @@ HRESULT HwndTerminal::Initialize()
|
||||
RETURN_IF_FAILED(dxEngine->Enable());
|
||||
_renderer->AddRenderEngine(dxEngine.get());
|
||||
|
||||
const auto pfn = std::bind(&::Microsoft::Console::Render::Renderer::IsGlyphWideByFont, _renderer.get(), std::placeholders::_1);
|
||||
SetGlyphWidthFallback(pfn);
|
||||
|
||||
_UpdateFont(USER_DEFAULT_SCREEN_DPI);
|
||||
RECT windowRect;
|
||||
GetWindowRect(_hwnd.get(), &windowRect);
|
||||
@@ -240,8 +181,8 @@ HRESULT HwndTerminal::Initialize()
|
||||
_terminal->SetBackgroundCallback([](auto) {});
|
||||
|
||||
_terminal->Create(COORD{ 80, 25 }, 1000, *_renderer);
|
||||
_terminal->SetDefaultBackground(RGB(12, 12, 12));
|
||||
_terminal->SetDefaultForeground(RGB(204, 204, 204));
|
||||
_terminal->SetDefaultBackground(RGB(5, 27, 80));
|
||||
_terminal->SetDefaultForeground(RGB(255, 255, 255));
|
||||
_terminal->SetWriteInputCallback([=](std::wstring & input) noexcept { _WriteTextToConnection(input); });
|
||||
localPointerToThread->EnablePainting();
|
||||
|
||||
@@ -250,33 +191,6 @@ HRESULT HwndTerminal::Initialize()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void HwndTerminal::Teardown() noexcept
|
||||
try
|
||||
{
|
||||
// As a rule, detach resources from the Terminal before shutting them down.
|
||||
// This ensures that teardown is reentrant.
|
||||
|
||||
// Shut down the renderer (and therefore the thread) before we implode
|
||||
if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
|
||||
{
|
||||
if (auto localRenderer{ std::exchange(_renderer, nullptr) })
|
||||
{
|
||||
localRenderer->TriggerTeardown();
|
||||
// renderer is destroyed
|
||||
}
|
||||
// renderEngine is destroyed
|
||||
}
|
||||
|
||||
if (auto localHwnd{ _hwnd.release() })
|
||||
{
|
||||
// If we're being called through WM_DESTROY, we won't get here (hwnd is already released)
|
||||
// If we're not, we may end up in Teardown _again_... but by the time we do, all other
|
||||
// resources have been released and will not be released again.
|
||||
DestroyWindow(localHwnd);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void HwndTerminal::RegisterScrollCallback(std::function<void(int, int, int)> callback)
|
||||
{
|
||||
_terminal->SetScrollPositionChangedCallback(callback);
|
||||
@@ -553,21 +467,11 @@ try
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
void HwndTerminal::_ClearSelection() noexcept
|
||||
try
|
||||
{
|
||||
auto lock{ _terminal->LockForWriting() };
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void _stdcall TerminalClearSelection(void* terminal)
|
||||
{
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_ClearSelection();
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
publicTerminal->_terminal->ClearSelection();
|
||||
}
|
||||
|
||||
bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
{
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
@@ -578,10 +482,9 @@ bool _stdcall TerminalIsSelectionActive(void* terminal)
|
||||
// Returns the selected text in the terminal.
|
||||
const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
{
|
||||
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
|
||||
|
||||
const auto bufferData = publicTerminal->_terminal->RetrieveSelectedTextFromBuffer(false);
|
||||
publicTerminal->_ClearSelection();
|
||||
|
||||
// convert text: vector<string> --> string
|
||||
std::wstring selectedText;
|
||||
@@ -591,6 +494,8 @@ const wchar_t* _stdcall TerminalGetSelection(void* terminal)
|
||||
}
|
||||
|
||||
auto returnText = wil::make_cotaskmem_string_nothrow(selectedText.c_str());
|
||||
TerminalClearSelection(terminal);
|
||||
|
||||
return returnText.release();
|
||||
}
|
||||
|
||||
@@ -636,30 +541,19 @@ bool HwndTerminal::_CanSendVTMouseInput() const noexcept
|
||||
bool HwndTerminal::_SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept
|
||||
try
|
||||
{
|
||||
til::point cursorPosition{
|
||||
const til::point cursorPosition{
|
||||
GET_X_LPARAM(lParam),
|
||||
GET_Y_LPARAM(lParam),
|
||||
};
|
||||
|
||||
const til::size fontSize{ this->_actualFont.GetSize() };
|
||||
short wheelDelta{ 0 };
|
||||
if (uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL)
|
||||
if (uMsg == WM_MOUSEWHEEL)
|
||||
{
|
||||
wheelDelta = HIWORD(wParam);
|
||||
|
||||
// If it's a *WHEEL event, it's in screen coordinates, not window (?!)
|
||||
POINT coordsToTransform = cursorPosition;
|
||||
ScreenToClient(_hwnd.get(), &coordsToTransform);
|
||||
cursorPosition = coordsToTransform;
|
||||
}
|
||||
|
||||
const TerminalInput::MouseButtonState state{
|
||||
WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
|
||||
};
|
||||
|
||||
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state);
|
||||
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -667,24 +561,20 @@ catch (...)
|
||||
return false;
|
||||
}
|
||||
|
||||
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept
|
||||
void HwndTerminal::_SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept
|
||||
try
|
||||
{
|
||||
auto modifiers = getControlKeyState();
|
||||
if (WI_IsFlagSet(flags, ENHANCED_KEY))
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
_terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown);
|
||||
const auto flags = getControlKeyState();
|
||||
_terminal->SendKeyEvent(vkey, scanCode, flags, keyDown);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept
|
||||
void HwndTerminal::_SendCharEvent(wchar_t ch, WORD scanCode) noexcept
|
||||
try
|
||||
{
|
||||
if (_terminal->IsSelectionActive())
|
||||
{
|
||||
_ClearSelection();
|
||||
_terminal->ClearSelection();
|
||||
if (ch == UNICODE_ESC)
|
||||
{
|
||||
// ESC should clear any selection before it triggers input.
|
||||
@@ -699,25 +589,21 @@ try
|
||||
return;
|
||||
}
|
||||
|
||||
auto modifiers = getControlKeyState();
|
||||
if (WI_IsFlagSet(flags, ENHANCED_KEY))
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
_terminal->SendCharEvent(ch, scanCode, modifiers);
|
||||
const auto flags = getControlKeyState();
|
||||
_terminal->SendCharEvent(ch, scanCode, flags);
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown)
|
||||
void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_SendKeyEvent(vkey, scanCode, flags, keyDown);
|
||||
publicTerminal->_SendKeyEvent(vkey, scanCode, keyDown);
|
||||
}
|
||||
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags)
|
||||
void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode)
|
||||
{
|
||||
const auto publicTerminal = static_cast<HwndTerminal*>(terminal);
|
||||
publicTerminal->_SendCharEvent(ch, scanCode, flags);
|
||||
publicTerminal->_SendCharEvent(ch, scanCode);
|
||||
}
|
||||
|
||||
void _stdcall DestroyTerminal(void* terminal)
|
||||
@@ -735,7 +621,6 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
|
||||
publicTerminal->_terminal->SetDefaultForeground(theme.DefaultForeground);
|
||||
publicTerminal->_terminal->SetDefaultBackground(theme.DefaultBackground);
|
||||
publicTerminal->_renderEngine->SetSelectionBackground(theme.DefaultSelectionBackground, theme.SelectionBackgroundAlpha);
|
||||
|
||||
// Set the font colors
|
||||
for (size_t tableIndex = 0; tableIndex < 16; tableIndex++)
|
||||
@@ -747,7 +632,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font
|
||||
|
||||
publicTerminal->_terminal->SetCursorStyle(theme.CursorStyle);
|
||||
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, DEFAULT_FONT_WEIGHT, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_desiredFont = { fontFamily, 0, 10, { 0, fontSize }, CP_UTF8 };
|
||||
publicTerminal->_UpdateFont(newDpi);
|
||||
|
||||
// When the font changes the terminal dimensions need to be recalculated since the available row and column
|
||||
|
||||
@@ -12,13 +12,10 @@
|
||||
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
|
||||
// Keep in sync with TerminalTheme.cs
|
||||
typedef struct _TerminalTheme
|
||||
{
|
||||
COLORREF DefaultBackground;
|
||||
COLORREF DefaultForeground;
|
||||
COLORREF DefaultSelectionBackground;
|
||||
float SelectionBackgroundAlpha;
|
||||
DispatchTypes::CursorStyle CursorStyle;
|
||||
COLORREF ColorTable[16];
|
||||
} TerminalTheme, *LPTerminalTheme;
|
||||
@@ -37,8 +34,8 @@ __declspec(dllexport) bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
__declspec(dllexport) void _stdcall DestroyTerminal(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
__declspec(dllexport) void _stdcall TerminalRegisterWriteCallback(void* terminal, const void __stdcall callback(wchar_t*));
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD flags, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
|
||||
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
__declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
__declspec(dllexport) void _stdcall TerminalSetFocus(void* terminal);
|
||||
@@ -54,10 +51,9 @@ public:
|
||||
HwndTerminal(HwndTerminal&&) = default;
|
||||
HwndTerminal& operator=(const HwndTerminal&) = default;
|
||||
HwndTerminal& operator=(HwndTerminal&&) = default;
|
||||
~HwndTerminal();
|
||||
~HwndTerminal() = default;
|
||||
|
||||
HRESULT Initialize();
|
||||
void Teardown() noexcept;
|
||||
void SendOutput(std::wstring_view data);
|
||||
HRESULT Refresh(const SIZE windowSize, _Out_ COORD* dimensions);
|
||||
void RegisterScrollCallback(std::function<void(int, int, int)> callback);
|
||||
@@ -96,8 +92,8 @@ private:
|
||||
friend void _stdcall TerminalClearSelection(void* terminal);
|
||||
friend const wchar_t* _stdcall TerminalGetSelection(void* terminal);
|
||||
friend bool _stdcall TerminalIsSelectionActive(void* terminal);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, WORD flags, bool keyDown);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode, WORD flags);
|
||||
friend void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode, bool keyDown);
|
||||
friend void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
|
||||
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
|
||||
friend void _stdcall TerminalBlinkCursor(void* terminal);
|
||||
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
|
||||
@@ -116,13 +112,11 @@ private:
|
||||
HRESULT _MoveSelection(LPARAM lParam) noexcept;
|
||||
IRawElementProviderSimple* _GetUiaProvider() noexcept;
|
||||
|
||||
void _ClearSelection() noexcept;
|
||||
|
||||
bool _CanSendVTMouseInput() const noexcept;
|
||||
bool _SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
void _SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown) noexcept;
|
||||
void _SendCharEvent(wchar_t ch, WORD scanCode, WORD flags) noexcept;
|
||||
void _SendKeyEvent(WORD vkey, WORD scanCode, bool keyDown) noexcept;
|
||||
void _SendCharEvent(wchar_t ch, WORD scanCode) noexcept;
|
||||
|
||||
// Inherited via IControlAccessibilityInfo
|
||||
COORD GetFontSize() const override;
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "ActionArgs.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "ActionAndArgs.g.cpp"
|
||||
|
||||
#include "JsonUtils.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
static constexpr std::string_view CopyTextKey{ "copy" };
|
||||
@@ -44,6 +47,8 @@ static constexpr std::string_view UnboundKey{ "unbound" };
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
// Specifically use a map here over an unordered_map. We want to be able to
|
||||
// iterate over these entries in-order when we're serializing the keybindings.
|
||||
// HERE BE DRAGONS:
|
||||
@@ -183,11 +188,9 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
else if (json.isObject())
|
||||
{
|
||||
const auto actionVal = json[JsonKey(ActionKey)];
|
||||
if (actionVal.isString())
|
||||
if (const auto actionString{ JsonUtils::GetValueForKey<std::optional<std::string>>(json, ActionKey) })
|
||||
{
|
||||
auto actionString = actionVal.asString();
|
||||
action = GetActionFromString(actionString);
|
||||
action = GetActionFromString(*actionString);
|
||||
argsVal = json;
|
||||
}
|
||||
}
|
||||
@@ -281,5 +284,4 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto found = GeneratedActionNames.find(_Action);
|
||||
return found != GeneratedActionNames.end() ? found->second : L"";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalWarnings.h"
|
||||
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
// Notes on defining ActionArgs and ActionEventArgs:
|
||||
// * All properties specific to an action should be defined as an ActionArgs
|
||||
// class that implements IActionArgs
|
||||
@@ -31,6 +33,7 @@
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
using namespace ::TerminalApp;
|
||||
using FromJsonResult = std::tuple<winrt::TerminalApp::IActionArgs, std::vector<::TerminalApp::SettingsLoadWarnings>>;
|
||||
|
||||
struct ActionEventArgs : public ActionEventArgsT<ActionEventArgs>
|
||||
@@ -73,26 +76,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<NewTerminalArgs>();
|
||||
if (auto commandline{ json[JsonKey(CommandlineKey)] })
|
||||
{
|
||||
args->_Commandline = winrt::to_hstring(commandline.asString());
|
||||
}
|
||||
if (auto startingDirectory{ json[JsonKey(StartingDirectoryKey)] })
|
||||
{
|
||||
args->_StartingDirectory = winrt::to_hstring(startingDirectory.asString());
|
||||
}
|
||||
if (auto tabTitle{ json[JsonKey(TabTitleKey)] })
|
||||
{
|
||||
args->_TabTitle = winrt::to_hstring(tabTitle.asString());
|
||||
}
|
||||
if (auto index{ json[JsonKey(ProfileIndexKey)] })
|
||||
{
|
||||
args->_ProfileIndex = index.asInt();
|
||||
}
|
||||
if (auto profile{ json[JsonKey(ProfileKey)] })
|
||||
{
|
||||
args->_Profile = winrt::to_hstring(profile.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, CommandlineKey, args->_Commandline);
|
||||
JsonUtils::GetValueForKey(json, StartingDirectoryKey, args->_StartingDirectory);
|
||||
JsonUtils::GetValueForKey(json, TabTitleKey, args->_TabTitle);
|
||||
JsonUtils::GetValueForKey(json, ProfileIndexKey, args->_ProfileIndex);
|
||||
JsonUtils::GetValueForKey(json, ProfileKey, args->_Profile);
|
||||
return *args;
|
||||
}
|
||||
};
|
||||
@@ -120,10 +108,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<CopyTextArgs>();
|
||||
if (auto singleLine{ json[JsonKey(SingleLineKey)] })
|
||||
{
|
||||
args->_SingleLine = singleLine.asBool();
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, SingleLineKey, args->_SingleLine);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
@@ -177,49 +162,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SwitchToTabArgs>();
|
||||
if (auto tabIndex{ json[JsonKey(TabIndexKey)] })
|
||||
{
|
||||
args->_TabIndex = tabIndex.asUInt();
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, TabIndexKey, args->_TabIndex);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
// Possible Direction values
|
||||
// TODO:GH#2550/#3475 - move these to a centralized deserializing place
|
||||
static constexpr std::string_view LeftString{ "left" };
|
||||
static constexpr std::string_view RightString{ "right" };
|
||||
static constexpr std::string_view UpString{ "up" };
|
||||
static constexpr std::string_view DownString{ "down" };
|
||||
|
||||
// Function Description:
|
||||
// - Helper function for parsing a Direction from a string
|
||||
// Arguments:
|
||||
// - directionString: the string to attempt to parse
|
||||
// Return Value:
|
||||
// - The encoded Direction value, or Direction::None if it was an invalid string
|
||||
static TerminalApp::Direction ParseDirection(const std::string& directionString)
|
||||
{
|
||||
if (directionString == LeftString)
|
||||
{
|
||||
return TerminalApp::Direction::Left;
|
||||
}
|
||||
else if (directionString == RightString)
|
||||
{
|
||||
return TerminalApp::Direction::Right;
|
||||
}
|
||||
else if (directionString == UpString)
|
||||
{
|
||||
return TerminalApp::Direction::Up;
|
||||
}
|
||||
else if (directionString == DownString)
|
||||
{
|
||||
return TerminalApp::Direction::Down;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::Direction::None;
|
||||
};
|
||||
|
||||
struct ResizePaneArgs : public ResizePaneArgsT<ResizePaneArgs>
|
||||
{
|
||||
ResizePaneArgs() = default;
|
||||
@@ -243,10 +190,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<ResizePaneArgs>();
|
||||
if (auto directionString{ json[JsonKey(DirectionKey)] })
|
||||
{
|
||||
args->_Direction = ParseDirection(directionString.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
|
||||
if (args->_Direction == TerminalApp::Direction::None)
|
||||
{
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
@@ -281,10 +225,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<MoveFocusArgs>();
|
||||
if (auto directionString{ json[JsonKey(DirectionKey)] })
|
||||
{
|
||||
args->_Direction = ParseDirection(directionString.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, DirectionKey, args->_Direction);
|
||||
if (args->_Direction == TerminalApp::Direction::None)
|
||||
{
|
||||
return { nullptr, { ::TerminalApp::SettingsLoadWarnings::MissingRequiredParameter } };
|
||||
@@ -319,48 +260,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<AdjustFontSizeArgs>();
|
||||
if (auto jsonDelta{ json[JsonKey(AdjustFontSizeDelta)] })
|
||||
{
|
||||
args->_Delta = jsonDelta.asInt();
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, AdjustFontSizeDelta, args->_Delta);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
// Possible SplitState values
|
||||
// TODO:GH#2550/#3475 - move these to a centralized deserializing place
|
||||
static constexpr std::string_view VerticalKey{ "vertical" };
|
||||
static constexpr std::string_view HorizontalKey{ "horizontal" };
|
||||
static constexpr std::string_view AutomaticKey{ "auto" };
|
||||
static TerminalApp::SplitState ParseSplitState(const std::string& stateString)
|
||||
{
|
||||
if (stateString == VerticalKey)
|
||||
{
|
||||
return TerminalApp::SplitState::Vertical;
|
||||
}
|
||||
else if (stateString == HorizontalKey)
|
||||
{
|
||||
return TerminalApp::SplitState::Horizontal;
|
||||
}
|
||||
else if (stateString == AutomaticKey)
|
||||
{
|
||||
return TerminalApp::SplitState::Automatic;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::SplitState::Automatic;
|
||||
};
|
||||
|
||||
// Possible SplitType values
|
||||
static constexpr std::string_view DuplicateKey{ "duplicate" };
|
||||
static TerminalApp::SplitType ParseSplitModeState(const std::string& stateString)
|
||||
{
|
||||
if (stateString == DuplicateKey)
|
||||
{
|
||||
return TerminalApp::SplitType::Duplicate;
|
||||
}
|
||||
return TerminalApp::SplitType::Manual;
|
||||
}
|
||||
|
||||
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
|
||||
{
|
||||
SplitPaneArgs() = default;
|
||||
@@ -391,48 +295,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SplitPaneArgs>();
|
||||
args->_TerminalArgs = NewTerminalArgs::FromJson(json);
|
||||
if (auto jsonStyle{ json[JsonKey(SplitKey)] })
|
||||
{
|
||||
args->_SplitStyle = ParseSplitState(jsonStyle.asString());
|
||||
}
|
||||
if (auto jsonStyle{ json[JsonKey(SplitModeKey)] })
|
||||
{
|
||||
args->_SplitMode = ParseSplitModeState(jsonStyle.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitStyle);
|
||||
JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
// Possible SettingsTarget values
|
||||
// TODO:GH#2550/#3475 - move these to a centralized deserializing place
|
||||
static constexpr std::string_view SettingsFileString{ "settingsFile" };
|
||||
static constexpr std::string_view DefaultsFileString{ "defaultsFile" };
|
||||
static constexpr std::string_view AllFilesString{ "allFiles" };
|
||||
|
||||
// Function Description:
|
||||
// - Helper function for parsing a SettingsTarget from a string
|
||||
// Arguments:
|
||||
// - targetString: the string to attempt to parse
|
||||
// Return Value:
|
||||
// - The encoded SettingsTarget value, or SettingsTarget::SettingsFile if it was an invalid string
|
||||
static TerminalApp::SettingsTarget ParseSettingsTarget(const std::string& targetString)
|
||||
{
|
||||
if (targetString == SettingsFileString)
|
||||
{
|
||||
return TerminalApp::SettingsTarget::SettingsFile;
|
||||
}
|
||||
else if (targetString == DefaultsFileString)
|
||||
{
|
||||
return TerminalApp::SettingsTarget::DefaultsFile;
|
||||
}
|
||||
else if (targetString == AllFilesString)
|
||||
{
|
||||
return TerminalApp::SettingsTarget::AllFiles;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TerminalApp::SettingsTarget::SettingsFile;
|
||||
};
|
||||
|
||||
struct OpenSettingsArgs : public OpenSettingsArgsT<OpenSettingsArgs>
|
||||
{
|
||||
OpenSettingsArgs() = default;
|
||||
@@ -456,10 +324,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<OpenSettingsArgs>();
|
||||
if (auto targetString{ json[JsonKey(TargetKey)] })
|
||||
{
|
||||
args->_Target = ParseSettingsTarget(targetString.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, TargetKey, args->_Target);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
@@ -487,16 +352,10 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SetTabColorArgs>();
|
||||
std::optional<til::color> temp;
|
||||
try
|
||||
if (const auto temp{ JsonUtils::GetValueForKey<std::optional<til::color>>(json, ColorKey) })
|
||||
{
|
||||
::TerminalApp::JsonUtils::GetOptionalColor(json, ColorKey, temp);
|
||||
if (temp.has_value())
|
||||
{
|
||||
args->_TabColor = static_cast<uint32_t>(temp.value());
|
||||
}
|
||||
args->_TabColor = static_cast<uint32_t>(*temp);
|
||||
}
|
||||
CATCH_LOG();
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
@@ -524,10 +383,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<RenameTabArgs>();
|
||||
if (auto title{ json[JsonKey(TitleKey)] })
|
||||
{
|
||||
args->_Title = winrt::to_hstring(title.asString());
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, TitleKey, args->_Title);
|
||||
return { *args, {} };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -911,7 +911,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - Implements the Alt handler (per GH#6421)
|
||||
// Return value:
|
||||
// - whether the key was handled
|
||||
bool AppLogic::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
|
||||
bool AppLogic::OnDirectKeyEvent(const uint32_t vkey, const bool down)
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
@@ -922,7 +922,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto keyListener{ focusedObject.try_as<IDirectKeyListener>() })
|
||||
{
|
||||
if (keyListener.OnDirectKeyEvent(vkey, scanCode, down))
|
||||
if (keyListener.OnDirectKeyEvent(vkey, down))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
hstring Title();
|
||||
void TitlebarClicked();
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
|
||||
|
||||
void WindowCloseButtonClicked();
|
||||
|
||||
|
||||
@@ -226,12 +226,9 @@ void CascadiaSettings::_ValidateProfilesHaveGuid()
|
||||
void CascadiaSettings::_ResolveDefaultProfile()
|
||||
{
|
||||
const auto unparsedDefaultProfile{ GlobalSettings().UnparsedDefaultProfile() };
|
||||
if (unparsedDefaultProfile)
|
||||
{
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(*unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ Utils::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) };
|
||||
GlobalSettings().DefaultProfile(defaultProfileGuid);
|
||||
}
|
||||
auto maybeParsedDefaultProfile{ _GetProfileGuidByName(unparsedDefaultProfile) };
|
||||
auto defaultProfileGuid{ Utils::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) };
|
||||
GlobalSettings().DefaultProfile(defaultProfileGuid);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -61,7 +61,7 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
// GH 3588, we need this below to know if the user chose something that wasn't our default.
|
||||
// Collect it up here in case it gets modified by any of the other layers between now and when
|
||||
// the user's preferences are loaded and layered.
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
const auto hardcodedDefaultGuid = resultPtr->GlobalSettings().UnparsedDefaultProfile();
|
||||
|
||||
std::optional<std::string> fileData = _ReadUserSettings();
|
||||
const bool foundFile = fileData.has_value();
|
||||
@@ -141,11 +141,12 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadAll()
|
||||
// is a lot of computation we can skip if no one cares.
|
||||
if (TraceLoggingProviderEnabled(g_hTerminalAppProvider, 0, MICROSOFT_KEYWORD_MEASURES))
|
||||
{
|
||||
const auto hardcodedDefaultGuidAsGuid = Utils::GuidFromString(hardcodedDefaultGuid);
|
||||
const auto guid = resultPtr->GlobalSettings().DefaultProfile();
|
||||
|
||||
// Compare to the defaults.json one that we set on install.
|
||||
// If it's different, log what the user chose.
|
||||
if (hardcodedDefaultGuid != guid)
|
||||
if (hardcodedDefaultGuidAsGuid != guid)
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
@@ -228,7 +229,6 @@ std::unique_ptr<CascadiaSettings> CascadiaSettings::LoadDefaults()
|
||||
// them from a file (and the potential that could fail)
|
||||
resultPtr->_ParseJsonString(DefaultJson, true);
|
||||
resultPtr->LayerJson(resultPtr->_defaultSettings);
|
||||
resultPtr->_ResolveDefaultProfile();
|
||||
|
||||
return resultPtr;
|
||||
}
|
||||
@@ -249,9 +249,9 @@ void CascadiaSettings::_LoadDynamicProfiles()
|
||||
const auto disabledProfileSources = CascadiaSettings::_GetDisabledProfileSourcesJsonObject(_userSettings);
|
||||
if (disabledProfileSources.isArray())
|
||||
{
|
||||
for (const auto& ns : disabledProfileSources)
|
||||
for (const auto& json : disabledProfileSources)
|
||||
{
|
||||
ignoredNamespaces.emplace(GetWstringFromJson(ns));
|
||||
ignoredNamespaces.emplace(JsonUtils::GetValue<std::wstring>(json));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,11 +114,11 @@
|
||||
</StackPanel.Resources>
|
||||
<Button Padding="5"
|
||||
Click="ClearColorButton_Click"
|
||||
x:Name="ClearColorButton" x:Uid="TabColorClearButton" CornerRadius="2" Content="Reset">
|
||||
x:Name="ClearColorButton" x:Uid="TabColorClearButton" Content="Reset">
|
||||
</Button>
|
||||
<Button Padding="5"
|
||||
Click="ShowColorPickerButton_Click"
|
||||
x:Name="CustomColorButton" x:Uid="TabColorCustomButton" CornerRadius="2" Content="Custom...">
|
||||
x:Name="CustomColorButton" x:Uid="TabColorCustomButton" Content="Custom...">
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
@@ -105,9 +105,9 @@ ColorScheme ColorScheme::FromJson(const Json::Value& json)
|
||||
// - true iff the json object has the same `name` as we do.
|
||||
bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
|
||||
{
|
||||
if (const auto name{ json[JsonKey(NameKey)] })
|
||||
std::wstring nameFromJson{};
|
||||
if (JsonUtils::GetValueForKey(json, NameKey, nameFromJson))
|
||||
{
|
||||
const auto nameFromJson = GetWstringFromJson(name);
|
||||
return nameFromJson == _schemeName;
|
||||
}
|
||||
return false;
|
||||
@@ -125,39 +125,16 @@ bool ColorScheme::ShouldBeLayered(const Json::Value& json) const
|
||||
// <none>
|
||||
void ColorScheme::LayerJson(const Json::Value& json)
|
||||
{
|
||||
if (auto name{ json[JsonKey(NameKey)] })
|
||||
{
|
||||
_schemeName = winrt::to_hstring(name.asString());
|
||||
}
|
||||
if (auto fgString{ json[JsonKey(ForegroundKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(fgString.asString());
|
||||
_defaultForeground = color;
|
||||
}
|
||||
if (auto bgString{ json[JsonKey(BackgroundKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(bgString.asString());
|
||||
_defaultBackground = color;
|
||||
}
|
||||
if (auto sbString{ json[JsonKey(SelectionBackgroundKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(sbString.asString());
|
||||
_selectionBackground = color;
|
||||
}
|
||||
if (auto sbString{ json[JsonKey(CursorColorKey)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(sbString.asString());
|
||||
_cursorColor = color;
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, NameKey, _schemeName);
|
||||
JsonUtils::GetValueForKey(json, ForegroundKey, _defaultForeground);
|
||||
JsonUtils::GetValueForKey(json, BackgroundKey, _defaultBackground);
|
||||
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _selectionBackground);
|
||||
JsonUtils::GetValueForKey(json, CursorColorKey, _cursorColor);
|
||||
|
||||
int i = 0;
|
||||
for (const auto& current : TableColors)
|
||||
{
|
||||
if (auto str{ json[JsonKey(current)] })
|
||||
{
|
||||
const auto color = Utils::ColorFromHexString(str.asString());
|
||||
_table.at(i) = color;
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, current, _table.at(i));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -200,11 +177,7 @@ til::color ColorScheme::GetCursorColor() const noexcept
|
||||
// - the name of the color scheme represented by `json` as a std::wstring optional
|
||||
// i.e. the value of the `name` property.
|
||||
// - returns std::nullopt if `json` doesn't have the `name` property
|
||||
std::optional<std::wstring> TerminalApp::ColorScheme::GetNameFromJson(const Json::Value& json)
|
||||
std::optional<std::wstring> ColorScheme::GetNameFromJson(const Json::Value& json)
|
||||
{
|
||||
if (const auto name{ json[JsonKey(NameKey)] })
|
||||
{
|
||||
return GetWstringFromJson(name);
|
||||
}
|
||||
return std::nullopt;
|
||||
return JsonUtils::GetValueForKey<std::optional<std::wstring>>(json, NameKey);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
|
||||
#include "Utils.h"
|
||||
#include "ActionAndArgs.h"
|
||||
#include "JsonUtils.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace ::TerminalApp;
|
||||
|
||||
static constexpr std::string_view NameKey{ "name" };
|
||||
static constexpr std::string_view IconPathKey{ "iconPath" };
|
||||
@@ -35,25 +37,17 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (name.isObject())
|
||||
{
|
||||
try
|
||||
if (const auto resourceKey{ JsonUtils::GetValueForKey<std::optional<std::wstring>>(name, "key") })
|
||||
{
|
||||
if (const auto keyJson{ name[JsonKey("key")] })
|
||||
if (HasLibraryResourceWithName(*resourceKey))
|
||||
{
|
||||
// Make sure the key is present before we try
|
||||
// loading it. Otherwise we'll crash
|
||||
const auto resourceKey = GetWstringFromJson(keyJson);
|
||||
if (HasLibraryResourceWithName(resourceKey))
|
||||
{
|
||||
return GetLibraryResourceString(resourceKey);
|
||||
}
|
||||
return GetLibraryResourceString(*resourceKey);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
else if (name.isString())
|
||||
{
|
||||
auto nameStr = name.asString();
|
||||
return winrt::to_hstring(nameStr);
|
||||
return JsonUtils::GetValue<winrt::hstring>(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,16 +187,14 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
const auto actionAndArgs = command.Action();
|
||||
_dispatch.DoAction(actionAndArgs);
|
||||
_close();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
"CommandPaletteDispatchedAction",
|
||||
TraceLoggingDescription("Event emitted when the user selects an action in the Command Palette"),
|
||||
TraceLoggingUInt32(_searchBox().Text().size(), "SearchTextLength", "Number of characters in the search string"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
_close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Windows::Data::Json;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace winrt::Microsoft::UI::Xaml::Controls;
|
||||
|
||||
static constexpr std::string_view LegacyKeybindingsKey{ "keybindings" };
|
||||
static constexpr std::string_view ActionsKey{ "actions" };
|
||||
static constexpr std::string_view BindingsKey{ "bindings" };
|
||||
static constexpr std::string_view DefaultProfileKey{ "defaultProfile" };
|
||||
static constexpr std::string_view AlwaysShowTabsKey{ "alwaysShowTabs" };
|
||||
static constexpr std::string_view InitialRowsKey{ "initialRows" };
|
||||
@@ -44,21 +44,6 @@ static constexpr std::string_view ForceFullRepaintRenderingKey{ "experimental.re
|
||||
static constexpr std::string_view SoftwareRenderingKey{ "experimental.rendering.software" };
|
||||
static constexpr std::string_view ForceVTInputKey{ "experimental.input.forceVT" };
|
||||
|
||||
// Launch mode values
|
||||
static constexpr std::wstring_view DefaultLaunchModeValue{ L"default" };
|
||||
static constexpr std::wstring_view MaximizedLaunchModeValue{ L"maximized" };
|
||||
static constexpr std::wstring_view FullscreenLaunchModeValue{ L"fullscreen" };
|
||||
|
||||
// Tab Width Mode values
|
||||
static constexpr std::wstring_view EqualTabWidthModeValue{ L"equal" };
|
||||
static constexpr std::wstring_view TitleLengthTabWidthModeValue{ L"titleLength" };
|
||||
static constexpr std::wstring_view TitleLengthCompactModeValue{ L"compact" };
|
||||
|
||||
// Theme values
|
||||
static constexpr std::wstring_view LightThemeValue{ L"light" };
|
||||
static constexpr std::wstring_view DarkThemeValue{ L"dark" };
|
||||
static constexpr std::wstring_view SystemThemeValue{ L"system" };
|
||||
|
||||
#ifdef _DEBUG
|
||||
static constexpr bool debugFeaturesDefault{ true };
|
||||
#else
|
||||
@@ -105,9 +90,9 @@ GUID GlobalAppSettings::DefaultProfile() const
|
||||
return _defaultProfile;
|
||||
}
|
||||
|
||||
std::optional<std::wstring> GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
std::wstring GlobalAppSettings::UnparsedDefaultProfile() const
|
||||
{
|
||||
return _unparsedDefaultProfile;
|
||||
return _unparsedDefaultProfile.value();
|
||||
}
|
||||
|
||||
AppKeyBindings GlobalAppSettings::GetKeybindings() const noexcept
|
||||
@@ -149,66 +134,51 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json)
|
||||
|
||||
void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
{
|
||||
if (auto defaultProfile{ json[JsonKey(DefaultProfileKey)] })
|
||||
{
|
||||
_unparsedDefaultProfile.emplace(GetWstringFromJson(defaultProfile));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, DefaultProfileKey, _unparsedDefaultProfile);
|
||||
|
||||
JsonUtils::GetBool(json, AlwaysShowTabsKey, _AlwaysShowTabs);
|
||||
JsonUtils::GetValueForKey(json, AlwaysShowTabsKey, _AlwaysShowTabs);
|
||||
|
||||
JsonUtils::GetBool(json, ConfirmCloseAllKey, _ConfirmCloseAllTabs);
|
||||
JsonUtils::GetValueForKey(json, ConfirmCloseAllKey, _ConfirmCloseAllTabs);
|
||||
|
||||
JsonUtils::GetInt(json, InitialRowsKey, _InitialRows);
|
||||
JsonUtils::GetValueForKey(json, InitialRowsKey, _InitialRows);
|
||||
|
||||
JsonUtils::GetInt(json, InitialColsKey, _InitialCols);
|
||||
JsonUtils::GetValueForKey(json, InitialColsKey, _InitialCols);
|
||||
|
||||
if (auto initialPosition{ json[JsonKey(InitialPositionKey)] })
|
||||
{
|
||||
_ParseInitialPosition(initialPosition.asString(), _InitialPosition);
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, InitialPositionKey, _InitialPosition);
|
||||
|
||||
JsonUtils::GetBool(json, ShowTitleInTitlebarKey, _ShowTitleInTitlebar);
|
||||
JsonUtils::GetValueForKey(json, ShowTitleInTitlebarKey, _ShowTitleInTitlebar);
|
||||
|
||||
JsonUtils::GetBool(json, ShowTabsInTitlebarKey, _ShowTabsInTitlebar);
|
||||
JsonUtils::GetValueForKey(json, ShowTabsInTitlebarKey, _ShowTabsInTitlebar);
|
||||
|
||||
JsonUtils::GetWstring(json, WordDelimitersKey, _WordDelimiters);
|
||||
JsonUtils::GetValueForKey(json, WordDelimitersKey, _WordDelimiters);
|
||||
|
||||
JsonUtils::GetBool(json, CopyOnSelectKey, _CopyOnSelect);
|
||||
JsonUtils::GetValueForKey(json, CopyOnSelectKey, _CopyOnSelect);
|
||||
|
||||
JsonUtils::GetBool(json, CopyFormattingKey, _CopyFormatting);
|
||||
JsonUtils::GetValueForKey(json, CopyFormattingKey, _CopyFormatting);
|
||||
|
||||
JsonUtils::GetBool(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
|
||||
JsonUtils::GetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
|
||||
|
||||
JsonUtils::GetBool(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
|
||||
JsonUtils::GetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
|
||||
|
||||
if (auto launchMode{ json[JsonKey(LaunchModeKey)] })
|
||||
{
|
||||
_LaunchMode = _ParseLaunchMode(GetWstringFromJson(launchMode));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode);
|
||||
|
||||
if (auto theme{ json[JsonKey(ThemeKey)] })
|
||||
{
|
||||
_Theme = _ParseTheme(GetWstringFromJson(theme));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, ThemeKey, _Theme);
|
||||
|
||||
if (auto tabWidthMode{ json[JsonKey(TabWidthModeKey)] })
|
||||
{
|
||||
_TabWidthMode = _ParseTabWidthMode(GetWstringFromJson(tabWidthMode));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, TabWidthModeKey, _TabWidthMode);
|
||||
|
||||
JsonUtils::GetBool(json, SnapToGridOnResizeKey, _SnapToGridOnResize);
|
||||
JsonUtils::GetValueForKey(json, SnapToGridOnResizeKey, _SnapToGridOnResize);
|
||||
|
||||
JsonUtils::GetBool(json, ForceFullRepaintRenderingKey, _ForceFullRepaintRendering);
|
||||
// GetValueForKey will only override the current value if the key exists
|
||||
JsonUtils::GetValueForKey(json, DebugFeaturesKey, _DebugFeaturesEnabled);
|
||||
|
||||
JsonUtils::GetBool(json, SoftwareRenderingKey, _SoftwareRendering);
|
||||
JsonUtils::GetBool(json, ForceVTInputKey, _ForceVTInput);
|
||||
JsonUtils::GetValueForKey(json, ForceFullRepaintRenderingKey, _ForceFullRepaintRendering);
|
||||
|
||||
// GetBool will only override the current value if the key exists
|
||||
JsonUtils::GetBool(json, DebugFeaturesKey, _DebugFeaturesEnabled);
|
||||
JsonUtils::GetValueForKey(json, SoftwareRenderingKey, _SoftwareRendering);
|
||||
JsonUtils::GetValueForKey(json, ForceVTInputKey, _ForceVTInput);
|
||||
|
||||
JsonUtils::GetBool(json, EnableStartupTaskKey, _StartOnUserLogin);
|
||||
JsonUtils::GetValueForKey(json, EnableStartupTaskKey, _StartOnUserLogin);
|
||||
|
||||
JsonUtils::GetBool(json, AlwaysOnTopKey, _AlwaysOnTop);
|
||||
JsonUtils::GetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop);
|
||||
|
||||
// This is a helper lambda to get the keybindings and commands out of both
|
||||
// and array of objects. We'll use this twice, once on the legacy
|
||||
@@ -233,117 +203,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
|
||||
}
|
||||
};
|
||||
parseBindings(LegacyKeybindingsKey);
|
||||
parseBindings(ActionsKey);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified cursor style corresponding
|
||||
// CursorStyle enum value
|
||||
// Arguments:
|
||||
// - themeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
ElementTheme GlobalAppSettings::_ParseTheme(const std::wstring& themeString) noexcept
|
||||
{
|
||||
if (themeString == LightThemeValue)
|
||||
{
|
||||
return ElementTheme::Light;
|
||||
}
|
||||
else if (themeString == DarkThemeValue)
|
||||
{
|
||||
return ElementTheme::Dark;
|
||||
}
|
||||
// default behavior for invalid data or SystemThemeValue
|
||||
return ElementTheme::Default;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the initial position string into
|
||||
// 2 coordinate values. We allow users to only provide one coordinate,
|
||||
// thus, we use comma as the separator:
|
||||
// (100, 100): standard input string
|
||||
// (, 100), (100, ): if a value is missing, we set this value as a default
|
||||
// (,): both x and y are set to default
|
||||
// (abc, 100): if a value is not valid, we treat it as default
|
||||
// (100, 100, 100): we only read the first two values, this is equivalent to (100, 100)
|
||||
// Arguments:
|
||||
// - initialPosition: the initial position string from json
|
||||
// ret: reference to a struct whose optionals will be populated
|
||||
// Return Value:
|
||||
// - None
|
||||
void GlobalAppSettings::_ParseInitialPosition(const std::string& initialPosition,
|
||||
LaunchPosition& ret) noexcept
|
||||
{
|
||||
static constexpr char singleCharDelim = ',';
|
||||
std::stringstream tokenStream(initialPosition);
|
||||
std::string token;
|
||||
uint8_t initialPosIndex = 0;
|
||||
|
||||
// Get initial position values till we run out of delimiter separated values in the stream
|
||||
// or we hit max number of allowable values (= 2)
|
||||
// Non-numeral values or empty string will be caught as exception and we do not assign them
|
||||
for (; std::getline(tokenStream, token, singleCharDelim) && (initialPosIndex < 2); initialPosIndex++)
|
||||
{
|
||||
try
|
||||
{
|
||||
int32_t position = std::stoi(token);
|
||||
if (initialPosIndex == 0)
|
||||
{
|
||||
ret.x.emplace(position);
|
||||
}
|
||||
|
||||
if (initialPosIndex == 1)
|
||||
{
|
||||
ret.y.emplace(position);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the user-specified launch mode
|
||||
// to a LaunchMode enum value
|
||||
// Arguments:
|
||||
// - launchModeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
LaunchMode GlobalAppSettings::_ParseLaunchMode(const std::wstring& launchModeString) noexcept
|
||||
{
|
||||
if (launchModeString == MaximizedLaunchModeValue)
|
||||
{
|
||||
return LaunchMode::MaximizedMode;
|
||||
}
|
||||
else if (launchModeString == FullscreenLaunchModeValue)
|
||||
{
|
||||
return LaunchMode::FullscreenMode;
|
||||
}
|
||||
|
||||
return LaunchMode::DefaultMode;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting the user-specified tab width
|
||||
// to a TabViewWidthMode enum value
|
||||
// Arguments:
|
||||
// - tabWidthModeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
TabViewWidthMode GlobalAppSettings::_ParseTabWidthMode(const std::wstring& tabWidthModeString) noexcept
|
||||
{
|
||||
if (tabWidthModeString == TitleLengthTabWidthModeValue)
|
||||
{
|
||||
return TabViewWidthMode::SizeToContent;
|
||||
}
|
||||
else if (tabWidthModeString == TitleLengthCompactModeValue)
|
||||
{
|
||||
return TabViewWidthMode::Compact;
|
||||
}
|
||||
// default behavior for invalid data or EqualTabWidthValue
|
||||
return TabViewWidthMode::Equal;
|
||||
parseBindings(BindingsKey);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -17,6 +17,7 @@ Author(s):
|
||||
#include "AppKeyBindings.h"
|
||||
#include "ColorScheme.h"
|
||||
#include "Command.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
@@ -28,12 +29,6 @@ namespace TerminalAppLocalTests
|
||||
namespace TerminalApp
|
||||
{
|
||||
class GlobalAppSettings;
|
||||
|
||||
struct LaunchPosition
|
||||
{
|
||||
std::optional<int> x;
|
||||
std::optional<int> y;
|
||||
};
|
||||
};
|
||||
|
||||
class TerminalApp::GlobalAppSettings final
|
||||
@@ -61,7 +56,7 @@ public:
|
||||
// by higher layers in the app.
|
||||
void DefaultProfile(const GUID defaultProfile) noexcept;
|
||||
GUID DefaultProfile() const;
|
||||
std::optional<std::wstring> UnparsedDefaultProfile() const;
|
||||
std::wstring UnparsedDefaultProfile() const;
|
||||
|
||||
GETSET_PROPERTY(int32_t, InitialRows); // default value set in constructor
|
||||
GETSET_PROPERTY(int32_t, InitialCols); // default value set in constructor
|
||||
@@ -96,15 +91,6 @@ private:
|
||||
std::unordered_map<std::wstring, ColorScheme> _colorSchemes;
|
||||
std::unordered_map<winrt::hstring, winrt::TerminalApp::Command> _commands;
|
||||
|
||||
static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept;
|
||||
|
||||
static winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode _ParseTabWidthMode(const std::wstring& tabWidthModeString) noexcept;
|
||||
|
||||
static void _ParseInitialPosition(const std::string& initialPosition,
|
||||
LaunchPosition& ret) noexcept;
|
||||
|
||||
static winrt::TerminalApp::LaunchMode _ParseLaunchMode(const std::wstring& launchModeString) noexcept;
|
||||
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::ColorSchemeTests;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace TerminalApp
|
||||
// If you update this one, please update the one in TerminalControl\TermControl.idl
|
||||
// If you change this interface, please update the guid.
|
||||
// If you press F7 or Alt and get a runtime error, go make sure both copies are the same.
|
||||
[uuid("0ddf4edc-3fda-4dee-97ca-a417ee3dd510")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, UInt8 scanCode, Boolean down);
|
||||
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, Boolean down);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "../../types/inc/Utils.hpp"
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalColor(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<til::color>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> til::color {
|
||||
return ::Microsoft::Console::Utils::ColorFromHexString(value.asString());
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalString(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<std::wstring>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> std::wstring {
|
||||
return GetWstringFromJson(value);
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalGuid(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<GUID>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> GUID {
|
||||
return ::Microsoft::Console::Utils::GuidFromString(GetWstringFromJson(value));
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetOptionalDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<double>& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> double {
|
||||
return value.asFloat();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isNumeric();
|
||||
};
|
||||
GetOptionalValue(json,
|
||||
key,
|
||||
target,
|
||||
conversionFn,
|
||||
validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
int& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> int {
|
||||
return value.asInt();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isInt();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetUInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
uint32_t& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> uint32_t {
|
||||
return value.asUInt();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isUInt();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
double& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> double {
|
||||
return value.asFloat();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isNumeric();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetBool(const Json::Value& json,
|
||||
std::string_view key,
|
||||
bool& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> bool {
|
||||
return value.asBool();
|
||||
};
|
||||
const auto validationFn = [](const Json::Value& value) -> bool {
|
||||
return value.isBool();
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, validationFn);
|
||||
}
|
||||
|
||||
void TerminalApp::JsonUtils::GetWstring(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::wstring& target)
|
||||
{
|
||||
const auto conversionFn = [](const Json::Value& value) -> std::wstring {
|
||||
return GetWstringFromJson(value);
|
||||
};
|
||||
GetValue(json, key, target, conversionFn, nullptr);
|
||||
}
|
||||
@@ -9,136 +9,483 @@ Abstract:
|
||||
- Helpers for the TerminalApp project
|
||||
Author(s):
|
||||
- Mike Griese - August 2019
|
||||
|
||||
- Dustin Howett - January 2020
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <json.h>
|
||||
|
||||
#include "../types/inc/utils.hpp"
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
// If we don't use winrt, nobody will include the ConversionTraits for winrt stuff.
|
||||
// If nobody includes it, these forward declarations will suffice.
|
||||
struct guid;
|
||||
struct hstring;
|
||||
namespace Windows::Foundation
|
||||
{
|
||||
template<typename T>
|
||||
struct IReference;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TerminalApp::JsonUtils
|
||||
{
|
||||
void GetOptionalColor(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<til::color>& target);
|
||||
|
||||
void GetOptionalString(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<std::wstring>& target);
|
||||
|
||||
void GetOptionalGuid(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<GUID>& target);
|
||||
|
||||
void GetOptionalDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<double>& target);
|
||||
|
||||
// Method Description:
|
||||
// - Helper that can be used for retrieving an optional value from a json
|
||||
// object, and parsing it's value to layer on a given target object.
|
||||
// - If the key we're looking for _doesn't_ exist in the json object,
|
||||
// we'll leave the target object unmodified.
|
||||
// - If the key exists in the json object, but is set to `null`, then
|
||||
// we'll instead set the target back to nullopt.
|
||||
// - Each caller should provide a conversion function that takes a
|
||||
// Json::Value and returns an object of the same type as target.
|
||||
// Arguments:
|
||||
// - json: The json object to search for the given key
|
||||
// - key: The key to look for in the json object
|
||||
// - target: the optional object to receive the value from json
|
||||
// - conversion: a std::function<T(const Json::Value&)> which can be used to
|
||||
// convert the Json::Value to the appropriate type.
|
||||
// - validation: optional, if provided, will be called first to ensure that
|
||||
// the json::value is of the correct type before attempting to call
|
||||
// `conversion`.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename T, typename F>
|
||||
void GetOptionalValue(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::optional<T>& target,
|
||||
F&& conversion,
|
||||
const std::function<bool(const Json::Value&)>& validation = nullptr)
|
||||
namespace Detail
|
||||
{
|
||||
if (json.isMember(JsonKey(key)))
|
||||
// Function Description:
|
||||
// - Returns a string_view to a Json::Value's internal string storage,
|
||||
// hopefully without copying it.
|
||||
__declspec(noinline) inline const std::string_view GetStringView(const Json::Value& json)
|
||||
{
|
||||
if (auto jsonVal{ json[JsonKey(key)] })
|
||||
{
|
||||
if (validation == nullptr || validation(jsonVal))
|
||||
{
|
||||
target = conversion(jsonVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This branch is hit when the json object contained the key,
|
||||
// but the key was set to `null`. In this case, explicitly clear
|
||||
// the target.
|
||||
target = std::nullopt;
|
||||
}
|
||||
const char* begin{ nullptr };
|
||||
const char* end{ nullptr };
|
||||
json.getString(&begin, &end);
|
||||
const std::string_view zeroCopyString{ begin, gsl::narrow_cast<size_t>(end - begin) };
|
||||
return zeroCopyString;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct DeduceOptional
|
||||
{
|
||||
using Type = typename std::decay<T>::type;
|
||||
static constexpr bool IsOptional = false;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<std::optional<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<::winrt::Windows::Foundation::IReference<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper that can be used for retrieving a value from a json
|
||||
// object, and parsing it's value to set on a given target object.
|
||||
// - If the key we're looking for _doesn't_ exist in the json object,
|
||||
// we'll leave the target object unmodified.
|
||||
// - If the key exists in the json object, we'll use the provided
|
||||
// `validation` function to ensure that the json value is of the
|
||||
// correct type.
|
||||
// - If we successfully validate the json value type (or no validation
|
||||
// function was provided), then we'll use `conversion` to parse the
|
||||
// value and place the result into `target`
|
||||
// - Each caller should provide a conversion function that takes a
|
||||
// Json::Value and returns an object of the same type as target.
|
||||
// - Unlike GetOptionalValue, if the key exists but is set to `null`, we'll
|
||||
// just ignore it.
|
||||
// Arguments:
|
||||
// - json: The json object to search for the given key
|
||||
// - key: The key to look for in the json object
|
||||
// - target: the optional object to receive the value from json
|
||||
// - conversion: a std::function<T(const Json::Value&)> which can be used to
|
||||
// convert the Json::Value to the appropriate type.
|
||||
// - validation: optional, if provided, will be called first to ensure that
|
||||
// the json::value is of the correct type before attempting to call
|
||||
// `conversion`.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename T, typename F>
|
||||
void GetValue(const Json::Value& json,
|
||||
std::string_view key,
|
||||
T& target,
|
||||
F&& conversion,
|
||||
const std::function<bool(const Json::Value&)>& validation = nullptr)
|
||||
// These exceptions cannot use localized messages, as we do not have
|
||||
// guaranteed access to the resource loader.
|
||||
class TypeMismatchException : public std::runtime_error
|
||||
{
|
||||
if (json.isMember(JsonKey(key)))
|
||||
public:
|
||||
TypeMismatchException() :
|
||||
runtime_error("unexpected data type") {}
|
||||
};
|
||||
|
||||
class KeyedException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
KeyedException(const std::string_view key, std::exception_ptr exception) :
|
||||
runtime_error(fmt::format("error parsing \"{0}\"", key).c_str()),
|
||||
_key{ key },
|
||||
_innerException{ std::move(exception) } {}
|
||||
|
||||
std::string GetKey() const
|
||||
{
|
||||
if (auto jsonVal{ json[JsonKey(key)] })
|
||||
return _key;
|
||||
}
|
||||
|
||||
[[noreturn]] void RethrowInner() const
|
||||
{
|
||||
std::rethrow_exception(_innerException);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _key;
|
||||
std::exception_ptr _innerException;
|
||||
};
|
||||
|
||||
class UnexpectedValueException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
UnexpectedValueException(const std::string_view value) :
|
||||
runtime_error(fmt::format("unexpected value \"{0}\"", value).c_str()),
|
||||
_value{ value } {}
|
||||
|
||||
std::string GetValue() const
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ConversionTrait
|
||||
{
|
||||
// Forward-declare these so the linker can pick up specializations from elsewhere!
|
||||
T FromJson(const Json::Value&);
|
||||
bool CanConvert(const Json::Value& json);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::string>
|
||||
{
|
||||
std::string FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asString();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::wstring>
|
||||
{
|
||||
std::wstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return til::u8u16(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WINRT_BASE_H
|
||||
template<>
|
||||
struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring>
|
||||
{
|
||||
// Leverage the wstring converter's validation
|
||||
winrt::hstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return winrt::hstring{ til::u8u16(Detail::GetStringView(json)) };
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<bool>
|
||||
{
|
||||
bool FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asBool();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isBool();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<int>
|
||||
{
|
||||
int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<unsigned int>
|
||||
{
|
||||
unsigned int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asUInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isUInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<float>
|
||||
{
|
||||
float FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asFloat();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<double>
|
||||
{
|
||||
double FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asDouble();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<GUID>
|
||||
{
|
||||
GUID FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::GuidFromString(til::u8u16(Detail::GetStringView(json)));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
if (validation == nullptr || validation(jsonVal))
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return string.length() == 38 && string.front() == '{' && string.back() == '}';
|
||||
}
|
||||
};
|
||||
|
||||
// (GUID and winrt::guid are mutually convertible!)
|
||||
template<>
|
||||
struct ConversionTrait<winrt::guid> : public ConversionTrait<GUID>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<til::color>
|
||||
{
|
||||
til::color FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::ColorFromHexString(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return (string.length() == 7 || string.length() == 4) && string.front() == '#';
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename TBase>
|
||||
struct EnumMapper
|
||||
{
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>;
|
||||
using ValueType = T;
|
||||
using pair_type = std::pair<std::string_view, T>;
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
const auto name{ Detail::GetStringView(json) };
|
||||
for (const auto& pair : TBase::mappings)
|
||||
{
|
||||
if (pair.first == name)
|
||||
{
|
||||
target = conversion(jsonVal);
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
throw UnexpectedValueException{ name };
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
// FlagMapper is EnumMapper, but it works for bitfields.
|
||||
// It supports a string (single flag) or an array of strings.
|
||||
// Does an O(n*m) search; meant for small search spaces!
|
||||
//
|
||||
// Cleverly leverage EnumMapper to do the heavy lifting.
|
||||
template<typename T, typename TBase>
|
||||
struct FlagMapper : public EnumMapper<T, TBase>
|
||||
{
|
||||
private:
|
||||
// Hide BaseEnumMapper so FlagMapper's consumers cannot see
|
||||
// it.
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>::BaseEnumMapper;
|
||||
|
||||
public:
|
||||
using BaseFlagMapper = FlagMapper<T, TBase>;
|
||||
static constexpr T AllSet{ static_cast<T>(~0u) };
|
||||
static constexpr T AllClear{ static_cast<T>(0u) };
|
||||
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
if (json.isString())
|
||||
{
|
||||
return BaseEnumMapper::FromJson(json);
|
||||
}
|
||||
else if (json.isArray())
|
||||
{
|
||||
unsigned int seen{ 0 };
|
||||
T value{};
|
||||
for (const auto& element : json)
|
||||
{
|
||||
const auto newFlag{ BaseEnumMapper::FromJson(element) };
|
||||
if (++seen > 1 &&
|
||||
((newFlag == AllClear && value != AllClear) ||
|
||||
(value == AllClear && newFlag != AllClear)))
|
||||
{
|
||||
// attempt to combine AllClear (explicitly) with anything else
|
||||
throw UnexpectedValueException{ element.asString() };
|
||||
}
|
||||
value |= newFlag;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// We'll only get here if CanConvert has failed us.
|
||||
return AllClear;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json) || json.isArray();
|
||||
}
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Helper that will populate a reference with a value converted from a json object.
|
||||
// Arguments:
|
||||
// - json: the json object to convert
|
||||
// - target: the value to populate with the converted result
|
||||
// Return Value:
|
||||
// - a boolean indicating whether the value existed (in this case, was non-null)
|
||||
//
|
||||
// GetValue, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
|
||||
{
|
||||
if constexpr (Detail::DeduceOptional<T>::IsOptional)
|
||||
{
|
||||
// FOR OPTION TYPES
|
||||
// - If the json object is set to `null`, then
|
||||
// we'll instead set the target back to the empty optional.
|
||||
if (json.isNull())
|
||||
{
|
||||
target = T{}; // zero-construct an empty optional
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (json)
|
||||
{
|
||||
if (!conv.CanConvert(json))
|
||||
{
|
||||
throw TypeMismatchException{};
|
||||
}
|
||||
|
||||
target = conv.FromJson(json);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
int& target);
|
||||
// GetValue, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
void GetUInt(const Json::Value& json,
|
||||
std::string_view key,
|
||||
uint32_t& target);
|
||||
// GetValueForKey, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
|
||||
{
|
||||
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetValue(*found, target, std::forward<Converter>(conv));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Wrap any caught exceptions in one that preserves context.
|
||||
throw KeyedException(key, std::current_exception());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetDouble(const Json::Value& json,
|
||||
std::string_view key,
|
||||
double& target);
|
||||
// GetValueForKey, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValueForKey(json, key, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized?
|
||||
}
|
||||
|
||||
void GetBool(const Json::Value& json,
|
||||
std::string_view key,
|
||||
bool& target);
|
||||
// GetValue, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValue(const Json::Value& json, T& target)
|
||||
{
|
||||
return GetValue(json, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
void GetWstring(const Json::Value& json,
|
||||
std::string_view key,
|
||||
std::wstring& target);
|
||||
// GetValue, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValue(const Json::Value& json)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
// GetValueForKey, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
|
||||
{
|
||||
return GetValueForKey(json, key, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// GetValueForKey, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
|
||||
{
|
||||
return GetValueForKey<T>(json, key, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
|
||||
// Uses the default converter for each v.
|
||||
// Careful: this can cause a template explosion.
|
||||
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
|
||||
{
|
||||
GetValueForKey(json, key1, val1);
|
||||
GetValuesForKeys(json, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
#define JSON_ENUM_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::EnumMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_FLAG_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::FlagMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_MAPPINGS(Count) \
|
||||
static constexpr std::array<pair_type, Count> mappings
|
||||
|
||||
@@ -1,490 +0,0 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- JsonUtils.h
|
||||
|
||||
Abstract:
|
||||
- Helpers for the TerminalApp project
|
||||
Author(s):
|
||||
- Mike Griese - August 2019
|
||||
- Dustin Howett - January 2020
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <json.h>
|
||||
|
||||
#include "../types/inc/utils.hpp"
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
// If we don't use winrt, nobody will include the ConversionTraits for winrt stuff.
|
||||
// If nobody includes it, these forward declarations will suffice.
|
||||
struct guid;
|
||||
struct hstring;
|
||||
namespace Windows::Foundation
|
||||
{
|
||||
template<typename T>
|
||||
struct IReference;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TerminalApp::JsonUtils
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
// Function Description:
|
||||
// - Returns a string_view to a Json::Value's internal string storage,
|
||||
// hopefully without copying it.
|
||||
__declspec(noinline) inline const std::string_view GetStringView(const Json::Value& json)
|
||||
{
|
||||
const char* begin{ nullptr };
|
||||
const char* end{ nullptr };
|
||||
json.getString(&begin, &end);
|
||||
const std::string_view zeroCopyString{ begin, gsl::narrow_cast<size_t>(end - begin) };
|
||||
return zeroCopyString;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct DeduceOptional
|
||||
{
|
||||
using Type = typename std::decay<T>::type;
|
||||
static constexpr bool IsOptional = false;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<std::optional<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
|
||||
template<typename TOpt>
|
||||
struct DeduceOptional<::winrt::Windows::Foundation::IReference<TOpt>>
|
||||
{
|
||||
using Type = typename std::decay<TOpt>::type;
|
||||
static constexpr bool IsOptional = true;
|
||||
};
|
||||
}
|
||||
|
||||
// These exceptions cannot use localized messages, as we do not have
|
||||
// guaranteed access to the resource loader.
|
||||
class TypeMismatchException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
TypeMismatchException() :
|
||||
runtime_error("unexpected data type") {}
|
||||
};
|
||||
|
||||
class KeyedException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
KeyedException(const std::string_view key, std::exception_ptr exception) :
|
||||
runtime_error(fmt::format("error parsing \"{0}\"", key).c_str()),
|
||||
_key{ key },
|
||||
_innerException{ std::move(exception) } {}
|
||||
|
||||
std::string GetKey() const
|
||||
{
|
||||
return _key;
|
||||
}
|
||||
|
||||
[[noreturn]] void RethrowInner() const
|
||||
{
|
||||
std::rethrow_exception(_innerException);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _key;
|
||||
std::exception_ptr _innerException;
|
||||
};
|
||||
|
||||
class UnexpectedValueException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
UnexpectedValueException(const std::string_view value) :
|
||||
runtime_error(fmt::format("unexpected value \"{0}\"", value).c_str()),
|
||||
_value{ value } {}
|
||||
|
||||
std::string GetValue() const
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ConversionTrait
|
||||
{
|
||||
// Forward-declare these so the linker can pick up specializations from elsewhere!
|
||||
T FromJson(const Json::Value&);
|
||||
bool CanConvert(const Json::Value& json);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::string>
|
||||
{
|
||||
std::string FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asString();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<std::wstring>
|
||||
{
|
||||
std::wstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return til::u8u16(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WINRT_BASE_H
|
||||
template<>
|
||||
struct ConversionTrait<winrt::hstring> : public ConversionTrait<std::wstring>
|
||||
{
|
||||
// Leverage the wstring converter's validation
|
||||
winrt::hstring FromJson(const Json::Value& json)
|
||||
{
|
||||
return winrt::hstring{ til::u8u16(Detail::GetStringView(json)) };
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<bool>
|
||||
{
|
||||
bool FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asBool();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isBool();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<int>
|
||||
{
|
||||
int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<unsigned int>
|
||||
{
|
||||
unsigned int FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asUInt();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isUInt();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<float>
|
||||
{
|
||||
float FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asFloat();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<double>
|
||||
{
|
||||
double FromJson(const Json::Value& json)
|
||||
{
|
||||
return json.asDouble();
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isNumeric();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<GUID>
|
||||
{
|
||||
GUID FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::GuidFromString(til::u8u16(Detail::GetStringView(json)));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return string.length() == 38 && string.front() == '{' && string.back() == '}';
|
||||
}
|
||||
};
|
||||
|
||||
// (GUID and winrt::guid are mutually convertible!)
|
||||
template<>
|
||||
struct ConversionTrait<winrt::guid> : public ConversionTrait<GUID>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ConversionTrait<til::color>
|
||||
{
|
||||
til::color FromJson(const Json::Value& json)
|
||||
{
|
||||
return ::Microsoft::Console::Utils::ColorFromHexString(Detail::GetStringView(json));
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
if (!json.isString())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto string{ Detail::GetStringView(json) };
|
||||
return (string.length() == 7 || string.length() == 4) && string.front() == '#';
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename TBase>
|
||||
struct EnumMapper
|
||||
{
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>;
|
||||
using pair_type = std::pair<std::string_view, T>;
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
const auto name{ Detail::GetStringView(json) };
|
||||
for (const auto& pair : TBase::mappings)
|
||||
{
|
||||
if (pair.first == name)
|
||||
{
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
throw UnexpectedValueException{ name };
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
// FlagMapper is EnumMapper, but it works for bitfields.
|
||||
// It supports a string (single flag) or an array of strings.
|
||||
// Does an O(n*m) search; meant for small search spaces!
|
||||
//
|
||||
// Cleverly leverage EnumMapper to do the heavy lifting.
|
||||
template<typename T, typename TBase>
|
||||
struct FlagMapper : public EnumMapper<T, TBase>
|
||||
{
|
||||
private:
|
||||
// Hide BaseEnumMapper so FlagMapper's consumers cannot see
|
||||
// it.
|
||||
using BaseEnumMapper = EnumMapper<T, TBase>::BaseEnumMapper;
|
||||
|
||||
public:
|
||||
using BaseFlagMapper = FlagMapper<T, TBase>;
|
||||
static constexpr T AllSet{ static_cast<T>(~0u) };
|
||||
static constexpr T AllClear{ static_cast<T>(0u) };
|
||||
|
||||
T FromJson(const Json::Value& json)
|
||||
{
|
||||
if (json.isString())
|
||||
{
|
||||
return BaseEnumMapper::FromJson(json);
|
||||
}
|
||||
else if (json.isArray())
|
||||
{
|
||||
unsigned int seen{ 0 };
|
||||
T value{};
|
||||
for (const auto& element : json)
|
||||
{
|
||||
const auto newFlag{ BaseEnumMapper::FromJson(element) };
|
||||
if (++seen > 1 &&
|
||||
((newFlag == AllClear && value != AllClear) ||
|
||||
(value == AllClear && newFlag != AllClear)))
|
||||
{
|
||||
// attempt to combine AllClear (explicitly) with anything else
|
||||
throw UnexpectedValueException{ element.asString() };
|
||||
}
|
||||
value |= newFlag;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// We'll only get here if CanConvert has failed us.
|
||||
return AllClear;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json) || json.isArray();
|
||||
}
|
||||
};
|
||||
|
||||
// Method Description:
|
||||
// - Helper that will populate a reference with a value converted from a json object.
|
||||
// Arguments:
|
||||
// - json: the json object to convert
|
||||
// - target: the value to populate with the converted result
|
||||
// Return Value:
|
||||
// - a boolean indicating whether the value existed (in this case, was non-null)
|
||||
//
|
||||
// GetValue, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
|
||||
{
|
||||
if constexpr (Detail::DeduceOptional<T>::IsOptional)
|
||||
{
|
||||
// FOR OPTION TYPES
|
||||
// - If the json object is set to `null`, then
|
||||
// we'll instead set the target back to the empty optional.
|
||||
if (json.isNull())
|
||||
{
|
||||
target = T{}; // zero-construct an empty optional
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (json)
|
||||
{
|
||||
if (!conv.CanConvert(json))
|
||||
{
|
||||
throw TypeMismatchException{};
|
||||
}
|
||||
|
||||
target = conv.FromJson(json);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetValue, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
// GetValueForKey, type-deduced, manual converter
|
||||
template<typename T, typename Converter>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
|
||||
{
|
||||
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetValue(*found, target, std::forward<Converter>(conv));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Wrap any caught exceptions in one that preserves context.
|
||||
throw KeyedException(key, std::current_exception());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// GetValueForKey, forced return type, manual converter
|
||||
template<typename T, typename Converter>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValueForKey(json, key, local, std::forward<Converter>(conv));
|
||||
return local; // returns zero-initialized?
|
||||
}
|
||||
|
||||
// GetValue, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValue(const Json::Value& json, T& target)
|
||||
{
|
||||
return GetValue(json, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// GetValue, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValue(const Json::Value& json)
|
||||
{
|
||||
std::decay_t<T> local{};
|
||||
GetValue(json, local, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
return local; // returns zero-initialized or value
|
||||
}
|
||||
|
||||
// GetValueForKey, type-deduced, with automatic converter
|
||||
template<typename T>
|
||||
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
|
||||
{
|
||||
return GetValueForKey(json, key, target, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// GetValueForKey, forced return type, with automatic converter
|
||||
template<typename T>
|
||||
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
|
||||
{
|
||||
return GetValueForKey<T>(json, key, ConversionTrait<typename Detail::DeduceOptional<T>::Type>{});
|
||||
}
|
||||
|
||||
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
|
||||
// Uses the default converter for each v.
|
||||
// Careful: this can cause a template explosion.
|
||||
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
|
||||
{
|
||||
GetValueForKey(json, key1, val1);
|
||||
GetValuesForKeys(json, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
#define JSON_ENUM_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::EnumMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_FLAG_MAPPER(...) \
|
||||
template<> \
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__> : \
|
||||
public ::TerminalApp::JsonUtils::FlagMapper<__VA_ARGS__, ::TerminalApp::JsonUtils::ConversionTrait<__VA_ARGS__>>
|
||||
|
||||
#define JSON_MAPPINGS(Count) \
|
||||
static constexpr std::array<pair_type, Count> mappings
|
||||
@@ -14,13 +14,11 @@ static constexpr int MAX_CHORD_PARTS = 4;
|
||||
|
||||
// clang-format off
|
||||
static const std::unordered_map<std::wstring_view, int32_t> vkeyNamePairs {
|
||||
{ L"app" , VK_APPS },
|
||||
{ L"backspace" , VK_BACK },
|
||||
{ L"tab" , VK_TAB },
|
||||
{ L"enter" , VK_RETURN },
|
||||
{ L"esc" , VK_ESCAPE },
|
||||
{ L"escape" , VK_ESCAPE },
|
||||
{ L"menu" , VK_APPS },
|
||||
{ L"space" , VK_SPACE },
|
||||
{ L"pgup" , VK_PRIOR },
|
||||
{ L"pageup" , VK_PRIOR },
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <DefaultSettings.h>
|
||||
|
||||
#include "LegacyProfileGeneratorNamespaces.h"
|
||||
#include "TerminalSettingsSerializationHelpers.h"
|
||||
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
@@ -52,57 +53,6 @@ static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageA
|
||||
static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" };
|
||||
static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" };
|
||||
|
||||
// Possible values for closeOnExit
|
||||
static constexpr std::string_view CloseOnExitAlways{ "always" };
|
||||
static constexpr std::string_view CloseOnExitGraceful{ "graceful" };
|
||||
static constexpr std::string_view CloseOnExitNever{ "never" };
|
||||
|
||||
// Possible values for Scrollbar state
|
||||
static constexpr std::wstring_view AlwaysVisible{ L"visible" };
|
||||
static constexpr std::wstring_view AlwaysHide{ L"hidden" };
|
||||
|
||||
// Possible values for Cursor Shape
|
||||
static constexpr std::wstring_view CursorShapeVintage{ L"vintage" };
|
||||
static constexpr std::wstring_view CursorShapeBar{ L"bar" };
|
||||
static constexpr std::wstring_view CursorShapeUnderscore{ L"underscore" };
|
||||
static constexpr std::wstring_view CursorShapeFilledbox{ L"filledBox" };
|
||||
static constexpr std::wstring_view CursorShapeEmptybox{ L"emptyBox" };
|
||||
|
||||
// Possible values for Font Weight
|
||||
static constexpr std::string_view FontWeightThin{ "thin" };
|
||||
static constexpr std::string_view FontWeightExtraLight{ "extra-light" };
|
||||
static constexpr std::string_view FontWeightLight{ "light" };
|
||||
static constexpr std::string_view FontWeightSemiLight{ "semi-light" };
|
||||
static constexpr std::string_view FontWeightNormal{ "normal" };
|
||||
static constexpr std::string_view FontWeightMedium{ "medium" };
|
||||
static constexpr std::string_view FontWeightSemiBold{ "semi-bold" };
|
||||
static constexpr std::string_view FontWeightBold{ "bold" };
|
||||
static constexpr std::string_view FontWeightExtraBold{ "extra-bold" };
|
||||
static constexpr std::string_view FontWeightBlack{ "black" };
|
||||
static constexpr std::string_view FontWeightExtraBlack{ "extra-black" };
|
||||
|
||||
// Possible values for Image Stretch Mode
|
||||
static constexpr std::string_view ImageStretchModeNone{ "none" };
|
||||
static constexpr std::string_view ImageStretchModeFill{ "fill" };
|
||||
static constexpr std::string_view ImageStretchModeUniform{ "uniform" };
|
||||
static constexpr std::string_view ImageStretchModeUniformTofill{ "uniformToFill" };
|
||||
|
||||
// Possible values for Image Alignment
|
||||
static constexpr std::string_view ImageAlignmentCenter{ "center" };
|
||||
static constexpr std::string_view ImageAlignmentLeft{ "left" };
|
||||
static constexpr std::string_view ImageAlignmentTop{ "top" };
|
||||
static constexpr std::string_view ImageAlignmentRight{ "right" };
|
||||
static constexpr std::string_view ImageAlignmentBottom{ "bottom" };
|
||||
static constexpr std::string_view ImageAlignmentTopLeft{ "topLeft" };
|
||||
static constexpr std::string_view ImageAlignmentTopRight{ "topRight" };
|
||||
static constexpr std::string_view ImageAlignmentBottomLeft{ "bottomLeft" };
|
||||
static constexpr std::string_view ImageAlignmentBottomRight{ "bottomRight" };
|
||||
|
||||
// Possible values for TextAntialiasingMode
|
||||
static constexpr std::wstring_view AntialiasingModeGrayscale{ L"grayscale" };
|
||||
static constexpr std::wstring_view AntialiasingModeCleartype{ L"cleartype" };
|
||||
static constexpr std::wstring_view AntialiasingModeAliased{ L"aliased" };
|
||||
|
||||
Profile::Profile() :
|
||||
Profile(std::nullopt)
|
||||
{
|
||||
@@ -248,8 +198,7 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map<std::w
|
||||
|
||||
if (_scrollbarState)
|
||||
{
|
||||
ScrollbarState result = ParseScrollbarState(_scrollbarState.value());
|
||||
terminalSettings.ScrollState(result);
|
||||
terminalSettings.ScrollState(_scrollbarState.value());
|
||||
}
|
||||
|
||||
if (HasBackgroundImage())
|
||||
@@ -350,11 +299,9 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const
|
||||
|
||||
// First, check that GUIDs match. This is easy. If they don't match, they
|
||||
// should _definitely_ not layer.
|
||||
if (json.isMember(JsonKey(GuidKey)))
|
||||
if (const auto otherGuid{ JsonUtils::GetValueForKey<std::optional<GUID>>(json, GuidKey) })
|
||||
{
|
||||
const auto guid{ json[JsonKey(GuidKey)] };
|
||||
const auto otherGuid = Utils::GuidFromString(GetWstringFromJson(guid));
|
||||
if (_guid.value() != otherGuid)
|
||||
if (otherGuid != _guid) // optional compare takes care of this
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -368,16 +315,17 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& otherSource = json.isMember(JsonKey(SourceKey)) ? json[JsonKey(SourceKey)] : Json::Value::null;
|
||||
std::optional<std::wstring> otherSource;
|
||||
bool otherHadSource = JsonUtils::GetValueForKey(json, SourceKey, otherSource);
|
||||
|
||||
// For profiles with a `source`, also check the `source` property.
|
||||
bool sourceMatches = false;
|
||||
if (_source.has_value())
|
||||
{
|
||||
if (json.isMember(JsonKey(SourceKey)))
|
||||
if (otherHadSource)
|
||||
{
|
||||
const auto otherSourceString = GetWstringFromJson(otherSource);
|
||||
sourceMatches = otherSourceString == _source.value();
|
||||
// If we have a source and the other has a source, compare them!
|
||||
sourceMatches = otherSource == _source;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -395,52 +343,13 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const
|
||||
}
|
||||
else
|
||||
{
|
||||
// We do not have a source. The only way we match is if source is set to null or "".
|
||||
if (otherSource.isNull() || (otherSource.isString() && otherSource == ""))
|
||||
{
|
||||
sourceMatches = true;
|
||||
}
|
||||
// We do not have a source. The only way we match is if source is unset or set to "".
|
||||
sourceMatches = (!otherSource.has_value() || otherSource.value() == L"");
|
||||
}
|
||||
|
||||
return sourceMatches;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function to convert a json value into a value of the Stretch enum.
|
||||
// Calls into ParseImageStretchMode. Used with JsonUtils::GetOptionalValue.
|
||||
// Arguments:
|
||||
// - json: the Json::Value object to parse.
|
||||
// Return Value:
|
||||
// - An appropriate value from Windows.UI.Xaml.Media.Stretch
|
||||
Media::Stretch Profile::_ConvertJsonToStretchMode(const Json::Value& json)
|
||||
{
|
||||
return Profile::ParseImageStretchMode(json.asString());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function to convert a json value into a value of the Stretch enum.
|
||||
// Calls into ParseImageAlignment. Used with JsonUtils::GetOptionalValue.
|
||||
// Arguments:
|
||||
// - json: the Json::Value object to parse.
|
||||
// Return Value:
|
||||
// - A pair of HorizontalAlignment and VerticalAlignment
|
||||
std::tuple<HorizontalAlignment, VerticalAlignment> Profile::_ConvertJsonToAlignment(const Json::Value& json)
|
||||
{
|
||||
return Profile::ParseImageAlignment(json.asString());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function to convert a json value into a bool.
|
||||
// Used with JsonUtils::GetOptionalValue.
|
||||
// Arguments:
|
||||
// - json: the Json::Value object to parse.
|
||||
// Return Value:
|
||||
// - A bool
|
||||
bool Profile::_ConvertJsonToBool(const Json::Value& json)
|
||||
{
|
||||
return json.asBool();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Layer values from the given json object on top of the existing properties
|
||||
// of this object. For any keys we're expecting to be able to parse in the
|
||||
@@ -456,89 +365,45 @@ bool Profile::_ConvertJsonToBool(const Json::Value& json)
|
||||
void Profile::LayerJson(const Json::Value& json)
|
||||
{
|
||||
// Profile-specific Settings
|
||||
JsonUtils::GetWstring(json, NameKey, _name);
|
||||
|
||||
JsonUtils::GetOptionalGuid(json, GuidKey, _guid);
|
||||
|
||||
JsonUtils::GetBool(json, HiddenKey, _hidden);
|
||||
JsonUtils::GetValueForKey(json, NameKey, _name);
|
||||
JsonUtils::GetValueForKey(json, GuidKey, _guid);
|
||||
JsonUtils::GetValueForKey(json, HiddenKey, _hidden);
|
||||
|
||||
// Core Settings
|
||||
JsonUtils::GetOptionalColor(json, ForegroundKey, _defaultForeground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, BackgroundKey, _defaultBackground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, SelectionBackgroundKey, _selectionBackground);
|
||||
|
||||
JsonUtils::GetOptionalColor(json, CursorColorKey, _cursorColor);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ColorSchemeKey, _schemeName);
|
||||
JsonUtils::GetValueForKey(json, ForegroundKey, _defaultForeground);
|
||||
JsonUtils::GetValueForKey(json, BackgroundKey, _defaultBackground);
|
||||
JsonUtils::GetValueForKey(json, SelectionBackgroundKey, _selectionBackground);
|
||||
JsonUtils::GetValueForKey(json, CursorColorKey, _cursorColor);
|
||||
JsonUtils::GetValueForKey(json, ColorSchemeKey, _schemeName);
|
||||
|
||||
// TODO:MSFT:20642297 - Use a sentinel value (-1) for "Infinite scrollback"
|
||||
JsonUtils::GetInt(json, HistorySizeKey, _historySize);
|
||||
|
||||
JsonUtils::GetBool(json, SnapOnInputKey, _snapOnInput);
|
||||
|
||||
JsonUtils::GetBool(json, AltGrAliasingKey, _altGrAliasing);
|
||||
|
||||
JsonUtils::GetUInt(json, CursorHeightKey, _cursorHeight);
|
||||
|
||||
if (json.isMember(JsonKey(CursorShapeKey)))
|
||||
{
|
||||
auto cursorShape{ json[JsonKey(CursorShapeKey)] };
|
||||
_cursorShape = _ParseCursorShape(GetWstringFromJson(cursorShape));
|
||||
}
|
||||
JsonUtils::GetOptionalString(json, TabTitleKey, _tabTitle);
|
||||
JsonUtils::GetValueForKey(json, HistorySizeKey, _historySize);
|
||||
JsonUtils::GetValueForKey(json, SnapOnInputKey, _snapOnInput);
|
||||
JsonUtils::GetValueForKey(json, AltGrAliasingKey, _altGrAliasing);
|
||||
JsonUtils::GetValueForKey(json, CursorHeightKey, _cursorHeight);
|
||||
JsonUtils::GetValueForKey(json, CursorShapeKey, _cursorShape);
|
||||
JsonUtils::GetValueForKey(json, TabTitleKey, _tabTitle);
|
||||
|
||||
// Control Settings
|
||||
JsonUtils::GetOptionalGuid(json, ConnectionTypeKey, _connectionType);
|
||||
|
||||
JsonUtils::GetWstring(json, CommandlineKey, _commandline);
|
||||
|
||||
JsonUtils::GetWstring(json, FontFaceKey, _fontFace);
|
||||
|
||||
JsonUtils::GetInt(json, FontSizeKey, _fontSize);
|
||||
|
||||
if (json.isMember(JsonKey(FontWeightKey)))
|
||||
{
|
||||
auto fontWeight{ json[JsonKey(FontWeightKey)] };
|
||||
_fontWeight = _ParseFontWeight(fontWeight);
|
||||
}
|
||||
|
||||
JsonUtils::GetDouble(json, AcrylicTransparencyKey, _acrylicTransparency);
|
||||
|
||||
JsonUtils::GetBool(json, UseAcrylicKey, _useAcrylic);
|
||||
|
||||
JsonUtils::GetBool(json, SuppressApplicationTitleKey, _suppressApplicationTitle);
|
||||
|
||||
if (json.isMember(JsonKey(CloseOnExitKey)))
|
||||
{
|
||||
auto closeOnExit{ json[JsonKey(CloseOnExitKey)] };
|
||||
_closeOnExitMode = ParseCloseOnExitMode(closeOnExit);
|
||||
}
|
||||
|
||||
JsonUtils::GetWstring(json, PaddingKey, _padding);
|
||||
|
||||
JsonUtils::GetOptionalString(json, ScrollbarStateKey, _scrollbarState);
|
||||
|
||||
JsonUtils::GetOptionalString(json, StartingDirectoryKey, _startingDirectory);
|
||||
|
||||
JsonUtils::GetOptionalString(json, IconKey, _icon);
|
||||
|
||||
JsonUtils::GetOptionalString(json, BackgroundImageKey, _backgroundImage);
|
||||
|
||||
JsonUtils::GetOptionalDouble(json, BackgroundImageOpacityKey, _backgroundImageOpacity);
|
||||
|
||||
JsonUtils::GetOptionalValue(json, BackgroundImageStretchModeKey, _backgroundImageStretchMode, &Profile::_ConvertJsonToStretchMode);
|
||||
|
||||
JsonUtils::GetOptionalValue(json, BackgroundImageAlignmentKey, _backgroundImageAlignment, &Profile::_ConvertJsonToAlignment);
|
||||
|
||||
JsonUtils::GetOptionalValue(json, RetroTerminalEffectKey, _retroTerminalEffect, Profile::_ConvertJsonToBool);
|
||||
|
||||
if (json.isMember(JsonKey(AntialiasingModeKey)))
|
||||
{
|
||||
auto antialiasingMode{ json[JsonKey(AntialiasingModeKey)] };
|
||||
_antialiasingMode = ParseTextAntialiasingMode(GetWstringFromJson(antialiasingMode));
|
||||
}
|
||||
JsonUtils::GetValueForKey(json, FontWeightKey, _fontWeight);
|
||||
JsonUtils::GetValueForKey(json, ConnectionTypeKey, _connectionType);
|
||||
JsonUtils::GetValueForKey(json, CommandlineKey, _commandline);
|
||||
JsonUtils::GetValueForKey(json, FontFaceKey, _fontFace);
|
||||
JsonUtils::GetValueForKey(json, FontSizeKey, _fontSize);
|
||||
JsonUtils::GetValueForKey(json, AcrylicTransparencyKey, _acrylicTransparency);
|
||||
JsonUtils::GetValueForKey(json, UseAcrylicKey, _useAcrylic);
|
||||
JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, _suppressApplicationTitle);
|
||||
JsonUtils::GetValueForKey(json, CloseOnExitKey, _closeOnExitMode);
|
||||
JsonUtils::GetValueForKey(json, PaddingKey, _padding);
|
||||
JsonUtils::GetValueForKey(json, ScrollbarStateKey, _scrollbarState);
|
||||
JsonUtils::GetValueForKey(json, StartingDirectoryKey, _startingDirectory);
|
||||
JsonUtils::GetValueForKey(json, IconKey, _icon);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageKey, _backgroundImage);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageOpacityKey, _backgroundImageOpacity);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageStretchModeKey, _backgroundImageStretchMode);
|
||||
JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _backgroundImageAlignment);
|
||||
JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _retroTerminalEffect);
|
||||
JsonUtils::GetValueForKey(json, AntialiasingModeKey, _antialiasingMode);
|
||||
}
|
||||
|
||||
void Profile::SetFontFace(std::wstring fontFace) noexcept
|
||||
@@ -770,249 +635,6 @@ std::wstring Profile::EvaluateStartingDirectory(const std::wstring& directory)
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified font weight value to its corresponding enum
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding value which maps to the string provided by the user
|
||||
winrt::Windows::UI::Text::FontWeight Profile::_ParseFontWeight(const Json::Value& json)
|
||||
{
|
||||
if (json.isUInt())
|
||||
{
|
||||
winrt::Windows::UI::Text::FontWeight weight;
|
||||
weight.Weight = static_cast<uint16_t>(json.asUInt());
|
||||
|
||||
// We're only accepting variable values between 100 and 990 so we don't go too crazy.
|
||||
if (weight.Weight >= 100 && weight.Weight <= 990)
|
||||
{
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (json.isString())
|
||||
{
|
||||
auto fontWeight = json.asString();
|
||||
if (fontWeight == FontWeightThin)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Thin();
|
||||
}
|
||||
else if (fontWeight == FontWeightExtraLight)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::ExtraLight();
|
||||
}
|
||||
else if (fontWeight == FontWeightLight)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Light();
|
||||
}
|
||||
else if (fontWeight == FontWeightSemiLight)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::SemiLight();
|
||||
}
|
||||
else if (fontWeight == FontWeightNormal)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Normal();
|
||||
}
|
||||
else if (fontWeight == FontWeightMedium)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Medium();
|
||||
}
|
||||
else if (fontWeight == FontWeightSemiBold)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::SemiBold();
|
||||
}
|
||||
else if (fontWeight == FontWeightBold)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Bold();
|
||||
}
|
||||
else if (fontWeight == FontWeightExtraBold)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::ExtraBold();
|
||||
}
|
||||
else if (fontWeight == FontWeightBlack)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::Black();
|
||||
}
|
||||
else if (fontWeight == FontWeightExtraBlack)
|
||||
{
|
||||
return winrt::Windows::UI::Text::FontWeights::ExtraBlack();
|
||||
}
|
||||
}
|
||||
|
||||
return winrt::Windows::UI::Text::FontWeights::Normal();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified closeOnExit value to its corresponding enum
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
CloseOnExitMode Profile::ParseCloseOnExitMode(const Json::Value& json)
|
||||
{
|
||||
if (json.isBool())
|
||||
{
|
||||
return json.asBool() ? CloseOnExitMode::Graceful : CloseOnExitMode::Never;
|
||||
}
|
||||
|
||||
if (json.isString())
|
||||
{
|
||||
auto closeOnExit = json.asString();
|
||||
if (closeOnExit == CloseOnExitAlways)
|
||||
{
|
||||
return CloseOnExitMode::Always;
|
||||
}
|
||||
else if (closeOnExit == CloseOnExitGraceful)
|
||||
{
|
||||
return CloseOnExitMode::Graceful;
|
||||
}
|
||||
else if (closeOnExit == CloseOnExitNever)
|
||||
{
|
||||
return CloseOnExitMode::Never;
|
||||
}
|
||||
}
|
||||
|
||||
return CloseOnExitMode::Graceful;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified scrollbar state to its corresponding enum
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
ScrollbarState Profile::ParseScrollbarState(const std::wstring& scrollbarState)
|
||||
{
|
||||
if (scrollbarState == AlwaysVisible)
|
||||
{
|
||||
return ScrollbarState::Visible;
|
||||
}
|
||||
else if (scrollbarState == AlwaysHide)
|
||||
{
|
||||
return ScrollbarState::Hidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ScrollbarState::Visible;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified image stretch mode
|
||||
// to the appropriate enum value
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
Media::Stretch Profile::ParseImageStretchMode(const std::string_view imageStretchMode)
|
||||
{
|
||||
if (imageStretchMode == ImageStretchModeNone)
|
||||
{
|
||||
return Media::Stretch::None;
|
||||
}
|
||||
else if (imageStretchMode == ImageStretchModeFill)
|
||||
{
|
||||
return Media::Stretch::Fill;
|
||||
}
|
||||
else if (imageStretchMode == ImageStretchModeUniform)
|
||||
{
|
||||
return Media::Stretch::Uniform;
|
||||
}
|
||||
else // Fall through to default behavior
|
||||
{
|
||||
return Media::Stretch::UniformToFill;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified image horizontal and vertical
|
||||
// alignment to the appropriate enum values tuple
|
||||
// Arguments:
|
||||
// - The value from the settings.json file
|
||||
// Return Value:
|
||||
// - The corresponding enum values tuple which maps to the string provided by the user
|
||||
std::tuple<HorizontalAlignment, VerticalAlignment> Profile::ParseImageAlignment(const std::string_view imageAlignment)
|
||||
{
|
||||
if (imageAlignment == ImageAlignmentTopLeft)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Left,
|
||||
VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottomLeft)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Left,
|
||||
VerticalAlignment::Bottom);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentLeft)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Left,
|
||||
VerticalAlignment::Center);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentTopRight)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Right,
|
||||
VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottomRight)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Right,
|
||||
VerticalAlignment::Bottom);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentRight)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Right,
|
||||
VerticalAlignment::Center);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentTop)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Center,
|
||||
VerticalAlignment::Top);
|
||||
}
|
||||
else if (imageAlignment == ImageAlignmentBottom)
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Center,
|
||||
VerticalAlignment::Bottom);
|
||||
}
|
||||
else // Fall through to default alignment
|
||||
{
|
||||
return std::make_tuple(HorizontalAlignment::Center,
|
||||
VerticalAlignment::Center);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified cursor style corresponding
|
||||
// CursorStyle enum value
|
||||
// Arguments:
|
||||
// - cursorShapeString: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
CursorStyle Profile::_ParseCursorShape(const std::wstring& cursorShapeString)
|
||||
{
|
||||
if (cursorShapeString == CursorShapeVintage)
|
||||
{
|
||||
return CursorStyle::Vintage;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeBar)
|
||||
{
|
||||
return CursorStyle::Bar;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeUnderscore)
|
||||
{
|
||||
return CursorStyle::Underscore;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeFilledbox)
|
||||
{
|
||||
return CursorStyle::FilledBox;
|
||||
}
|
||||
else if (cursorShapeString == CursorShapeEmptybox)
|
||||
{
|
||||
return CursorStyle::EmptyBox;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return CursorStyle::Bar;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - If this profile never had a GUID set for it, generate a runtime GUID for
|
||||
// the profile. If a profile had their guid manually set to {0}, this method
|
||||
@@ -1078,17 +700,13 @@ GUID Profile::_GenerateGuidForProfile(const std::wstring& name, const std::optio
|
||||
// - The json's `guid`, or a guid synthesized for it.
|
||||
GUID Profile::GetGuidOrGenerateForJson(const Json::Value& json) noexcept
|
||||
{
|
||||
std::optional<GUID> guid{ std::nullopt };
|
||||
|
||||
JsonUtils::GetOptionalGuid(json, GuidKey, guid);
|
||||
if (guid)
|
||||
if (const auto guid{ JsonUtils::GetValueForKey<std::optional<GUID>>(json, GuidKey) })
|
||||
{
|
||||
return guid.value();
|
||||
}
|
||||
|
||||
const auto name = GetWstringFromJson(json[JsonKey(NameKey)]);
|
||||
std::optional<std::wstring> source{ std::nullopt };
|
||||
JsonUtils::GetOptionalString(json, SourceKey, source);
|
||||
const auto name{ JsonUtils::GetValueForKey<std::wstring>(json, NameKey) };
|
||||
const auto source{ JsonUtils::GetValueForKey<std::optional<std::wstring>>(json, SourceKey) };
|
||||
|
||||
return Profile::_GenerateGuidForProfile(name, source);
|
||||
}
|
||||
@@ -1097,28 +715,3 @@ void Profile::SetRetroTerminalEffect(bool value) noexcept
|
||||
{
|
||||
_retroTerminalEffect = value;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper function for converting a user-specified antialiasing mode
|
||||
// corresponding TextAntialiasingMode enum value
|
||||
// Arguments:
|
||||
// - antialiasingMode: The string value from the settings file to parse
|
||||
// Return Value:
|
||||
// - The corresponding enum value which maps to the string provided by the user
|
||||
TextAntialiasingMode Profile::ParseTextAntialiasingMode(const std::wstring& antialiasingMode)
|
||||
{
|
||||
if (antialiasingMode == AntialiasingModeCleartype)
|
||||
{
|
||||
return TextAntialiasingMode::Cleartype;
|
||||
}
|
||||
else if (antialiasingMode == AntialiasingModeAliased)
|
||||
{
|
||||
return TextAntialiasingMode::Aliased;
|
||||
}
|
||||
else if (antialiasingMode == AntialiasingModeGrayscale)
|
||||
{
|
||||
return TextAntialiasingMode::Grayscale;
|
||||
}
|
||||
// default behavior for invalid data
|
||||
return TextAntialiasingMode::Grayscale;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ Author(s):
|
||||
--*/
|
||||
#pragma once
|
||||
#include "ColorScheme.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
@@ -35,14 +36,7 @@ constexpr GUID RUNTIME_GENERATED_PROFILE_NAMESPACE_GUID = { 0xf65ddb7e, 0x706b,
|
||||
namespace TerminalApp
|
||||
{
|
||||
class Profile;
|
||||
|
||||
enum class CloseOnExitMode
|
||||
{
|
||||
Never = 0,
|
||||
Graceful,
|
||||
Always
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class TerminalApp::Profile final
|
||||
{
|
||||
@@ -107,24 +101,8 @@ public:
|
||||
private:
|
||||
static std::wstring EvaluateStartingDirectory(const std::wstring& directory);
|
||||
|
||||
static winrt::Microsoft::Terminal::Settings::ScrollbarState ParseScrollbarState(const std::wstring& scrollbarState);
|
||||
static winrt::Windows::UI::Xaml::Media::Stretch ParseImageStretchMode(const std::string_view imageStretchMode);
|
||||
static winrt::Windows::UI::Xaml::Media::Stretch _ConvertJsonToStretchMode(const Json::Value& json);
|
||||
static std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> ParseImageAlignment(const std::string_view imageAlignment);
|
||||
static std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment> _ConvertJsonToAlignment(const Json::Value& json);
|
||||
|
||||
static winrt::Windows::UI::Text::FontWeight _ParseFontWeight(const Json::Value& json);
|
||||
|
||||
static CloseOnExitMode ParseCloseOnExitMode(const Json::Value& json);
|
||||
|
||||
static winrt::Microsoft::Terminal::Settings::CursorStyle _ParseCursorShape(const std::wstring& cursorShapeString);
|
||||
|
||||
static winrt::Microsoft::Terminal::Settings::TextAntialiasingMode ParseTextAntialiasingMode(const std::wstring& antialiasingMode);
|
||||
|
||||
static GUID _GenerateGuidForProfile(const std::wstring& name, const std::optional<std::wstring>& source) noexcept;
|
||||
|
||||
static bool _ConvertJsonToBool(const Json::Value& json);
|
||||
|
||||
std::optional<GUID> _guid{ std::nullopt };
|
||||
std::optional<std::wstring> _source{ std::nullopt };
|
||||
std::wstring _name;
|
||||
@@ -159,7 +137,7 @@ private:
|
||||
std::optional<winrt::Windows::UI::Xaml::Media::Stretch> _backgroundImageStretchMode;
|
||||
std::optional<std::tuple<winrt::Windows::UI::Xaml::HorizontalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment>> _backgroundImageAlignment;
|
||||
|
||||
std::optional<std::wstring> _scrollbarState;
|
||||
std::optional<::winrt::Microsoft::Terminal::Settings::ScrollbarState> _scrollbarState;
|
||||
CloseOnExitMode _closeOnExitMode;
|
||||
std::wstring _padding;
|
||||
|
||||
|
||||
28
src/cascadia/TerminalApp/SettingsTypes.h
Normal file
28
src/cascadia/TerminalApp/SettingsTypes.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- SettingsTypes.h
|
||||
|
||||
Abstract:
|
||||
- Types used in the settings model (non-exported)
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
enum class CloseOnExitMode
|
||||
{
|
||||
Never = 0,
|
||||
Graceful,
|
||||
Always
|
||||
};
|
||||
|
||||
struct LaunchPosition
|
||||
{
|
||||
std::optional<int> x;
|
||||
std::optional<int> y;
|
||||
};
|
||||
};
|
||||
@@ -166,28 +166,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_newTabButton.Click([weakThis{ get_weak() }](auto&&, auto&&) {
|
||||
if (auto page{ weakThis.get() })
|
||||
{
|
||||
// if alt is pressed, open a pane
|
||||
const CoreWindow window = CoreWindow::GetForCurrentThread();
|
||||
const auto rAltState = window.GetKeyState(VirtualKey::RightMenu);
|
||||
const auto lAltState = window.GetKeyState(VirtualKey::LeftMenu);
|
||||
const bool altPressed = WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) ||
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
// Check for DebugTap
|
||||
bool debugTap = page->_settings->GlobalSettings().DebugFeaturesEnabled() &&
|
||||
WI_IsFlagSet(lAltState, CoreVirtualKeyStates::Down) &&
|
||||
WI_IsFlagSet(rAltState, CoreVirtualKeyStates::Down);
|
||||
|
||||
if (altPressed && !debugTap)
|
||||
{
|
||||
page->_SplitPane(TerminalApp::SplitState::Automatic,
|
||||
TerminalApp::SplitType::Manual,
|
||||
nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
page->_OpenNewTab(nullptr);
|
||||
}
|
||||
page->_OpenNewTab(nullptr);
|
||||
}
|
||||
});
|
||||
_tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged });
|
||||
@@ -405,22 +384,25 @@ namespace winrt::TerminalApp::implementation
|
||||
const auto& profile = _settings->GetProfiles()[profileIndex];
|
||||
auto profileMenuItem = WUX::Controls::MenuFlyoutItem{};
|
||||
|
||||
// Add the keyboard shortcuts based on the number of profiles defined
|
||||
// Look for a keychord that is bound to the equivalent
|
||||
// NewTab(ProfileIndex=N) action
|
||||
auto actionAndArgs = winrt::make_self<winrt::TerminalApp::implementation::ActionAndArgs>();
|
||||
actionAndArgs->Action(ShortcutAction::NewTab);
|
||||
auto newTabArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
|
||||
newTerminalArgs->ProfileIndex(profileIndex);
|
||||
newTabArgs->TerminalArgs(*newTerminalArgs);
|
||||
actionAndArgs->Args(*newTabArgs);
|
||||
auto profileKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(*actionAndArgs) };
|
||||
|
||||
// make sure we find one to display
|
||||
if (profileKeyChord)
|
||||
// add the keyboard shortcuts for the first 9 profiles
|
||||
if (profileIndex < 9)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(profileMenuItem, profileKeyChord);
|
||||
// Look for a keychord that is bound to the equivalent
|
||||
// NewTab(ProfileIndex=N) action
|
||||
auto actionAndArgs = winrt::make_self<winrt::TerminalApp::implementation::ActionAndArgs>();
|
||||
actionAndArgs->Action(ShortcutAction::NewTab);
|
||||
auto newTabArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTabArgs>();
|
||||
auto newTerminalArgs = winrt::make_self<winrt::TerminalApp::implementation::NewTerminalArgs>();
|
||||
newTerminalArgs->ProfileIndex(profileIndex);
|
||||
newTabArgs->TerminalArgs(*newTerminalArgs);
|
||||
actionAndArgs->Args(*newTabArgs);
|
||||
auto profileKeyChord{ keyBindings.GetKeyBindingForActionWithArgs(*actionAndArgs) };
|
||||
|
||||
// make sure we find one to display
|
||||
if (profileKeyChord)
|
||||
{
|
||||
_SetAcceleratorForMenuItem(profileMenuItem, profileKeyChord);
|
||||
}
|
||||
}
|
||||
|
||||
auto profileName = profile.GetName();
|
||||
@@ -1796,7 +1778,7 @@ namespace winrt::TerminalApp::implementation
|
||||
tab->SetFocused(true);
|
||||
|
||||
// Raise an event that our title changed
|
||||
_titleChangeHandlers(*this, tab->GetActiveTitle());
|
||||
_titleChangeHandlers(*this, Title());
|
||||
|
||||
// Raise an event that our titlebar color changed
|
||||
std::optional<Windows::UI::Color> color = tab->GetTabColor();
|
||||
|
||||
272
src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h
Normal file
272
src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- TerminalSettingsSerializationHelpers.h
|
||||
|
||||
Abstract:
|
||||
- Specializations of the JsonUtils helpers for things that might end up in a
|
||||
settings document.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "JsonUtils.h"
|
||||
#include "SettingsTypes.h"
|
||||
|
||||
#include <winrt/Microsoft.Terminal.Settings.h>
|
||||
#include <winrt/TerminalApp.h>
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::CursorStyle)
|
||||
{
|
||||
static constexpr std::array<pair_type, 5> mappings = {
|
||||
pair_type{ "bar", ValueType::Bar },
|
||||
pair_type{ "vintage", ValueType::Vintage },
|
||||
pair_type{ "underscore", ValueType::Underscore },
|
||||
pair_type{ "filledBox", ValueType::FilledBox },
|
||||
pair_type{ "emptyBox", ValueType::EmptyBox }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Windows::UI::Xaml::Media::Stretch)
|
||||
{
|
||||
static constexpr std::array<pair_type, 4> mappings = {
|
||||
pair_type{ "uniformToFill", ValueType::UniformToFill },
|
||||
pair_type{ "none", ValueType::None },
|
||||
pair_type{ "fill", ValueType::Fill },
|
||||
pair_type{ "uniform", ValueType::Uniform }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::ScrollbarState)
|
||||
{
|
||||
static constexpr std::array<pair_type, 2> mappings = {
|
||||
pair_type{ "visible", ValueType::Visible },
|
||||
pair_type{ "hidden", ValueType::Hidden }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(std::tuple<::winrt::Windows::UI::Xaml::HorizontalAlignment, ::winrt::Windows::UI::Xaml::VerticalAlignment>)
|
||||
{
|
||||
// reduce repetition
|
||||
using HA = ::winrt::Windows::UI::Xaml::HorizontalAlignment;
|
||||
using VA = ::winrt::Windows::UI::Xaml::VerticalAlignment;
|
||||
static constexpr std::array<pair_type, 9> mappings = {
|
||||
pair_type{ "center", std::make_tuple(HA::Center, VA::Center) },
|
||||
pair_type{ "topLeft", std::make_tuple(HA::Left, VA::Top) },
|
||||
pair_type{ "bottomLeft", std::make_tuple(HA::Left, VA::Bottom) },
|
||||
pair_type{ "left", std::make_tuple(HA::Left, VA::Center) },
|
||||
pair_type{ "topRight", std::make_tuple(HA::Right, VA::Top) },
|
||||
pair_type{ "bottomRight", std::make_tuple(HA::Right, VA::Bottom) },
|
||||
pair_type{ "right", std::make_tuple(HA::Right, VA::Center) },
|
||||
pair_type{ "top", std::make_tuple(HA::Center, VA::Top) },
|
||||
pair_type{ "bottom", std::make_tuple(HA::Center, VA::Bottom) }
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::TextAntialiasingMode)
|
||||
{
|
||||
static constexpr std::array<pair_type, 3> mappings = {
|
||||
pair_type{ "grayscale", ValueType::Grayscale },
|
||||
pair_type{ "cleartype", ValueType::Cleartype },
|
||||
pair_type{ "aliased", ValueType::Aliased }
|
||||
};
|
||||
};
|
||||
|
||||
// Type Description:
|
||||
// - Helper for converting a user-specified closeOnExit value to its corresponding enum
|
||||
JSON_ENUM_MAPPER(::TerminalApp::CloseOnExitMode)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "always", ValueType::Always },
|
||||
pair_type{ "graceful", ValueType::Graceful },
|
||||
pair_type{ "never", ValueType::Never },
|
||||
};
|
||||
|
||||
// Override mapping parser to add boolean parsing
|
||||
CloseOnExitMode FromJson(const Json::Value& json)
|
||||
{
|
||||
if (json.isBool())
|
||||
{
|
||||
return json.asBool() ? ValueType::Graceful : ValueType::Never;
|
||||
}
|
||||
return EnumMapper::FromJson(json);
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return EnumMapper::CanConvert(json) || json.isBool();
|
||||
}
|
||||
};
|
||||
|
||||
// This specialization isn't using JSON_ENUM_MAPPER because we need to have a different
|
||||
// value type (unsinged int) and return type (FontWeight struct). JSON_ENUM_MAPPER
|
||||
// expects that the value type _is_ the return type.
|
||||
template<>
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<::winrt::Windows::UI::Text::FontWeight> :
|
||||
public ::TerminalApp::JsonUtils::EnumMapper<
|
||||
unsigned int,
|
||||
::TerminalApp::JsonUtils::ConversionTrait<::winrt::Windows::UI::Text::FontWeight>>
|
||||
{
|
||||
// The original parser used the font weight getters Bold(), Normal(), etc.
|
||||
// They were both cumbersome and *not constant expressions*
|
||||
JSON_MAPPINGS(11) = {
|
||||
pair_type{ "thin", 100u },
|
||||
pair_type{ "extra-light", 200u },
|
||||
pair_type{ "light", 300u },
|
||||
pair_type{ "semi-light", 350u },
|
||||
pair_type{ "normal", 400u },
|
||||
pair_type{ "medium", 500u },
|
||||
pair_type{ "semi-bold", 600u },
|
||||
pair_type{ "bold", 700u },
|
||||
pair_type{ "extra-bold", 800u },
|
||||
pair_type{ "black", 900u },
|
||||
pair_type{ "extra-black", 950u },
|
||||
};
|
||||
|
||||
// Override mapping parser to add boolean parsing
|
||||
auto FromJson(const Json::Value& json)
|
||||
{
|
||||
unsigned int value{ 400 };
|
||||
if (json.isUInt())
|
||||
{
|
||||
value = json.asUInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
value = BaseEnumMapper::FromJson(json);
|
||||
}
|
||||
|
||||
::winrt::Windows::UI::Text::FontWeight weight{
|
||||
static_cast<uint16_t>(std::clamp(value, 100u, 990u))
|
||||
};
|
||||
return weight;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return BaseEnumMapper::CanConvert(json) || json.isUInt();
|
||||
}
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Windows::UI::Xaml::ElementTheme)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "system", ValueType::Default },
|
||||
pair_type{ "light", ValueType::Light },
|
||||
pair_type{ "dark", ValueType::Dark },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::LaunchMode)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "default", ValueType::DefaultMode },
|
||||
pair_type{ "maximized", ValueType::MaximizedMode },
|
||||
pair_type{ "fullscreen", ValueType::FullscreenMode },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "equal", ValueType::Equal },
|
||||
pair_type{ "titleLength", ValueType::SizeToContent },
|
||||
pair_type{ "compact", ValueType::Compact },
|
||||
};
|
||||
};
|
||||
|
||||
// Type Description:
|
||||
// - Helper for converting the initial position string into
|
||||
// 2 coordinate values. We allow users to only provide one coordinate,
|
||||
// thus, we use comma as the separator:
|
||||
// (100, 100): standard input string
|
||||
// (, 100), (100, ): if a value is missing, we set this value as a default
|
||||
// (,): both x and y are set to default
|
||||
// (abc, 100): if a value is not valid, we treat it as default
|
||||
// (100, 100, 100): we only read the first two values, this is equivalent to (100, 100)
|
||||
template<>
|
||||
struct ::TerminalApp::JsonUtils::ConversionTrait<::TerminalApp::LaunchPosition>
|
||||
{
|
||||
::TerminalApp::LaunchPosition FromJson(const Json::Value& json)
|
||||
{
|
||||
::TerminalApp::LaunchPosition ret;
|
||||
std::string initialPosition{ json.asString() };
|
||||
static constexpr char singleCharDelim = ',';
|
||||
std::stringstream tokenStream(initialPosition);
|
||||
std::string token;
|
||||
uint8_t initialPosIndex = 0;
|
||||
|
||||
// Get initial position values till we run out of delimiter separated values in the stream
|
||||
// or we hit max number of allowable values (= 2)
|
||||
// Non-numeral values or empty string will be caught as exception and we do not assign them
|
||||
for (; std::getline(tokenStream, token, singleCharDelim) && (initialPosIndex < 2); initialPosIndex++)
|
||||
{
|
||||
try
|
||||
{
|
||||
int32_t position = std::stoi(token);
|
||||
if (initialPosIndex == 0)
|
||||
{
|
||||
ret.x.emplace(position);
|
||||
}
|
||||
|
||||
if (initialPosIndex == 1)
|
||||
{
|
||||
ret.y.emplace(position);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CanConvert(const Json::Value& json)
|
||||
{
|
||||
return json.isString();
|
||||
}
|
||||
};
|
||||
|
||||
// Possible Direction values
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::Direction)
|
||||
{
|
||||
JSON_MAPPINGS(4) = {
|
||||
pair_type{ "left", ValueType::Left },
|
||||
pair_type{ "right", ValueType::Right },
|
||||
pair_type{ "up", ValueType::Up },
|
||||
pair_type{ "down", ValueType::Down },
|
||||
};
|
||||
};
|
||||
|
||||
// Possible SplitState values
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::SplitState)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "vertical", ValueType::Vertical },
|
||||
pair_type{ "horizontal", ValueType::Horizontal },
|
||||
pair_type{ "auto", ValueType::Automatic },
|
||||
};
|
||||
};
|
||||
|
||||
// Possible SplitType values
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::SplitType)
|
||||
{
|
||||
JSON_MAPPINGS(1) = {
|
||||
pair_type{ "duplicate", ValueType::Duplicate },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::TerminalApp::SettingsTarget)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "settingsFile", ValueType::SettingsFile },
|
||||
pair_type{ "defaultsFile", ValueType::DefaultsFile },
|
||||
pair_type{ "allFiles", ValueType::AllFiles },
|
||||
};
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Utils.h"
|
||||
|
||||
// Method Description:
|
||||
// - Constructs a wstring from a given Json::Value object. Reads the object as
|
||||
// a std::string using asString, then builds an hstring from that std::string,
|
||||
// then converts that hstring into a std::wstring.
|
||||
// Arguments:
|
||||
// - json: the Json::Value to parse as a string
|
||||
// Return Value:
|
||||
// - the wstring equivalent of the value in json
|
||||
std::wstring GetWstringFromJson(const Json::Value& json)
|
||||
{
|
||||
return winrt::to_hstring(json.asString()).c_str();
|
||||
}
|
||||
@@ -13,8 +13,6 @@ Author(s):
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
std::wstring GetWstringFromJson(const Json::Value& json);
|
||||
|
||||
// Method Description:
|
||||
// - Create a std::string from a string_view. We do this because we can't look
|
||||
// up a key in a Json::Value with a string_view directly, so instead we'll use
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
],
|
||||
"actions":
|
||||
"keybindings":
|
||||
[
|
||||
// Application-level Keys
|
||||
{ "command": "closeWindow", "keys": "alt+f4" },
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
"foreground": "#839496",
|
||||
"background": "#002B36",
|
||||
"cursorColor": "#FFFFFF",
|
||||
"black": "#002B36",
|
||||
"black": "#073642",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -195,7 +195,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#073642",
|
||||
"brightBlack": "#002B36",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
@@ -209,7 +209,7 @@
|
||||
"foreground": "#657B83",
|
||||
"background": "#FDF6E3",
|
||||
"cursorColor": "#002B36",
|
||||
"black": "#002B36",
|
||||
"black": "#073642",
|
||||
"red": "#DC322F",
|
||||
"green": "#859900",
|
||||
"yellow": "#B58900",
|
||||
@@ -217,7 +217,7 @@
|
||||
"purple": "#D33682",
|
||||
"cyan": "#2AA198",
|
||||
"white": "#EEE8D5",
|
||||
"brightBlack": "#073642",
|
||||
"brightBlack": "#002B36",
|
||||
"brightRed": "#CB4B16",
|
||||
"brightGreen": "#586E75",
|
||||
"brightYellow": "#657B83",
|
||||
@@ -271,7 +271,7 @@
|
||||
"brightWhite": "#EEEEEC"
|
||||
}
|
||||
],
|
||||
"actions":
|
||||
"bindings":
|
||||
[
|
||||
// Application-level Keys
|
||||
{ "command": "closeWindow", "keys": "alt+f4" },
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<ClInclude Include="../JsonUtils.h" />
|
||||
<ClInclude Include="../Utils.h" />
|
||||
<ClInclude Include="../DefaultProfileUtils.h" />
|
||||
<ClInclude Include="../TerminalSettingsSerializationHelpers.h" />
|
||||
<ClInclude Include="../TerminalWarnings.h" />
|
||||
<ClInclude Include="../IDynamicProfileGenerator.h" />
|
||||
<ClInclude Include="../PowershellCoreProfileGenerator.h" />
|
||||
@@ -178,8 +179,6 @@
|
||||
<ClCompile Include="../CascadiaSettingsSerialization.cpp" />
|
||||
<ClCompile Include="../AppKeyBindingsSerialization.cpp" />
|
||||
<ClCompile Include="../KeyChordSerialization.cpp" />
|
||||
<ClCompile Include="../JsonUtils.cpp" />
|
||||
<ClCompile Include="../Utils.cpp" />
|
||||
<ClCompile Include="../DefaultProfileUtils.cpp" />
|
||||
<ClCompile Include="../PowershellCoreProfileGenerator.cpp" />
|
||||
<ClCompile Include="../WslDistroGenerator.cpp" />
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="../init.cpp" />
|
||||
<ClCompile Include="../Utils.cpp" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="../AzureCloudShellGenerator.cpp">
|
||||
@@ -50,9 +49,6 @@
|
||||
<ClCompile Include="$(OpenConsoleDir)\dep\jsoncpp\jsoncpp.cpp">
|
||||
<Filter>json</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="../JsonUtils.cpp">
|
||||
<Filter>json</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="../Tab.cpp">
|
||||
<Filter>tab</Filter>
|
||||
</ClCompile>
|
||||
@@ -92,6 +88,9 @@
|
||||
<ClInclude Include="../GlobalAppSettings.h">
|
||||
<Filter>settings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../TerminalSettingsSerializationHelpers.h">
|
||||
<Filter>settings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="../KeyChordSerialization.h">
|
||||
<Filter>settings</Filter>
|
||||
</ClInclude>
|
||||
@@ -199,4 +198,4 @@
|
||||
<Filter>app</Filter>
|
||||
</ApplicationDefinition>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -28,8 +28,6 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/)
|
||||
TraceLoggingUnregister(g_hTerminalConnectionProvider);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
@@ -11,6 +11,6 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
[uuid("65b8b8c5-988f-43ff-aba9-e89368da1598")]
|
||||
interface IMouseWheelListener
|
||||
{
|
||||
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta, Boolean leftButtonDown, Boolean midButtonDown, Boolean rightButtonDown);
|
||||
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "TermControlAutomationPeer.h"
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace ::Microsoft::Console::VirtualTerminal;
|
||||
using namespace ::Microsoft::Terminal::Core;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Input;
|
||||
@@ -555,21 +554,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()
|
||||
@@ -673,7 +672,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)
|
||||
@@ -738,11 +737,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
|
||||
const auto ch = e.Character();
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
auto modifiers = _GetPressedModifierKeys();
|
||||
if (e.KeyStatus().IsExtendedKey)
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
const bool handled = _terminal->SendCharEvent(ch, scanCode, modifiers);
|
||||
e.Handled(handled);
|
||||
}
|
||||
@@ -752,7 +747,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// normally. Namely, the keys we're concerned with are F7 down and Alt up.
|
||||
// Return value:
|
||||
// - Whether the key was handled.
|
||||
bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
|
||||
bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const bool down)
|
||||
{
|
||||
const auto modifiers{ _GetPressedModifierKeys() };
|
||||
auto handled = false;
|
||||
@@ -760,7 +755,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
// Manually generate an Alt KeyUp event into the key bindings or terminal.
|
||||
// This is required as part of GH#6421.
|
||||
(void)_TrySendKeyEvent(VK_MENU, scanCode, modifiers, false);
|
||||
// GH#6513 - make sure to set the scancode too, otherwise conpty
|
||||
// will think this is a NUL
|
||||
(void)_TrySendKeyEvent(VK_MENU, LOWORD(MapVirtualKeyW(VK_MENU, MAPVK_VK_TO_VSC)), modifiers, false);
|
||||
handled = true;
|
||||
}
|
||||
else if (vkey == VK_F7 && down)
|
||||
@@ -782,7 +779,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
if (!handled)
|
||||
{
|
||||
// _TrySendKeyEvent pretends it didn't handle F7 for some unknown reason.
|
||||
(void)_TrySendKeyEvent(VK_F7, scanCode, modifiers, true);
|
||||
(void)_TrySendKeyEvent(VK_F7, 0, modifiers, true);
|
||||
// GH#6438: Note that we're _not_ sending the key up here - that'll
|
||||
// get passed through XAML to our KeyUp handler normally.
|
||||
handled = true;
|
||||
@@ -829,13 +826,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
auto modifiers = _GetPressedModifierKeys();
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
const auto vkey = gsl::narrow_cast<WORD>(e.OriginalKey());
|
||||
const auto scanCode = gsl::narrow_cast<WORD>(e.KeyStatus().ScanCode);
|
||||
if (e.KeyStatus().IsExtendedKey)
|
||||
{
|
||||
modifiers |= ControlKeyStates::EnhancedKey;
|
||||
}
|
||||
|
||||
// Alt-Numpad# input will send us a character once the user releases
|
||||
// Alt, so we should be ignoring the individual keydowns. The character
|
||||
@@ -1008,8 +1001,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
||||
return _terminal->SendMouseEvent(terminalPosition, uiButton, modifiers, sWheelDelta, state);
|
||||
return _terminal->SendMouseEvent(terminalPosition, uiButton, modifiers, sWheelDelta);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1333,12 +1325,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
|
||||
const auto point = args.GetCurrentPoint(*this);
|
||||
const auto props = point.Properties();
|
||||
const TerminalInput::MouseButtonState state{ props.IsLeftButtonPressed(), props.IsMiddleButtonPressed(), props.IsRightButtonPressed() };
|
||||
auto result = _DoMouseWheel(point.Position(),
|
||||
ControlKeyStates{ args.KeyModifiers() },
|
||||
point.Properties().MouseWheelDelta(),
|
||||
state);
|
||||
point.Properties().IsLeftButtonPressed());
|
||||
if (result)
|
||||
{
|
||||
args.Handled(true);
|
||||
@@ -1361,7 +1351,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
bool TermControl::_DoMouseWheel(const Windows::Foundation::Point point,
|
||||
const ControlKeyStates modifiers,
|
||||
const int32_t delta,
|
||||
const TerminalInput::MouseButtonState state)
|
||||
const bool isLeftButtonPressed)
|
||||
{
|
||||
if (_CanSendVTMouseInput())
|
||||
{
|
||||
@@ -1373,8 +1363,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
return _terminal->SendMouseEvent(_GetTerminalPosition(point),
|
||||
WM_MOUSEWHEEL,
|
||||
_GetPressedModifierKeys(),
|
||||
::base::saturated_cast<short>(delta),
|
||||
state);
|
||||
::base::saturated_cast<short>(delta));
|
||||
}
|
||||
|
||||
const auto ctrlPressed = modifiers.IsCtrlPressed();
|
||||
@@ -1390,7 +1379,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
_MouseScrollHandler(delta, point, state.isLeftButtonDown);
|
||||
_MouseScrollHandler(delta, point, isLeftButtonPressed);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1404,16 +1393,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
// - location: the location of the mouse during this event. This location is
|
||||
// relative to the origin of the control
|
||||
// - delta: the mouse wheel delta that triggered this event.
|
||||
// - state: the state for each of the mouse buttons individually (pressed/unpressed)
|
||||
bool TermControl::OnMouseWheel(const Windows::Foundation::Point location,
|
||||
const int32_t delta,
|
||||
const bool leftButtonDown,
|
||||
const bool midButtonDown,
|
||||
const bool rightButtonDown)
|
||||
const int32_t delta)
|
||||
{
|
||||
const auto modifiers = _GetPressedModifierKeys();
|
||||
TerminalInput::MouseButtonState state{ leftButtonDown, midButtonDown, rightButtonDown };
|
||||
return _DoMouseWheel(location, modifiers, delta, state);
|
||||
return _DoMouseWheel(location, modifiers, delta, false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
#include "SearchBoxControl.h"
|
||||
#include "ThrottledFunc.h"
|
||||
|
||||
namespace Microsoft::Console::VirtualTerminal
|
||||
{
|
||||
struct MouseButtonState;
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
{
|
||||
struct CopyToClipboardEventArgs :
|
||||
@@ -86,15 +81,15 @@ 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);
|
||||
|
||||
void CreateSearchBoxControl();
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
|
||||
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta);
|
||||
|
||||
~TermControl();
|
||||
|
||||
@@ -226,7 +221,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
|
||||
void _MouseScrollHandler(const double mouseDelta, const Windows::Foundation::Point point, const bool isLeftButtonPressed);
|
||||
void _MouseZoomHandler(const double delta);
|
||||
void _MouseTransparencyHandler(const double delta);
|
||||
bool _DoMouseWheel(const Windows::Foundation::Point point, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);
|
||||
bool _DoMouseWheel(const Windows::Foundation::Point point, const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta, const bool isLeftButtonPressed);
|
||||
|
||||
bool _CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
bool _ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace Microsoft.Terminal.TerminalControl
|
||||
// If you update this one, please update TerminalApp\IDirectKeyListener.idl.
|
||||
// If you change this interface, please update the guid.
|
||||
// If you press F7 or Alt and get a runtime error, go make sure both copies are the same.
|
||||
[uuid("0ddf4edc-3fda-4dee-97ca-a417ee3dd510")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, UInt8 scanCode, Boolean down);
|
||||
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")] interface IDirectKeyListener {
|
||||
Boolean OnDirectKeyEvent(UInt32 vkey, Boolean down);
|
||||
}
|
||||
|
||||
runtimeclass CopyToClipboardEventArgs
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace Microsoft::Terminal::Core
|
||||
virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept = 0;
|
||||
|
||||
virtual bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept = 0;
|
||||
virtual bool SetCursorColor(const DWORD color) noexcept = 0;
|
||||
|
||||
virtual bool SetDefaultForeground(const DWORD color) noexcept = 0;
|
||||
virtual bool SetDefaultBackground(const DWORD color) noexcept = 0;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Microsoft::Terminal::Core
|
||||
ITerminalInput& operator=(ITerminalInput&&) = default;
|
||||
|
||||
virtual bool SendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates states, const bool keyDown) = 0;
|
||||
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) = 0;
|
||||
virtual bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) = 0;
|
||||
virtual bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) = 0;
|
||||
|
||||
// void SendMouseEvent(uint row, uint col, KeyModifiers modifiers);
|
||||
|
||||
@@ -430,20 +430,8 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
|
||||
_StoreKeyEvent(vkey, scanCode);
|
||||
|
||||
// As a Terminal we're mostly interested in getting key events from physical hardware (mouse & keyboard).
|
||||
// We're thus ignoring events whose values are outside the valid range and unlikely to be generated by the current keyboard.
|
||||
// It's very likely that a proper followup character event will be sent to us.
|
||||
// This prominently happens using AutoHotKey's keyboard remapping feature,
|
||||
// which sends input events whose vkey is 0xff and scanCode is 0.
|
||||
// We need to check for this early, as _CharacterFromKeyEvent() always returns 0 for such invalid values,
|
||||
// making us believe that this is an actual non-character input, while it usually isn't.
|
||||
// GH#7064
|
||||
if (vkey == 0 || vkey >= 0xff || scanCode == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto isAltOnlyPressed = states.IsAltPressed() && !states.IsCtrlPressed();
|
||||
const auto isSuppressedAltGrAlias = !_altGrAliasing && states.IsAltPressed() && states.IsCtrlPressed();
|
||||
|
||||
// DON'T manually handle Alt+Space - the system will use this to bring up
|
||||
// the system menu for restore, min/maximize, size, move, close.
|
||||
@@ -463,7 +451,6 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
// as TerminalInput::HandleKey will then fall back to using the vkey which
|
||||
// is the underlying ASCII character (e.g. A-Z) on the keyboard in our case.
|
||||
// See GH#5525/GH#6211 for more details
|
||||
const auto isSuppressedAltGrAlias = !_altGrAliasing && states.IsAltPressed() && states.IsCtrlPressed() && !states.IsAltGrPressed();
|
||||
const auto ch = isSuppressedAltGrAlias ? UNICODE_NULL : _CharacterFromKeyEvent(vkey, scanCode, states);
|
||||
|
||||
// Delegate it to the character event handler if this key event can be
|
||||
@@ -497,16 +484,16 @@ bool Terminal::SendKeyEvent(const WORD vkey,
|
||||
// Return Value:
|
||||
// - true if we translated the key event, and it should not be processed any further.
|
||||
// - false if we did not translate the key, and it should be processed into a character.
|
||||
bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const TerminalInput::MouseButtonState state)
|
||||
bool Terminal::SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta)
|
||||
{
|
||||
// GH#6401: VT applications should be able to receive mouse events from outside the
|
||||
// terminal buffer. This is likely to happen when the user drags the cursor offscreen.
|
||||
// We shouldn't throw away perfectly good events when they're offscreen, so we just
|
||||
// clamp them to be within the range [(0, 0), (W, H)].
|
||||
#pragma warning(suppress : 26496) // analysis can't tell we're assigning through a reference below
|
||||
auto clampedPos{ viewportPos };
|
||||
_mutableViewport.ToOrigin().Clamp(clampedPos);
|
||||
return _terminalInput->HandleMouse(clampedPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta, state);
|
||||
// viewportPos must be within the dimensions of the viewport
|
||||
const auto viewportDimensions = _mutableViewport.Dimensions();
|
||||
if (viewportPos.X < 0 || viewportPos.X >= viewportDimensions.X || viewportPos.Y < 0 || viewportPos.Y >= viewportDimensions.Y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _terminalInput->HandleMouse(viewportPos, uiButton, GET_KEYSTATE_WPARAM(states.Value()), wheelDelta);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -830,12 +817,8 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
|
||||
}
|
||||
}
|
||||
|
||||
// If the viewport moved, or we circled the buffer, we might need to update
|
||||
// our _scrollOffset
|
||||
if (updatedViewport || newRows != 0)
|
||||
if (updatedViewport)
|
||||
{
|
||||
const auto oldScrollOffset = _scrollOffset;
|
||||
|
||||
// scroll if...
|
||||
// - no selection is active
|
||||
// - viewport is already at the bottom
|
||||
@@ -843,18 +826,6 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
|
||||
|
||||
_scrollOffset = scrollToOutput ? 0 : _scrollOffset + scrollAmount + newRows;
|
||||
|
||||
// Clamp the range to make sure that we don't scroll way off the top of the buffer
|
||||
_scrollOffset = std::clamp(_scrollOffset,
|
||||
0,
|
||||
_buffer->GetSize().Height() - _mutableViewport.Height());
|
||||
|
||||
// If the new scroll offset is different, then we'll still want to raise a scroll event
|
||||
updatedViewport = updatedViewport || (oldScrollOffset != _scrollOffset);
|
||||
}
|
||||
|
||||
// If the viewport moved, then send a scrolling notification.
|
||||
if (updatedViewport)
|
||||
{
|
||||
_NotifyScrollEvent();
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ public:
|
||||
bool SetWindowTitle(std::wstring_view title) noexcept override;
|
||||
bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept override;
|
||||
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override;
|
||||
bool SetCursorColor(const COLORREF color) noexcept override;
|
||||
bool SetDefaultForeground(const COLORREF color) noexcept override;
|
||||
bool SetDefaultBackground(const COLORREF color) noexcept override;
|
||||
|
||||
@@ -116,7 +115,7 @@ public:
|
||||
#pragma region ITerminalInput
|
||||
// These methods are defined in Terminal.cpp
|
||||
bool SendKeyEvent(const WORD vkey, const WORD scanCode, const Microsoft::Terminal::Core::ControlKeyStates states, const bool keyDown) override;
|
||||
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta, const Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state) override;
|
||||
bool SendMouseEvent(const COORD viewportPos, const unsigned int uiButton, const ControlKeyStates states, const short wheelDelta) override;
|
||||
bool SendCharEvent(const wchar_t ch, const WORD scanCode, const ControlKeyStates states) override;
|
||||
|
||||
[[nodiscard]] HRESULT UserResize(const COORD viewportSize) noexcept override;
|
||||
|
||||
@@ -64,14 +64,6 @@ COORD Terminal::GetCursorPosition() noexcept
|
||||
return newPos;
|
||||
}
|
||||
|
||||
bool Terminal::SetCursorColor(const COLORREF color) noexcept
|
||||
try
|
||||
{
|
||||
_buffer->GetCursor().SetColor(color);
|
||||
return true;
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
// Method Description:
|
||||
// - Moves the cursor down one line, and possibly also to the leftmost column.
|
||||
// Arguments:
|
||||
|
||||
@@ -146,13 +146,6 @@ try
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
bool TerminalDispatch::SetCursorColor(const DWORD color) noexcept
|
||||
try
|
||||
{
|
||||
return _terminalApi.SetCursorColor(color);
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
bool TerminalDispatch::SetClipboard(std::wstring_view content) noexcept
|
||||
try
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
|
||||
bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept override;
|
||||
bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override;
|
||||
bool SetCursorColor(const DWORD color) noexcept override;
|
||||
|
||||
bool SetClipboard(std::wstring_view content) noexcept override;
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace TerminalCoreUnitTests
|
||||
|
||||
TEST_METHOD(AltShiftKey);
|
||||
TEST_METHOD(AltSpace);
|
||||
TEST_METHOD(InvalidKeyEvent);
|
||||
|
||||
void _VerifyExpectedInput(std::wstring& actualInput)
|
||||
{
|
||||
@@ -66,14 +65,4 @@ namespace TerminalCoreUnitTests
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(L' ', 0, ControlKeyStates::LeftAltPressed, false));
|
||||
VERIFY_IS_FALSE(term.SendCharEvent(L' ', 0, ControlKeyStates::LeftAltPressed));
|
||||
}
|
||||
|
||||
void InputTest::InvalidKeyEvent()
|
||||
{
|
||||
// Certain applications like AutoHotKey and its keyboard remapping feature,
|
||||
// send us key events using SendInput() whose values are outside of the valid range.
|
||||
// We don't want to handle those events as we're probably going to get a proper followup character event.
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(0, 123, {}, true));
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(255, 123, {}, true));
|
||||
VERIFY_IS_FALSE(term.SendKeyEvent(123, 0, {}, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,6 @@ class TerminalCoreUnitTests::TerminalBufferTests final
|
||||
TEST_METHOD(TestWrappingCharByChar);
|
||||
TEST_METHOD(TestWrappingALongString);
|
||||
|
||||
TEST_METHOD(DontSnapToOutputTest);
|
||||
|
||||
TEST_METHOD_SETUP(MethodSetup)
|
||||
{
|
||||
// STEP 1: Set up the Terminal
|
||||
@@ -151,85 +149,3 @@ void TerminalBufferTests::TestWrappingALongString()
|
||||
|
||||
TestUtils::VerifyExpectedString(termTb, TestUtils::Test100CharsString, { 0, 0 });
|
||||
}
|
||||
|
||||
void TerminalBufferTests::DontSnapToOutputTest()
|
||||
{
|
||||
auto& termTb = *term->_buffer;
|
||||
auto& termSm = *term->_stateMachine;
|
||||
const auto initialView = term->GetViewport();
|
||||
|
||||
VERIFY_ARE_EQUAL(0, initialView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight, initialView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(0, term->_scrollOffset);
|
||||
|
||||
// -1 so that we don't print the last \n
|
||||
for (int i = 0; i < TerminalViewHeight + 8 - 1; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
}
|
||||
|
||||
const auto secondView = term->GetViewport();
|
||||
|
||||
VERIFY_ARE_EQUAL(8, secondView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 8, secondView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(0, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Scroll up one line");
|
||||
term->_scrollOffset = 1;
|
||||
|
||||
const auto thirdView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(7, thirdView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 7, thirdView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(1, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print a few lines, to see that the viewport stays where it was");
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
}
|
||||
|
||||
const auto fourthView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(7, fourthView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 7, fourthView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(1 + 8, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print enough lines to get the buffer just about ready to "
|
||||
L"circle (on the next newline)");
|
||||
auto viewBottom = term->_mutableViewport.BottomInclusive();
|
||||
do
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
viewBottom = term->_mutableViewport.BottomInclusive();
|
||||
} while (viewBottom < termTb.GetSize().BottomInclusive());
|
||||
|
||||
const auto fifthView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(7, fifthView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 7, fifthView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(TerminalHistoryLength - 7, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print 3 more lines, and see that we stick to where the old "
|
||||
L"rows now are in the buffer (after circling)");
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"_scrollOffset: %d", term->_scrollOffset));
|
||||
}
|
||||
const auto sixthView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(4, sixthView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight + 4, sixthView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(TerminalHistoryLength - 4, term->_scrollOffset);
|
||||
|
||||
Log::Comment(L"Print 8 more lines, and see that we're now just stuck at the"
|
||||
L"top of the buffer");
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
termSm.ProcessString(L"x\n");
|
||||
Log::Comment(NoThrowString().Format(
|
||||
L"_scrollOffset: %d", term->_scrollOffset));
|
||||
}
|
||||
const auto seventhView = term->GetViewport();
|
||||
VERIFY_ARE_EQUAL(0, seventhView.Top());
|
||||
VERIFY_ARE_EQUAL(TerminalViewHeight, seventhView.BottomExclusive());
|
||||
VERIFY_ARE_EQUAL(TerminalHistoryLength, term->_scrollOffset);
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ using namespace winrt::Windows::Foundation::Numerics;
|
||||
using namespace ::Microsoft::Console;
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
AppHost::AppHost() noexcept :
|
||||
_app{},
|
||||
_logic{ nullptr }, // don't make one, we're going to take a ref on app's
|
||||
@@ -68,11 +64,11 @@ AppHost::~AppHost()
|
||||
_app = nullptr;
|
||||
}
|
||||
|
||||
bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down)
|
||||
bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const bool down)
|
||||
{
|
||||
if (_logic)
|
||||
{
|
||||
return _logic.OnDirectKeyEvent(vkey, scanCode, down);
|
||||
return _logic.OnDirectKeyEvent(vkey, down);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -409,11 +405,7 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
|
||||
|
||||
const til::point offsetPoint = coord - controlOrigin;
|
||||
|
||||
const auto lButtonDown = WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed);
|
||||
const auto mButtonDown = WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed);
|
||||
const auto rButtonDown = WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed);
|
||||
|
||||
if (control.OnMouseWheel(offsetPoint, delta, lButtonDown, mButtonDown, rButtonDown))
|
||||
if (control.OnMouseWheel(offsetPoint, delta))
|
||||
{
|
||||
// If the element handled the mouse wheel event, don't
|
||||
// continue to iterate over the remaining controls.
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
|
||||
void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
|
||||
void Initialize();
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
|
||||
|
||||
private:
|
||||
bool _useNonClientArea;
|
||||
|
||||
@@ -107,62 +107,68 @@ 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))
|
||||
// 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))
|
||||
{
|
||||
// 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, 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;
|
||||
}
|
||||
|
||||
@@ -62,16 +62,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </summary>
|
||||
WM_CHAR = 0x0102,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is pressed. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYDOWN = 0x0104,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when a system key is released. A system key is F10 or Alt+Something.
|
||||
/// </summary>
|
||||
WM_SYSKEYUP = 0x0105,
|
||||
|
||||
/// <summary>
|
||||
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the cursor. Otherwise, the message is posted to the window that has captured the mouse.
|
||||
/// </summary>
|
||||
@@ -225,10 +215,10 @@ namespace Microsoft.Terminal.Wpf
|
||||
public static extern void DestroyTerminal(IntPtr terminal);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSendKeyEvent(IntPtr terminal, ushort vkey, ushort scanCode, ushort flags, bool keyDown);
|
||||
public static extern void TerminalSendKeyEvent(IntPtr terminal, ushort vkey, ushort scanCode, bool keyDown);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSendCharEvent(IntPtr terminal, char ch, ushort scanCode, ushort flags);
|
||||
public static extern void TerminalSendCharEvent(IntPtr terminal, char ch, ushort scanCode);
|
||||
|
||||
[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
|
||||
public static extern void TerminalSetTheme(IntPtr terminal, [MarshalAs(UnmanagedType.Struct)] TerminalTheme theme, string fontFamily, short fontSize, int newDpi);
|
||||
|
||||
@@ -20,20 +20,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </remarks>
|
||||
public class TerminalContainer : HwndHost
|
||||
{
|
||||
private static void UnpackKeyMessage(IntPtr wParam, IntPtr lParam, out ushort vkey, out ushort scanCode, out ushort flags)
|
||||
{
|
||||
ulong scanCodeAndFlags = (((ulong)lParam) & 0xFFFF0000) >> 16;
|
||||
scanCode = (ushort)(scanCodeAndFlags & 0x00FFu);
|
||||
flags = (ushort)(scanCodeAndFlags & 0xFF00u);
|
||||
vkey = (ushort)wParam;
|
||||
}
|
||||
|
||||
private static void UnpackCharMessage(IntPtr wParam, IntPtr lParam, out char character, out ushort scanCode, out ushort flags)
|
||||
{
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vKey, out scanCode, out flags);
|
||||
character = (char)vKey;
|
||||
}
|
||||
|
||||
private ITerminalConnection connection;
|
||||
private IntPtr hwnd;
|
||||
private IntPtr terminal;
|
||||
@@ -138,20 +124,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.TriggerResize(this.RenderSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected text from the terminal renderer and clears the selection.
|
||||
/// </summary>
|
||||
/// <returns>The selected text, empty if no text is selected.</returns>
|
||||
internal string GetSelectedText()
|
||||
{
|
||||
if (NativeMethods.TerminalIsSelectionActive(this.terminal))
|
||||
{
|
||||
return NativeMethods.TerminalGetSelection(this.terminal);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a refresh of the terminal with the given size.
|
||||
/// </summary>
|
||||
@@ -263,35 +235,29 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.Focus();
|
||||
NativeMethods.SetFocus(this.hwnd);
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYDOWN: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYDOWN:
|
||||
{
|
||||
// WM_KEYDOWN lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
|
||||
NativeMethods.TerminalSetCursorVisible(this.terminal, true);
|
||||
ulong scanCode = (((ulong)lParam) & 0x00FF0000) >> 16;
|
||||
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vkey, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, vkey, scanCode, flags, true);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, (ushort)scanCode, true);
|
||||
this.blinkTimer?.Start();
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_SYSKEYUP: // fallthrough
|
||||
case NativeMethods.WindowMessage.WM_KEYUP:
|
||||
{
|
||||
// WM_KEYUP lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keyup
|
||||
UnpackKeyMessage(wParam, lParam, out ushort vkey, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, scanCode, flags, false);
|
||||
ulong scanCode = (((ulong)lParam) & 0x00FF0000) >> 16;
|
||||
NativeMethods.TerminalSendKeyEvent(this.terminal, (ushort)wParam, (ushort)scanCode, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case NativeMethods.WindowMessage.WM_CHAR:
|
||||
{
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
UnpackCharMessage(wParam, lParam, out char character, out ushort scanCode, out ushort flags);
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, character, scanCode, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
// WM_CHAR lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-char
|
||||
NativeMethods.TerminalSendCharEvent(this.terminal, (char)wParam, (ushort)((uint)lParam >> 16));
|
||||
break;
|
||||
case NativeMethods.WindowMessage.WM_WINDOWPOSCHANGED:
|
||||
var windowpos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(lParam, typeof(NativeMethods.WINDOWPOS));
|
||||
if (((NativeMethods.SetWindowPosFlags)windowpos.flags).HasFlag(NativeMethods.SetWindowPosFlags.SWP_NOSIZE))
|
||||
|
||||
@@ -70,15 +70,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
this.termContainer.SetTheme(theme, fontFamily, fontSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected text in the terminal, clearing the selection. Otherwise returns an empty string.
|
||||
/// </summary>
|
||||
/// <returns>Selected text, empty string if no content is selected.</returns>
|
||||
public string GetSelectedText()
|
||||
{
|
||||
return this.termContainer.GetSelectedText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the terminal to the specified rows and columns.
|
||||
/// </summary>
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// <summary>
|
||||
/// Structure for color handling in the terminal.
|
||||
/// </summary>
|
||||
/// <remarks>Keep in sync with HwndTerminal.hpp.</remarks>
|
||||
/// <remarks>Pack = 1 removes the padding added by some compilers/processors for optimization purposes.</remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TerminalTheme
|
||||
{
|
||||
@@ -66,16 +66,6 @@ namespace Microsoft.Terminal.Wpf
|
||||
/// </summary>
|
||||
public uint DefaultForeground;
|
||||
|
||||
/// <summary>
|
||||
/// The default selection background color of the terminal, represented in Win32 COLORREF format.
|
||||
/// </summary>
|
||||
public uint DefaultSelectionBackground;
|
||||
|
||||
/// <summary>
|
||||
/// The opacity alpha for the selection color of the terminal, must be between 1.0 and 0.0.
|
||||
/// </summary>
|
||||
public float SelectionBackgroundAlpha;
|
||||
|
||||
/// <summary>
|
||||
/// The style of cursor to use in the terminal.
|
||||
/// </summary>
|
||||
|
||||
@@ -28,8 +28,6 @@ namespace TerminalAppUnitTests
|
||||
TEST_METHOD(ParseSimpleColorScheme);
|
||||
TEST_METHOD(ProfileGeneratesGuid);
|
||||
|
||||
TEST_METHOD(TestWrongValueType);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
InitializeJsonReader();
|
||||
@@ -169,58 +167,4 @@ namespace TerminalAppUnitTests
|
||||
VERIFY_ARE_EQUAL(profile3.GetGuid(), nullGuid);
|
||||
VERIFY_ARE_EQUAL(profile4.GetGuid(), cmdGuid);
|
||||
}
|
||||
|
||||
void JsonTests::TestWrongValueType()
|
||||
{
|
||||
// This json blob has a whole bunch of settings with the wrong value
|
||||
// types - strings for int values, ints for strings, floats for ints,
|
||||
// etc. When we encounter data that's the wrong data type, we should
|
||||
// gracefully ignore it, as opposed to throwing an exception, causing us
|
||||
// to fail to load the settings at all.
|
||||
|
||||
const std::string settings0String{ R"(
|
||||
{
|
||||
"defaultProfile" : "{00000000-1111-0000-0000-000000000000}",
|
||||
"profiles": [
|
||||
{
|
||||
"guid" : "{00000000-1111-0000-0000-000000000000}",
|
||||
"acrylicOpacity" : "0.5",
|
||||
"closeOnExit" : "true",
|
||||
"fontSize" : "10",
|
||||
"historySize" : 1234.5678,
|
||||
"padding" : 20,
|
||||
"snapOnInput" : "false",
|
||||
"icon" : 4,
|
||||
"backgroundImageOpacity": false,
|
||||
"useAcrylic" : 14
|
||||
}
|
||||
]
|
||||
})" };
|
||||
|
||||
const auto settings0Json = VerifyParseSucceeded(settings0String);
|
||||
|
||||
CascadiaSettings settings;
|
||||
|
||||
settings._ParseJsonString(settings0String, false);
|
||||
// We should not throw an exception trying to parse the settings here.
|
||||
settings.LayerJson(settings._userSettings);
|
||||
|
||||
VERIFY_ARE_EQUAL(1u, settings._profiles.size());
|
||||
auto& profile = settings._profiles.at(0);
|
||||
Profile defaults{};
|
||||
|
||||
VERIFY_ARE_EQUAL(defaults._acrylicTransparency, profile._acrylicTransparency);
|
||||
VERIFY_ARE_EQUAL(defaults._closeOnExitMode, profile._closeOnExitMode);
|
||||
VERIFY_ARE_EQUAL(defaults._fontSize, profile._fontSize);
|
||||
VERIFY_ARE_EQUAL(defaults._historySize, profile._historySize);
|
||||
// A 20 as an int can still be treated as a json string
|
||||
VERIFY_ARE_EQUAL(L"20", profile._padding);
|
||||
VERIFY_ARE_EQUAL(defaults._snapOnInput, profile._snapOnInput);
|
||||
// 4 is a valid string value
|
||||
VERIFY_ARE_EQUAL(L"4", profile._icon);
|
||||
// false is not a valid optional<double>
|
||||
VERIFY_IS_FALSE(profile._backgroundImageOpacity.has_value());
|
||||
VERIFY_ARE_EQUAL(defaults._useAcrylic, profile._useAcrylic);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "../TerminalApp/JsonUtilsNew.h"
|
||||
#include "../TerminalApp/JsonUtils.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace WEX::Logging;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<Target Name="CleanUpPrecompForSmallCIAgents"
|
||||
DependsOnTargets="_ComputePrecompToCleanUp"
|
||||
AfterTargets="AfterBuild"
|
||||
Condition="'$(AGENT_ID)' != '' and !$(ProjectName.Contains('TerminalApp'))">
|
||||
Condition="'$(AGENT_ID)' != ''">
|
||||
<!-- We just need to keep *TerminalApp*'s PCHs because they get rebuilt more often. -->
|
||||
<Delete Files="@(_PCHFileToCleanWithTimestamp)"/>
|
||||
<Touch Files="@(_PCHFileToCleanWithTimestamp)" Time="%(LastWriteTime)" AlwaysCreate="true" />
|
||||
|
||||
@@ -22,20 +22,12 @@
|
||||
at the winmd we build to generate type info.
|
||||
In general, cppwinrt projects all want this.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'=='true'">
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'!='false'">
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<WindowsStoreApp>true</WindowsStoreApp>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(OpenConsoleUniversalApp)'!='true'">
|
||||
<!-- Some of our projects include the cppwinrt build options to
|
||||
just build cppwinrt things, but don't want the store bits
|
||||
in full swing. MinimalCoreWin != false means "don't let me
|
||||
use win32 APIs"
|
||||
-->
|
||||
<MinimalCoreWin>false</MinimalCoreWin>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- This is magic that tells msbuild to link against the Desktop platform
|
||||
instead of the App platform. This you definitely want, because we're not
|
||||
|
||||
@@ -315,7 +315,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
|
||||
// Copy any regions that overlap from this map to the new one.
|
||||
// Just iterate our runs...
|
||||
for (const auto& run : *this)
|
||||
for (const auto run : *this)
|
||||
{
|
||||
// intersect them with the new map
|
||||
// so we don't attempt to set bits that fit outside
|
||||
|
||||
@@ -23,17 +23,12 @@
|
||||
#pragma hdrstop
|
||||
|
||||
using namespace Microsoft::Console::Interactivity::Win32;
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||
// For usage with WM_SYSKEYDOWN message processing.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646286(v=vs.85).aspx
|
||||
// Bit 29 is whether ALT was held when the message was posted.
|
||||
#define WM_SYSKEYDOWN_ALT_PRESSED (0x20000000)
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
// ----------------------------
|
||||
// Helpers
|
||||
// ----------------------------
|
||||
@@ -128,20 +123,7 @@ bool HandleTerminalMouseEvent(const COORD cMousePosition,
|
||||
// Virtual terminal input mode
|
||||
if (IsInVirtualTerminalInputMode())
|
||||
{
|
||||
const TerminalInput::MouseButtonState state{
|
||||
WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed),
|
||||
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
|
||||
};
|
||||
|
||||
// GH#6401: VT applications should be able to receive mouse events from outside the
|
||||
// terminal buffer. This is likely to happen when the user drags the cursor offscreen.
|
||||
// We shouldn't throw away perfectly good events when they're offscreen, so we just
|
||||
// clamp them to be within the range [(0, 0), (W, H)].
|
||||
auto clampedPosition{ cMousePosition };
|
||||
const auto clampViewport{ gci.GetActiveOutputBuffer().GetViewport().ToOrigin() };
|
||||
clampViewport.Clamp(clampedPosition);
|
||||
fWasHandled = gci.GetActiveInputBuffer()->GetTerminalInput().HandleMouse(clampedPosition, uiButton, sModifierKeystate, sWheelDelta, state);
|
||||
fWasHandled = gci.GetActiveInputBuffer()->GetTerminalInput().HandleMouse(cMousePosition, uiButton, sModifierKeystate, sWheelDelta);
|
||||
}
|
||||
|
||||
return fWasHandled;
|
||||
@@ -653,25 +635,6 @@ BOOL HandleMouseEvent(const SCREEN_INFORMATION& ScreenInfo,
|
||||
|
||||
if (HandleTerminalMouseEvent(MousePosition, Message, GET_KEYSTATE_WPARAM(wParam), sDelta))
|
||||
{
|
||||
// GH#6401: Capturing the mouse ensures that we get drag/release events
|
||||
// even if the user moves outside the window.
|
||||
// HandleTerminalMouseEvent returns false if the terminal's not in VT mode,
|
||||
// so capturing/releasing here should not impact other console mouse event
|
||||
// consumers.
|
||||
switch (Message)
|
||||
{
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
SetCapture(ServiceLocator::LocateConsoleWindow()->GetWindowHandle());
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
ReleaseCapture();
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,113 +847,57 @@ CATCH_RETURN();
|
||||
auto mutableOrigin = origin;
|
||||
|
||||
// Draw each run separately.
|
||||
for (INT32 runIndex = 0; runIndex < gsl::narrow<INT32>(_runs.size()); ++runIndex)
|
||||
for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex)
|
||||
{
|
||||
// Get the run
|
||||
const Run& run = _runs.at(runIndex);
|
||||
|
||||
if (!WI_IsFlagSet(run.bidiLevel, 1))
|
||||
// Prepare the glyph run and description objects by converting our
|
||||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
|
||||
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
|
||||
glyphRun.isSideways = false;
|
||||
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
|
||||
glyphRunDescription.clusterMap = _glyphClusters.data();
|
||||
glyphRunDescription.localeName = _localeName.data();
|
||||
glyphRunDescription.string = _text.data();
|
||||
glyphRunDescription.stringLength = run.textLength;
|
||||
glyphRunDescription.textPosition = run.textStart;
|
||||
|
||||
// Calculate the origin for the next run based on the amount of space
|
||||
// that would be consumed. We are doing this calculation now, not after,
|
||||
// because if the text is RTL then we need to advance immediately, before the
|
||||
// write call since DirectX expects the origin to the RIGHT of the text for RTL.
|
||||
const auto postOriginX = std::accumulate(_glyphAdvances.begin() + run.glyphStart,
|
||||
_glyphAdvances.begin() + run.glyphStart + run.glyphCount,
|
||||
mutableOrigin.x);
|
||||
|
||||
// Check for RTL, if it is, apply space adjustment.
|
||||
if (WI_IsFlagSet(glyphRun.bidiLevel, 1))
|
||||
{
|
||||
RETURN_IF_FAILED(_DrawGlyphRun(clientDrawingContext, renderer, mutableOrigin, run));
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
// This is the RTL behavior. We will advance to the last contiguous RTL run, draw that,
|
||||
// and then keep on going backwards from there, and then move runIndex beyond.
|
||||
// Let's say we have runs abcdEFGh, where runs EFG are RTL.
|
||||
// Then we will draw them in the order abcdGFEh
|
||||
else
|
||||
{
|
||||
const INT32 originalRunIndex = runIndex;
|
||||
INT32 lastIndexRTL = runIndex;
|
||||
|
||||
// Step 1: Get to the last contiguous RTL run from here
|
||||
while (lastIndexRTL < gsl::narrow<INT32>(_runs.size()) - 1) // only could ever advance if there's something left
|
||||
{
|
||||
const Run& nextRun = _runs.at(gsl::narrow_cast<size_t>(lastIndexRTL + 1));
|
||||
if (WI_IsFlagSet(nextRun.bidiLevel, 1))
|
||||
{
|
||||
lastIndexRTL++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Try to draw it
|
||||
RETURN_IF_FAILED(renderer->DrawGlyphRun(clientDrawingContext,
|
||||
mutableOrigin.x,
|
||||
mutableOrigin.y,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
&glyphRun,
|
||||
&glyphRunDescription,
|
||||
run.drawingEffect.Get()));
|
||||
|
||||
// Go from the last to the first and draw
|
||||
for (runIndex = lastIndexRTL; runIndex >= originalRunIndex; runIndex--)
|
||||
{
|
||||
const Run& currentRun = _runs.at(runIndex);
|
||||
RETURN_IF_FAILED(_DrawGlyphRun(clientDrawingContext, renderer, mutableOrigin, currentRun));
|
||||
}
|
||||
runIndex = lastIndexRTL; // and the for loop will take the increment to the last one
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Draw the given run
|
||||
// - The origin is updated to be after the run.
|
||||
// Arguments:
|
||||
// - clientDrawingContext - Optional pointer to information that the renderer might need
|
||||
// while attempting to graphically place the text onto the screen
|
||||
// - renderer - The interface to be used for actually putting text onto the screen
|
||||
// - origin - pixel point of top left corner on final surface for drawing
|
||||
// - run - the run to be drawn
|
||||
[[nodiscard]] HRESULT CustomTextLayout::_DrawGlyphRun(_In_opt_ void* clientDrawingContext,
|
||||
gsl::not_null<IDWriteTextRenderer*> renderer,
|
||||
D2D_POINT_2F& mutableOrigin,
|
||||
const Run& run) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
// Prepare the glyph run and description objects by converting our
|
||||
// internal storage representation into something that matches DWrite's structures.
|
||||
DWRITE_GLYPH_RUN glyphRun;
|
||||
glyphRun.bidiLevel = run.bidiLevel;
|
||||
glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale;
|
||||
glyphRun.fontFace = run.fontFace.Get();
|
||||
glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart);
|
||||
glyphRun.glyphCount = run.glyphCount;
|
||||
glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart);
|
||||
glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart);
|
||||
glyphRun.isSideways = false;
|
||||
|
||||
DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription;
|
||||
glyphRunDescription.clusterMap = _glyphClusters.data();
|
||||
glyphRunDescription.localeName = _localeName.data();
|
||||
glyphRunDescription.string = _text.data();
|
||||
glyphRunDescription.stringLength = run.textLength;
|
||||
glyphRunDescription.textPosition = run.textStart;
|
||||
|
||||
// Calculate the origin for the next run based on the amount of space
|
||||
// that would be consumed. We are doing this calculation now, not after,
|
||||
// because if the text is RTL then we need to advance immediately, before the
|
||||
// write call since DirectX expects the origin to the RIGHT of the text for RTL.
|
||||
const auto postOriginX = std::accumulate(_glyphAdvances.begin() + run.glyphStart,
|
||||
_glyphAdvances.begin() + run.glyphStart + run.glyphCount,
|
||||
mutableOrigin.x);
|
||||
|
||||
// Check for RTL, if it is, apply space adjustment.
|
||||
if (WI_IsFlagSet(glyphRun.bidiLevel, 1))
|
||||
{
|
||||
// Either way, we should be at this point by the end of writing this sequence,
|
||||
// whether it was LTR or RTL.
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
|
||||
// Try to draw it
|
||||
RETURN_IF_FAILED(renderer->DrawGlyphRun(clientDrawingContext,
|
||||
mutableOrigin.x,
|
||||
mutableOrigin.y,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
&glyphRun,
|
||||
&glyphRunDescription,
|
||||
run.drawingEffect.Get()));
|
||||
|
||||
// Either way, we should be at this point by the end of writing this sequence,
|
||||
// whether it was LTR or RTL.
|
||||
mutableOrigin.x = postOriginX;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
|
||||
@@ -147,10 +147,6 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT _DrawGlyphRuns(_In_opt_ void* clientDrawingContext,
|
||||
IDWriteTextRenderer* renderer,
|
||||
const D2D_POINT_2F origin) noexcept;
|
||||
[[nodiscard]] HRESULT _DrawGlyphRun(_In_opt_ void* clientDrawingContext,
|
||||
gsl::not_null<IDWriteTextRenderer*> renderer,
|
||||
D2D_POINT_2F& mutableOrigin,
|
||||
const Run& run) noexcept;
|
||||
|
||||
[[nodiscard]] static constexpr UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept;
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ DxEngine::DxEngine() :
|
||||
_glyphCell{},
|
||||
_boxDrawingEffect{},
|
||||
_haveDeviceResources{ false },
|
||||
_swapChainHandle{ INVALID_HANDLE_VALUE },
|
||||
_swapChainDesc{ 0 },
|
||||
_swapChainFrameLatencyWaitableObject{ INVALID_HANDLE_VALUE },
|
||||
_recreateDeviceRequested{ false },
|
||||
@@ -455,8 +456,7 @@ try
|
||||
|
||||
switch (_chainMode)
|
||||
{
|
||||
case SwapChainMode::ForHwnd:
|
||||
{
|
||||
case SwapChainMode::ForHwnd: {
|
||||
// use the HWND's dimensions for the swap chain dimensions.
|
||||
RECT rect = { 0 };
|
||||
RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect));
|
||||
@@ -485,8 +485,11 @@ try
|
||||
|
||||
break;
|
||||
}
|
||||
case SwapChainMode::ForComposition:
|
||||
{
|
||||
case SwapChainMode::ForComposition: {
|
||||
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>();
|
||||
@@ -496,10 +499,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:
|
||||
@@ -825,14 +829,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)
|
||||
@@ -989,15 +993,13 @@ CATCH_RETURN();
|
||||
{
|
||||
switch (_chainMode)
|
||||
{
|
||||
case SwapChainMode::ForHwnd:
|
||||
{
|
||||
case SwapChainMode::ForHwnd: {
|
||||
RECT clientRect = { 0 };
|
||||
LOG_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &clientRect));
|
||||
|
||||
return til::rectangle{ clientRect }.size();
|
||||
}
|
||||
case SwapChainMode::ForComposition:
|
||||
{
|
||||
case SwapChainMode::ForComposition: {
|
||||
return _sizeTarget;
|
||||
}
|
||||
default:
|
||||
@@ -1393,7 +1395,7 @@ try
|
||||
// Use a transform by the size of one cell to convert cells-to-pixels
|
||||
// as we clear.
|
||||
_d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Scale(_glyphCell));
|
||||
for (const auto& rect : _invalidMap.runs())
|
||||
for (const auto rect : _invalidMap.runs())
|
||||
{
|
||||
// Use aliased.
|
||||
// For graphics reasons, it'll look better because it will ensure that
|
||||
@@ -2229,12 +2231,10 @@ CATCH_RETURN();
|
||||
|
||||
switch (_chainMode)
|
||||
{
|
||||
case SwapChainMode::ForHwnd:
|
||||
{
|
||||
case SwapChainMode::ForHwnd: {
|
||||
return D2D1::ColorF(rgb);
|
||||
}
|
||||
case SwapChainMode::ForComposition:
|
||||
{
|
||||
case SwapChainMode::ForComposition: {
|
||||
// Get the A value we've snuck into the highest byte
|
||||
const BYTE a = ((color >> 24) & 0xFF);
|
||||
const float aFloat = a / 255.0f;
|
||||
@@ -2252,12 +2252,12 @@ CATCH_RETURN();
|
||||
// - color - GDI Color
|
||||
// Return Value:
|
||||
// - N/A
|
||||
void DxEngine::SetSelectionBackground(const COLORREF color, const float alpha) noexcept
|
||||
void DxEngine::SetSelectionBackground(const COLORREF color) noexcept
|
||||
{
|
||||
_selectionBackground = D2D1::ColorF(GetRValue(color) / 255.0f,
|
||||
GetGValue(color) / 255.0f,
|
||||
GetBValue(color) / 255.0f,
|
||||
alpha);
|
||||
0.5f);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -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;
|
||||
@@ -114,7 +114,7 @@ namespace Microsoft::Console::Render
|
||||
|
||||
float GetScaling() const noexcept;
|
||||
|
||||
void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept;
|
||||
void SetSelectionBackground(const COLORREF color) noexcept;
|
||||
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept;
|
||||
void SetDefaultTextBackgroundOpacity(const float opacity) noexcept;
|
||||
|
||||
@@ -190,9 +190,11 @@ 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;
|
||||
|
||||
wil::unique_handle _swapChainHandle;
|
||||
DXGI_SWAP_CHAIN_DESC1 _swapChainDesc;
|
||||
::Microsoft::WRL::ComPtr<IDXGISwapChain1> _dxgiSwapChain;
|
||||
wil::unique_handle _swapChainFrameLatencyWaitableObject;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <typeinfo>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <dcomp.h>
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
@@ -1578,9 +1578,6 @@ bool AdaptDispatch::TabClear(const size_t clearType)
|
||||
case DispatchTypes::TabClearType::ClearAllColumns:
|
||||
success = _ClearAllTabStops();
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -1698,9 +1695,6 @@ bool AdaptDispatch::DesignateCodingSystem(const wchar_t codingSystem)
|
||||
_termOutput.EnableGrTranslation(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -66,9 +66,8 @@ bool TerminalOutput::Designate94Charset(size_t gsetNumber, const std::pair<wchar
|
||||
return _SetTranslationTable(gsetNumber, DecCyrillic);
|
||||
case L'5': // Russian NRCS
|
||||
return _SetTranslationTable(gsetNumber, RussianNrcs);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
case L'"':
|
||||
switch (charset.second)
|
||||
{
|
||||
@@ -78,9 +77,8 @@ bool TerminalOutput::Designate94Charset(size_t gsetNumber, const std::pair<wchar
|
||||
return _SetTranslationTable(gsetNumber, GreekNrcs);
|
||||
case L'4': // DEC Hebrew
|
||||
return _SetTranslationTable(gsetNumber, DecHebrew);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
case L'%':
|
||||
switch (charset.second)
|
||||
{
|
||||
@@ -94,9 +92,8 @@ bool TerminalOutput::Designate94Charset(size_t gsetNumber, const std::pair<wchar
|
||||
return _SetTranslationTable(gsetNumber, DecSupplemental);
|
||||
case L'6': // Portuguese NRCS
|
||||
return _SetTranslationTable(gsetNumber, PortugueseNrcs);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
|
||||
mouseInput->EnableDefaultTracking(true);
|
||||
|
||||
@@ -309,8 +309,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -328,8 +327,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -347,8 +345,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -374,7 +371,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
|
||||
mouseInput->SetUtf8ExtendedMode(true);
|
||||
|
||||
@@ -394,8 +391,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -413,8 +409,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -432,8 +427,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -459,7 +453,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
|
||||
mouseInput->SetSGRExtendedMode(true);
|
||||
|
||||
@@ -477,7 +471,7 @@ public:
|
||||
|
||||
// validate translation
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled,
|
||||
mouseInput->HandleMouse(Coord, uiButton, sModifierKeystate, sScrollDelta, {}),
|
||||
mouseInput->HandleMouse(Coord, uiButton, sModifierKeystate, sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -494,8 +488,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -513,8 +506,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -540,7 +532,7 @@ public:
|
||||
|
||||
bool fExpectedKeyHandled = false;
|
||||
s_pwszInputExpected = L"\x0";
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta, {}));
|
||||
VERIFY_ARE_EQUAL(fExpectedKeyHandled, mouseInput->HandleMouse({ 0, 0 }, uiButton, sModifierKeystate, sScrollDelta));
|
||||
|
||||
// Default Tracking, Default Encoding
|
||||
mouseInput->EnableDefaultTracking(true);
|
||||
@@ -558,8 +550,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -579,8 +570,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
|
||||
@@ -599,8 +589,7 @@ public:
|
||||
mouseInput->HandleMouse(Coord,
|
||||
uiButton,
|
||||
sModifierKeystate,
|
||||
sScrollDelta,
|
||||
{}),
|
||||
sScrollDelta),
|
||||
NoThrowString().Format(L"(x,y)=(%d,%d)", Coord.X, Coord.Y));
|
||||
}
|
||||
}
|
||||
@@ -617,31 +606,31 @@ public:
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling up");
|
||||
s_pwszInputExpected = L"\x1B[A";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling down");
|
||||
s_pwszInputExpected = L"\x1B[B";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA));
|
||||
|
||||
Log::Comment(L"Enable cursor keys mode");
|
||||
mouseInput->ChangeCursorKeysMode(true);
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling up");
|
||||
s_pwszInputExpected = L"\x1BOA";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
|
||||
Log::Comment(L"Test mouse wheel scrolling down");
|
||||
s_pwszInputExpected = L"\x1BOB";
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
|
||||
VERIFY_IS_TRUE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA));
|
||||
|
||||
Log::Comment(L"Confirm no effect when scroll mode is disabled");
|
||||
mouseInput->UseAlternateScreenBuffer();
|
||||
mouseInput->EnableAlternateScroll(false);
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
|
||||
Log::Comment(L"Confirm no effect when using the main buffer");
|
||||
mouseInput->UseMainScreenBuffer();
|
||||
mouseInput->EnableAlternateScroll(true);
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA, {}));
|
||||
VERIFY_IS_FALSE(mouseInput->HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, WHEEL_DELTA));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,6 +13,10 @@ using namespace Microsoft::Console::VirtualTerminal;
|
||||
#endif
|
||||
static const int s_MaxDefaultCoordinate = 94;
|
||||
|
||||
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
|
||||
// "If the high-order bit is 1, the key is down; otherwise, it is up."
|
||||
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
|
||||
|
||||
// Alternate scroll sequences
|
||||
static constexpr std::wstring_view CursorUpSequence{ L"\x1b[A" };
|
||||
static constexpr std::wstring_view CursorDownSequence{ L"\x1b[B" };
|
||||
@@ -29,6 +33,7 @@ static constexpr std::wstring_view ApplicationDownSequence{ L"\x1bOB" };
|
||||
// - true iff button is a button message to translate
|
||||
static constexpr bool _isButtonMsg(const unsigned int button) noexcept
|
||||
{
|
||||
bool isButton = false;
|
||||
switch (button)
|
||||
{
|
||||
case WM_LBUTTONDBLCLK:
|
||||
@@ -42,10 +47,10 @@ static constexpr bool _isButtonMsg(const unsigned int button) noexcept
|
||||
case WM_MBUTTONDBLCLK:
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
isButton = true;
|
||||
break;
|
||||
}
|
||||
return isButton;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -79,6 +84,7 @@ static constexpr bool _isWheelMsg(const unsigned int buttonCode) noexcept
|
||||
// - true iff button is a button down event
|
||||
static constexpr bool _isButtonDown(const unsigned int button) noexcept
|
||||
{
|
||||
bool isButtonDown = false;
|
||||
switch (button)
|
||||
{
|
||||
case WM_LBUTTONDBLCLK:
|
||||
@@ -89,32 +95,32 @@ static constexpr bool _isButtonDown(const unsigned int button) noexcept
|
||||
case WM_MBUTTONDBLCLK:
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
isButtonDown = true;
|
||||
break;
|
||||
}
|
||||
return isButtonDown;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Retrieves which mouse button is currently pressed. This is needed because
|
||||
// MOUSEMOVE events do not also tell us if any mouse buttons are pressed during the move.
|
||||
// Parameters:
|
||||
// - state - the current state of which mouse buttons are pressed
|
||||
// <none>
|
||||
// Return value:
|
||||
// - a button corresponding to any pressed mouse buttons, else WM_LBUTTONUP if none are pressed.
|
||||
constexpr unsigned int TerminalInput::s_GetPressedButton(const MouseButtonState state) noexcept
|
||||
unsigned int TerminalInput::s_GetPressedButton() noexcept
|
||||
{
|
||||
// Will be treated as a release, or no button pressed.
|
||||
unsigned int button = WM_LBUTTONUP;
|
||||
if (state.isLeftButtonDown)
|
||||
// TODO GH#4869: Have the caller pass the mouse button state into HandleMouse
|
||||
unsigned int button = WM_LBUTTONUP; // Will be treated as a release, or no button pressed.
|
||||
if (WI_IsFlagSet(GetKeyState(VK_LBUTTON), KeyPressed))
|
||||
{
|
||||
button = WM_LBUTTONDOWN;
|
||||
}
|
||||
else if (state.isMiddleButtonDown)
|
||||
else if (WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed))
|
||||
{
|
||||
button = WM_MBUTTONDOWN;
|
||||
}
|
||||
else if (state.isRightButtonDown)
|
||||
else if (WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed))
|
||||
{
|
||||
button = WM_RBUTTONDOWN;
|
||||
}
|
||||
@@ -178,10 +184,6 @@ static constexpr int _windowsButtonToXEncoding(const unsigned int button,
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
xvalue = delta > 0 ? 0x40 : 0x41;
|
||||
break;
|
||||
default:
|
||||
xvalue = 0;
|
||||
break;
|
||||
}
|
||||
if (isHover)
|
||||
{
|
||||
@@ -237,10 +239,6 @@ static constexpr int _windowsButtonToSGREncoding(const unsigned int button,
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
xvalue = delta > 0 ? 0x40 : 0x41;
|
||||
break;
|
||||
default:
|
||||
xvalue = 0;
|
||||
break;
|
||||
}
|
||||
if (isHover)
|
||||
{
|
||||
@@ -300,14 +298,12 @@ bool TerminalInput::IsTrackingMouseInput() const noexcept
|
||||
// - button - the message to decode.
|
||||
// - modifierKeyState - the modifier keys pressed with this button
|
||||
// - delta - the amount that the scroll wheel changed (should be 0 unless button is a WM_MOUSE*WHEEL)
|
||||
// - state - the state of the mouse buttons at this moment
|
||||
// Return value:
|
||||
// - true if the event was handled and we should stop event propagation to the default window handler.
|
||||
bool TerminalInput::HandleMouse(const COORD position,
|
||||
const unsigned int button,
|
||||
const short modifierKeyState,
|
||||
const short delta,
|
||||
const MouseButtonState state)
|
||||
const short delta)
|
||||
{
|
||||
if (Utils::Sign(delta) != Utils::Sign(_mouseInputState.accumulatedDelta))
|
||||
{
|
||||
@@ -358,7 +354,7 @@ bool TerminalInput::HandleMouse(const COORD position,
|
||||
// _GetPressedButton will return the first pressed mouse button.
|
||||
// If it returns WM_LBUTTONUP, then we can assume that the mouse
|
||||
// moved without a button being pressed.
|
||||
const unsigned int realButton = isHover ? s_GetPressedButton(state) : button;
|
||||
const unsigned int realButton = isHover ? s_GetPressedButton() : button;
|
||||
|
||||
// In default mode, only button presses/releases are sent
|
||||
// In ButtonEvent mode, changing coord hovers WITH A BUTTON PRESSED
|
||||
|
||||
@@ -378,7 +378,7 @@ static bool _searchWithModifier(const KeyEvent& keyEvent, InputSender sender)
|
||||
{ s_modifierKeyMapping.data(), s_modifierKeyMapping.size() });
|
||||
if (match)
|
||||
{
|
||||
const auto& v = match.value();
|
||||
const auto v = match.value();
|
||||
if (!v.sequence.empty())
|
||||
{
|
||||
std::wstring modified{ v.sequence }; // Make a copy so we can modify it.
|
||||
|
||||
@@ -43,19 +43,10 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
|
||||
#pragma region MouseInput
|
||||
// These methods are defined in mouseInput.cpp
|
||||
|
||||
struct MouseButtonState
|
||||
{
|
||||
bool isLeftButtonDown;
|
||||
bool isMiddleButtonDown;
|
||||
bool isRightButtonDown;
|
||||
};
|
||||
|
||||
bool HandleMouse(const COORD position,
|
||||
const unsigned int button,
|
||||
const short modifierKeyState,
|
||||
const short delta,
|
||||
const MouseButtonState state);
|
||||
const short delta);
|
||||
|
||||
bool IsTrackingMouseInput() const noexcept;
|
||||
#pragma endregion
|
||||
@@ -145,7 +136,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
bool _ShouldSendAlternateScroll(const unsigned int button, const short delta) const noexcept;
|
||||
bool _SendAlternateScroll(const short delta) const noexcept;
|
||||
|
||||
static constexpr unsigned int s_GetPressedButton(const MouseButtonState state) noexcept;
|
||||
static unsigned int s_GetPressedButton() noexcept;
|
||||
#pragma endregion
|
||||
};
|
||||
}
|
||||
|
||||
@@ -408,7 +408,6 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
|
||||
// even if we failed to parse a portion of this sequence.
|
||||
success = _UpdateSGRMouseButtonState(wch, parameters, buttonState, eventFlags) && success;
|
||||
success = success && _WriteMouseEvent(col, row, buttonState, modifierState, eventFlags);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
success = false;
|
||||
@@ -433,7 +432,6 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
|
||||
success = _GetXYPosition(parameters, row, col);
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case CsiActionCodes::ArrowUp:
|
||||
case CsiActionCodes::ArrowDown:
|
||||
case CsiActionCodes::ArrowRight:
|
||||
@@ -479,7 +477,7 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
|
||||
_lookingForDSR = false;
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
__fallthrough;
|
||||
case CsiActionCodes::Generic:
|
||||
case CsiActionCodes::ArrowUp:
|
||||
case CsiActionCodes::ArrowDown:
|
||||
@@ -1366,8 +1364,6 @@ bool InputStateMachineEngine::_GenerateWin32Key(const gsl::span<const size_t> pa
|
||||
case 1:
|
||||
key.SetVirtualKeyCode(::base::saturated_cast<WORD>(til::at(parameters, 0)));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -435,9 +435,6 @@ bool OutputStateMachineEngine::_IntermediateScsDispatch(const wchar_t wch,
|
||||
success = _dispatch->Designate96Charset(3, charset);
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG3);
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return success;
|
||||
@@ -809,15 +806,9 @@ bool OutputStateMachineEngine::_IntermediateGreaterThanOrEqualDispatch(const wch
|
||||
success = _dispatch->TertiaryDeviceAttributes();
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::DA3);
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return success;
|
||||
@@ -1339,9 +1330,6 @@ bool OutputStateMachineEngine::_GetDeviceStatusOperation(const gsl::span<const s
|
||||
statusType = DispatchTypes::AnsiStatusType::CPR_CursorPositionReport;
|
||||
success = true;
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,8 +128,6 @@ bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) noexcept
|
||||
dst.push_back(tmp);
|
||||
state = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
iter++;
|
||||
@@ -156,7 +154,6 @@ bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) noexcept
|
||||
return false;
|
||||
}
|
||||
iter++; // Skip the padding character and fallthrough to "single trailing padding character" case.
|
||||
[[fallthrough]];
|
||||
case 3:
|
||||
while (iter < src.cend())
|
||||
{
|
||||
@@ -166,9 +163,6 @@ bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) noexcept
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (state != 0) // When no padding, we must be in state 0.
|
||||
|
||||
288
src/tools/ScratchIsland/AppHost.cpp
Normal file
288
src/tools/ScratchIsland/AppHost.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
// 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()
|
||||
{
|
||||
{
|
||||
auto nativePanel2 = _swp0.as<ISwapChainPanelNative2>();
|
||||
nativePanel2;
|
||||
}
|
||||
|
||||
co_await winrt::resume_background();
|
||||
{
|
||||
auto nativePanel2 = _swp0.as<ISwapChainPanelNative2>();
|
||||
nativePanel2;
|
||||
}
|
||||
|
||||
auto host0 = _manager.CreateHost();
|
||||
host0.Attach(_swp0);
|
||||
|
||||
co_await winrt::resume_foreground(_swp0.Dispatcher());
|
||||
_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.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);
|
||||
}
|
||||
39
src/tools/ScratchIsland/AppHost.h
Normal file
39
src/tools/ScratchIsland/AppHost.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "IslandWindow.h"
|
||||
#include <winrt/ScratchWinRTServer.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()
|
||||
{
|
||||
}
|
||||
76
src/tools/ScratchIsland/HostManager.cpp
Normal file
76
src/tools/ScratchIsland/HostManager.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#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<ScratchWinRTServer::HostClass>();
|
||||
}
|
||||
|
||||
Collections::IObservableVector<ScratchWinRTServer::HostClass> HostManager::Hosts()
|
||||
{
|
||||
return _hosts;
|
||||
}
|
||||
|
||||
static void _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
|
||||
);
|
||||
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(1000);
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
ScratchWinRTServer::HostClass HostManager::CreateHost()
|
||||
{
|
||||
// 1. Generate a GUID.
|
||||
winrt::guid g{ Utils::CreateGuid() };
|
||||
|
||||
// 2. Spawn a Server.exe, with the guid on the commandline
|
||||
_createHostClassProcess(g);
|
||||
|
||||
auto host = create_instance<winrt::ScratchWinRTServer::HostClass>(g, CLSCTX_LOCAL_SERVER);
|
||||
THROW_IF_NULL_ALLOC(host);
|
||||
|
||||
_hosts.Append(host);
|
||||
|
||||
return host;
|
||||
}
|
||||
}
|
||||
65
src/tools/ScratchIsland/HostManager.h
Normal file
65
src/tools/ScratchIsland/HostManager.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "HostManager.g.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<ScratchWinRTServer::HostClass> Hosts();
|
||||
ScratchWinRTServer::HostClass CreateHost();
|
||||
|
||||
private:
|
||||
Windows::Foundation::Collections::IObservableVector<ScratchWinRTServer::HostClass> _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 @@
|
||||
|
||||
|
||||
namespace ScratchIsland
|
||||
{
|
||||
[default_interface] runtimeclass HostManager // : IScratchInterface
|
||||
{
|
||||
HostManager();
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<ScratchWinRTServer.HostClass> Hosts { get; };
|
||||
ScratchWinRTServer.HostClass 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
|
||||
|
||||
172
src/tools/ScratchIsland/ScratchIsland.vcxproj
Normal file
172
src/tools/ScratchIsland/ScratchIsland.vcxproj
Normal file
@@ -0,0 +1,172 @@
|
||||
<?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" />
|
||||
</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" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="HostManager.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"
|
||||
75
src/tools/ScratchIsland/pch.h
Normal file
75
src/tools/ScratchIsland/pch.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*++
|
||||
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 "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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user