Compare commits

..

32 Commits

Author SHA1 Message Date
Dustin L. Howett
9021900904 Migrate spelling-0.0.21 changes from main 2020-07-21 08:24:04 -05:00
Dustin L. Howett
f6b051945f Migrate spelling-0.0.19 changes from main 2020-07-21 08:24:04 -05:00
Mike Griese
52b05e065e None of these suggestions worked for me, I'm sure I'm off my rocker 2020-07-21 08:24:04 -05:00
Mike Griese
81a80257c9 It's monday, and I tried TODO this, but the call to
```c++
    auto nativePanel2 = panel.try_as<ISwapChainPanelNative2>();
```

keeps failing in the server, but succeeding in the window process. This doesn't make any sense to me.
2020-07-21 07:52:49 -05:00
Mike Griese
1ea9fc26c8 Get the island spawning a server 2020-07-17 14:16:06 -05:00
Mike Griese
e1d15105d7 add some swapchainpanels 2020-07-17 13:28:47 -05:00
Mike Griese
d321ec084c Hey, lets make sure to get some XAML in there 2020-07-17 13:17:41 -05:00
Mike Griese
f33c69d8b4 Add a scratch island project for testing 2020-07-17 13:01:07 -05:00
Michael Niksa
30b8335479 Use DComp surface handle for Swap Chain management. 2020-07-17 08:42:47 -05:00
Mike Griese
93b79fb23c Merge remote-tracking branch 'origin/master' into dev/migrie/oop-scratch-2 2020-07-17 08:42:19 -05:00
Dustin L. Howett
efb1fddb99 Convert most of our JSON deserializers to use type-based conversion (#6590)
This pull request converts the following JSON deserializers to use the
new JSON deserializer pattern:

* Profile
* Command
* ColorScheme
* Action/Args
* GlobalSettings
* CascadiaSettingsSerialization

This is the completion of a long-term JSON refactoring that makes our
parser and deserializer more type-safe and robust. We're finally able to
get rid of all our manual enum conversion code and unify JSON conversion
around _types_ instead of around _keys_.

I've introduced another file filled with template specializations,
TerminalSettingsSerializationHelpers.h, which comprises a single unit
that holds all of the JSON deserializers (and eventually serializers)
for every type that comes from TerminalApp or TerminalSettings.

I've also moved some types out of Profile and GlobalAppSettings into a
new SettingsTypes.h to improve settings locality.

This does to some extent constitute a breaking change for already-broken
settings. Instead of parsing "successfully" (where invalid values are
null or 0 or unknown or unset), deserialization will now fail when
there's a type mismatch. Because of that, some tests had to be removed.

While I was on a refactoring spree, I removed a number of helpless
helpers, like GetWstringFromJson (which converted a u8 string to an
hstring to make a wstring out of its data pointer :|) and
_ConvertJsonToBool.

In the future, we can make the error types more robust and give them
position and type information such that a conformant application can
display rich error information ("line 3 column 3, I expected a string,
you gave me an integer").

Closes #2550.
2020-07-17 01:31:09 +00:00
Dustin L. Howett
7bc5de613c version: bump to 1.3 on master 2020-07-16 18:18:33 -07:00
Mike Griese
7b8806b1fe These notes about ISwapChainPanelNative2 are VERY IMPORTANT 2020-07-16 09:53:20 -05:00
Mike Griese
8c5041b2ae dead ends, all dead ends 2020-07-16 07:42:43 -05:00
Mike Griese
a511ab0bc7 Revert "I was thinking of making the HostClass a COM class by default, but this is horrible. Then, the client wouldn't be able to use it to get the winrt type. We want to use the winrt type in Lists in the client. So this won't work"
This reverts commit 501c47adb2.
2020-07-15 16:28:35 -05:00
Mike Griese
501c47adb2 I was thinking of making the HostClass a COM class by default, but this is horrible. Then, the client wouldn't be able to use it to get the winrt type. We want to use the winrt type in Lists in the client. So this won't work 2020-07-15 16:28:27 -05:00
Mike Griese
72121721f3 So this doesn't work because HostClass is fundamentally a WinRT type, not a CoClass. What if it _wasn't_ a runtimeclass? 2020-07-15 16:08:44 -05:00
Mike Griese
3e80b6a466 The server's lifetime is based on the lifetime of the first client it creates now. 2020-07-15 12:52:29 -05:00
Mike Griese
a80e1d3f73 this works neatly for having a HostManager for all the hosts, but the server eats the newline as well as the client, so I'm removing that bit of it 2020-07-15 12:04:20 -05:00
Mike Griese
f45df9a717 This ALSO works 2020-07-15 09:43:29 -05:00
Mike Griese
b37f69826e This experiment has been a wild success 2020-07-15 09:34:55 -05:00
Mike Griese
b2e76c1812 register a single instance of a class over the lifetime of the process?? 2020-07-14 17:01:48 -05:00
Mike Griese
2179e16e3d Mostly refactor these scratch projects to use our shared helpers 2020-07-14 16:53:17 -05:00
Mike Griese
fafc0e1316 Merge remote-tracking branch 'origin/master' into dev/migrie/oop-scratch-2 2020-07-14 14:53:47 -05:00
Mike Griese
ca6dff9f20 revert the XAML bits from the server 2020-07-14 14:18:48 -05:00
Mike Griese
186ff8f638 If you do this, the server will crash when it tries to instantiate the XAML button 2020-07-14 14:16:47 -05:00
Mike Griese
37810aac71 merge branch 'origin/master' 2020-07-14 10:29:54 -05:00
Mike Griese
33bc88b225 oh cool, I can make a ScratchClass 2020-07-10 15:38:04 -05:00
Mike Griese
d56137876e curious, I can get the IClosable, but not the ScratchInterface? 2020-07-10 15:23:58 -05:00
Mike Griese
f928d41917 this is all dead end 2020-07-10 12:02:29 -05:00
Mike Griese
e4cc3104ab Merge remote-tracking branch 'origin/master' into dev/migrie/oop-scratch-2 2020-07-10 10:12:36 -05:00
Mike Griese
0c10b4b265 Well, this actually kinda works. Probably horribly insecure 2020-06-16 17:02:42 -05:00
130 changed files with 5004 additions and 2622 deletions

View File

@@ -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

View File

@@ -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'

View File

@@ -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>

View File

@@ -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` |

View File

@@ -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"
],

Binary file not shown.

Binary file not shown.

View File

@@ -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

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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);

View File

@@ -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());
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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"";
}
}

View File

@@ -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, {} };
}
};

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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:

View File

@@ -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));
}
}

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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:

View File

@@ -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;
};

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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 },

View File

@@ -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;
}

View File

@@ -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;

View 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;
};
};

View File

@@ -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();

View 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 },
};
};

View File

@@ -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();
}

View File

@@ -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

View File

@@ -72,7 +72,7 @@
"brightWhite": "#FFFFFF"
}
],
"actions":
"keybindings":
[
// Application-level Keys
{ "command": "closeWindow", "keys": "alt+f4" },

View File

@@ -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" },

View File

@@ -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" />

View File

@@ -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>

View File

@@ -28,8 +28,6 @@ BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/)
TraceLoggingUnregister(g_hTerminalConnectionProvider);
}
break;
default:
break;
}
return TRUE;

View File

@@ -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);
}
}

View File

@@ -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:

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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:

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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));
}
}

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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))

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -3,7 +3,7 @@
#include "precomp.h"
#include "../TerminalApp/JsonUtilsNew.h"
#include "../TerminalApp/JsonUtils.h"
using namespace Microsoft::Console;
using namespace WEX::Logging;

View File

@@ -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" />

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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:

View File

@@ -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;

View File

@@ -21,6 +21,8 @@
#include <typeinfo>
#include <stdexcept>
#include <dcomp.h>
#include <dxgi.h>
#include <dxgi1_2.h>
#include <dxgi1_3.h>

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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));
}
};

View File

@@ -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

View File

@@ -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.

View File

@@ -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
};
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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.

View 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);
}

View 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;
};

View 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()
{
}

View 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;
}
}

View 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,
&registrationHostManager));
printf("registrationHostManager:%d\n", registrationHostManager);
}
};

View File

@@ -0,0 +1,12 @@
namespace ScratchIsland
{
[default_interface] runtimeclass HostManager // : IScratchInterface
{
HostManager();
Windows.Foundation.Collections.IObservableVector<ScratchWinRTServer.HostClass> Hosts { get; };
ScratchWinRTServer.HostClass CreateHost();
};
}

View 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<>);

View 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;
};

View File

@@ -0,0 +1 @@

View 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>

View 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

View 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>

View 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;
}

View 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>

View File

@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"

View 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"

View 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