diff --git a/README.md b/README.md
index 512a14846ef..4f994bd5f67 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,15 @@ Related repositories include:
> 👉 Note: Windows Terminal requires Windows 10 1903 (build 18362) or later
-### Manually installing builds from this repository
+### Microsoft Store [Recommended]
+
+Install the [Windows Terminal from the Microsoft Store][store-install-link]. This allows you to always be on the latest version when we release new builds with automatic upgrades.
+
+This is our preferred method.
+
+### Other install methods
+
+#### Via GitHub
For users who are unable to install Terminal from the Microsoft Store, Terminal builds can be manually downloaded from this repository's [Releases page](https://github.com/microsoft/terminal/releases).
@@ -26,7 +34,7 @@ For users who are unable to install Terminal from the Microsoft Store, Terminal
> * Be sure to install the [Desktop Bridge VC++ v14 Redistributable Package](https://www.microsoft.com/en-us/download/details.aspx?id=53175) otherwise Terminal may not install and/or run and may crash at startup
> * Terminal will not auto-update when new builds are released so you will need to regularly install the latest Terminal release to receive all the latest fixes and improvements!
-### Install via Chocolatey (unofficial)
+#### Via Chocolatey (unofficial)
[Chocolatey](https://chocolatey.org) users can download and install the latest Terminal release by installing the `microsoft-windows-terminal` package:
@@ -131,7 +139,7 @@ All project documentation is located in the `./doc` folder. If you would like to
We are excited to work alongside you, our amazing community, to build and enhance Windows Terminal\!
-***BEFORE you start work on a feature/fix***, please read & follow our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/contributing.md) to help avoid any wasted or duplicate effort.
+***BEFORE you start work on a feature/fix***, please read & follow our [Contributor's Guide](https://github.com/microsoft/terminal/blob/master/CONTRIBUTING.md) to help avoid any wasted or duplicate effort.
## Communicating with the Team
@@ -219,3 +227,4 @@ For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [open
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
[conduct-email]: mailto:opencode@microsoft.com
+[store-install-link]: https://aka.ms/windowsterminal
diff --git a/custom.props b/custom.props
index c1e9ebda542..55b6bee4b84 100644
--- a/custom.props
+++ b/custom.props
@@ -5,7 +5,7 @@
true
2020
0
- 9
+ 10
Windows Terminal
diff --git a/res/terminal/images-Dev/LargeTile.scale-100.png b/res/terminal/images-Dev/LargeTile.scale-100.png
index cdbcbc705a6..4e6e8a25d05 100644
Binary files a/res/terminal/images-Dev/LargeTile.scale-100.png and b/res/terminal/images-Dev/LargeTile.scale-100.png differ
diff --git a/res/terminal/images-Dev/LargeTile.scale-125.png b/res/terminal/images-Dev/LargeTile.scale-125.png
index 00c77f7b337..96e62159f4a 100644
Binary files a/res/terminal/images-Dev/LargeTile.scale-125.png and b/res/terminal/images-Dev/LargeTile.scale-125.png differ
diff --git a/res/terminal/images-Dev/LargeTile.scale-150.png b/res/terminal/images-Dev/LargeTile.scale-150.png
index cf3f071853e..e37e7cbdfcf 100644
Binary files a/res/terminal/images-Dev/LargeTile.scale-150.png and b/res/terminal/images-Dev/LargeTile.scale-150.png differ
diff --git a/res/terminal/images-Dev/LargeTile.scale-200.png b/res/terminal/images-Dev/LargeTile.scale-200.png
index 1e01e2e349a..1da99f0dab2 100644
Binary files a/res/terminal/images-Dev/LargeTile.scale-200.png and b/res/terminal/images-Dev/LargeTile.scale-200.png differ
diff --git a/res/terminal/images-Dev/LargeTile.scale-400.png b/res/terminal/images-Dev/LargeTile.scale-400.png
index 684123cb251..7745809c91d 100644
Binary files a/res/terminal/images-Dev/LargeTile.scale-400.png and b/res/terminal/images-Dev/LargeTile.scale-400.png differ
diff --git a/res/terminal/images-Dev/LockScreenLogo.scale-100.png b/res/terminal/images-Dev/LockScreenLogo.scale-100.png
index 36f4fb56fb4..1b9c0cb0b8a 100644
Binary files a/res/terminal/images-Dev/LockScreenLogo.scale-100.png and b/res/terminal/images-Dev/LockScreenLogo.scale-100.png differ
diff --git a/res/terminal/images-Dev/LockScreenLogo.scale-125.png b/res/terminal/images-Dev/LockScreenLogo.scale-125.png
index 35164b236ab..fffa61a58cc 100644
Binary files a/res/terminal/images-Dev/LockScreenLogo.scale-125.png and b/res/terminal/images-Dev/LockScreenLogo.scale-125.png differ
diff --git a/res/terminal/images-Dev/LockScreenLogo.scale-150.png b/res/terminal/images-Dev/LockScreenLogo.scale-150.png
index fb176d1c2bc..7ff9fee6342 100644
Binary files a/res/terminal/images-Dev/LockScreenLogo.scale-150.png and b/res/terminal/images-Dev/LockScreenLogo.scale-150.png differ
diff --git a/res/terminal/images-Dev/LockScreenLogo.scale-200.png b/res/terminal/images-Dev/LockScreenLogo.scale-200.png
index cb31d4302b0..6da33a0b104 100644
Binary files a/res/terminal/images-Dev/LockScreenLogo.scale-200.png and b/res/terminal/images-Dev/LockScreenLogo.scale-200.png differ
diff --git a/res/terminal/images-Dev/LockScreenLogo.scale-400.png b/res/terminal/images-Dev/LockScreenLogo.scale-400.png
index 61463a6636e..c946c4178eb 100644
Binary files a/res/terminal/images-Dev/LockScreenLogo.scale-400.png and b/res/terminal/images-Dev/LockScreenLogo.scale-400.png differ
diff --git a/res/terminal/images-Dev/SmallTile.scale-100.png b/res/terminal/images-Dev/SmallTile.scale-100.png
index 463c44f611d..a1ac45dd0c5 100644
Binary files a/res/terminal/images-Dev/SmallTile.scale-100.png and b/res/terminal/images-Dev/SmallTile.scale-100.png differ
diff --git a/res/terminal/images-Dev/SmallTile.scale-125.png b/res/terminal/images-Dev/SmallTile.scale-125.png
index d81c4579c1a..4f5f8ce48ef 100644
Binary files a/res/terminal/images-Dev/SmallTile.scale-125.png and b/res/terminal/images-Dev/SmallTile.scale-125.png differ
diff --git a/res/terminal/images-Dev/SmallTile.scale-150.png b/res/terminal/images-Dev/SmallTile.scale-150.png
index 9a83a8d6dba..3496403219a 100644
Binary files a/res/terminal/images-Dev/SmallTile.scale-150.png and b/res/terminal/images-Dev/SmallTile.scale-150.png differ
diff --git a/res/terminal/images-Dev/SmallTile.scale-200.png b/res/terminal/images-Dev/SmallTile.scale-200.png
index 436a551b556..1e3e3051853 100644
Binary files a/res/terminal/images-Dev/SmallTile.scale-200.png and b/res/terminal/images-Dev/SmallTile.scale-200.png differ
diff --git a/res/terminal/images-Dev/SmallTile.scale-400.png b/res/terminal/images-Dev/SmallTile.scale-400.png
index 8f41f2b44ae..9902fe1cb09 100644
Binary files a/res/terminal/images-Dev/SmallTile.scale-400.png and b/res/terminal/images-Dev/SmallTile.scale-400.png differ
diff --git a/res/terminal/images-Dev/SplashScreen.scale-100.png b/res/terminal/images-Dev/SplashScreen.scale-100.png
index 23ca268d6df..0367bd61bda 100644
Binary files a/res/terminal/images-Dev/SplashScreen.scale-100.png and b/res/terminal/images-Dev/SplashScreen.scale-100.png differ
diff --git a/res/terminal/images-Dev/SplashScreen.scale-125.png b/res/terminal/images-Dev/SplashScreen.scale-125.png
index b9ef4209254..9f83e0bf13f 100644
Binary files a/res/terminal/images-Dev/SplashScreen.scale-125.png and b/res/terminal/images-Dev/SplashScreen.scale-125.png differ
diff --git a/res/terminal/images-Dev/SplashScreen.scale-150.png b/res/terminal/images-Dev/SplashScreen.scale-150.png
index 327a8abe314..b33f97c61c4 100644
Binary files a/res/terminal/images-Dev/SplashScreen.scale-150.png and b/res/terminal/images-Dev/SplashScreen.scale-150.png differ
diff --git a/res/terminal/images-Dev/SplashScreen.scale-200.png b/res/terminal/images-Dev/SplashScreen.scale-200.png
index 8f19b92913d..688cf4997d2 100644
Binary files a/res/terminal/images-Dev/SplashScreen.scale-200.png and b/res/terminal/images-Dev/SplashScreen.scale-200.png differ
diff --git a/res/terminal/images-Dev/SplashScreen.scale-400.png b/res/terminal/images-Dev/SplashScreen.scale-400.png
index 633baa5e1ce..a7d18e657d9 100644
Binary files a/res/terminal/images-Dev/SplashScreen.scale-400.png and b/res/terminal/images-Dev/SplashScreen.scale-400.png differ
diff --git a/res/terminal/images-Dev/Square150x150Logo.scale-100.png b/res/terminal/images-Dev/Square150x150Logo.scale-100.png
index 8558b35417b..a46950bf8ca 100644
Binary files a/res/terminal/images-Dev/Square150x150Logo.scale-100.png and b/res/terminal/images-Dev/Square150x150Logo.scale-100.png differ
diff --git a/res/terminal/images-Dev/Square150x150Logo.scale-125.png b/res/terminal/images-Dev/Square150x150Logo.scale-125.png
index 1b4d92b3005..172a4110414 100644
Binary files a/res/terminal/images-Dev/Square150x150Logo.scale-125.png and b/res/terminal/images-Dev/Square150x150Logo.scale-125.png differ
diff --git a/res/terminal/images-Dev/Square150x150Logo.scale-150.png b/res/terminal/images-Dev/Square150x150Logo.scale-150.png
index cc70c170638..276db24f40f 100644
Binary files a/res/terminal/images-Dev/Square150x150Logo.scale-150.png and b/res/terminal/images-Dev/Square150x150Logo.scale-150.png differ
diff --git a/res/terminal/images-Dev/Square150x150Logo.scale-200.png b/res/terminal/images-Dev/Square150x150Logo.scale-200.png
index 4d2cf4a17c5..82112ad0a87 100644
Binary files a/res/terminal/images-Dev/Square150x150Logo.scale-200.png and b/res/terminal/images-Dev/Square150x150Logo.scale-200.png differ
diff --git a/res/terminal/images-Dev/Square150x150Logo.scale-400.png b/res/terminal/images-Dev/Square150x150Logo.scale-400.png
index f7d426af6e1..0eb1ba9dba0 100644
Binary files a/res/terminal/images-Dev/Square150x150Logo.scale-400.png and b/res/terminal/images-Dev/Square150x150Logo.scale-400.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.scale-100.png b/res/terminal/images-Dev/Square44x44Logo.scale-100.png
index 8af60bbd53c..39433f821d5 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.scale-100.png and b/res/terminal/images-Dev/Square44x44Logo.scale-100.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.scale-125.png b/res/terminal/images-Dev/Square44x44Logo.scale-125.png
index aca4adf7cf9..6c39d362289 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.scale-125.png and b/res/terminal/images-Dev/Square44x44Logo.scale-125.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.scale-150.png b/res/terminal/images-Dev/Square44x44Logo.scale-150.png
index fd3b8f8c541..670e5799090 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.scale-150.png and b/res/terminal/images-Dev/Square44x44Logo.scale-150.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.scale-200.png b/res/terminal/images-Dev/Square44x44Logo.scale-200.png
index 7b64b5349c2..5fadd3fcaab 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.scale-200.png and b/res/terminal/images-Dev/Square44x44Logo.scale-200.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.scale-400.png b/res/terminal/images-Dev/Square44x44Logo.scale-400.png
index 8d94e1f61e3..1ec34428864 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.scale-400.png and b/res/terminal/images-Dev/Square44x44Logo.scale-400.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-16.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-16.png
index b501a7282ed..2cf1b295255 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-16.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-16.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-16_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-16_altform-unplated.png
index b501a7282ed..2cf1b295255 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-16_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-16_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-20.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-20.png
index 7dc3ed6a449..288f3dcc3fc 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-20.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-20.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-20_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-20_altform-unplated.png
index 7dc3ed6a449..288f3dcc3fc 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-20_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-20_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-24.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-24.png
index 36f4fb56fb4..1b9c0cb0b8a 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-24.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-24.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-24_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-24_altform-unplated.png
index 36f4fb56fb4..1b9c0cb0b8a 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-24_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-256.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-256.png
index 7b728384b2c..af2414b4b81 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-256.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-256.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-256_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-256_altform-unplated.png
index 7b728384b2c..af2414b4b81 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-256_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-256_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-30.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-30.png
index 35164b236ab..fffa61a58cc 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-30.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-30.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-30_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-30_altform-unplated.png
index 35164b236ab..fffa61a58cc 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-30_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-30_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-32.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-32.png
index 5b3c3b30756..188e2fac5fa 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-32.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-32.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-32_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-32_altform-unplated.png
index 5b3c3b30756..188e2fac5fa 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-32_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-32_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-36.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-36.png
index fb176d1c2bc..7ff9fee6342 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-36.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-36.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-36_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-36_altform-unplated.png
index fb176d1c2bc..7ff9fee6342 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-36_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-36_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-40.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-40.png
index f89148b6353..491db14a82e 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-40.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-40.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-40_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-40_altform-unplated.png
index f89148b6353..491db14a82e 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-40_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-40_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-48.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-48.png
index cb31d4302b0..6da33a0b104 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-48.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-48.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-48_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-48_altform-unplated.png
index cb31d4302b0..6da33a0b104 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-48_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-48_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-60.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-60.png
index d79c03363a6..d65ab32d355 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-60.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-60.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-60_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-60_altform-unplated.png
index d79c03363a6..d65ab32d355 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-60_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-60_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-64.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-64.png
index 9fad6857cf8..b12c3852d57 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-64.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-64.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-64_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-64_altform-unplated.png
index 9fad6857cf8..b12c3852d57 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-64_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-64_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-72.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-72.png
index 1aa4b6cb1fb..c088faa649a 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-72.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-72.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-72_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-72_altform-unplated.png
index 1aa4b6cb1fb..c088faa649a 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-72_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-72_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-80.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-80.png
index d4eedeca2dc..954b10a9b6f 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-80.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-80.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-80_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-80_altform-unplated.png
index d4eedeca2dc..954b10a9b6f 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-80_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-80_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-96.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-96.png
index 61463a6636e..c946c4178eb 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-96.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-96.png differ
diff --git a/res/terminal/images-Dev/Square44x44Logo.targetsize-96_altform-unplated.png b/res/terminal/images-Dev/Square44x44Logo.targetsize-96_altform-unplated.png
index 61463a6636e..c946c4178eb 100644
Binary files a/res/terminal/images-Dev/Square44x44Logo.targetsize-96_altform-unplated.png and b/res/terminal/images-Dev/Square44x44Logo.targetsize-96_altform-unplated.png differ
diff --git a/res/terminal/images-Dev/StoreLogo.scale-100.png b/res/terminal/images-Dev/StoreLogo.scale-100.png
index a4278328def..1aad67feef0 100644
Binary files a/res/terminal/images-Dev/StoreLogo.scale-100.png and b/res/terminal/images-Dev/StoreLogo.scale-100.png differ
diff --git a/res/terminal/images-Dev/StoreLogo.scale-125.png b/res/terminal/images-Dev/StoreLogo.scale-125.png
index 8634415ec65..3777ac2769c 100644
Binary files a/res/terminal/images-Dev/StoreLogo.scale-125.png and b/res/terminal/images-Dev/StoreLogo.scale-125.png differ
diff --git a/res/terminal/images-Dev/StoreLogo.scale-150.png b/res/terminal/images-Dev/StoreLogo.scale-150.png
index da579e40543..e1afde34529 100644
Binary files a/res/terminal/images-Dev/StoreLogo.scale-150.png and b/res/terminal/images-Dev/StoreLogo.scale-150.png differ
diff --git a/res/terminal/images-Dev/StoreLogo.scale-200.png b/res/terminal/images-Dev/StoreLogo.scale-200.png
index 4d141ef9697..c4d700c8d1f 100644
Binary files a/res/terminal/images-Dev/StoreLogo.scale-200.png and b/res/terminal/images-Dev/StoreLogo.scale-200.png differ
diff --git a/res/terminal/images-Dev/StoreLogo.scale-400.png b/res/terminal/images-Dev/StoreLogo.scale-400.png
index bde22ffebc0..796260678f2 100644
Binary files a/res/terminal/images-Dev/StoreLogo.scale-400.png and b/res/terminal/images-Dev/StoreLogo.scale-400.png differ
diff --git a/res/terminal/images-Dev/Wide310x150Logo.scale-100.png b/res/terminal/images-Dev/Wide310x150Logo.scale-100.png
index 435a50803a6..1f747141cbb 100644
Binary files a/res/terminal/images-Dev/Wide310x150Logo.scale-100.png and b/res/terminal/images-Dev/Wide310x150Logo.scale-100.png differ
diff --git a/res/terminal/images-Dev/Wide310x150Logo.scale-125.png b/res/terminal/images-Dev/Wide310x150Logo.scale-125.png
index 80df5b9d87f..2d75d7d759c 100644
Binary files a/res/terminal/images-Dev/Wide310x150Logo.scale-125.png and b/res/terminal/images-Dev/Wide310x150Logo.scale-125.png differ
diff --git a/res/terminal/images-Dev/Wide310x150Logo.scale-150.png b/res/terminal/images-Dev/Wide310x150Logo.scale-150.png
index 765c3385eeb..9db6d710f1a 100644
Binary files a/res/terminal/images-Dev/Wide310x150Logo.scale-150.png and b/res/terminal/images-Dev/Wide310x150Logo.scale-150.png differ
diff --git a/res/terminal/images-Dev/Wide310x150Logo.scale-200.png b/res/terminal/images-Dev/Wide310x150Logo.scale-200.png
index 23ca268d6df..0367bd61bda 100644
Binary files a/res/terminal/images-Dev/Wide310x150Logo.scale-200.png and b/res/terminal/images-Dev/Wide310x150Logo.scale-200.png differ
diff --git a/res/terminal/images-Dev/Wide310x150Logo.scale-400.png b/res/terminal/images-Dev/Wide310x150Logo.scale-400.png
index 8f19b92913d..688cf4997d2 100644
Binary files a/res/terminal/images-Dev/Wide310x150Logo.scale-400.png and b/res/terminal/images-Dev/Wide310x150Logo.scale-400.png differ
diff --git a/res/terminal/images-Universal/LargeTile.scale-100.png b/res/terminal/images-Universal/LargeTile.scale-100.png
index 4a83a59a663..20d9f18169f 100644
Binary files a/res/terminal/images-Universal/LargeTile.scale-100.png and b/res/terminal/images-Universal/LargeTile.scale-100.png differ
diff --git a/res/terminal/images-Universal/LargeTile.scale-125.png b/res/terminal/images-Universal/LargeTile.scale-125.png
index 2df601ffd04..fd77a3192ec 100644
Binary files a/res/terminal/images-Universal/LargeTile.scale-125.png and b/res/terminal/images-Universal/LargeTile.scale-125.png differ
diff --git a/res/terminal/images-Universal/LargeTile.scale-150.png b/res/terminal/images-Universal/LargeTile.scale-150.png
index aee6e6a9968..57d81682890 100644
Binary files a/res/terminal/images-Universal/LargeTile.scale-150.png and b/res/terminal/images-Universal/LargeTile.scale-150.png differ
diff --git a/res/terminal/images-Universal/LargeTile.scale-200.png b/res/terminal/images-Universal/LargeTile.scale-200.png
index 1b8343b5f98..ed031de1ea2 100644
Binary files a/res/terminal/images-Universal/LargeTile.scale-200.png and b/res/terminal/images-Universal/LargeTile.scale-200.png differ
diff --git a/res/terminal/images-Universal/LargeTile.scale-400.png b/res/terminal/images-Universal/LargeTile.scale-400.png
index 78d582650f7..04e8afad1bd 100644
Binary files a/res/terminal/images-Universal/LargeTile.scale-400.png and b/res/terminal/images-Universal/LargeTile.scale-400.png differ
diff --git a/res/terminal/images-Universal/LockScreenLogo.scale-100.png b/res/terminal/images-Universal/LockScreenLogo.scale-100.png
index 6cfcd243266..257eec38d8b 100644
Binary files a/res/terminal/images-Universal/LockScreenLogo.scale-100.png and b/res/terminal/images-Universal/LockScreenLogo.scale-100.png differ
diff --git a/res/terminal/images-Universal/LockScreenLogo.scale-125.png b/res/terminal/images-Universal/LockScreenLogo.scale-125.png
index e69ea97723e..151e8f8915d 100644
Binary files a/res/terminal/images-Universal/LockScreenLogo.scale-125.png and b/res/terminal/images-Universal/LockScreenLogo.scale-125.png differ
diff --git a/res/terminal/images-Universal/LockScreenLogo.scale-150.png b/res/terminal/images-Universal/LockScreenLogo.scale-150.png
index 049f5bb8f84..5849dba6f02 100644
Binary files a/res/terminal/images-Universal/LockScreenLogo.scale-150.png and b/res/terminal/images-Universal/LockScreenLogo.scale-150.png differ
diff --git a/res/terminal/images-Universal/LockScreenLogo.scale-200.png b/res/terminal/images-Universal/LockScreenLogo.scale-200.png
index 903daa7ad00..2cf732e9064 100644
Binary files a/res/terminal/images-Universal/LockScreenLogo.scale-200.png and b/res/terminal/images-Universal/LockScreenLogo.scale-200.png differ
diff --git a/res/terminal/images-Universal/LockScreenLogo.scale-400.png b/res/terminal/images-Universal/LockScreenLogo.scale-400.png
index d5f2fe178e4..3ad06c9d2f4 100644
Binary files a/res/terminal/images-Universal/LockScreenLogo.scale-400.png and b/res/terminal/images-Universal/LockScreenLogo.scale-400.png differ
diff --git a/res/terminal/images-Universal/SmallTile.scale-100.png b/res/terminal/images-Universal/SmallTile.scale-100.png
index 4a816a187a9..cde85ebbac9 100644
Binary files a/res/terminal/images-Universal/SmallTile.scale-100.png and b/res/terminal/images-Universal/SmallTile.scale-100.png differ
diff --git a/res/terminal/images-Universal/SmallTile.scale-125.png b/res/terminal/images-Universal/SmallTile.scale-125.png
index eddac38ce9c..6394934ec3e 100644
Binary files a/res/terminal/images-Universal/SmallTile.scale-125.png and b/res/terminal/images-Universal/SmallTile.scale-125.png differ
diff --git a/res/terminal/images-Universal/SmallTile.scale-150.png b/res/terminal/images-Universal/SmallTile.scale-150.png
index 3386841195a..fd231248ff4 100644
Binary files a/res/terminal/images-Universal/SmallTile.scale-150.png and b/res/terminal/images-Universal/SmallTile.scale-150.png differ
diff --git a/res/terminal/images-Universal/SmallTile.scale-200.png b/res/terminal/images-Universal/SmallTile.scale-200.png
index caa52df8ff2..ff24b499048 100644
Binary files a/res/terminal/images-Universal/SmallTile.scale-200.png and b/res/terminal/images-Universal/SmallTile.scale-200.png differ
diff --git a/res/terminal/images-Universal/SmallTile.scale-400.png b/res/terminal/images-Universal/SmallTile.scale-400.png
index ad836ae4fe9..1f8c99b2c4c 100644
Binary files a/res/terminal/images-Universal/SmallTile.scale-400.png and b/res/terminal/images-Universal/SmallTile.scale-400.png differ
diff --git a/res/terminal/images-Universal/SplashScreen.scale-100.png b/res/terminal/images-Universal/SplashScreen.scale-100.png
index 95ab759e0d3..4cb4d9f457e 100644
Binary files a/res/terminal/images-Universal/SplashScreen.scale-100.png and b/res/terminal/images-Universal/SplashScreen.scale-100.png differ
diff --git a/res/terminal/images-Universal/SplashScreen.scale-125.png b/res/terminal/images-Universal/SplashScreen.scale-125.png
index 2cfe557b56a..226a590769e 100644
Binary files a/res/terminal/images-Universal/SplashScreen.scale-125.png and b/res/terminal/images-Universal/SplashScreen.scale-125.png differ
diff --git a/res/terminal/images-Universal/SplashScreen.scale-150.png b/res/terminal/images-Universal/SplashScreen.scale-150.png
index c94b7fe2511..225b1736625 100644
Binary files a/res/terminal/images-Universal/SplashScreen.scale-150.png and b/res/terminal/images-Universal/SplashScreen.scale-150.png differ
diff --git a/res/terminal/images-Universal/SplashScreen.scale-200.png b/res/terminal/images-Universal/SplashScreen.scale-200.png
index e7eac1555ae..71fffea6564 100644
Binary files a/res/terminal/images-Universal/SplashScreen.scale-200.png and b/res/terminal/images-Universal/SplashScreen.scale-200.png differ
diff --git a/res/terminal/images-Universal/SplashScreen.scale-400.png b/res/terminal/images-Universal/SplashScreen.scale-400.png
index d8629c26e9f..36e19921a3b 100644
Binary files a/res/terminal/images-Universal/SplashScreen.scale-400.png and b/res/terminal/images-Universal/SplashScreen.scale-400.png differ
diff --git a/res/terminal/images-Universal/Square150x150Logo.scale-100.png b/res/terminal/images-Universal/Square150x150Logo.scale-100.png
index 23da4910c5a..7807fe391e4 100644
Binary files a/res/terminal/images-Universal/Square150x150Logo.scale-100.png and b/res/terminal/images-Universal/Square150x150Logo.scale-100.png differ
diff --git a/res/terminal/images-Universal/Square150x150Logo.scale-125.png b/res/terminal/images-Universal/Square150x150Logo.scale-125.png
index 7490622f4e2..772069b0a8e 100644
Binary files a/res/terminal/images-Universal/Square150x150Logo.scale-125.png and b/res/terminal/images-Universal/Square150x150Logo.scale-125.png differ
diff --git a/res/terminal/images-Universal/Square150x150Logo.scale-150.png b/res/terminal/images-Universal/Square150x150Logo.scale-150.png
index 245964f37ca..e10142d18ef 100644
Binary files a/res/terminal/images-Universal/Square150x150Logo.scale-150.png and b/res/terminal/images-Universal/Square150x150Logo.scale-150.png differ
diff --git a/res/terminal/images-Universal/Square150x150Logo.scale-200.png b/res/terminal/images-Universal/Square150x150Logo.scale-200.png
index 942bc9ac408..3675e159767 100644
Binary files a/res/terminal/images-Universal/Square150x150Logo.scale-200.png and b/res/terminal/images-Universal/Square150x150Logo.scale-200.png differ
diff --git a/res/terminal/images-Universal/Square150x150Logo.scale-400.png b/res/terminal/images-Universal/Square150x150Logo.scale-400.png
index 4df70b8eccd..ab2fb81fa42 100644
Binary files a/res/terminal/images-Universal/Square150x150Logo.scale-400.png and b/res/terminal/images-Universal/Square150x150Logo.scale-400.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.scale-100.png b/res/terminal/images-Universal/Square44x44Logo.scale-100.png
index eee18e1b10d..5add3816c1a 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.scale-100.png and b/res/terminal/images-Universal/Square44x44Logo.scale-100.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.scale-125.png b/res/terminal/images-Universal/Square44x44Logo.scale-125.png
index d72f72fadba..09eaa01cb07 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.scale-125.png and b/res/terminal/images-Universal/Square44x44Logo.scale-125.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.scale-150.png b/res/terminal/images-Universal/Square44x44Logo.scale-150.png
index dd87669492c..6fce68f3c26 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.scale-150.png and b/res/terminal/images-Universal/Square44x44Logo.scale-150.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.scale-200.png b/res/terminal/images-Universal/Square44x44Logo.scale-200.png
index f197ab5fc6a..8fbe3f6aaf5 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.scale-200.png and b/res/terminal/images-Universal/Square44x44Logo.scale-200.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.scale-400.png b/res/terminal/images-Universal/Square44x44Logo.scale-400.png
index dd14eedbcc2..630456e0b76 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.scale-400.png and b/res/terminal/images-Universal/Square44x44Logo.scale-400.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-16.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-16.png
index fe4f7de90d5..5e2b3a609d7 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-16.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-16.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-16_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-16_altform-unplated.png
index fe4f7de90d5..5e2b3a609d7 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-16_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-16_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-20.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-20.png
index 29ce742f575..8af06b0aab6 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-20.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-20.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-20_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-20_altform-unplated.png
index 29ce742f575..8af06b0aab6 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-20_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-20_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-24.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-24.png
index 6cfcd243266..257eec38d8b 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-24.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-24.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-24_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-24_altform-unplated.png
index 6cfcd243266..257eec38d8b 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-24_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-256.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-256.png
index 4184b1f0b3c..59f4296e235 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-256.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-256.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-256_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-256_altform-unplated.png
index 4184b1f0b3c..59f4296e235 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-256_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-256_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-30.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-30.png
index e69ea97723e..151e8f8915d 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-30.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-30.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-30_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-30_altform-unplated.png
index e69ea97723e..151e8f8915d 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-30_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-30_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-32.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-32.png
index e76f1a0b5f0..059461903f2 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-32.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-32.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-32_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-32_altform-unplated.png
index e76f1a0b5f0..059461903f2 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-32_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-32_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-36.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-36.png
index 049f5bb8f84..5849dba6f02 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-36.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-36.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-36_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-36_altform-unplated.png
index 049f5bb8f84..5849dba6f02 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-36_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-36_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-40.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-40.png
index 7c87c55038f..b366af89a69 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-40.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-40.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-40_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-40_altform-unplated.png
index 7c87c55038f..b366af89a69 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-40_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-40_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-48.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-48.png
index 903daa7ad00..2cf732e9064 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-48.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-48.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-48_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-48_altform-unplated.png
index 903daa7ad00..2cf732e9064 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-48_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-48_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-60.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-60.png
index b942eeaa8b1..a29fbc78ccd 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-60.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-60.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-60_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-60_altform-unplated.png
index b942eeaa8b1..a29fbc78ccd 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-60_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-60_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-64.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-64.png
index 08066e0ff14..5cde15961f1 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-64.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-64.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-64_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-64_altform-unplated.png
index 08066e0ff14..5cde15961f1 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-64_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-64_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-72.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-72.png
index 9372c45dac9..30a2cfa3902 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-72.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-72.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-72_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-72_altform-unplated.png
index 9372c45dac9..30a2cfa3902 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-72_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-72_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-80.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-80.png
index 8036b319e66..4f0016125d1 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-80.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-80.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-80_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-80_altform-unplated.png
index 8036b319e66..4f0016125d1 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-80_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-80_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-96.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-96.png
index d5f2fe178e4..3ad06c9d2f4 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-96.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-96.png differ
diff --git a/res/terminal/images-Universal/Square44x44Logo.targetsize-96_altform-unplated.png b/res/terminal/images-Universal/Square44x44Logo.targetsize-96_altform-unplated.png
index d5f2fe178e4..3ad06c9d2f4 100644
Binary files a/res/terminal/images-Universal/Square44x44Logo.targetsize-96_altform-unplated.png and b/res/terminal/images-Universal/Square44x44Logo.targetsize-96_altform-unplated.png differ
diff --git a/res/terminal/images-Universal/StoreLogo.scale-100.png b/res/terminal/images-Universal/StoreLogo.scale-100.png
index bc93b430d39..fa575eb48c7 100644
Binary files a/res/terminal/images-Universal/StoreLogo.scale-100.png and b/res/terminal/images-Universal/StoreLogo.scale-100.png differ
diff --git a/res/terminal/images-Universal/StoreLogo.scale-125.png b/res/terminal/images-Universal/StoreLogo.scale-125.png
index adf9c3947cd..4ab038b6aab 100644
Binary files a/res/terminal/images-Universal/StoreLogo.scale-125.png and b/res/terminal/images-Universal/StoreLogo.scale-125.png differ
diff --git a/res/terminal/images-Universal/StoreLogo.scale-150.png b/res/terminal/images-Universal/StoreLogo.scale-150.png
index abbf53e6ac8..5ea72b4b8fd 100644
Binary files a/res/terminal/images-Universal/StoreLogo.scale-150.png and b/res/terminal/images-Universal/StoreLogo.scale-150.png differ
diff --git a/res/terminal/images-Universal/StoreLogo.scale-200.png b/res/terminal/images-Universal/StoreLogo.scale-200.png
index de0081e8766..f9dce98de97 100644
Binary files a/res/terminal/images-Universal/StoreLogo.scale-200.png and b/res/terminal/images-Universal/StoreLogo.scale-200.png differ
diff --git a/res/terminal/images-Universal/StoreLogo.scale-400.png b/res/terminal/images-Universal/StoreLogo.scale-400.png
index a15234ce403..a51e51d618d 100644
Binary files a/res/terminal/images-Universal/StoreLogo.scale-400.png and b/res/terminal/images-Universal/StoreLogo.scale-400.png differ
diff --git a/res/terminal/images-Universal/Wide310x150Logo.scale-100.png b/res/terminal/images-Universal/Wide310x150Logo.scale-100.png
index c40de6eff71..172b7523bf7 100644
Binary files a/res/terminal/images-Universal/Wide310x150Logo.scale-100.png and b/res/terminal/images-Universal/Wide310x150Logo.scale-100.png differ
diff --git a/res/terminal/images-Universal/Wide310x150Logo.scale-125.png b/res/terminal/images-Universal/Wide310x150Logo.scale-125.png
index f88c5fa00a4..b17bd824365 100644
Binary files a/res/terminal/images-Universal/Wide310x150Logo.scale-125.png and b/res/terminal/images-Universal/Wide310x150Logo.scale-125.png differ
diff --git a/res/terminal/images-Universal/Wide310x150Logo.scale-150.png b/res/terminal/images-Universal/Wide310x150Logo.scale-150.png
index 30177aa006b..60798baec5f 100644
Binary files a/res/terminal/images-Universal/Wide310x150Logo.scale-150.png and b/res/terminal/images-Universal/Wide310x150Logo.scale-150.png differ
diff --git a/res/terminal/images-Universal/Wide310x150Logo.scale-200.png b/res/terminal/images-Universal/Wide310x150Logo.scale-200.png
index 95ab759e0d3..4cb4d9f457e 100644
Binary files a/res/terminal/images-Universal/Wide310x150Logo.scale-200.png and b/res/terminal/images-Universal/Wide310x150Logo.scale-200.png differ
diff --git a/res/terminal/images-Universal/Wide310x150Logo.scale-400.png b/res/terminal/images-Universal/Wide310x150Logo.scale-400.png
index e7eac1555ae..71fffea6564 100644
Binary files a/res/terminal/images-Universal/Wide310x150Logo.scale-400.png and b/res/terminal/images-Universal/Wide310x150Logo.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/LargeTile.scale-100.png b/res/terminal/images-UniversalDev/LargeTile.scale-100.png
index 759826e6d30..c4b6e2f0eb3 100644
Binary files a/res/terminal/images-UniversalDev/LargeTile.scale-100.png and b/res/terminal/images-UniversalDev/LargeTile.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/LargeTile.scale-125.png b/res/terminal/images-UniversalDev/LargeTile.scale-125.png
index 73fd5b2743f..e319702d763 100644
Binary files a/res/terminal/images-UniversalDev/LargeTile.scale-125.png and b/res/terminal/images-UniversalDev/LargeTile.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/LargeTile.scale-150.png b/res/terminal/images-UniversalDev/LargeTile.scale-150.png
index 9e80d5ef440..ca28d81bddb 100644
Binary files a/res/terminal/images-UniversalDev/LargeTile.scale-150.png and b/res/terminal/images-UniversalDev/LargeTile.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/LargeTile.scale-200.png b/res/terminal/images-UniversalDev/LargeTile.scale-200.png
index 1bfe9db04aa..db918a66451 100644
Binary files a/res/terminal/images-UniversalDev/LargeTile.scale-200.png and b/res/terminal/images-UniversalDev/LargeTile.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/LargeTile.scale-400.png b/res/terminal/images-UniversalDev/LargeTile.scale-400.png
index fb0a0046f1f..24ff15394e1 100644
Binary files a/res/terminal/images-UniversalDev/LargeTile.scale-400.png and b/res/terminal/images-UniversalDev/LargeTile.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/LockScreenLogo.scale-100.png b/res/terminal/images-UniversalDev/LockScreenLogo.scale-100.png
index 8d2d86a7034..a186bed5272 100644
Binary files a/res/terminal/images-UniversalDev/LockScreenLogo.scale-100.png and b/res/terminal/images-UniversalDev/LockScreenLogo.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/LockScreenLogo.scale-125.png b/res/terminal/images-UniversalDev/LockScreenLogo.scale-125.png
index 255e4f7a6b8..015e2edee75 100644
Binary files a/res/terminal/images-UniversalDev/LockScreenLogo.scale-125.png and b/res/terminal/images-UniversalDev/LockScreenLogo.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/LockScreenLogo.scale-150.png b/res/terminal/images-UniversalDev/LockScreenLogo.scale-150.png
index 90d1e3eebec..a1c76d2b696 100644
Binary files a/res/terminal/images-UniversalDev/LockScreenLogo.scale-150.png and b/res/terminal/images-UniversalDev/LockScreenLogo.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/LockScreenLogo.scale-200.png b/res/terminal/images-UniversalDev/LockScreenLogo.scale-200.png
index 459096a49f0..d93c4df8d04 100644
Binary files a/res/terminal/images-UniversalDev/LockScreenLogo.scale-200.png and b/res/terminal/images-UniversalDev/LockScreenLogo.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/LockScreenLogo.scale-400.png b/res/terminal/images-UniversalDev/LockScreenLogo.scale-400.png
index b8cec41e543..931051b650a 100644
Binary files a/res/terminal/images-UniversalDev/LockScreenLogo.scale-400.png and b/res/terminal/images-UniversalDev/LockScreenLogo.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/SmallTile.scale-100.png b/res/terminal/images-UniversalDev/SmallTile.scale-100.png
index ce5e50eebf0..6d9c851f2db 100644
Binary files a/res/terminal/images-UniversalDev/SmallTile.scale-100.png and b/res/terminal/images-UniversalDev/SmallTile.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/SmallTile.scale-125.png b/res/terminal/images-UniversalDev/SmallTile.scale-125.png
index e5e395110cf..9b7068355cd 100644
Binary files a/res/terminal/images-UniversalDev/SmallTile.scale-125.png and b/res/terminal/images-UniversalDev/SmallTile.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/SmallTile.scale-150.png b/res/terminal/images-UniversalDev/SmallTile.scale-150.png
index 9b175e4ef24..af51a9aa0a3 100644
Binary files a/res/terminal/images-UniversalDev/SmallTile.scale-150.png and b/res/terminal/images-UniversalDev/SmallTile.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/SmallTile.scale-200.png b/res/terminal/images-UniversalDev/SmallTile.scale-200.png
index 4ccf2f16a83..afa5b128723 100644
Binary files a/res/terminal/images-UniversalDev/SmallTile.scale-200.png and b/res/terminal/images-UniversalDev/SmallTile.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/SmallTile.scale-400.png b/res/terminal/images-UniversalDev/SmallTile.scale-400.png
index 90baaee69bc..1f5ca05e572 100644
Binary files a/res/terminal/images-UniversalDev/SmallTile.scale-400.png and b/res/terminal/images-UniversalDev/SmallTile.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/SplashScreen.scale-100.png b/res/terminal/images-UniversalDev/SplashScreen.scale-100.png
index 4abd56c47f2..ac502d7abd9 100644
Binary files a/res/terminal/images-UniversalDev/SplashScreen.scale-100.png and b/res/terminal/images-UniversalDev/SplashScreen.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/SplashScreen.scale-125.png b/res/terminal/images-UniversalDev/SplashScreen.scale-125.png
index 097db06d0d8..fa975cb3e5f 100644
Binary files a/res/terminal/images-UniversalDev/SplashScreen.scale-125.png and b/res/terminal/images-UniversalDev/SplashScreen.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/SplashScreen.scale-150.png b/res/terminal/images-UniversalDev/SplashScreen.scale-150.png
index 46adc93691f..e17c759f58e 100644
Binary files a/res/terminal/images-UniversalDev/SplashScreen.scale-150.png and b/res/terminal/images-UniversalDev/SplashScreen.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/SplashScreen.scale-200.png b/res/terminal/images-UniversalDev/SplashScreen.scale-200.png
index 4605ac22850..853ba2e553e 100644
Binary files a/res/terminal/images-UniversalDev/SplashScreen.scale-200.png and b/res/terminal/images-UniversalDev/SplashScreen.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/SplashScreen.scale-400.png b/res/terminal/images-UniversalDev/SplashScreen.scale-400.png
index ab798721fb1..659ae08735e 100644
Binary files a/res/terminal/images-UniversalDev/SplashScreen.scale-400.png and b/res/terminal/images-UniversalDev/SplashScreen.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/Square150x150Logo.scale-100.png b/res/terminal/images-UniversalDev/Square150x150Logo.scale-100.png
index 23cbe571936..3315504af23 100644
Binary files a/res/terminal/images-UniversalDev/Square150x150Logo.scale-100.png and b/res/terminal/images-UniversalDev/Square150x150Logo.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/Square150x150Logo.scale-125.png b/res/terminal/images-UniversalDev/Square150x150Logo.scale-125.png
index 7180fcea885..93512364414 100644
Binary files a/res/terminal/images-UniversalDev/Square150x150Logo.scale-125.png and b/res/terminal/images-UniversalDev/Square150x150Logo.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/Square150x150Logo.scale-150.png b/res/terminal/images-UniversalDev/Square150x150Logo.scale-150.png
index b5259cebb58..3b07d3ca2ae 100644
Binary files a/res/terminal/images-UniversalDev/Square150x150Logo.scale-150.png and b/res/terminal/images-UniversalDev/Square150x150Logo.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/Square150x150Logo.scale-200.png b/res/terminal/images-UniversalDev/Square150x150Logo.scale-200.png
index 96d33312c73..141f2154112 100644
Binary files a/res/terminal/images-UniversalDev/Square150x150Logo.scale-200.png and b/res/terminal/images-UniversalDev/Square150x150Logo.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/Square150x150Logo.scale-400.png b/res/terminal/images-UniversalDev/Square150x150Logo.scale-400.png
index 3dc9270e639..5e7b5c6327e 100644
Binary files a/res/terminal/images-UniversalDev/Square150x150Logo.scale-400.png and b/res/terminal/images-UniversalDev/Square150x150Logo.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.scale-100.png b/res/terminal/images-UniversalDev/Square44x44Logo.scale-100.png
index 6f52b106d05..132d5d38866 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.scale-100.png and b/res/terminal/images-UniversalDev/Square44x44Logo.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.scale-125.png b/res/terminal/images-UniversalDev/Square44x44Logo.scale-125.png
index c47dba3358a..44acfbf8744 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.scale-125.png and b/res/terminal/images-UniversalDev/Square44x44Logo.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.scale-150.png b/res/terminal/images-UniversalDev/Square44x44Logo.scale-150.png
index 0078e54d05b..bfda7012e59 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.scale-150.png and b/res/terminal/images-UniversalDev/Square44x44Logo.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.scale-200.png b/res/terminal/images-UniversalDev/Square44x44Logo.scale-200.png
index 027dc54c9f3..fab57281ba8 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.scale-200.png and b/res/terminal/images-UniversalDev/Square44x44Logo.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.scale-400.png b/res/terminal/images-UniversalDev/Square44x44Logo.scale-400.png
index ffb62b859dc..7814eab2a4c 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.scale-400.png and b/res/terminal/images-UniversalDev/Square44x44Logo.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16.png
index 402e5b44ad2..b5aae1f2217 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16_altform-unplated.png
index 402e5b44ad2..b5aae1f2217 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-16_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20.png
index a6702d49cd4..72073f06333 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20_altform-unplated.png
index a6702d49cd4..72073f06333 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-20_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24.png
index 8d2d86a7034..a186bed5272 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24_altform-unplated.png
index 8d2d86a7034..a186bed5272 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256.png
index 3ebef2e57a6..0a5942634fa 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256_altform-unplated.png
index 3ebef2e57a6..0a5942634fa 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-256_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30.png
index 255e4f7a6b8..015e2edee75 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30_altform-unplated.png
index 255e4f7a6b8..015e2edee75 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-30_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32.png
index 726178cba14..c3a159493d4 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32_altform-unplated.png
index 726178cba14..c3a159493d4 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-32_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36.png
index 90d1e3eebec..a1c76d2b696 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36_altform-unplated.png
index 90d1e3eebec..a1c76d2b696 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-36_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40.png
index 61065fe9915..ec461fb8b90 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40_altform-unplated.png
index 61065fe9915..ec461fb8b90 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-40_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48.png
index 459096a49f0..d93c4df8d04 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48_altform-unplated.png
index 459096a49f0..d93c4df8d04 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-48_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60.png
index 372ef0dadbe..36a3aa20de6 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60_altform-unplated.png
index 372ef0dadbe..36a3aa20de6 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-60_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64.png
index 3605ee2f052..e2f49bbc31a 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64_altform-unplated.png
index 3605ee2f052..e2f49bbc31a 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-64_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72.png
index 4b40d590035..1cc1e3bce9f 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72_altform-unplated.png
index 4b40d590035..1cc1e3bce9f 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-72_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80.png
index 1cfdbc736c6..65cb25deb4b 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80_altform-unplated.png
index 1cfdbc736c6..65cb25deb4b 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-80_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96.png
index b8cec41e543..931051b650a 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96.png differ
diff --git a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96_altform-unplated.png b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96_altform-unplated.png
index b8cec41e543..931051b650a 100644
Binary files a/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96_altform-unplated.png and b/res/terminal/images-UniversalDev/Square44x44Logo.targetsize-96_altform-unplated.png differ
diff --git a/res/terminal/images-UniversalDev/StoreLogo.scale-100.png b/res/terminal/images-UniversalDev/StoreLogo.scale-100.png
index 39c3bbb2693..f9656b5e1d9 100644
Binary files a/res/terminal/images-UniversalDev/StoreLogo.scale-100.png and b/res/terminal/images-UniversalDev/StoreLogo.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/StoreLogo.scale-125.png b/res/terminal/images-UniversalDev/StoreLogo.scale-125.png
index 67fbd0d10c5..3a032967e37 100644
Binary files a/res/terminal/images-UniversalDev/StoreLogo.scale-125.png and b/res/terminal/images-UniversalDev/StoreLogo.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/StoreLogo.scale-150.png b/res/terminal/images-UniversalDev/StoreLogo.scale-150.png
index 7de33e3bddb..2b614d6df74 100644
Binary files a/res/terminal/images-UniversalDev/StoreLogo.scale-150.png and b/res/terminal/images-UniversalDev/StoreLogo.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/StoreLogo.scale-200.png b/res/terminal/images-UniversalDev/StoreLogo.scale-200.png
index 555fb5aff0f..6f9cb96a685 100644
Binary files a/res/terminal/images-UniversalDev/StoreLogo.scale-200.png and b/res/terminal/images-UniversalDev/StoreLogo.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/StoreLogo.scale-400.png b/res/terminal/images-UniversalDev/StoreLogo.scale-400.png
index ab6858c9520..3c4887caafc 100644
Binary files a/res/terminal/images-UniversalDev/StoreLogo.scale-400.png and b/res/terminal/images-UniversalDev/StoreLogo.scale-400.png differ
diff --git a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-100.png b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-100.png
index 61e7d555afc..2003e0c724d 100644
Binary files a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-100.png and b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-100.png differ
diff --git a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-125.png b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-125.png
index 4a932a440e1..34a309ad210 100644
Binary files a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-125.png and b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-125.png differ
diff --git a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-150.png b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-150.png
index b7a1f6ecdf3..d389d1ae4cd 100644
Binary files a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-150.png and b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-150.png differ
diff --git a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-200.png b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-200.png
index 4abd56c47f2..ac502d7abd9 100644
Binary files a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-200.png and b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-200.png differ
diff --git a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-400.png b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-400.png
index 4605ac22850..853ba2e553e 100644
Binary files a/res/terminal/images-UniversalDev/Wide310x150Logo.scale-400.png and b/res/terminal/images-UniversalDev/Wide310x150Logo.scale-400.png differ
diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp
index b733672fd18..69f64d41a93 100644
--- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp
+++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp
@@ -341,7 +341,7 @@ static const std::mapAction maping. The created object is of schema:
+// KeyBinding->Action maping.
// {
// keys:[String],
// command:String
diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp
index 9bbd6ab25ca..9abb3274317 100644
--- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp
+++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp
@@ -39,7 +39,6 @@ static constexpr std::string_view SchemesKey{ "schemes" };
static constexpr std::string_view DisabledProfileSourcesKey{ "disabledProfileSources" };
static constexpr std::string_view Utf8Bom{ u8"\uFEFF" };
-static constexpr std::string_view DefaultProfilesIndentation{ " " };
static constexpr std::string_view SettingsSchemaFragment{ "\n"
R"( "$schema": "https://aka.ms/terminal-profiles-schema")" };
@@ -59,6 +58,11 @@ std::unique_ptr CascadiaSettings::LoadAll()
{
auto resultPtr = LoadDefaults();
+ // 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().GetDefaultProfile();
+
std::optional fileData = _ReadUserSettings();
const bool foundFile = fileData.has_value();
@@ -129,6 +133,57 @@ std::unique_ptr CascadiaSettings::LoadAll()
// If this throws, the app will catch it and use the default settings
resultPtr->_ValidateSettings();
+ // GH 3855 - Gathering Data on custom profiles to inform better defaults
+ // Do it after everything else so it won't happen unless validation passed.
+ // Also, avoid processing unless someone's listening for measures. The keybindings work, at least,
+ // is a lot of computation we can skip if no one cares.
+ if (TraceLoggingProviderEnabled(g_hTerminalAppProvider, 0, MICROSOFT_KEYWORD_MEASURES))
+ {
+ auto guid = resultPtr->GlobalSettings().GetDefaultProfile();
+
+ // Compare to the defaults.json one that we set on install.
+ // If it's different, log what the user chose.
+ if (hardcodedDefaultGuid != guid)
+ {
+ TraceLoggingWrite(
+ g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
+ "CustomDefaultProfile",
+ TraceLoggingDescription("Event emitted when user has chosen a different default profile than hardcoded one on load/reload"),
+ TraceLoggingGuid(guid, "DefaultProfile", "ID of user-chosen default profile"),
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
+ }
+
+ // If the user had keybinding settings preferences, we want to learn from them to make better defaults
+ auto userKeybindings = resultPtr->_userSettings[JsonKey(KeybindingsKey)];
+ if (!userKeybindings.empty())
+ {
+ // If there are custom key bindings, let's understand what they are because maybe the defaults aren't good enough
+
+ // Run it through the object so we can parse it apart and then only serialize the fields we're interested in
+ // and avoid extraneous data.
+ auto akb = winrt::make_self();
+ akb->LayerJson(userKeybindings);
+ auto value = akb->ToJson();
+
+ // Reserialize the keybindings
+ Json::StreamWriterBuilder wbuilder;
+ // Use 4 spaces to indent instead of \t
+ wbuilder.settings_["indentation"] = " ";
+ wbuilder.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
+
+ const auto keybindingsString = Json::writeString(wbuilder, value);
+
+ TraceLoggingWrite(
+ g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
+ "CustomKeybindings",
+ TraceLoggingDescription("Event emitted when custom keybindings are idenfitied on load/reload"),
+ TraceLoggingUtf8String(keybindingsString.c_str(), "Keybindings", "Keybindings as JSON"),
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
+ }
+ }
+
return resultPtr;
}
@@ -358,6 +413,11 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
const auto numProfiles = userProfilesObj.size();
const auto lastProfile = userProfilesObj[numProfiles - 1];
size_t currentInsertIndex = lastProfile.getOffsetLimit();
+ // Find the position of the first non-tab/space character before the last profile...
+ const auto lastProfileIndentStartsAt{ _userSettingsString.find_last_not_of(" \t", lastProfile.getOffsetStart() - 1) };
+ // ... and impute the user's preferred indentation.
+ // (we're taking a copy because a string_view into a string we mutate is a no-no.)
+ const std::string indentation{ _userSettingsString, lastProfileIndentStartsAt + 1, lastProfile.getOffsetStart() - lastProfileIndentStartsAt - 1 };
bool changedFile = false;
@@ -388,17 +448,17 @@ bool CascadiaSettings::_AppendDynamicProfilesToUserSettings()
const auto diff = profile.GenerateStub();
auto profileSerialization = Json::writeString(wbuilder, diff);
- // Add 8 spaces to the start of each line
- profileSerialization.insert(0, DefaultProfilesIndentation);
+ // Add the user's indent to the start of each line
+ profileSerialization.insert(0, indentation);
// Get the first newline
size_t pos = profileSerialization.find("\n");
// for each newline...
while (pos != std::string::npos)
{
// Insert 8 spaces immediately following the current newline
- profileSerialization.insert(pos + 1, DefaultProfilesIndentation);
+ profileSerialization.insert(pos + 1, indentation);
// Get the next newline
- pos = profileSerialization.find("\n", pos + 9);
+ pos = profileSerialization.find("\n", pos + indentation.size() + 1);
}
// Write a comma, newline to the file
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index fd5f304cb39..19fb2bfe87e 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -481,9 +481,13 @@ namespace winrt::TerminalApp::implementation
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"TabInformation",
TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
+ TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"),
+ TraceLoggingBool(settings.UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
+ TraceLoggingFloat64(settings.TintOpacity(), "TintOpacity", "Opacity preference from the settings"),
+ TraceLoggingWideString(settings.FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
}
@@ -838,25 +842,6 @@ namespace winrt::TerminalApp::implementation
{
_lastTabClosedHandlers(*this, nullptr);
}
-
- if (auto indexOpt{ _GetFocusedTabIndex() })
- {
- auto focusedTabIndex = *indexOpt;
- if (tabIndex == focusedTabIndex)
- {
- uint32_t tabCount = _tabs.Size();
- if (focusedTabIndex >= tabCount)
- {
- focusedTabIndex = tabCount - 1;
- }
- else if (focusedTabIndex < 0)
- {
- focusedTabIndex = 0;
- }
-
- _SelectTab(focusedTabIndex);
- }
- }
}
// Method Description:
diff --git a/src/cascadia/TerminalApp/defaults.json b/src/cascadia/TerminalApp/defaults.json
index ce9463a0c75..8ddf2a38fa8 100644
--- a/src/cascadia/TerminalApp/defaults.json
+++ b/src/cascadia/TerminalApp/defaults.json
@@ -8,7 +8,7 @@
"showTabsInTitlebar": true,
"showTerminalTitleInTitlebar": true,
"tabWidthMode": "equal",
- "snapToGridOnResize": false,
+ "snapToGridOnResize": true,
"wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502",
"confirmCloseAllTabs": true,
diff --git a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj
index e2d49d6b9ba..618b41a0484 100644
--- a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj
+++ b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj
@@ -286,7 +286,7 @@
pch.h
- ..;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);
+ ..;$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);
4702;%(DisableSpecificWarnings)
diff --git a/src/cascadia/TerminalApp/userDefaults.json b/src/cascadia/TerminalApp/userDefaults.json
index 1f3c5a1ad6b..e5b325a1d86 100644
--- a/src/cascadia/TerminalApp/userDefaults.json
+++ b/src/cascadia/TerminalApp/userDefaults.json
@@ -7,22 +7,29 @@
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"profiles":
- [
+ {
+ "defaults":
{
- // Make changes here to the powershell.exe profile
- "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
- "name": "Windows PowerShell",
- "commandline": "powershell.exe",
- "hidden": false
+ // Put settings here that you want to apply to all profiles
},
- {
- // Make changes here to the cmd.exe profile
- "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
- "name": "cmd",
- "commandline": "cmd.exe",
- "hidden": false
- }
- ],
+ "list":
+ [
+ {
+ // Make changes here to the powershell.exe profile
+ "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
+ "name": "Windows PowerShell",
+ "commandline": "powershell.exe",
+ "hidden": false
+ },
+ {
+ // Make changes here to the cmd.exe profile
+ "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
+ "name": "cmd",
+ "commandline": "cmd.exe",
+ "hidden": false
+ }
+ ]
+ },
// Add custom color schemes to this array
"schemes": [],
diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp
index 4f352a6340d..a42d34ff6e2 100644
--- a/src/cascadia/TerminalConnection/ConptyConnection.cpp
+++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp
@@ -153,6 +153,19 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
DeleteProcThreadAttributeList(siEx.lpAttributeList);
+ const std::filesystem::path processName = wil::GetModuleFileNameExW(_piClient.hProcess, nullptr);
+ _clientName = processName.filename().wstring();
+
+#pragma warning(suppress : 26477 26485 26494 26482 26446) // We don't control TraceLoggingWrite
+ TraceLoggingWrite(
+ g_hTerminalConnectionProvider,
+ "ConPtyConnected",
+ TraceLoggingDescription("Event emitted when ConPTY connection is started"),
+ TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"),
+ TraceLoggingWideString(_clientName.c_str(), "Client", "The attached client process"),
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
+ TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
+
return S_OK;
}
CATCH_RETURN();
diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h
index c9888ab44d2..b450f657ee1 100644
--- a/src/cascadia/TerminalConnection/ConptyConnection.h
+++ b/src/cascadia/TerminalConnection/ConptyConnection.h
@@ -41,6 +41,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
hstring _startingDirectory;
hstring _startingTitle;
guid _guid{}; // A unique session identifier for connected client
+ hstring _clientName{}; // The name of the process hosted by this ConPTY connection (as of launch).
bool _receivedFirstByte{ false };
std::chrono::high_resolution_clock::time_point _startTime{};
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 773e18efedb..550f397b042 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -1,2259 +1,2278 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-#include "pch.h"
-#include "TermControl.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "..\..\types\inc\GlyphWidth.hpp"
-
-#include "TermControl.g.cpp"
-#include "TermControlAutomationPeer.h"
-
-using namespace ::Microsoft::Console::Types;
-using namespace ::Microsoft::Terminal::Core;
-using namespace winrt::Windows::UI::Xaml;
-using namespace winrt::Windows::UI::Xaml::Automation::Peers;
-using namespace winrt::Windows::UI::Core;
-using namespace winrt::Windows::System;
-using namespace winrt::Microsoft::Terminal::Settings;
-using namespace winrt::Windows::ApplicationModel::DataTransfer;
-
-namespace winrt::Microsoft::Terminal::TerminalControl::implementation
-{
- // 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 */)
- {
- 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;
- }
-
- TermControl::TermControl() :
- TermControl(Settings::TerminalSettings{}, TerminalConnection::ITerminalConnection{ nullptr })
- {
- }
-
- TermControl::TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection) :
- _connection{ connection },
- _initializedTerminal{ false },
- _root{ nullptr },
- _swapChainPanel{ nullptr },
- _settings{ settings },
- _closing{ false },
- _isTerminalInitiatedScroll{ false },
- _autoScrollVelocity{ 0 },
- _autoScrollingPointerPoint{ std::nullopt },
- _autoScrollTimer{},
- _lastAutoScrollUpdateTime{ std::nullopt },
- _desiredFont{ DEFAULT_FONT_FACE, 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 },
- _actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false },
- _touchAnchor{ std::nullopt },
- _cursorTimer{},
- _lastMouseClick{},
- _lastMouseClickPos{},
- _searchBox{ nullptr },
- _tsfInputControl{ nullptr }
- {
- _EnsureStaticInitialization();
- _Create();
- }
-
- void TermControl::_Create()
- {
- Controls::Grid container;
-
- Controls::ColumnDefinition contentColumn{};
- Controls::ColumnDefinition scrollbarColumn{};
- contentColumn.Width(GridLength{ 1.0, GridUnitType::Star });
- scrollbarColumn.Width(GridLength{ 1.0, GridUnitType::Auto });
-
- container.ColumnDefinitions().Append(contentColumn);
- container.ColumnDefinitions().Append(scrollbarColumn);
-
- _scrollBar = Controls::Primitives::ScrollBar{};
- _scrollBar.Orientation(Controls::Orientation::Vertical);
- _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
- _scrollBar.HorizontalAlignment(HorizontalAlignment::Right);
- _scrollBar.VerticalAlignment(VerticalAlignment::Stretch);
-
- // Initialize the scrollbar with some placeholder values.
- // The scrollbar will be updated with real values on _Initialize
- _scrollBar.Maximum(1);
- _scrollBar.ViewportSize(10);
- _scrollBar.IsTabStop(false);
- _scrollBar.SmallChange(1);
- _scrollBar.LargeChange(4);
- _scrollBar.Visibility(Visibility::Visible);
-
- _tsfInputControl = TSFInputControl();
- _tsfInputControl.CompositionCompleted({ this, &TermControl::_CompositionCompleted });
- _tsfInputControl.CurrentCursorPosition({ this, &TermControl::_CurrentCursorPositionHandler });
- _tsfInputControl.CurrentFontInfo({ this, &TermControl::_FontInfoHandler });
- container.Children().Append(_tsfInputControl);
-
- // Create the SwapChainPanel that will display our content
- Controls::SwapChainPanel swapChainPanel;
-
- _sizeChangedRevoker = swapChainPanel.SizeChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainSizeChanged });
- _compositionScaleChangedRevoker = swapChainPanel.CompositionScaleChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainScaleChanged });
-
- // Initialize the terminal only once the swapchainpanel is loaded - that
- // way, we'll be able to query the real pixel size it got on layout
- _layoutUpdatedRevoker = swapChainPanel.LayoutUpdated(winrt::auto_revoke, [this](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.
-
- if (_InitializeTerminal())
- {
- // Only let this succeed once.
- _layoutUpdatedRevoker.revoke();
- }
- });
-
- container.Children().Append(swapChainPanel);
- container.Children().Append(_scrollBar);
- Controls::Grid::SetColumn(swapChainPanel, 0);
- Controls::Grid::SetColumn(_scrollBar, 1);
-
- Controls::Grid root{};
- Controls::Image bgImageLayer{};
- root.Children().Append(bgImageLayer);
- root.Children().Append(container);
-
- _root = root;
- _bgImageLayer = bgImageLayer;
-
- _swapChainPanel = swapChainPanel;
- this->Content(_root);
-
- _ApplyUISettings();
-
- // These are important:
- // 1. When we get tapped, focus us
- _tappedRevoker = this->Tapped(winrt::auto_revoke, [this](auto&, auto& e) {
- Focus(FocusState::Pointer);
- e.Handled(true);
- });
- // 2. Make sure we can be focused (why this isn't `Focusable` I'll never know)
- this->IsTabStop(true);
- // 3. Actually not sure about this one. Maybe it isn't necessary either.
- this->AllowFocusOnInteraction(true);
-
- // DON'T CALL _InitializeTerminal here - wait until the swap chain is loaded to do that.
-
- // Subscribe to the connection's disconnected event and call our connection closed handlers.
- _connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
- _ConnectionStateChangedHandlers(*this, nullptr);
- });
-
- _root.AllowDrop(true);
- _root.Drop({ get_weak(), &TermControl::_DragDropHandler });
- _root.DragOver({ get_weak(), &TermControl::_DragOverHandler });
- }
-
- // Method Description:
- // - Create the SearchBoxControl object, and attach it
- // to the Terminal Control root
- // Arguments:
- // -
- // Return Value:
- // -
- void TermControl::CreateSearchBoxControl()
- {
- if (!_searchBox)
- {
- _searchBox = winrt::make_self();
- _searchBox->HorizontalAlignment(HorizontalAlignment::Right);
- _searchBox->VerticalAlignment(VerticalAlignment::Top);
- // We need to make sure the searchbox does not overlap
- // with the scroll bar
- Thickness searchBoxPadding = { 0, 0, _scrollBar.ActualWidth(), 0 };
- _searchBox->Margin(searchBoxPadding);
-
- _root.Children().Append(*_searchBox);
-
- // Event handlers
- _searchBox->Search({ get_weak(), &TermControl::_Search });
- _searchBox->Closed({ get_weak(), &TermControl::_CloseSearchBoxControl });
- }
-
- _searchBox->SetFocusOnTextbox();
- }
-
- // Method Description:
- // - Search text in text buffer. This is triggered if the user click
- // search button or press enter.
- // Arguments:
- // - text: the text to search
- // - goForward: boolean that represents if the current search direction is forward
- // - caseSensitive: boolean that represents if the current search is case sensitive
- // Return Value:
- // -
- void TermControl::_Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
- {
- if (text.size() == 0)
- {
- return;
- }
-
- const Search::Direction direction = goForward ?
- Search::Direction::Forward :
- Search::Direction::Backward;
-
- const Search::Sensitivity sensitivity = caseSensitive ?
- Search::Sensitivity::CaseSensitive :
- Search::Sensitivity::CaseInsensitive;
-
- Search search(*GetUiaData(), text.c_str(), direction, sensitivity);
- auto lock = _terminal->LockForWriting();
- if (search.FindNext())
- {
- _terminal->SetBoxSelection(false);
- search.Select();
- _renderer->TriggerSelection();
- }
- }
-
- // Method Description:
- // - The handler for the close button or pressing "Esc" when focusing on the
- // search dialog.
- // This removes the SearchBoxControl object from the XAML tree,
- // reset smart pointer and set focus back to Terminal
- // Arguments:
- // - IInspectable: not used
- // - RoutedEventArgs: not used
- // Return Value:
- // -
- void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
- {
- unsigned int idx;
- _root.Children().IndexOf(*_searchBox, idx);
- _root.Children().RemoveAt(idx);
-
- _searchBox = nullptr;
-
- // Set focus back to terminal control
- this->Focus(FocusState::Programmatic);
- }
-
- // Method Description:
- // - Given new settings for this profile, applies the settings to the current terminal.
- // Arguments:
- // - newSettings: New settings values for the profile in this terminal.
- // Return Value:
- // -
- winrt::fire_and_forget TermControl::UpdateSettings(Settings::IControlSettings newSettings)
- {
- _settings = newSettings;
- auto weakThis{ get_weak() };
-
- // Dispatch a call to the UI thread to apply the new settings to the
- // terminal.
- co_await winrt::resume_foreground(_root.Dispatcher());
-
- // If 'weakThis' is locked, then we can safely work with 'this'
- if (auto control{ weakThis.get() })
- {
- // Update our control settings
- _ApplyUISettings();
-
- // Update DxEngine's SelectionBackground
- _renderEngine->SetSelectionBackground(_settings.SelectionBackground());
-
- // Update the terminal core with its new Core settings
- _terminal->UpdateSettings(_settings);
-
- // Refresh our font with the renderer
- _UpdateFont();
-
- const auto width = _swapChainPanel.ActualWidth();
- const auto height = _swapChainPanel.ActualHeight();
- if (width != 0 && height != 0)
- {
- // If the font size changed, or the _swapchainPanel's size changed
- // for any reason, we'll need to make sure to also resize the
- // buffer. _DoResize will invalidate everything for us.
- auto lock = _terminal->LockForWriting();
- _DoResize(width, height);
- }
-
- // set TSF Foreground
- Media::SolidColorBrush foregroundBrush{};
- foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
- _tsfInputControl.Foreground(foregroundBrush);
- }
- }
-
- // Method Description:
- // - Style our UI elements based on the values in our _settings, and set up
- // other control-specific settings. This method will be called whenever
- // the settings are reloaded.
- // * Calls _InitializeBackgroundBrush to set up the Xaml brush responsible
- // for the control's background
- // * Calls _BackgroundColorChanged to style the background of the control
- // - Core settings will be passed to the terminal in _InitializeTerminal
- // Arguments:
- // -
- // Return Value:
- // -
- void TermControl::_ApplyUISettings()
- {
- _InitializeBackgroundBrush();
-
- uint32_t bg = _settings.DefaultBackground();
- _BackgroundColorChanged(bg);
-
- // Apply padding as swapChainPanel's margin
- auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
- auto existingMargin = _swapChainPanel.Margin();
- _swapChainPanel.Margin(newMargin);
-
- if (newMargin != existingMargin && newMargin != Thickness{ 0 })
- {
- TraceLoggingWrite(g_hTerminalControlProvider,
- "NonzeroPaddingApplied",
- TraceLoggingDescription("An event emitted when a control has padding applied to it"),
- TraceLoggingStruct(4, "Padding"),
- TraceLoggingFloat64(newMargin.Left, "Left"),
- TraceLoggingFloat64(newMargin.Top, "Top"),
- TraceLoggingFloat64(newMargin.Right, "Right"),
- TraceLoggingFloat64(newMargin.Bottom, "Bottom"),
- TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
- TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
- }
-
- // Initialize our font information.
- const auto fontFace = _settings.FontFace();
- const short fontHeight = gsl::narrow(_settings.FontSize());
- // The font width doesn't terribly matter, we'll only be using the
- // height to look it up
- // The other params here also largely don't matter.
- // The family is only used to determine if the font is truetype or
- // not, but DX doesn't use that info at all.
- // The Codepage is additionally not actually used by the DX engine at all.
- _actualFont = { fontFace, 0, 10, { 0, fontHeight }, CP_UTF8, false };
- _desiredFont = { _actualFont };
-
- // set TSF Foreground
- Media::SolidColorBrush foregroundBrush{};
- foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
- _tsfInputControl.Foreground(foregroundBrush);
- _tsfInputControl.Margin(newMargin);
-
- // set number of rows to scroll at a time
- _rowsToScroll = _settings.RowsToScroll();
- }
-
- // Method Description:
- // - Set up each layer's brush used to display the control's background.
- // - Respects the settings for acrylic, background image and opacity from
- // _settings.
- // * If acrylic is not enabled, setup a solid color background, otherwise
- // use bgcolor as acrylic's tint
- // - Avoids image flickering and acrylic brush redraw if settings are changed
- // but the appropriate brush is still in place.
- // - Does not apply background color outside of acrylic mode;
- // _BackgroundColorChanged must be called to do so.
- // Arguments:
- // -
- // Return Value:
- // -
- void TermControl::_InitializeBackgroundBrush()
- {
- if (_settings.UseAcrylic())
- {
- // See if we've already got an acrylic background brush
- // to avoid the flicker when setting up a new one
- auto acrylic = _root.Background().try_as();
-
- // Instantiate a brush if there's not already one there
- if (acrylic == nullptr)
- {
- acrylic = Media::AcrylicBrush{};
- acrylic.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
- }
-
- // see GH#1082: Initialize background color so we don't get a
- // fade/flash when _BackgroundColorChanged is called
- uint32_t color = _settings.DefaultBackground();
- winrt::Windows::UI::Color bgColor{};
- bgColor.R = GetRValue(color);
- bgColor.G = GetGValue(color);
- bgColor.B = GetBValue(color);
- bgColor.A = 255;
-
- acrylic.FallbackColor(bgColor);
- acrylic.TintColor(bgColor);
-
- // Apply brush settings
- acrylic.TintOpacity(_settings.TintOpacity());
-
- // Apply brush to control if it's not already there
- if (_root.Background() != acrylic)
- {
- _root.Background(acrylic);
- }
- }
- else
- {
- Media::SolidColorBrush solidColor{};
- _root.Background(solidColor);
- }
-
- if (!_settings.BackgroundImage().empty())
- {
- Windows::Foundation::Uri imageUri{ _settings.BackgroundImage() };
-
- // Check if the image brush is already pointing to the image
- // in the modified settings; if it isn't (or isn't there),
- // set a new image source for the brush
- auto imageSource = _bgImageLayer.Source().try_as();
-
- if (imageSource == nullptr ||
- imageSource.UriSource() == nullptr ||
- imageSource.UriSource().RawUri() != imageUri.RawUri())
- {
- // Note that BitmapImage handles the image load asynchronously,
- // which is especially important since the image
- // may well be both large and somewhere out on the
- // internet.
- Media::Imaging::BitmapImage image(imageUri);
- _bgImageLayer.Source(image);
- }
-
- // Apply stretch, opacity and alignment settings
- _bgImageLayer.Stretch(_settings.BackgroundImageStretchMode());
- _bgImageLayer.Opacity(_settings.BackgroundImageOpacity());
- _bgImageLayer.HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
- _bgImageLayer.VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
- }
- else
- {
- _bgImageLayer.Source(nullptr);
- }
- }
-
- // Method Description:
- // - Style the background of the control with the provided background color
- // Arguments:
- // - color: The background color to use as a uint32 (aka DWORD COLORREF)
- // Return Value:
- // -
- winrt::fire_and_forget TermControl::_BackgroundColorChanged(const uint32_t color)
- {
- auto weakThis{ get_weak() };
-
- co_await winrt::resume_foreground(_root.Dispatcher());
-
- if (auto control{ weakThis.get() })
- {
- const auto R = GetRValue(color);
- const auto G = GetGValue(color);
- const auto B = GetBValue(color);
-
- winrt::Windows::UI::Color bgColor{};
- bgColor.R = R;
- bgColor.G = G;
- bgColor.B = B;
- bgColor.A = 255;
-
- if (auto acrylic = _root.Background().try_as())
- {
- acrylic.FallbackColor(bgColor);
- acrylic.TintColor(bgColor);
- }
- else if (auto solidColor = _root.Background().try_as())
- {
- solidColor.Color(bgColor);
- }
-
- // Set the default background as transparent to prevent the
- // DX layer from overwriting the background image or acrylic effect
- _settings.DefaultBackground(ARGB(0, R, G, B));
- }
- }
-
- TermControl::~TermControl()
- {
- Close();
- }
-
- // Method Description:
- // - Creates an automation peer for the Terminal Control, enabling accessibility on our control.
- // Arguments:
- // - None
- // Return Value:
- // - The automation peer for our control
- Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
- try
- {
- if (GetUiaData())
- {
- // create a custom automation peer with this code pattern:
- // (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
- auto autoPeer = winrt::make_self(this);
-
- _uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
- _renderer->AddRenderEngine(_uiaEngine.get());
- return *autoPeer;
- }
- return nullptr;
- }
- catch (...)
- {
- LOG_CAUGHT_EXCEPTION();
- return nullptr;
- }
-
- ::Microsoft::Console::Types::IUiaData* TermControl::GetUiaData() const
- {
- return _terminal.get();
- }
-
- const FontInfo TermControl::GetActualFont() const
- {
- return _actualFont;
- }
-
- const Windows::UI::Xaml::Thickness TermControl::GetPadding() const
- {
- return _swapChainPanel.Margin();
- }
-
- TerminalConnection::ConnectionState TermControl::ConnectionState() const
- {
- return _connection.State();
- }
-
- winrt::fire_and_forget TermControl::SwapChainChanged()
- {
- if (!_initializedTerminal)
- {
- return;
- }
-
- auto chain = _renderEngine->GetSwapChain();
- auto weakThis{ get_weak() };
-
- co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
-
- // If 'weakThis' is locked, then we can safely work with 'this'
- if (auto control{ weakThis.get() })
- {
- auto lock = _terminal->LockForWriting();
- auto nativePanel = _swapChainPanel.as();
- nativePanel->SetSwapChain(chain.Get());
- }
- }
-
- winrt::fire_and_forget TermControl::_SwapChainRoutine()
- {
- auto chain = _renderEngine->GetSwapChain();
- auto weakThis{ get_weak() };
-
- co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
-
- if (auto control{ weakThis.get() })
- {
- _terminal->LockConsole();
- auto nativePanel = _swapChainPanel.as();
- nativePanel->SetSwapChain(chain.Get());
- _terminal->UnlockConsole();
- }
- }
-
- bool TermControl::_InitializeTerminal()
- {
- if (_initializedTerminal)
- {
- return false;
- }
-
- const auto windowWidth = _swapChainPanel.ActualWidth(); // Width() and Height() are NaN?
- const auto windowHeight = _swapChainPanel.ActualHeight();
-
- if (windowWidth == 0 || windowHeight == 0)
- {
- return false;
- }
-
- _terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
-
- // First create the render thread.
- // Then stash a local pointer to the render thread so we can initialize it and enable it
- // to paint itself *after* we hand off its ownership to the renderer.
- // We split up construction and initialization of the render thread object this way
- // because the renderer and render thread have circular references to each other.
- auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
- auto* const localPointerToThread = renderThread.get();
-
- // Now create the renderer and initialize the render thread.
- _renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
- ::Microsoft::Console::Render::IRenderTarget& renderTarget = *_renderer;
-
- THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
-
- // Set up the DX Engine
- auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
- _renderer->AddRenderEngine(dxEngine.get());
-
- // Initialize our font with the renderer
- // We don't have to care about DPI. We'll get a change message immediately if it's not 96
- // and react accordingly.
- _UpdateFont(true);
-
- const COORD windowSize{ static_cast(windowWidth), static_cast(windowHeight) };
-
- // Fist set up the dx engine with the window size in pixels.
- // Then, using the font, get the number of characters that can fit.
- // Resize our terminal connection to match that size, and initialize the terminal with that size.
- const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
- THROW_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
-
- // Update DxEngine's SelectionBackground
- dxEngine->SetSelectionBackground(_settings.SelectionBackground());
-
- const auto vp = dxEngine->GetViewportInCharacters(viewInPixels);
- const auto width = vp.Width();
- const auto height = vp.Height();
- _connection.Resize(height, width);
-
- // Override the default width and height to match the size of the swapChainPanel
- _settings.InitialCols(width);
- _settings.InitialRows(height);
-
- _terminal->CreateFromSettings(_settings, renderTarget);
-
- // Tell the DX Engine to notify us when the swap chain changes.
- dxEngine->SetCallback(std::bind(&TermControl::SwapChainChanged, this));
-
- // TODO:GH#3927 - Make it possible to hot-reload this setting. Right
- // here, the setting will only be used when the Terminal is initialized.
- dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
-
- THROW_IF_FAILED(dxEngine->Enable());
- _renderEngine = std::move(dxEngine);
-
- // This event is explicitly revoked in the destructor: does not need weak_ref
- auto onReceiveOutputFn = [this](const hstring str) {
- _terminal->Write(str);
- };
- _connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn);
-
- auto inputFn = std::bind(&TermControl::_SendInputToConnection, this, std::placeholders::_1);
- _terminal->SetWriteInputCallback(inputFn);
-
- _SwapChainRoutine();
-
- // Set up the height of the ScrollViewer and the grid we're using to fake our scrolling height
- auto bottom = _terminal->GetViewport().BottomExclusive();
- auto bufferHeight = bottom;
-
- const auto originalMaximum = _scrollBar.Maximum();
- const auto originalMinimum = _scrollBar.Minimum();
- const auto originalValue = _scrollBar.Value();
- const auto originalViewportSize = _scrollBar.ViewportSize();
-
- _scrollBar.Maximum(bufferHeight - bufferHeight);
- _scrollBar.Minimum(0);
- _scrollBar.Value(0);
- _scrollBar.ViewportSize(bufferHeight);
- _scrollBar.ValueChanged({ this, &TermControl::_ScrollbarChangeHandler });
- _scrollBar.PointerPressed({ this, &TermControl::_CapturePointer });
- _scrollBar.PointerReleased({ this, &TermControl::_ReleasePointerCapture });
-
- // Apply settings for scrollbar
- if (_settings.ScrollState() == ScrollbarState::Visible)
- {
- _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
- }
- else if (_settings.ScrollState() == ScrollbarState::Hidden)
- {
- _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
-
- // In the scenario where the user has turned off the OS setting to automatically hide scollbars, the
- // Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
- // achieve the intended effect.
- _scrollBar.Visibility(Visibility::Collapsed);
- }
- else
- {
- // Default behavior
- _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
- }
-
- _root.PointerWheelChanged({ this, &TermControl::_MouseWheelHandler });
-
- // These need to be hooked up to the SwapChainPanel because we don't want the scrollbar to respond to pointer events (GitHub #950)
- _swapChainPanel.PointerPressed({ this, &TermControl::_PointerPressedHandler });
- _swapChainPanel.PointerMoved({ this, &TermControl::_PointerMovedHandler });
- _swapChainPanel.PointerReleased({ this, &TermControl::_PointerReleasedHandler });
-
- localPointerToThread->EnablePainting();
-
- // No matter what order these guys are in, The KeyDown's will fire
- // before the CharacterReceived, so we can't easily get characters
- // first, then fallback to getting keys from vkeys.
- // TODO: This apparently handles keys and characters correctly, though
- // I'd keep an eye on it, and test more.
- // I presume that the characters that aren't translated by terminalInput
- // just end up getting ignored, and the rest of the input comes
- // through CharacterReceived.
- // I don't believe there's a difference between KeyDown and
- // PreviewKeyDown for our purposes
- // These two handlers _must_ be on this, not _root.
- this->PreviewKeyDown({ this, &TermControl::_KeyDownHandler });
- this->CharacterReceived({ this, &TermControl::_CharacterHandler });
-
- auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
- _terminal->SetTitleChangedCallback(pfnTitleChanged);
-
- auto pfnBackgroundColorChanged = std::bind(&TermControl::_BackgroundColorChanged, this, std::placeholders::_1);
- _terminal->SetBackgroundCallback(pfnBackgroundColorChanged);
-
- auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
- _terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged);
-
- static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000));
- _autoScrollTimer.Interval(AutoScrollUpdateInterval);
- _autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll });
-
- // Set up blinking cursor
- int blinkTime = GetCaretBlinkTime();
- if (blinkTime != INFINITE)
- {
- // Create a timer
- _cursorTimer = std::make_optional(DispatcherTimer());
- _cursorTimer.value().Interval(std::chrono::milliseconds(blinkTime));
- _cursorTimer.value().Tick({ get_weak(), &TermControl::_BlinkCursor });
- _cursorTimer.value().Start();
- }
- else
- {
- // The user has disabled cursor blinking
- _cursorTimer = std::nullopt;
- }
-
- // import value from WinUser (convert from milli-seconds to micro-seconds)
- _multiClickTimer = GetDoubleClickTime() * 1000;
-
- _gotFocusRevoker = this->GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler });
- _lostFocusRevoker = this->LostFocus(winrt::auto_revoke, { this, &TermControl::_LostFocusHandler });
-
- // Focus the control here. If we do it up above (in _Create_), then the
- // focus won't actually get passed to us. I believe this is because
- // we're not technically a part of the UI tree yet, so focusing us
- // becomes a no-op.
- this->Focus(FocusState::Programmatic);
-
- _connection.Start();
- _initializedTerminal = true;
- _InitializedHandlers(*this, nullptr);
- return true;
- }
-
- void TermControl::_CharacterHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
- Input::CharacterReceivedRoutedEventArgs const& e)
- {
- if (_closing)
- {
- return;
- }
-
- const auto ch = e.Character();
-
- const bool handled = _terminal->SendCharEvent(ch);
- e.Handled(handled);
- }
-
- void TermControl::_KeyDownHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
- Input::KeyRoutedEventArgs const& e)
- {
- // If the current focused element is a child element of searchbox,
- // we do not send this event up to terminal
- if (_searchBox && _searchBox->ContainsFocus())
- {
- return;
- }
-
- // mark event as handled and do nothing if...
- // - closing
- // - key modifier is pressed
- // NOTE: for key combos like CTRL + C, two events are fired (one for CTRL, one for 'C'). We care about the 'C' event and then check for key modifiers below.
- if (_closing ||
- e.OriginalKey() == VirtualKey::Control ||
- e.OriginalKey() == VirtualKey::Shift ||
- e.OriginalKey() == VirtualKey::Menu ||
- e.OriginalKey() == VirtualKey::LeftWindows ||
- e.OriginalKey() == VirtualKey::RightWindows)
-
- {
- e.Handled(true);
- return;
- }
-
- const auto modifiers = _GetPressedModifierKeys();
- const auto vkey = gsl::narrow_cast(e.OriginalKey());
- const auto scanCode = gsl::narrow_cast(e.KeyStatus().ScanCode);
- bool handled = false;
-
- // GH#2235: Terminal::Settings hasn't been modified to differentiate between AltGr and Ctrl+Alt yet.
- // -> Don't check for key bindings if this is an AltGr key combination.
- if (!modifiers.IsAltGrPressed())
- {
- auto bindings = _settings.KeyBindings();
- if (bindings)
- {
- handled = bindings.TryKeyChord({
- modifiers.IsCtrlPressed(),
- modifiers.IsAltPressed(),
- modifiers.IsShiftPressed(),
- vkey,
- });
- }
- }
-
- if (!handled)
- {
- handled = _TrySendKeyEvent(vkey, scanCode, modifiers);
- }
-
- // Manually prevent keyboard navigation with tab. We want to send tab to
- // the terminal, and we don't want to be able to escape focus of the
- // control with tab.
- if (e.OriginalKey() == VirtualKey::Tab)
- {
- handled = true;
- }
-
- e.Handled(handled);
- }
-
- // Method Description:
- // - Send this particular key event to the terminal.
- // See Terminal::SendKeyEvent for more information.
- // - Clears the current selection.
- // - Makes the cursor briefly visible during typing.
- // Arguments:
- // - vkey: The vkey of the key pressed.
- // - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
- bool TermControl::_TrySendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates modifiers)
- {
- // When there is a selection active, escape should clear it and NOT flow through
- // to the terminal. With any other keypress, it should clear the selection AND
- // flow through to the terminal.
- if (_terminal->IsSelectionActive())
- {
- _terminal->ClearSelection();
-
- if (vkey == VK_ESCAPE)
- {
- return true;
- }
- }
-
- // If the terminal translated the key, mark the event as handled.
- // This will prevent the system from trying to get the character out
- // of it and sending us a CharacterReceived event.
- const auto handled = vkey ? _terminal->SendKeyEvent(vkey, scanCode, modifiers) : true;
-
- if (_cursorTimer.has_value())
- {
- // Manually show the cursor when a key is pressed. Restarting
- // the timer prevents flickering.
- _terminal->SetCursorVisible(true);
- _cursorTimer.value().Start();
- }
-
- return handled;
- }
-
- // Method Description:
- // - handle a mouse click event. Begin selection process.
- // Arguments:
- // - sender: the XAML element responding to the pointer input
- // - args: event data
- void TermControl::_PointerPressedHandler(Windows::Foundation::IInspectable const& sender,
- Input::PointerRoutedEventArgs const& args)
- {
- _CapturePointer(sender, args);
-
- const auto ptr = args.Pointer();
- const auto point = args.GetCurrentPoint(_root);
-
- if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
- {
- // Ignore mouse events while the terminal does not have focus.
- // This prevents the user from selecting and copying text if they
- // click inside the current tab to refocus the terminal window.
- if (!_focused)
- {
- args.Handled(true);
- return;
- }
-
- const auto modifiers = static_cast(args.KeyModifiers());
- // static_cast to a uint32_t because we can't use the WI_IsFlagSet
- // macro directly with a VirtualKeyModifiers
- const auto altEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Menu));
- const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift));
-
- if (point.Properties().IsLeftButtonPressed())
- {
- const auto cursorPosition = point.Position();
- const auto terminalPosition = _GetTerminalPosition(cursorPosition);
-
- // handle ALT key
- _terminal->SetBoxSelection(altEnabled);
-
- auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp());
-
- // This formula enables the number of clicks to cycle properly between single-, double-, and triple-click.
- // To increase the number of acceptable click states, simply increment MAX_CLICK_COUNT and add another if-statement
- const unsigned int MAX_CLICK_COUNT = 3;
- const auto multiClickMapper = clickCount > MAX_CLICK_COUNT ? ((clickCount + MAX_CLICK_COUNT - 1) % MAX_CLICK_COUNT) + 1 : clickCount;
-
- if (multiClickMapper == 3)
- {
- _terminal->TripleClickSelection(terminalPosition);
- }
- else if (multiClickMapper == 2)
- {
- _terminal->DoubleClickSelection(terminalPosition);
- }
- else
- {
- if (shiftEnabled && _terminal->IsSelectionActive())
- {
- _terminal->SetEndSelectionPosition(terminalPosition);
- }
- else
- {
- // save location before rendering
- _terminal->SetSelectionAnchor(terminalPosition);
- }
-
- _lastMouseClick = point.Timestamp();
- _lastMouseClickPos = cursorPosition;
- }
- _renderer->TriggerSelection();
- }
- else if (point.Properties().IsRightButtonPressed())
- {
- // copyOnSelect causes right-click to always paste
- if (_terminal->IsCopyOnSelectActive() || !_terminal->IsSelectionActive())
- {
- PasteTextFromClipboard();
- }
- else
- {
- CopySelectionToClipboard(!shiftEnabled);
- }
- }
- }
- else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
- {
- const auto contactRect = point.Properties().ContactRect();
- // Set our touch rect, to start a pan.
- _touchAnchor = winrt::Windows::Foundation::Point{ contactRect.X, contactRect.Y };
- }
-
- args.Handled(true);
- }
-
- // Method Description:
- // - handle a mouse moved event. Specifically handling mouse drag to update selection process.
- // Arguments:
- // - sender: not used
- // - args: event data
- void TermControl::_PointerMovedHandler(Windows::Foundation::IInspectable const& /*sender*/,
- Input::PointerRoutedEventArgs const& args)
- {
- const auto ptr = args.Pointer();
- const auto point = args.GetCurrentPoint(_root);
-
- if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
- {
- if (point.Properties().IsLeftButtonPressed())
- {
- const auto cursorPosition = point.Position();
- _SetEndSelectionPointAtCursor(cursorPosition);
-
- const double cursorBelowBottomDist = cursorPosition.Y - _swapChainPanel.Margin().Top - _swapChainPanel.ActualHeight();
- const double cursorAboveTopDist = -1 * cursorPosition.Y + _swapChainPanel.Margin().Top;
-
- constexpr double MinAutoScrollDist = 2.0; // Arbitrary value
- double newAutoScrollVelocity = 0.0;
- if (cursorBelowBottomDist > MinAutoScrollDist)
- {
- newAutoScrollVelocity = _GetAutoScrollSpeed(cursorBelowBottomDist);
- }
- else if (cursorAboveTopDist > MinAutoScrollDist)
- {
- newAutoScrollVelocity = -1.0 * _GetAutoScrollSpeed(cursorAboveTopDist);
- }
-
- if (newAutoScrollVelocity != 0)
- {
- _TryStartAutoScroll(point, newAutoScrollVelocity);
- }
- else
- {
- _TryStopAutoScroll(ptr.PointerId());
- }
- }
- }
- else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch && _touchAnchor)
- {
- const auto contactRect = point.Properties().ContactRect();
- winrt::Windows::Foundation::Point newTouchPoint{ contactRect.X, contactRect.Y };
- const auto anchor = _touchAnchor.value();
-
- // Get the difference between the point we've dragged to and the start of the touch.
- const float fontHeight = float(_actualFont.GetSize().Y);
-
- const float dy = newTouchPoint.Y - anchor.Y;
-
- // If we've moved more than one row of text, we'll want to scroll the viewport
- if (std::abs(dy) > fontHeight)
- {
- // Multiply by -1, because moving the touch point down will
- // create a positive delta, but we want the viewport to move up,
- // so we'll need a negative scroll amount (and the inverse for
- // panning down)
- const float numRows = -1.0f * (dy / fontHeight);
-
- const auto currentOffset = this->GetScrollOffset();
- const double newValue = (numRows) + (currentOffset);
-
- _scrollBar.Value(static_cast(newValue));
-
- // Use this point as our new scroll anchor.
- _touchAnchor = newTouchPoint;
- }
- }
- args.Handled(true);
- }
-
- // Method Description:
- // - Event handler for the PointerReleased event. We use this to de-anchor
- // touch events, to stop scrolling via touch.
- // Arguments:
- // - sender: the XAML element responding to the pointer input
- // - args: event data
- void TermControl::_PointerReleasedHandler(Windows::Foundation::IInspectable const& sender,
- Input::PointerRoutedEventArgs const& args)
- {
- _ReleasePointerCapture(sender, args);
-
- const auto ptr = args.Pointer();
-
- if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
- {
- const auto modifiers = static_cast(args.KeyModifiers());
- // static_cast to a uint32_t because we can't use the WI_IsFlagSet
- // macro directly with a VirtualKeyModifiers
- const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift));
-
- if (_terminal->IsCopyOnSelectActive())
- {
- CopySelectionToClipboard(!shiftEnabled);
- }
- }
- else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
- {
- _touchAnchor = std::nullopt;
- }
-
- _TryStopAutoScroll(ptr.PointerId());
-
- args.Handled(true);
- }
-
- // Method Description:
- // - Event handler for the PointerWheelChanged event. This is raised in
- // response to mouse wheel changes. Depending upon what modifier keys are
- // pressed, different actions will take place.
- // Arguments:
- // - args: the event args containing information about t`he mouse wheel event.
- void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
- Input::PointerRoutedEventArgs const& args)
- {
- const auto point = args.GetCurrentPoint(_root);
- const auto delta = point.Properties().MouseWheelDelta();
-
- // Get the state of the Ctrl & Shift keys
- // static_cast to a uint32_t because we can't use the WI_IsFlagSet macro
- // directly with a VirtualKeyModifiers
- const auto modifiers = static_cast(args.KeyModifiers());
- const auto ctrlPressed = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Control));
- const auto shiftPressed = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift));
-
- if (ctrlPressed && shiftPressed)
- {
- _MouseTransparencyHandler(delta);
- }
- else if (ctrlPressed)
- {
- _MouseZoomHandler(delta);
- }
- else
- {
- _MouseScrollHandler(delta, point);
- }
- }
-
- // Method Description:
- // - Adjust the opacity of the acrylic background in response to a mouse
- // scrolling event.
- // Arguments:
- // - mouseDelta: the mouse wheel delta that triggered this event.
- void TermControl::_MouseTransparencyHandler(const double mouseDelta)
- {
- // Transparency is on a scale of [0.0,1.0], so only increment by .01.
- const auto effectiveDelta = mouseDelta < 0 ? -.01 : .01;
-
- if (_settings.UseAcrylic())
- {
- try
- {
- auto acrylicBrush = _root.Background().as();
- acrylicBrush.TintOpacity(acrylicBrush.TintOpacity() + effectiveDelta);
- }
- CATCH_LOG();
- }
- }
-
- // Method Description:
- // - Adjust the font size of the terminal in response to a mouse scrolling
- // event.
- // Arguments:
- // - mouseDelta: the mouse wheel delta that triggered this event.
- void TermControl::_MouseZoomHandler(const double mouseDelta)
- {
- const auto fontDelta = mouseDelta < 0 ? -1 : 1;
- AdjustFontSize(fontDelta);
- }
-
- // Method Description:
- // - Reset the font size of the terminal to its default size.
- // Arguments:
- // - none
- void TermControl::ResetFontSize()
- {
- _SetFontSize(_settings.FontSize());
- }
-
- // Method Description:
- // - Adjust the font size of the terminal control.
- // Arguments:
- // - fontSizeDelta: The amount to increase or decrease the font size by.
- void TermControl::AdjustFontSize(int fontSizeDelta)
- {
- const auto newSize = _desiredFont.GetEngineSize().Y + fontSizeDelta;
- _SetFontSize(newSize);
- }
-
- // Method Description:
- // - Scroll the visible viewport in response to a mouse wheel event.
- // Arguments:
- // - mouseDelta: the mouse wheel delta that triggered this event.
- void TermControl::_MouseScrollHandler(const double mouseDelta, Windows::UI::Input::PointerPoint const& pointerPoint)
- {
- const auto currentOffset = this->GetScrollOffset();
-
- // negative = down, positive = up
- // However, for us, the signs are flipped.
- const auto rowDelta = mouseDelta < 0 ? 1.0 : -1.0;
-
- // With one of the precision mouses, one click is always a multiple of 120,
- // but the "smooth scrolling" mode results in non-int values
-
- double newValue = (_rowsToScroll * rowDelta) + (currentOffset);
-
- // The scroll bar's ValueChanged handler will actually move the viewport
- // for us.
- _scrollBar.Value(static_cast(newValue));
-
- if (_terminal->IsSelectionActive() && pointerPoint.Properties().IsLeftButtonPressed())
- {
- // If user is mouse selecting and scrolls, they then point at new character.
- // Make sure selection reflects that immediately.
- _SetEndSelectionPointAtCursor(pointerPoint.Position());
- }
- }
-
- void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/,
- Controls::Primitives::RangeBaseValueChangedEventArgs const& args)
- {
- if (_isTerminalInitiatedScroll)
- {
- return;
- }
-
- const auto newValue = static_cast(args.NewValue());
-
- // This is a scroll event that wasn't initiated by the terminal
- // itself - it was initiated by the mouse wheel, or the scrollbar.
- _terminal->UserScrollViewport(newValue);
-
- // We've just told the terminal to update its viewport to reflect the
- // new scroll value so the scroll bar matches the viewport now.
- _willUpdateScrollBarToMatchViewport.store(false);
- }
-
- // Method Description:
- // - captures the pointer so that none of the other XAML elements respond to pointer events
- // Arguments:
- // - sender: XAML element that is interacting with pointer
- // - args: pointer data (i.e.: mouse, touch)
- // Return Value:
- // - true if we successfully capture the pointer, false otherwise.
- bool TermControl::_CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& args)
- {
- IUIElement uielem;
- if (sender.try_as(uielem))
- {
- uielem.CapturePointer(args.Pointer());
- return true;
- }
- return false;
- }
-
- // Method Description:
- // - releases the captured pointer because we're done responding to XAML pointer events
- // Arguments:
- // - sender: XAML element that is interacting with pointer
- // - args: pointer data (i.e.: mouse, touch)
- // Return Value:
- // - true if we release capture of the pointer, false otherwise.
- bool TermControl::_ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& args)
- {
- IUIElement uielem;
- if (sender.try_as(uielem))
- {
- uielem.ReleasePointerCapture(args.Pointer());
- return true;
- }
- return false;
- }
-
- // Method Description:
- // - Starts new pointer related auto scroll behavior, or continues existing one.
- // Does nothing when there is already auto scroll associated with another pointer.
- // Arguments:
- // - pointerPoint: info about pointer that causes auto scroll. Pointer's position
- // is later used to update selection.
- // - scrollVelocity: target velocity of scrolling in characters / sec
- void TermControl::_TryStartAutoScroll(Windows::UI::Input::PointerPoint const& pointerPoint, const double scrollVelocity)
- {
- // Allow only one pointer at the time
- if (!_autoScrollingPointerPoint.has_value() || _autoScrollingPointerPoint.value().PointerId() == pointerPoint.PointerId())
- {
- _autoScrollingPointerPoint = pointerPoint;
- _autoScrollVelocity = scrollVelocity;
-
- // If this is first time the auto scroll update is about to be called,
- // kick-start it by initializing its time delta as if it started now
- if (!_lastAutoScrollUpdateTime.has_value())
- {
- _lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
- }
-
- // Apparently this check is not necessary but greatly improves performance
- if (!_autoScrollTimer.IsEnabled())
- {
- _autoScrollTimer.Start();
- }
- }
- }
-
- // Method Description:
- // - Stops auto scroll if it's active and is associated with supplied pointer id.
- // Arguments:
- // - pointerId: id of pointer for which to stop auto scroll
- void TermControl::_TryStopAutoScroll(const uint32_t pointerId)
- {
- if (_autoScrollingPointerPoint.has_value() && pointerId == _autoScrollingPointerPoint.value().PointerId())
- {
- _autoScrollingPointerPoint = std::nullopt;
- _autoScrollVelocity = 0;
- _lastAutoScrollUpdateTime = std::nullopt;
-
- // Apparently this check is not necessary but greatly improves performance
- if (_autoScrollTimer.IsEnabled())
- {
- _autoScrollTimer.Stop();
- }
- }
- }
-
- // Method Description:
- // - Called continuously to gradually scroll viewport when user is
- // mouse selecting outside it (to 'follow' the cursor).
- // Arguments:
- // - none
- void TermControl::_UpdateAutoScroll(Windows::Foundation::IInspectable const& /* sender */,
- Windows::Foundation::IInspectable const& /* e */)
- {
- if (_autoScrollVelocity != 0)
- {
- const auto timeNow = std::chrono::high_resolution_clock::now();
-
- if (_lastAutoScrollUpdateTime.has_value())
- {
- static constexpr double microSecPerSec = 1000000.0;
- const double deltaTime = std::chrono::duration_cast(timeNow - _lastAutoScrollUpdateTime.value()).count() / microSecPerSec;
- _scrollBar.Value(_scrollBar.Value() + _autoScrollVelocity * deltaTime);
-
- if (_autoScrollingPointerPoint.has_value())
- {
- _SetEndSelectionPointAtCursor(_autoScrollingPointerPoint.value().Position());
- }
- }
-
- _lastAutoScrollUpdateTime = timeNow;
- }
- }
-
- // Method Description:
- // - Event handler for the GotFocus event. This is used to...
- // - enable accessibility notifications for this TermControl
- // - start blinking the cursor when the window is focused
- // - update the number of lines to scroll to the value set in the system
- void TermControl::_GotFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
- RoutedEventArgs const& /* args */)
- {
- if (_closing)
- {
- return;
- }
- _focused = true;
-
- if (_uiaEngine.get())
- {
- THROW_IF_FAILED(_uiaEngine->Enable());
- }
-
- if (_tsfInputControl != nullptr)
- {
- _tsfInputControl.NotifyFocusEnter();
- }
-
- if (_cursorTimer.has_value())
- {
- // When the terminal focuses, show the cursor immediately
- _terminal->SetCursorVisible(true);
- _cursorTimer.value().Start();
- }
- _rowsToScroll = _settings.RowsToScroll();
- }
-
- // Method Description:
- // - Event handler for the LostFocus event. This is used to...
- // - disable accessibility notifications for this TermControl
- // - hide and stop blinking the cursor when the window loses focus.
- void TermControl::_LostFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
- RoutedEventArgs const& /* args */)
- {
- if (_closing)
- {
- return;
- }
- _focused = false;
-
- if (_uiaEngine.get())
- {
- THROW_IF_FAILED(_uiaEngine->Disable());
- }
-
- if (_tsfInputControl != nullptr)
- {
- _tsfInputControl.NotifyFocusLeave();
- }
-
- if (_cursorTimer.has_value())
- {
- _cursorTimer.value().Stop();
- _terminal->SetCursorVisible(false);
- }
- }
-
- // Method Description:
- // - Writes the given sequence as input to the active terminal connection,
- // Arguments:
- // - wstr: the string of characters to write to the terminal connection.
- // Return Value:
- // -
- void TermControl::_SendInputToConnection(const std::wstring& wstr)
- {
- _connection.WriteInput(wstr);
- }
-
- // Method Description:
- // - Pre-process text pasted (presumably from the clipboard)
- // before sending it over the terminal's connection, converting
- // Windows-space \r\n line-endings to \r line-endings
- void TermControl::_SendPastedTextToConnection(const std::wstring& wstr)
- {
- // Some notes on this implementation:
- //
- // - std::regex can do this in a single line, but is somewhat
- // overkill for a simple search/replace operation (and its
- // performance guarantees aren't exactly stellar)
- // - The STL doesn't have a simple string search/replace method.
- // This fact is lamentable.
- // - This line-ending conversion is intentionally fairly
- // conservative, to avoid stripping out lone \n characters
- // where they could conceivably be intentional.
-
- std::wstring stripped{ wstr };
-
- std::wstring::size_type pos = 0;
-
- while ((pos = stripped.find(L"\r\n", pos)) != std::wstring::npos)
- {
- stripped.replace(pos, 2, L"\r");
- }
-
- _connection.WriteInput(stripped);
- _terminal->TrySnapOnInput();
- }
-
- // Method Description:
- // - Update the font with the renderer. This will be called either when the
- // font changes or the DPI changes, as DPI changes will necessitate a
- // font change. This method will *not* change the buffer/viewport size
- // to account for the new glyph dimensions. Callers should make sure to
- // appropriately call _DoResize after this method is called.
- // Arguments:
- // - initialUpdate: whether this font update should be considered as being
- // concerned with initialization process. Value forwarded to event handler.
- void TermControl::_UpdateFont(const bool initialUpdate)
- {
- auto lock = _terminal->LockForWriting();
-
- const int newDpi = static_cast(static_cast(USER_DEFAULT_SCREEN_DPI) * _swapChainPanel.CompositionScaleX());
-
- // TODO: MSFT:20895307 If the font doesn't exist, this doesn't
- // actually fail. We need a way to gracefully fallback.
- _renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
-
- const auto actualNewSize = _actualFont.GetSize();
- _fontSizeChangedHandlers(actualNewSize.X, actualNewSize.Y, initialUpdate);
- }
-
- // Method Description:
- // - Set the font size of the terminal control.
- // Arguments:
- // - fontSize: The size of the font.
- void TermControl::_SetFontSize(int fontSize)
- {
- try
- {
- // Make sure we have a non-zero font size
- const auto newSize = std::max(gsl::narrow(fontSize), static_cast(1));
- const auto fontFace = _settings.FontFace();
- _actualFont = { fontFace, 0, 10, { 0, newSize }, CP_UTF8, false };
- _desiredFont = { _actualFont };
-
- // Refresh our font with the renderer
- _UpdateFont();
- // Resize the terminal's BUFFER to match the new font size. This does
- // NOT change the size of the window, because that can lead to more
- // problems (like what happens when you change the font size while the
- // window is maximized?)
- auto lock = _terminal->LockForWriting();
- _DoResize(_swapChainPanel.ActualWidth(), _swapChainPanel.ActualHeight());
- }
- CATCH_LOG();
- }
-
- // Method Description:
- // - Triggered when the swapchain changes size. We use this to resize the
- // terminal buffers to match the new visible size.
- // Arguments:
- // - e: a SizeChangedEventArgs with the new dimensions of the SwapChainPanel
- void TermControl::_SwapChainSizeChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/,
- SizeChangedEventArgs const& e)
- {
- if (!_initializedTerminal)
- {
- return;
- }
-
- auto lock = _terminal->LockForWriting();
-
- const auto foundationSize = e.NewSize();
-
- _DoResize(foundationSize.Width, foundationSize.Height);
- }
-
- void TermControl::_SwapChainScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
- Windows::Foundation::IInspectable const& /*args*/)
- {
- const auto scale = sender.CompositionScaleX();
- const auto dpi = (int)(scale * USER_DEFAULT_SCREEN_DPI);
-
- // TODO: MSFT: 21169071 - Shouldn't this all happen through _renderer and trigger the invalidate automatically on DPI change?
- THROW_IF_FAILED(_renderEngine->UpdateDpi(dpi));
- _renderer->TriggerRedrawAll();
- }
-
- // Method Description:
- // - Toggle the cursor on and off when called by the cursor blink timer.
- // Arguments:
- // - sender: not used
- // - e: not used
- void TermControl::_BlinkCursor(Windows::Foundation::IInspectable const& /* sender */,
- Windows::Foundation::IInspectable const& /* e */)
- {
- if ((_closing) || (!_terminal->IsCursorBlinkingAllowed() && _terminal->IsCursorVisible()))
- {
- return;
- }
- _terminal->SetCursorVisible(!_terminal->IsCursorVisible());
- }
-
- // Method Description:
- // - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
- // Arguments:
- // - cursorPosition: in pixels, relative to the origin of the control
- void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition)
- {
- auto terminalPosition = _GetTerminalPosition(cursorPosition);
-
- const short lastVisibleRow = std::max(_terminal->GetViewport().Height() - 1, 0);
- const short lastVisibleCol = std::max(_terminal->GetViewport().Width() - 1, 0);
-
- terminalPosition.Y = std::clamp(terminalPosition.Y, 0, lastVisibleRow);
- terminalPosition.X = std::clamp(terminalPosition.X, 0, lastVisibleCol);
-
- // save location (for rendering) + render
- _terminal->SetEndSelectionPosition(terminalPosition);
- _renderer->TriggerSelection();
- }
-
- // Method Description:
- // - Process a resize event that was initiated by the user. This can either
- // be due to the user resizing the window (causing the swapchain to
- // resize) or due to the DPI changing (causing us to need to resize the
- // buffer to match)
- // Arguments:
- // - newWidth: the new width of the swapchain, in pixels.
- // - newHeight: the new height of the swapchain, in pixels.
- void TermControl::_DoResize(const double newWidth, const double newHeight)
- {
- SIZE size;
- size.cx = static_cast(newWidth);
- size.cy = static_cast(newHeight);
-
- // Don't actually resize so small that a single character wouldn't fit
- // in either dimension. The buffer really doesn't like being size 0.
- if (size.cx < _actualFont.GetSize().X || size.cy < _actualFont.GetSize().Y)
- {
- return;
- }
-
- // Tell the dx engine that our window is now the new size.
- THROW_IF_FAILED(_renderEngine->SetWindowSize(size));
-
- // Invalidate everything
- _renderer->TriggerRedrawAll();
-
- // Convert our new dimensions to characters
- const auto viewInPixels = Viewport::FromDimensions({ 0, 0 },
- { static_cast(size.cx), static_cast(size.cy) });
- const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
-
- // If this function succeeds with S_FALSE, then the terminal didn't
- // actually change size. No need to notify the connection of this
- // no-op.
- // TODO: MSFT:20642295 Resizing the buffer will corrupt it
- // I believe we'll need support for CSI 2J, and additionally I think
- // we're resetting the viewport to the top
- const HRESULT hr = _terminal->UserResize({ vp.Width(), vp.Height() });
- if (SUCCEEDED(hr) && hr != S_FALSE)
- {
- _connection.Resize(vp.Height(), vp.Width());
- }
- }
-
- void TermControl::_TerminalTitleChanged(const std::wstring_view& wstr)
- {
- _titleChangedHandlers(winrt::hstring{ wstr });
- }
-
- // Method Description:
- // - Update the position and size of the scrollbar to match the given
- // viewport top, viewport height, and buffer size.
- // The change will be actually handled in _ScrollbarChangeHandler.
- // This should be done on the UI thread. Make sure the caller is calling
- // us in a RunAsync block.
- // Arguments:
- // - viewTop: the top of the visible viewport, in rows. 0 indicates the top
- // of the buffer.
- // - viewHeight: the height of the viewport in rows.
- // - bufferSize: the length of the buffer, in rows
- void TermControl::_ScrollbarUpdater(Controls::Primitives::ScrollBar scrollBar,
- const int viewTop,
- const int viewHeight,
- const int bufferSize)
- {
- // The terminal is already in the scroll position it wants, so no need
- // to tell it to scroll.
- _isTerminalInitiatedScroll = true;
-
- const auto hiddenContent = bufferSize - viewHeight;
- scrollBar.Maximum(hiddenContent);
- scrollBar.Minimum(0);
- scrollBar.ViewportSize(viewHeight);
- scrollBar.Value(viewTop);
-
- _isTerminalInitiatedScroll = false;
- }
-
- // Method Description:
- // - Update the position and size of the scrollbar to match the given
- // viewport top, viewport height, and buffer size.
- // Additionally fires a ScrollPositionChanged event for anyone who's
- // registered an event handler for us.
- // Arguments:
- // - viewTop: the top of the visible viewport, in rows. 0 indicates the top
- // of the buffer.
- // - viewHeight: the height of the viewport in rows.
- // - bufferSize: the length of the buffer, in rows
- winrt::fire_and_forget TermControl::_TerminalScrollPositionChanged(const int viewTop,
- const int viewHeight,
- const int bufferSize)
- {
- // Since this callback fires from non-UI thread, we might be already
- // closed/closing.
- if (_closing.load())
- {
- return;
- }
-
- _scrollPositionChangedHandlers(viewTop, viewHeight, bufferSize);
-
- auto weakThis{ get_weak() };
-
- co_await winrt::resume_foreground(_scrollBar.Dispatcher());
-
- // Even if we weren't closed/closing few lines above, we might be
- // while waiting for this block of code to be dispatched.
- // If 'weakThis' is locked, then we can safely work with 'this'
- if (auto control{ weakThis.get() })
- {
- if (!_closing.load())
- {
- // Update our scrollbar
- _ScrollbarUpdater(_scrollBar, viewTop, viewHeight, bufferSize);
- }
- }
- }
-
- hstring TermControl::Title()
- {
- if (!_initializedTerminal)
- return L"";
-
- hstring hstr(_terminal->GetConsoleTitle());
- return hstr;
- }
-
- // Method Description:
- // - Given a copy-able selection, get the selected text from the buffer and send it to the
- // Windows Clipboard (CascadiaWin32:main.cpp).
- // - CopyOnSelect does NOT clear the selection
- // Arguments:
- // - trimTrailingWhitespace: enable removing any whitespace from copied selection
- // and get text to appear on separate lines.
- bool TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace)
- {
- // no selection --> nothing to copy
- if (_terminal == nullptr || !_terminal->IsSelectionActive())
- {
- return false;
- }
- // extract text from buffer
- const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace);
-
- // convert text: vector --> string
- std::wstring textData;
- for (const auto& text : bufferData.text)
- {
- textData += text;
- }
-
- // convert text to HTML format
- const auto htmlData = TextBuffer::GenHTML(bufferData,
- _actualFont.GetUnscaledSize().Y,
- _actualFont.GetFaceName(),
- _settings.DefaultBackground(),
- "Windows Terminal");
-
- // convert to RTF format
- const auto rtfData = TextBuffer::GenRTF(bufferData,
- _actualFont.GetUnscaledSize().Y,
- _actualFont.GetFaceName(),
- _settings.DefaultBackground());
-
- if (!_terminal->IsCopyOnSelectActive())
- {
- _terminal->ClearSelection();
- }
-
- // send data up for clipboard
- auto copyArgs = winrt::make_self(winrt::hstring(textData),
- winrt::to_hstring(htmlData),
- winrt::to_hstring(rtfData));
- _clipboardCopyHandlers(*this, *copyArgs);
- return true;
- }
-
- // Method Description:
- // - Initiate a paste operation.
- void TermControl::PasteTextFromClipboard()
- {
- // attach TermControl::_SendInputToConnection() as the clipboardDataHandler.
- // This is called when the clipboard data is loaded.
- auto clipboardDataHandler = std::bind(&TermControl::_SendPastedTextToConnection, this, std::placeholders::_1);
- auto pasteArgs = winrt::make_self(clipboardDataHandler);
-
- // send paste event up to TermApp
- _clipboardPasteHandlers(*this, *pasteArgs);
- }
-
- void TermControl::Close()
- {
- if (!_closing.exchange(true))
- {
- // Stop accepting new output and state changes before we disconnect everything.
- _connection.TerminalOutput(_connectionOutputEventToken);
- _connectionStateChangedRevoker.revoke();
-
- _tsfInputControl.Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
-
- if (auto localConnection{ std::exchange(_connection, nullptr) })
- {
- localConnection.Close();
- // connection is destroyed.
- }
-
- if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
- {
- if (auto localRenderer{ std::exchange(_renderer, nullptr) })
- {
- localRenderer->TriggerTeardown();
- // renderer is destroyed
- }
- // renderEngine is destroyed
- }
-
- if (auto localTerminal{ std::exchange(_terminal, nullptr) })
- {
- _initializedTerminal = false;
- // terminal is destroyed.
- }
- }
- }
-
- // Method Description:
- // - Scrolls the viewport of the terminal and updates the scroll bar accordingly
- // Arguments:
- // - viewTop: the viewTop to scroll to
- void TermControl::ScrollViewport(int viewTop)
- {
- _scrollBar.Value(viewTop);
- }
-
- int TermControl::GetScrollOffset()
- {
- return _terminal->GetScrollOffset();
- }
-
- // Function Description:
- // - Gets the height of the terminal in lines of text
- // Return Value:
- // - The height of the terminal in lines of text
- int TermControl::GetViewHeight() const
- {
- const auto viewPort = _terminal->GetViewport();
- return viewPort.Height();
- }
-
- // Function Description:
- // - Determines how much space (in pixels) an app would need to reserve to
- // create a control with the settings stored in the settings param. This
- // accounts for things like the font size and face, the initialRows and
- // initialCols, and scrollbar visibility. The returned sized is based upon
- // the provided DPI value
- // Arguments:
- // - settings: A IControlSettings with the settings to get the pixel size of.
- // - dpi: The DPI we should create the terminal at. This affects things such
- // as font size, scrollbar and other control scaling, etc. Make sure the
- // caller knows what monitor the control is about to appear on.
- // Return Value:
- // - a point containing the requested dimensions in pixels.
- winrt::Windows::Foundation::Point TermControl::GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi)
- {
- // Initialize our font information.
- const auto fontFace = settings.FontFace();
- const short fontHeight = gsl::narrow(settings.FontSize());
- // The font width doesn't terribly matter, we'll only be using the
- // height to look it up
- // The other params here also largely don't matter.
- // The family is only used to determine if the font is truetype or
- // not, but DX doesn't use that info at all.
- // The Codepage is additionally not actually used by the DX engine at all.
- FontInfo actualFont = { fontFace, 0, 10, { 0, fontHeight }, CP_UTF8, false };
- FontInfoDesired desiredFont = { actualFont };
-
- // If the settings have negative or zero row or column counts, ignore those counts.
- // (The lower TerminalCore layer also has upper bounds as well, but at this layer
- // we may eventually impose different ones depending on how many pixels we can address.)
- const auto cols = std::max(settings.InitialCols(), 1);
- const auto rows = std::max(settings.InitialRows(), 1);
-
- // Create a DX engine and initialize it with our font and DPI. We'll
- // then use it to measure how much space the requested rows and columns
- // will take up.
- // TODO: MSFT:21254947 - use a static function to do this instead of
- // instantiating a DxEngine
- auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
- THROW_IF_FAILED(dxEngine->UpdateDpi(dpi));
- THROW_IF_FAILED(dxEngine->UpdateFont(desiredFont, actualFont));
-
- const float scale = dxEngine->GetScaling();
- const auto fontSize = actualFont.GetSize();
-
- // Manually multiply by the scaling factor. The DX engine doesn't
- // actually store the scaled font size in the fontInfo.GetSize()
- // property when the DX engine is in Composition mode (which it is for
- // the Terminal). At runtime, this is fine, as we'll transform
- // everything by our scaling, so it'll work out. However, right now we
- // need to get the exact pixel count.
- const float fFontWidth = gsl::narrow(fontSize.X * scale);
- const float fFontHeight = gsl::narrow(fontSize.Y * scale);
-
- // UWP XAML scrollbars aren't guaranteed to be the same size as the
- // ComCtl scrollbars, but it's certainly close enough.
- const auto scrollbarSize = GetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
-
- double width = cols * fFontWidth;
-
- // Reserve additional space if scrollbar is intended to be visible
- if (settings.ScrollState() == ScrollbarState::Visible)
- {
- width += scrollbarSize;
- }
-
- double height = rows * fFontHeight;
- auto thickness = _ParseThicknessFromPadding(settings.Padding());
- width += thickness.Left + thickness.Right;
- height += thickness.Top + thickness.Bottom;
-
- return { gsl::narrow_cast(width), gsl::narrow_cast(height) };
- }
-
- // Method Description:
- // - Get the size of a single character of this control. The size is in
- // DIPs. If you need it in _pixels_, you'll need to multiply by the
- // current display scaling.
- // Arguments:
- // -
- // Return Value:
- // - The dimensions of a single character of this control, in DIPs
- winrt::Windows::Foundation::Size TermControl::CharacterDimensions() const
- {
- const auto fontSize = _actualFont.GetSize();
- return { gsl::narrow_cast(fontSize.X), gsl::narrow_cast(fontSize.Y) };
- }
-
- // Method Description:
- // - Get the absolute minimum size that this control can be resized to and
- // still have 1x1 character visible. This includes the space needed for
- // the scrollbar and the padding.
- // Arguments:
- // -
- // Return Value:
- // - The minimum size that this terminal control can be resized to and still
- // have a visible character.
- winrt::Windows::Foundation::Size TermControl::MinimumSize() const
- {
- const auto fontSize = _actualFont.GetSize();
- double width = fontSize.X;
- double height = fontSize.Y;
- // Reserve additional space if scrollbar is intended to be visible
- if (_settings.ScrollState() == ScrollbarState::Visible)
- {
- width += _scrollBar.ActualWidth();
- }
-
- // Account for the size of any padding
- const auto padding = _swapChainPanel.Margin();
- width += padding.Left + padding.Right;
- height += padding.Top + padding.Bottom;
-
- return { gsl::narrow_cast(width), gsl::narrow_cast(height) };
- }
-
- // Method Description:
- // - Adjusts given dimension (width or height) so that it aligns to the character grid.
- // The snap is always downward.
- // Arguments:
- // - widthOrHeight: if true operates on width, otherwise on height
- // - dimension: a dimension (width or height) to be snapped
- // Return Value:
- // - A dimension that would be aligned to the character grid.
- float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const
- {
- const auto fontSize = _actualFont.GetSize();
- const auto fontDimension = widthOrHeight ? fontSize.X : fontSize.Y;
-
- const auto padding = _swapChainPanel.Margin();
- auto nonTerminalArea = gsl::narrow_cast(widthOrHeight ?
- padding.Left + padding.Right :
- padding.Top + padding.Bottom);
-
- if (widthOrHeight && _settings.ScrollState() == ScrollbarState::Visible)
- {
- nonTerminalArea += gsl::narrow_cast(_scrollBar.ActualWidth());
- }
-
- const auto gridSize = dimension - nonTerminalArea;
- const int cells = static_cast(gridSize / fontDimension);
- return cells * fontDimension + nonTerminalArea;
- }
-
- // Method Description:
- // - Create XAML Thickness object based on padding props provided.
- // Used for controlling the TermControl XAML Grid container's Padding prop.
- // Arguments:
- // - padding: 2D padding values
- // Single Double value provides uniform padding
- // Two Double values provide isometric horizontal & vertical padding
- // Four Double values provide independent padding for 4 sides of the bounding rectangle
- // Return Value:
- // - Windows::UI::Xaml::Thickness object
- Windows::UI::Xaml::Thickness TermControl::_ParseThicknessFromPadding(const hstring padding)
- {
- const wchar_t singleCharDelim = L',';
- std::wstringstream tokenStream(padding.c_str());
- std::wstring token;
- uint8_t paddingPropIndex = 0;
- std::array thicknessArr = {};
- size_t* idx = nullptr;
-
- // Get padding values till we run out of delimiter separated values in the stream
- // or we hit max number of allowable values (= 4) for the bounding rectangle
- // Non-numeral values detected will default to 0
- // std::getline will not throw exception unless flags are set on the wstringstream
- // std::stod will throw invalid_argument exception if the input is an invalid double value
- // std::stod will throw out_of_range exception if the input value is more than DBL_MAX
- try
- {
- for (; std::getline(tokenStream, token, singleCharDelim) && (paddingPropIndex < thicknessArr.size()); paddingPropIndex++)
- {
- // std::stod internally calls wcstod which handles whitespace prefix (which is ignored)
- // & stops the scan when first char outside the range of radix is encountered
- // We'll be permissive till the extent that stod function allows us to be by default
- // Ex. a value like 100.3#535w2 will be read as 100.3, but ;df25 will fail
- thicknessArr[paddingPropIndex] = std::stod(token, idx);
- }
- }
- catch (...)
- {
- // If something goes wrong, even if due to a single bad padding value, we'll reset the index & return default 0 padding
- paddingPropIndex = 0;
- LOG_CAUGHT_EXCEPTION();
- }
-
- switch (paddingPropIndex)
- {
- case 1:
- return ThicknessHelper::FromUniformLength(thicknessArr[0]);
- case 2:
- return ThicknessHelper::FromLengths(thicknessArr[0], thicknessArr[1], thicknessArr[0], thicknessArr[1]);
- // No case for paddingPropIndex = 3, since it's not a norm to provide just Left, Top & Right padding values leaving out Bottom
- case 4:
- return ThicknessHelper::FromLengths(thicknessArr[0], thicknessArr[1], thicknessArr[2], thicknessArr[3]);
- default:
- return Thickness();
- }
- }
-
- // Method Description:
- // - Get the modifier keys that are currently pressed. This can be used to
- // find out which modifiers (ctrl, alt, shift) are pressed in events that
- // don't necessarily include that state.
- // Return Value:
- // - The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
- ControlKeyStates TermControl::_GetPressedModifierKeys() const
- {
- CoreWindow window = CoreWindow::GetForCurrentThread();
- // DONT USE
- // != CoreVirtualKeyStates::None
- // OR
- // == CoreVirtualKeyStates::Down
- // Sometimes with the key down, the state is Down | Locked.
- // Sometimes with the key up, the state is Locked.
- // IsFlagSet(Down) is the only correct solution.
-
- struct KeyModifier
- {
- VirtualKey vkey;
- ControlKeyStates flags;
- };
-
- constexpr std::array modifiers{ {
- { VirtualKey::RightMenu, ControlKeyStates::RightAltPressed },
- { VirtualKey::LeftMenu, ControlKeyStates::LeftAltPressed },
- { VirtualKey::RightControl, ControlKeyStates::RightCtrlPressed },
- { VirtualKey::LeftControl, ControlKeyStates::LeftCtrlPressed },
- { VirtualKey::Shift, ControlKeyStates::ShiftPressed },
- } };
-
- ControlKeyStates flags;
-
- for (const auto& mod : modifiers)
- {
- const auto state = window.GetKeyState(mod.vkey);
- const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
-
- if (isDown)
- {
- flags |= mod.flags;
- }
- }
-
- return flags;
- }
-
- // Method Description:
- // - Gets the corresponding viewport terminal position for the cursor
- // by excluding the padding and normalizing with the font size.
- // This is used for selection.
- // Arguments:
- // - cursorPosition: the (x,y) position of a given cursor (i.e.: mouse cursor).
- // NOTE: origin (0,0) is top-left.
- // Return Value:
- // - the corresponding viewport terminal position for the given Point parameter
- const COORD TermControl::_GetTerminalPosition(winrt::Windows::Foundation::Point cursorPosition)
- {
- // Exclude padding from cursor position calculation
- COORD terminalPosition = {
- static_cast(cursorPosition.X - _swapChainPanel.Margin().Left),
- static_cast(cursorPosition.Y - _swapChainPanel.Margin().Top)
- };
-
- const auto fontSize = _actualFont.GetSize();
- FAIL_FAST_IF(fontSize.X == 0);
- FAIL_FAST_IF(fontSize.Y == 0);
-
- // Normalize to terminal coordinates by using font size
- terminalPosition.X /= fontSize.X;
- terminalPosition.Y /= fontSize.Y;
-
- return terminalPosition;
- }
-
- // Method Description:
- // - Composition Completion handler for the TSFInputControl that
- // handles writing text out to TerminalConnection
- // Arguments:
- // - text: the text to write to TerminalConnection
- // Return Value:
- // -
- void TermControl::_CompositionCompleted(winrt::hstring text)
- {
- _connection.WriteInput(text);
- }
-
- // Method Description:
- // - CurrentCursorPosition handler for the TSFInputControl that
- // handles returning current cursor position.
- // Arguments:
- // - eventArgs: event for storing the current cursor position
- // Return Value:
- // -
- void TermControl::_CurrentCursorPositionHandler(const IInspectable& /*sender*/, const CursorPositionEventArgs& eventArgs)
- {
- const COORD cursorPos = _terminal->GetCursorPosition();
- Windows::Foundation::Point p = { gsl::narrow(cursorPos.X), gsl::narrow(cursorPos.Y) };
- eventArgs.CurrentPosition(p);
- }
-
- // Method Description:
- // - FontInfo handler for the TSFInputControl that
- // handles returning current font information
- // Arguments:
- // - eventArgs: event for storing the current font information
- // Return Value:
- // -
- void TermControl::_FontInfoHandler(const IInspectable& /*sender*/, const FontInfoEventArgs& eventArgs)
- {
- eventArgs.FontSize(CharacterDimensions());
- eventArgs.FontFace(_actualFont.GetFaceName());
- }
-
- // Method Description:
- // - Returns the number of clicks that occurred (double and triple click support)
- // Arguments:
- // - clickPos: the (x,y) position of a given cursor (i.e.: mouse cursor).
- // NOTE: origin (0,0) is top-left.
- // - clickTime: the timestamp that the click occurred
- // Return Value:
- // - if the click is in the same position as the last click and within the timeout, the number of clicks within that time window
- // - otherwise, 1
- const unsigned int TermControl::_NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime)
- {
- // if click occurred at a different location or past the multiClickTimer...
- Timestamp delta;
- THROW_IF_FAILED(UInt64Sub(clickTime, _lastMouseClick, &delta));
- if (clickPos != _lastMouseClickPos || delta > _multiClickTimer)
- {
- // exit early. This is a single click.
- _multiClickCounter = 1;
- }
- else
- {
- _multiClickCounter++;
- }
- return _multiClickCounter;
- }
-
- // Method Description:
- // - Calculates speed of single axis of auto scrolling. It has to allow for both
- // fast and precise selection.
- // Arguments:
- // - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
- // Return Value:
- // - positive speed in characters / sec
- double TermControl::_GetAutoScrollSpeed(double cursorDistanceFromBorder) const
- {
- // The numbers below just feel well, feel free to change.
- // TODO: Maybe account for space beyond border that user has available
- return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
- }
-
- // Method Description:
- // - Async handler for the "Drop" event. If a file was dropped onto our
- // root, we'll try to get the path of the file dropped onto us, and write
- // the full path of the file to our terminal connection. Like conhost, if
- // the path contains a space, we'll wrap the path in quotes.
- // - Unlike conhost, if multiple files are dropped onto the terminal, we'll
- // write all the paths to the terminal, separated by spaces.
- // Arguments:
- // - e: The DragEventArgs from the Drop event
- // Return Value:
- // -
- winrt::fire_and_forget TermControl::_DoDragDrop(DragEventArgs const e)
- {
- if (e.DataView().Contains(StandardDataFormats::StorageItems()))
- {
- auto items = co_await e.DataView().GetStorageItemsAsync();
- if (items.Size() > 0)
- {
- std::wstring allPaths;
- for (auto item : items)
- {
- // Join the paths with spaces
- if (!allPaths.empty())
- {
- allPaths += L" ";
- }
-
- std::wstring fullPath{ item.Path() };
- const auto containsSpaces = std::find(fullPath.begin(),
- fullPath.end(),
- L' ') != fullPath.end();
-
- auto lock = _terminal->LockForWriting();
-
- if (containsSpaces)
- {
- fullPath.insert(0, L"\"");
- fullPath += L"\"";
- }
-
- allPaths += fullPath;
- }
- _SendInputToConnection(allPaths);
- }
- }
- }
-
- // Method Description:
- // - Synchronous handler for the "Drop" event. We'll dispatch the async
- // _DoDragDrop method to handle this, because getting information about
- // the file that was potentially dropped onto us must be done off the UI
- // thread.
- // Arguments:
- // - e: The DragEventArgs from the Drop event
- // Return Value:
- // -
- void TermControl::_DragDropHandler(Windows::Foundation::IInspectable const& /*sender*/,
- DragEventArgs const& e)
- {
- // Dispatch an async method to handle the drop event.
- _DoDragDrop(e);
- }
-
- // Method Description:
- // - Handle the DragOver event. We'll signal that the drag operation we
- // support is the "copy" operation, and we'll also customize the
- // appearance of the drag-drop UI, by removing the preview and setting a
- // custom caption. For more information, see
- // https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#customize-the-ui
- // Arguments:
- // - e: The DragEventArgs from the DragOver event
- // Return Value:
- // -
- void TermControl::_DragOverHandler(Windows::Foundation::IInspectable const& /*sender*/,
- DragEventArgs const& e)
- {
- // Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
- e.AcceptedOperation(winrt::Windows::ApplicationModel::DataTransfer::DataPackageOperation::Copy);
-
- // Sets custom UI text
- e.DragUIOverride().Caption(RS_(L"DragFileCaption"));
- // Sets if the caption is visible
- e.DragUIOverride().IsCaptionVisible(true);
- // Sets if the dragged content is visible
- e.DragUIOverride().IsContentVisible(false);
- // Sets if the glyph is visibile
- e.DragUIOverride().IsGlyphVisible(false);
- }
-
- // -------------------------------- WinRT Events ---------------------------------
- // Winrt events need a method for adding a callback to the event and removing the callback.
- // These macros will define them both for you.
- DEFINE_EVENT(TermControl, TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs);
- DEFINE_EVENT(TermControl, FontSizeChanged, _fontSizeChangedHandlers, TerminalControl::FontSizeChangedEventArgs);
- DEFINE_EVENT(TermControl, ScrollPositionChanged, _scrollPositionChangedHandlers, TerminalControl::ScrollPositionChangedEventArgs);
-
- DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs);
- DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs);
- // clang-format on
-}
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "pch.h"
+#include "TermControl.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "..\..\types\inc\GlyphWidth.hpp"
+
+#include "TermControl.g.cpp"
+#include "TermControlAutomationPeer.h"
+
+using namespace ::Microsoft::Console::Types;
+using namespace ::Microsoft::Terminal::Core;
+using namespace winrt::Windows::UI::Xaml;
+using namespace winrt::Windows::UI::Xaml::Automation::Peers;
+using namespace winrt::Windows::UI::Core;
+using namespace winrt::Windows::System;
+using namespace winrt::Microsoft::Terminal::Settings;
+using namespace winrt::Windows::ApplicationModel::DataTransfer;
+
+namespace winrt::Microsoft::Terminal::TerminalControl::implementation
+{
+ // 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 */)
+ {
+ 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;
+ }
+
+ TermControl::TermControl() :
+ TermControl(Settings::TerminalSettings{}, TerminalConnection::ITerminalConnection{ nullptr })
+ {
+ }
+
+ TermControl::TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection) :
+ _connection{ connection },
+ _initializedTerminal{ false },
+ _root{ nullptr },
+ _swapChainPanel{ nullptr },
+ _settings{ settings },
+ _closing{ false },
+ _isTerminalInitiatedScroll{ false },
+ _autoScrollVelocity{ 0 },
+ _autoScrollingPointerPoint{ std::nullopt },
+ _autoScrollTimer{},
+ _lastAutoScrollUpdateTime{ std::nullopt },
+ _desiredFont{ DEFAULT_FONT_FACE, 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 },
+ _actualFont{ DEFAULT_FONT_FACE, 0, 10, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false },
+ _touchAnchor{ std::nullopt },
+ _cursorTimer{},
+ _lastMouseClick{},
+ _lastMouseClickPos{},
+ _searchBox{ nullptr },
+ _tsfInputControl{ nullptr }
+ {
+ _EnsureStaticInitialization();
+ _Create();
+ }
+
+ void TermControl::_Create()
+ {
+ Controls::Grid container;
+
+ Controls::ColumnDefinition contentColumn{};
+ Controls::ColumnDefinition scrollbarColumn{};
+ contentColumn.Width(GridLength{ 1.0, GridUnitType::Star });
+ scrollbarColumn.Width(GridLength{ 1.0, GridUnitType::Auto });
+
+ container.ColumnDefinitions().Append(contentColumn);
+ container.ColumnDefinitions().Append(scrollbarColumn);
+
+ _scrollBar = Controls::Primitives::ScrollBar{};
+ _scrollBar.Orientation(Controls::Orientation::Vertical);
+ _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
+ _scrollBar.HorizontalAlignment(HorizontalAlignment::Right);
+ _scrollBar.VerticalAlignment(VerticalAlignment::Stretch);
+
+ // Initialize the scrollbar with some placeholder values.
+ // The scrollbar will be updated with real values on _Initialize
+ _scrollBar.Maximum(1);
+ _scrollBar.ViewportSize(10);
+ _scrollBar.IsTabStop(false);
+ _scrollBar.SmallChange(1);
+ _scrollBar.LargeChange(4);
+ _scrollBar.Visibility(Visibility::Visible);
+
+ _tsfInputControl = TSFInputControl();
+ _tsfInputControl.CompositionCompleted({ this, &TermControl::_CompositionCompleted });
+ _tsfInputControl.CurrentCursorPosition({ this, &TermControl::_CurrentCursorPositionHandler });
+ _tsfInputControl.CurrentFontInfo({ this, &TermControl::_FontInfoHandler });
+ container.Children().Append(_tsfInputControl);
+
+ // Create the SwapChainPanel that will display our content
+ Controls::SwapChainPanel swapChainPanel;
+
+ _sizeChangedRevoker = swapChainPanel.SizeChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainSizeChanged });
+ _compositionScaleChangedRevoker = swapChainPanel.CompositionScaleChanged(winrt::auto_revoke, { this, &TermControl::_SwapChainScaleChanged });
+
+ // Initialize the terminal only once the swapchainpanel is loaded - that
+ // way, we'll be able to query the real pixel size it got on layout
+ _layoutUpdatedRevoker = swapChainPanel.LayoutUpdated(winrt::auto_revoke, [this](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.
+
+ if (_InitializeTerminal())
+ {
+ // Only let this succeed once.
+ _layoutUpdatedRevoker.revoke();
+ }
+ });
+
+ container.Children().Append(swapChainPanel);
+ container.Children().Append(_scrollBar);
+ Controls::Grid::SetColumn(swapChainPanel, 0);
+ Controls::Grid::SetColumn(_scrollBar, 1);
+
+ Controls::Grid root{};
+ Controls::Image bgImageLayer{};
+ root.Children().Append(bgImageLayer);
+ root.Children().Append(container);
+
+ _root = root;
+ _bgImageLayer = bgImageLayer;
+
+ _swapChainPanel = swapChainPanel;
+ this->Content(_root);
+
+ _ApplyUISettings();
+
+ // These are important:
+ // 1. When we get tapped, focus us
+ _tappedRevoker = this->Tapped(winrt::auto_revoke, [this](auto&, auto& e) {
+ Focus(FocusState::Pointer);
+ e.Handled(true);
+ });
+ // 2. Make sure we can be focused (why this isn't `Focusable` I'll never know)
+ this->IsTabStop(true);
+ // 3. Actually not sure about this one. Maybe it isn't necessary either.
+ this->AllowFocusOnInteraction(true);
+
+ // DON'T CALL _InitializeTerminal here - wait until the swap chain is loaded to do that.
+
+ // Subscribe to the connection's disconnected event and call our connection closed handlers.
+ _connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
+ _ConnectionStateChangedHandlers(*this, nullptr);
+ });
+
+ _root.AllowDrop(true);
+ _root.Drop({ get_weak(), &TermControl::_DragDropHandler });
+ _root.DragOver({ get_weak(), &TermControl::_DragOverHandler });
+ }
+
+ // Method Description:
+ // - Create the SearchBoxControl object, and attach it
+ // to the Terminal Control root
+ // Arguments:
+ // -
+ // Return Value:
+ // -
+ void TermControl::CreateSearchBoxControl()
+ {
+ if (!_searchBox)
+ {
+ _searchBox = winrt::make_self();
+ _searchBox->HorizontalAlignment(HorizontalAlignment::Right);
+ _searchBox->VerticalAlignment(VerticalAlignment::Top);
+ // We need to make sure the searchbox does not overlap
+ // with the scroll bar
+ Thickness searchBoxPadding = { 0, 0, _scrollBar.ActualWidth(), 0 };
+ _searchBox->Margin(searchBoxPadding);
+
+ _root.Children().Append(*_searchBox);
+
+ // Event handlers
+ _searchBox->Search({ get_weak(), &TermControl::_Search });
+ _searchBox->Closed({ get_weak(), &TermControl::_CloseSearchBoxControl });
+ }
+
+ _searchBox->SetFocusOnTextbox();
+ }
+
+ // Method Description:
+ // - Search text in text buffer. This is triggered if the user click
+ // search button or press enter.
+ // Arguments:
+ // - text: the text to search
+ // - goForward: boolean that represents if the current search direction is forward
+ // - caseSensitive: boolean that represents if the current search is case sensitive
+ // Return Value:
+ // -
+ void TermControl::_Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive)
+ {
+ if (text.size() == 0)
+ {
+ return;
+ }
+
+ const Search::Direction direction = goForward ?
+ Search::Direction::Forward :
+ Search::Direction::Backward;
+
+ const Search::Sensitivity sensitivity = caseSensitive ?
+ Search::Sensitivity::CaseSensitive :
+ Search::Sensitivity::CaseInsensitive;
+
+ Search search(*GetUiaData(), text.c_str(), direction, sensitivity);
+ auto lock = _terminal->LockForWriting();
+ if (search.FindNext())
+ {
+ _terminal->SetBoxSelection(false);
+ search.Select();
+ _renderer->TriggerSelection();
+ }
+ }
+
+ // Method Description:
+ // - The handler for the close button or pressing "Esc" when focusing on the
+ // search dialog.
+ // This removes the SearchBoxControl object from the XAML tree,
+ // reset smart pointer and set focus back to Terminal
+ // Arguments:
+ // - IInspectable: not used
+ // - RoutedEventArgs: not used
+ // Return Value:
+ // -
+ void TermControl::_CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& /*sender*/, RoutedEventArgs const& /*args*/)
+ {
+ unsigned int idx;
+ _root.Children().IndexOf(*_searchBox, idx);
+ _root.Children().RemoveAt(idx);
+
+ _searchBox = nullptr;
+
+ // Set focus back to terminal control
+ this->Focus(FocusState::Programmatic);
+ }
+
+ // Method Description:
+ // - Given new settings for this profile, applies the settings to the current terminal.
+ // Arguments:
+ // - newSettings: New settings values for the profile in this terminal.
+ // Return Value:
+ // -
+ winrt::fire_and_forget TermControl::UpdateSettings(Settings::IControlSettings newSettings)
+ {
+ _settings = newSettings;
+ auto weakThis{ get_weak() };
+
+ // Dispatch a call to the UI thread to apply the new settings to the
+ // terminal.
+ co_await winrt::resume_foreground(_root.Dispatcher());
+
+ // If 'weakThis' is locked, then we can safely work with 'this'
+ if (auto control{ weakThis.get() })
+ {
+ // Update our control settings
+ _ApplyUISettings();
+
+ // Update DxEngine's SelectionBackground
+ _renderEngine->SetSelectionBackground(_settings.SelectionBackground());
+
+ // Update the terminal core with its new Core settings
+ _terminal->UpdateSettings(_settings);
+
+ // Refresh our font with the renderer
+ _UpdateFont();
+
+ const auto width = _swapChainPanel.ActualWidth();
+ const auto height = _swapChainPanel.ActualHeight();
+ if (width != 0 && height != 0)
+ {
+ // If the font size changed, or the _swapchainPanel's size changed
+ // for any reason, we'll need to make sure to also resize the
+ // buffer. _DoResize will invalidate everything for us.
+ auto lock = _terminal->LockForWriting();
+ _DoResize(width, height);
+ }
+
+ // set TSF Foreground
+ Media::SolidColorBrush foregroundBrush{};
+ foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
+ _tsfInputControl.Foreground(foregroundBrush);
+ }
+ }
+
+ // Method Description:
+ // - Style our UI elements based on the values in our _settings, and set up
+ // other control-specific settings. This method will be called whenever
+ // the settings are reloaded.
+ // * Calls _InitializeBackgroundBrush to set up the Xaml brush responsible
+ // for the control's background
+ // * Calls _BackgroundColorChanged to style the background of the control
+ // - Core settings will be passed to the terminal in _InitializeTerminal
+ // Arguments:
+ // -
+ // Return Value:
+ // -
+ void TermControl::_ApplyUISettings()
+ {
+ _InitializeBackgroundBrush();
+
+ uint32_t bg = _settings.DefaultBackground();
+ _BackgroundColorChanged(bg);
+
+ // Apply padding as swapChainPanel's margin
+ auto newMargin = _ParseThicknessFromPadding(_settings.Padding());
+ auto existingMargin = _swapChainPanel.Margin();
+ _swapChainPanel.Margin(newMargin);
+
+ // Initialize our font information.
+ const auto fontFace = _settings.FontFace();
+ const short fontHeight = gsl::narrow(_settings.FontSize());
+ // The font width doesn't terribly matter, we'll only be using the
+ // height to look it up
+ // The other params here also largely don't matter.
+ // The family is only used to determine if the font is truetype or
+ // not, but DX doesn't use that info at all.
+ // The Codepage is additionally not actually used by the DX engine at all.
+ _actualFont = { fontFace, 0, 10, { 0, fontHeight }, CP_UTF8, false };
+ _desiredFont = { _actualFont };
+
+ // set TSF Foreground
+ Media::SolidColorBrush foregroundBrush{};
+ foregroundBrush.Color(ColorRefToColor(_settings.DefaultForeground()));
+ _tsfInputControl.Foreground(foregroundBrush);
+ _tsfInputControl.Margin(newMargin);
+
+ // set number of rows to scroll at a time
+ _rowsToScroll = _settings.RowsToScroll();
+ }
+
+ // Method Description:
+ // - Set up each layer's brush used to display the control's background.
+ // - Respects the settings for acrylic, background image and opacity from
+ // _settings.
+ // * If acrylic is not enabled, setup a solid color background, otherwise
+ // use bgcolor as acrylic's tint
+ // - Avoids image flickering and acrylic brush redraw if settings are changed
+ // but the appropriate brush is still in place.
+ // - Does not apply background color outside of acrylic mode;
+ // _BackgroundColorChanged must be called to do so.
+ // Arguments:
+ // -
+ // Return Value:
+ // -
+ void TermControl::_InitializeBackgroundBrush()
+ {
+ if (_settings.UseAcrylic())
+ {
+ // See if we've already got an acrylic background brush
+ // to avoid the flicker when setting up a new one
+ auto acrylic = _root.Background().try_as();
+
+ // Instantiate a brush if there's not already one there
+ if (acrylic == nullptr)
+ {
+ acrylic = Media::AcrylicBrush{};
+ acrylic.BackgroundSource(Media::AcrylicBackgroundSource::HostBackdrop);
+ }
+
+ // see GH#1082: Initialize background color so we don't get a
+ // fade/flash when _BackgroundColorChanged is called
+ uint32_t color = _settings.DefaultBackground();
+ winrt::Windows::UI::Color bgColor{};
+ bgColor.R = GetRValue(color);
+ bgColor.G = GetGValue(color);
+ bgColor.B = GetBValue(color);
+ bgColor.A = 255;
+
+ acrylic.FallbackColor(bgColor);
+ acrylic.TintColor(bgColor);
+
+ // Apply brush settings
+ acrylic.TintOpacity(_settings.TintOpacity());
+
+ // Apply brush to control if it's not already there
+ if (_root.Background() != acrylic)
+ {
+ _root.Background(acrylic);
+ }
+ }
+ else
+ {
+ Media::SolidColorBrush solidColor{};
+ _root.Background(solidColor);
+ }
+
+ if (!_settings.BackgroundImage().empty())
+ {
+ Windows::Foundation::Uri imageUri{ _settings.BackgroundImage() };
+
+ // Check if the image brush is already pointing to the image
+ // in the modified settings; if it isn't (or isn't there),
+ // set a new image source for the brush
+ auto imageSource = _bgImageLayer.Source().try_as();
+
+ if (imageSource == nullptr ||
+ imageSource.UriSource() == nullptr ||
+ imageSource.UriSource().RawUri() != imageUri.RawUri())
+ {
+ // Note that BitmapImage handles the image load asynchronously,
+ // which is especially important since the image
+ // may well be both large and somewhere out on the
+ // internet.
+ Media::Imaging::BitmapImage image(imageUri);
+ _bgImageLayer.Source(image);
+ }
+
+ // Apply stretch, opacity and alignment settings
+ _bgImageLayer.Stretch(_settings.BackgroundImageStretchMode());
+ _bgImageLayer.Opacity(_settings.BackgroundImageOpacity());
+ _bgImageLayer.HorizontalAlignment(_settings.BackgroundImageHorizontalAlignment());
+ _bgImageLayer.VerticalAlignment(_settings.BackgroundImageVerticalAlignment());
+ }
+ else
+ {
+ _bgImageLayer.Source(nullptr);
+ }
+ }
+
+ // Method Description:
+ // - Style the background of the control with the provided background color
+ // Arguments:
+ // - color: The background color to use as a uint32 (aka DWORD COLORREF)
+ // Return Value:
+ // -
+ winrt::fire_and_forget TermControl::_BackgroundColorChanged(const uint32_t color)
+ {
+ auto weakThis{ get_weak() };
+
+ co_await winrt::resume_foreground(_root.Dispatcher());
+
+ if (auto control{ weakThis.get() })
+ {
+ const auto R = GetRValue(color);
+ const auto G = GetGValue(color);
+ const auto B = GetBValue(color);
+
+ winrt::Windows::UI::Color bgColor{};
+ bgColor.R = R;
+ bgColor.G = G;
+ bgColor.B = B;
+ bgColor.A = 255;
+
+ if (auto acrylic = _root.Background().try_as())
+ {
+ acrylic.FallbackColor(bgColor);
+ acrylic.TintColor(bgColor);
+ }
+ else if (auto solidColor = _root.Background().try_as())
+ {
+ solidColor.Color(bgColor);
+ }
+
+ // Set the default background as transparent to prevent the
+ // DX layer from overwriting the background image or acrylic effect
+ _settings.DefaultBackground(ARGB(0, R, G, B));
+ }
+ }
+
+ TermControl::~TermControl()
+ {
+ Close();
+ }
+
+ // Method Description:
+ // - Creates an automation peer for the Terminal Control, enabling accessibility on our control.
+ // Arguments:
+ // - None
+ // Return Value:
+ // - The automation peer for our control
+ Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer()
+ try
+ {
+ if (GetUiaData())
+ {
+ // create a custom automation peer with this code pattern:
+ // (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
+ auto autoPeer = winrt::make_self(this);
+
+ _uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
+ _renderer->AddRenderEngine(_uiaEngine.get());
+ return *autoPeer;
+ }
+ return nullptr;
+ }
+ catch (...)
+ {
+ LOG_CAUGHT_EXCEPTION();
+ return nullptr;
+ }
+
+ ::Microsoft::Console::Types::IUiaData* TermControl::GetUiaData() const
+ {
+ return _terminal.get();
+ }
+
+ const FontInfo TermControl::GetActualFont() const
+ {
+ return _actualFont;
+ }
+
+ const Windows::UI::Xaml::Thickness TermControl::GetPadding() const
+ {
+ return _swapChainPanel.Margin();
+ }
+
+ TerminalConnection::ConnectionState TermControl::ConnectionState() const
+ {
+ return _connection.State();
+ }
+
+ winrt::fire_and_forget TermControl::SwapChainChanged()
+ {
+ if (!_initializedTerminal)
+ {
+ return;
+ }
+
+ auto chain = _renderEngine->GetSwapChain();
+ auto weakThis{ get_weak() };
+
+ co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
+
+ // If 'weakThis' is locked, then we can safely work with 'this'
+ if (auto control{ weakThis.get() })
+ {
+ auto lock = _terminal->LockForWriting();
+ auto nativePanel = _swapChainPanel.as();
+ nativePanel->SetSwapChain(chain.Get());
+ }
+ }
+
+ winrt::fire_and_forget TermControl::_SwapChainRoutine()
+ {
+ auto chain = _renderEngine->GetSwapChain();
+ auto weakThis{ get_weak() };
+
+ co_await winrt::resume_foreground(_swapChainPanel.Dispatcher());
+
+ if (auto control{ weakThis.get() })
+ {
+ _terminal->LockConsole();
+ auto nativePanel = _swapChainPanel.as();
+ nativePanel->SetSwapChain(chain.Get());
+ _terminal->UnlockConsole();
+ }
+ }
+
+ bool TermControl::_InitializeTerminal()
+ {
+ if (_initializedTerminal)
+ {
+ return false;
+ }
+
+ const auto windowWidth = _swapChainPanel.ActualWidth(); // Width() and Height() are NaN?
+ const auto windowHeight = _swapChainPanel.ActualHeight();
+
+ if (windowWidth == 0 || windowHeight == 0)
+ {
+ return false;
+ }
+
+ _terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>();
+
+ // First create the render thread.
+ // Then stash a local pointer to the render thread so we can initialize it and enable it
+ // to paint itself *after* we hand off its ownership to the renderer.
+ // We split up construction and initialization of the render thread object this way
+ // because the renderer and render thread have circular references to each other.
+ auto renderThread = std::make_unique<::Microsoft::Console::Render::RenderThread>();
+ auto* const localPointerToThread = renderThread.get();
+
+ // Now create the renderer and initialize the render thread.
+ _renderer = std::make_unique<::Microsoft::Console::Render::Renderer>(_terminal.get(), nullptr, 0, std::move(renderThread));
+ ::Microsoft::Console::Render::IRenderTarget& renderTarget = *_renderer;
+
+ THROW_IF_FAILED(localPointerToThread->Initialize(_renderer.get()));
+
+ // Set up the DX Engine
+ auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
+ _renderer->AddRenderEngine(dxEngine.get());
+
+ // Initialize our font with the renderer
+ // We don't have to care about DPI. We'll get a change message immediately if it's not 96
+ // and react accordingly.
+ _UpdateFont(true);
+
+ const COORD windowSize{ static_cast(windowWidth), static_cast(windowHeight) };
+
+ // Fist set up the dx engine with the window size in pixels.
+ // Then, using the font, get the number of characters that can fit.
+ // Resize our terminal connection to match that size, and initialize the terminal with that size.
+ const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, windowSize);
+ THROW_IF_FAILED(dxEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() }));
+
+ // Update DxEngine's SelectionBackground
+ dxEngine->SetSelectionBackground(_settings.SelectionBackground());
+
+ const auto vp = dxEngine->GetViewportInCharacters(viewInPixels);
+ const auto width = vp.Width();
+ const auto height = vp.Height();
+ _connection.Resize(height, width);
+
+ // Override the default width and height to match the size of the swapChainPanel
+ _settings.InitialCols(width);
+ _settings.InitialRows(height);
+
+ _terminal->CreateFromSettings(_settings, renderTarget);
+
+ // Tell the DX Engine to notify us when the swap chain changes.
+ dxEngine->SetCallback(std::bind(&TermControl::SwapChainChanged, this));
+
+ // TODO:GH#3927 - Make it possible to hot-reload this setting. Right
+ // here, the setting will only be used when the Terminal is initialized.
+ dxEngine->SetRetroTerminalEffects(_settings.RetroTerminalEffect());
+
+ THROW_IF_FAILED(dxEngine->Enable());
+ _renderEngine = std::move(dxEngine);
+
+ // This event is explicitly revoked in the destructor: does not need weak_ref
+ auto onReceiveOutputFn = [this](const hstring str) {
+ _terminal->Write(str);
+ };
+ _connectionOutputEventToken = _connection.TerminalOutput(onReceiveOutputFn);
+
+ auto inputFn = std::bind(&TermControl::_SendInputToConnection, this, std::placeholders::_1);
+ _terminal->SetWriteInputCallback(inputFn);
+
+ _SwapChainRoutine();
+
+ // Set up the height of the ScrollViewer and the grid we're using to fake our scrolling height
+ auto bottom = _terminal->GetViewport().BottomExclusive();
+ auto bufferHeight = bottom;
+
+ const auto originalMaximum = _scrollBar.Maximum();
+ const auto originalMinimum = _scrollBar.Minimum();
+ const auto originalValue = _scrollBar.Value();
+ const auto originalViewportSize = _scrollBar.ViewportSize();
+
+ _scrollBar.Maximum(bufferHeight - bufferHeight);
+ _scrollBar.Minimum(0);
+ _scrollBar.Value(0);
+ _scrollBar.ViewportSize(bufferHeight);
+ _scrollBar.ValueChanged({ this, &TermControl::_ScrollbarChangeHandler });
+ _scrollBar.PointerPressed({ this, &TermControl::_CapturePointer });
+ _scrollBar.PointerReleased({ this, &TermControl::_ReleasePointerCapture });
+
+ // Apply settings for scrollbar
+ if (_settings.ScrollState() == ScrollbarState::Visible)
+ {
+ _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
+ }
+ else if (_settings.ScrollState() == ScrollbarState::Hidden)
+ {
+ _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::None);
+
+ // In the scenario where the user has turned off the OS setting to automatically hide scollbars, the
+ // Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to
+ // achieve the intended effect.
+ _scrollBar.Visibility(Visibility::Collapsed);
+ }
+ else
+ {
+ // Default behavior
+ _scrollBar.IndicatorMode(Controls::Primitives::ScrollingIndicatorMode::MouseIndicator);
+ }
+
+ _root.PointerWheelChanged({ this, &TermControl::_MouseWheelHandler });
+
+ // These need to be hooked up to the SwapChainPanel because we don't want the scrollbar to respond to pointer events (GitHub #950)
+ _swapChainPanel.PointerPressed({ this, &TermControl::_PointerPressedHandler });
+ _swapChainPanel.PointerMoved({ this, &TermControl::_PointerMovedHandler });
+ _swapChainPanel.PointerReleased({ this, &TermControl::_PointerReleasedHandler });
+
+ localPointerToThread->EnablePainting();
+
+ // No matter what order these guys are in, The KeyDown's will fire
+ // before the CharacterReceived, so we can't easily get characters
+ // first, then fallback to getting keys from vkeys.
+ // TODO: This apparently handles keys and characters correctly, though
+ // I'd keep an eye on it, and test more.
+ // I presume that the characters that aren't translated by terminalInput
+ // just end up getting ignored, and the rest of the input comes
+ // through CharacterReceived.
+ // I don't believe there's a difference between KeyDown and
+ // PreviewKeyDown for our purposes
+ // These two handlers _must_ be on this, not _root.
+ this->PreviewKeyDown({ this, &TermControl::_KeyDownHandler });
+ this->CharacterReceived({ this, &TermControl::_CharacterHandler });
+
+ auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1);
+ _terminal->SetTitleChangedCallback(pfnTitleChanged);
+
+ auto pfnBackgroundColorChanged = std::bind(&TermControl::_BackgroundColorChanged, this, std::placeholders::_1);
+ _terminal->SetBackgroundCallback(pfnBackgroundColorChanged);
+
+ auto pfnScrollPositionChanged = std::bind(&TermControl::_TerminalScrollPositionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+ _terminal->SetScrollPositionChangedCallback(pfnScrollPositionChanged);
+
+ static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast(1.0 / 30.0 * 1000000));
+ _autoScrollTimer.Interval(AutoScrollUpdateInterval);
+ _autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll });
+
+ // Set up blinking cursor
+ int blinkTime = GetCaretBlinkTime();
+ if (blinkTime != INFINITE)
+ {
+ // Create a timer
+ _cursorTimer = std::make_optional(DispatcherTimer());
+ _cursorTimer.value().Interval(std::chrono::milliseconds(blinkTime));
+ _cursorTimer.value().Tick({ get_weak(), &TermControl::_BlinkCursor });
+ _cursorTimer.value().Start();
+ }
+ else
+ {
+ // The user has disabled cursor blinking
+ _cursorTimer = std::nullopt;
+ }
+
+ // import value from WinUser (convert from milli-seconds to micro-seconds)
+ _multiClickTimer = GetDoubleClickTime() * 1000;
+
+ _gotFocusRevoker = this->GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler });
+ _lostFocusRevoker = this->LostFocus(winrt::auto_revoke, { this, &TermControl::_LostFocusHandler });
+
+ // Focus the control here. If we do it up above (in _Create_), then the
+ // focus won't actually get passed to us. I believe this is because
+ // we're not technically a part of the UI tree yet, so focusing us
+ // becomes a no-op.
+ this->Focus(FocusState::Programmatic);
+
+ _connection.Start();
+ _initializedTerminal = true;
+ _InitializedHandlers(*this, nullptr);
+ return true;
+ }
+
+ void TermControl::_CharacterHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
+ Input::CharacterReceivedRoutedEventArgs const& e)
+ {
+ if (_closing)
+ {
+ return;
+ }
+
+ const auto ch = e.Character();
+
+ const bool handled = _terminal->SendCharEvent(ch);
+ e.Handled(handled);
+ }
+
+ void TermControl::_KeyDownHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/,
+ Input::KeyRoutedEventArgs const& e)
+ {
+ // If the current focused element is a child element of searchbox,
+ // we do not send this event up to terminal
+ if (_searchBox && _searchBox->ContainsFocus())
+ {
+ return;
+ }
+
+ // mark event as handled and do nothing if...
+ // - closing
+ // - key modifier is pressed
+ // NOTE: for key combos like CTRL + C, two events are fired (one for CTRL, one for 'C'). We care about the 'C' event and then check for key modifiers below.
+ if (_closing ||
+ e.OriginalKey() == VirtualKey::Control ||
+ e.OriginalKey() == VirtualKey::Shift ||
+ e.OriginalKey() == VirtualKey::Menu ||
+ e.OriginalKey() == VirtualKey::LeftWindows ||
+ e.OriginalKey() == VirtualKey::RightWindows)
+
+ {
+ e.Handled(true);
+ return;
+ }
+
+ const auto modifiers = _GetPressedModifierKeys();
+ const auto vkey = gsl::narrow_cast(e.OriginalKey());
+ const auto scanCode = gsl::narrow_cast(e.KeyStatus().ScanCode);
+ bool handled = false;
+
+ // GH#2235: Terminal::Settings hasn't been modified to differentiate between AltGr and Ctrl+Alt yet.
+ // -> Don't check for key bindings if this is an AltGr key combination.
+ if (!modifiers.IsAltGrPressed())
+ {
+ auto bindings = _settings.KeyBindings();
+ if (bindings)
+ {
+ handled = bindings.TryKeyChord({
+ modifiers.IsCtrlPressed(),
+ modifiers.IsAltPressed(),
+ modifiers.IsShiftPressed(),
+ vkey,
+ });
+ }
+ }
+
+ if (!handled)
+ {
+ handled = _TrySendKeyEvent(vkey, scanCode, modifiers);
+ }
+
+ // Manually prevent keyboard navigation with tab. We want to send tab to
+ // the terminal, and we don't want to be able to escape focus of the
+ // control with tab.
+ if (e.OriginalKey() == VirtualKey::Tab)
+ {
+ handled = true;
+ }
+
+ e.Handled(handled);
+ }
+
+ // Method Description:
+ // - Send this particular key event to the terminal.
+ // See Terminal::SendKeyEvent for more information.
+ // - Clears the current selection.
+ // - Makes the cursor briefly visible during typing.
+ // Arguments:
+ // - vkey: The vkey of the key pressed.
+ // - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
+ bool TermControl::_TrySendKeyEvent(const WORD vkey, const WORD scanCode, const ControlKeyStates modifiers)
+ {
+ // When there is a selection active, escape should clear it and NOT flow through
+ // to the terminal. With any other keypress, it should clear the selection AND
+ // flow through to the terminal.
+ if (_terminal->IsSelectionActive())
+ {
+ _terminal->ClearSelection();
+
+ if (vkey == VK_ESCAPE)
+ {
+ return true;
+ }
+ }
+
+ // If the terminal translated the key, mark the event as handled.
+ // This will prevent the system from trying to get the character out
+ // of it and sending us a CharacterReceived event.
+ const auto handled = vkey ? _terminal->SendKeyEvent(vkey, scanCode, modifiers) : true;
+
+ if (_cursorTimer.has_value())
+ {
+ // Manually show the cursor when a key is pressed. Restarting
+ // the timer prevents flickering.
+ _terminal->SetCursorVisible(true);
+ _cursorTimer.value().Start();
+ }
+
+ return handled;
+ }
+
+ // Method Description:
+ // - handle a mouse click event. Begin selection process.
+ // Arguments:
+ // - sender: the XAML element responding to the pointer input
+ // - args: event data
+ void TermControl::_PointerPressedHandler(Windows::Foundation::IInspectable const& sender,
+ Input::PointerRoutedEventArgs const& args)
+ {
+ _CapturePointer(sender, args);
+
+ const auto ptr = args.Pointer();
+ const auto point = args.GetCurrentPoint(_root);
+
+ if (!_focused)
+ {
+ Focus(FocusState::Pointer);
+
+ // Save the click position here when the terminal does not have focus
+ // because they might be performing a click-drag selection. Since we
+ // only want to start the selection when the user moves the pointer with
+ // the left mouse button held down, the PointerMovedHandler will use
+ // this saved position to set the SelectionAnchor.
+ _clickDragStartPos = point.Position();
+ }
+
+ if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
+ {
+ const auto modifiers = static_cast(args.KeyModifiers());
+ // static_cast to a uint32_t because we can't use the WI_IsFlagSet
+ // macro directly with a VirtualKeyModifiers
+ const auto altEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Menu));
+ const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift));
+
+ if (point.Properties().IsLeftButtonPressed())
+ {
+ // _clickDragStartPos having a value signifies to us that
+ // the user clicked on an unfocused terminal. We don't want
+ // a single left click from out of focus to start a selection,
+ // so we return fast here.
+ if (_clickDragStartPos)
+ {
+ args.Handled(true);
+ return;
+ }
+
+ const auto cursorPosition = point.Position();
+ const auto terminalPosition = _GetTerminalPosition(cursorPosition);
+
+ // handle ALT key
+ _terminal->SetBoxSelection(altEnabled);
+
+ auto clickCount = _NumberOfClicks(cursorPosition, point.Timestamp());
+
+ // This formula enables the number of clicks to cycle properly between single-, double-, and triple-click.
+ // To increase the number of acceptable click states, simply increment MAX_CLICK_COUNT and add another if-statement
+ const unsigned int MAX_CLICK_COUNT = 3;
+ const auto multiClickMapper = clickCount > MAX_CLICK_COUNT ? ((clickCount + MAX_CLICK_COUNT - 1) % MAX_CLICK_COUNT) + 1 : clickCount;
+
+ if (multiClickMapper == 3)
+ {
+ _terminal->TripleClickSelection(terminalPosition);
+ }
+ else if (multiClickMapper == 2)
+ {
+ _terminal->DoubleClickSelection(terminalPosition);
+ }
+ else
+ {
+ if (shiftEnabled && _terminal->IsSelectionActive())
+ {
+ _terminal->SetEndSelectionPosition(terminalPosition);
+ }
+ else
+ {
+ // save location before rendering
+ _terminal->SetSelectionAnchor(terminalPosition);
+ }
+
+ _lastMouseClick = point.Timestamp();
+ _lastMouseClickPos = cursorPosition;
+ }
+ _renderer->TriggerSelection();
+ }
+ else if (point.Properties().IsRightButtonPressed())
+ {
+ // copyOnSelect causes right-click to always paste
+ if (_terminal->IsCopyOnSelectActive() || !_terminal->IsSelectionActive())
+ {
+ PasteTextFromClipboard();
+ }
+ else
+ {
+ CopySelectionToClipboard(!shiftEnabled);
+ }
+ }
+ }
+ else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
+ {
+ const auto contactRect = point.Properties().ContactRect();
+ // Set our touch rect, to start a pan.
+ _touchAnchor = winrt::Windows::Foundation::Point{ contactRect.X, contactRect.Y };
+ }
+
+ args.Handled(true);
+ }
+
+ // Method Description:
+ // - handle a mouse moved event. Specifically handling mouse drag to update selection process.
+ // Arguments:
+ // - sender: not used
+ // - args: event data
+ void TermControl::_PointerMovedHandler(Windows::Foundation::IInspectable const& /*sender*/,
+ Input::PointerRoutedEventArgs const& args)
+ {
+ const auto ptr = args.Pointer();
+ const auto point = args.GetCurrentPoint(_root);
+
+ if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
+ {
+ if (point.Properties().IsLeftButtonPressed())
+ {
+ // If this does not have a value, it means that PointerPressedHandler already
+ // set the SelectionAnchor. If it does have a value, that means the user is
+ // performing a click-drag selection on an unfocused terminal, so
+ // a SelectionAnchor isn't set yet. We'll have to set it here.
+ if (_clickDragStartPos)
+ {
+ _terminal->SetSelectionAnchor(_GetTerminalPosition(*_clickDragStartPos));
+ }
+
+ const auto cursorPosition = point.Position();
+ _SetEndSelectionPointAtCursor(cursorPosition);
+
+ const double cursorBelowBottomDist = cursorPosition.Y - _swapChainPanel.Margin().Top - _swapChainPanel.ActualHeight();
+ const double cursorAboveTopDist = -1 * cursorPosition.Y + _swapChainPanel.Margin().Top;
+
+ constexpr double MinAutoScrollDist = 2.0; // Arbitrary value
+ double newAutoScrollVelocity = 0.0;
+ if (cursorBelowBottomDist > MinAutoScrollDist)
+ {
+ newAutoScrollVelocity = _GetAutoScrollSpeed(cursorBelowBottomDist);
+ }
+ else if (cursorAboveTopDist > MinAutoScrollDist)
+ {
+ newAutoScrollVelocity = -1.0 * _GetAutoScrollSpeed(cursorAboveTopDist);
+ }
+
+ if (newAutoScrollVelocity != 0)
+ {
+ _TryStartAutoScroll(point, newAutoScrollVelocity);
+ }
+ else
+ {
+ _TryStopAutoScroll(ptr.PointerId());
+ }
+ }
+ }
+ else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch && _touchAnchor)
+ {
+ const auto contactRect = point.Properties().ContactRect();
+ winrt::Windows::Foundation::Point newTouchPoint{ contactRect.X, contactRect.Y };
+ const auto anchor = _touchAnchor.value();
+
+ // Get the difference between the point we've dragged to and the start of the touch.
+ const float fontHeight = float(_actualFont.GetSize().Y);
+
+ const float dy = newTouchPoint.Y - anchor.Y;
+
+ // If we've moved more than one row of text, we'll want to scroll the viewport
+ if (std::abs(dy) > fontHeight)
+ {
+ // Multiply by -1, because moving the touch point down will
+ // create a positive delta, but we want the viewport to move up,
+ // so we'll need a negative scroll amount (and the inverse for
+ // panning down)
+ const float numRows = -1.0f * (dy / fontHeight);
+
+ const auto currentOffset = this->GetScrollOffset();
+ const double newValue = (numRows) + (currentOffset);
+
+ _scrollBar.Value(static_cast(newValue));
+
+ // Use this point as our new scroll anchor.
+ _touchAnchor = newTouchPoint;
+ }
+ }
+ args.Handled(true);
+ }
+
+ // Method Description:
+ // - Event handler for the PointerReleased event. We use this to de-anchor
+ // touch events, to stop scrolling via touch.
+ // Arguments:
+ // - sender: the XAML element responding to the pointer input
+ // - args: event data
+ void TermControl::_PointerReleasedHandler(Windows::Foundation::IInspectable const& sender,
+ Input::PointerRoutedEventArgs const& args)
+ {
+ _ReleasePointerCapture(sender, args);
+
+ const auto ptr = args.Pointer();
+
+ _clickDragStartPos = std::nullopt;
+
+ if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen)
+ {
+ const auto modifiers = static_cast(args.KeyModifiers());
+ // static_cast to a uint32_t because we can't use the WI_IsFlagSet
+ // macro directly with a VirtualKeyModifiers
+ const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift));
+
+ if (_terminal->IsCopyOnSelectActive())
+ {
+ CopySelectionToClipboard(!shiftEnabled);
+ }
+ }
+ else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch)
+ {
+ _touchAnchor = std::nullopt;
+ }
+
+ _TryStopAutoScroll(ptr.PointerId());
+
+ args.Handled(true);
+ }
+
+ // Method Description:
+ // - Event handler for the PointerWheelChanged event. This is raised in
+ // response to mouse wheel changes. Depending upon what modifier keys are
+ // pressed, different actions will take place.
+ // Arguments:
+ // - args: the event args containing information about t`he mouse wheel event.
+ void TermControl::_MouseWheelHandler(Windows::Foundation::IInspectable const& /*sender*/,
+ Input::PointerRoutedEventArgs const& args)
+ {
+ const auto point = args.GetCurrentPoint(_root);
+ const auto delta = point.Properties().MouseWheelDelta();
+
+ // Get the state of the Ctrl & Shift keys
+ // static_cast to a uint32_t because we can't use the WI_IsFlagSet macro
+ // directly with a VirtualKeyModifiers
+ const auto modifiers = static_cast(args.KeyModifiers());
+ const auto ctrlPressed = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Control));
+ const auto shiftPressed = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift));
+
+ if (ctrlPressed && shiftPressed)
+ {
+ _MouseTransparencyHandler(delta);
+ }
+ else if (ctrlPressed)
+ {
+ _MouseZoomHandler(delta);
+ }
+ else
+ {
+ _MouseScrollHandler(delta, point);
+ }
+ }
+
+ // Method Description:
+ // - Adjust the opacity of the acrylic background in response to a mouse
+ // scrolling event.
+ // Arguments:
+ // - mouseDelta: the mouse wheel delta that triggered this event.
+ void TermControl::_MouseTransparencyHandler(const double mouseDelta)
+ {
+ // Transparency is on a scale of [0.0,1.0], so only increment by .01.
+ const auto effectiveDelta = mouseDelta < 0 ? -.01 : .01;
+
+ if (_settings.UseAcrylic())
+ {
+ try
+ {
+ auto acrylicBrush = _root.Background().as();
+ acrylicBrush.TintOpacity(acrylicBrush.TintOpacity() + effectiveDelta);
+ }
+ CATCH_LOG();
+ }
+ }
+
+ // Method Description:
+ // - Adjust the font size of the terminal in response to a mouse scrolling
+ // event.
+ // Arguments:
+ // - mouseDelta: the mouse wheel delta that triggered this event.
+ void TermControl::_MouseZoomHandler(const double mouseDelta)
+ {
+ const auto fontDelta = mouseDelta < 0 ? -1 : 1;
+ AdjustFontSize(fontDelta);
+ }
+
+ // Method Description:
+ // - Reset the font size of the terminal to its default size.
+ // Arguments:
+ // - none
+ void TermControl::ResetFontSize()
+ {
+ _SetFontSize(_settings.FontSize());
+ }
+
+ // Method Description:
+ // - Adjust the font size of the terminal control.
+ // Arguments:
+ // - fontSizeDelta: The amount to increase or decrease the font size by.
+ void TermControl::AdjustFontSize(int fontSizeDelta)
+ {
+ const auto newSize = _desiredFont.GetEngineSize().Y + fontSizeDelta;
+ _SetFontSize(newSize);
+ }
+
+ // Method Description:
+ // - Scroll the visible viewport in response to a mouse wheel event.
+ // Arguments:
+ // - mouseDelta: the mouse wheel delta that triggered this event.
+ void TermControl::_MouseScrollHandler(const double mouseDelta, Windows::UI::Input::PointerPoint const& pointerPoint)
+ {
+ const auto currentOffset = this->GetScrollOffset();
+
+ // negative = down, positive = up
+ // However, for us, the signs are flipped.
+ const auto rowDelta = mouseDelta < 0 ? 1.0 : -1.0;
+
+ // With one of the precision mouses, one click is always a multiple of 120,
+ // but the "smooth scrolling" mode results in non-int values
+
+ double newValue = (_rowsToScroll * rowDelta) + (currentOffset);
+
+ // The scroll bar's ValueChanged handler will actually move the viewport
+ // for us.
+ _scrollBar.Value(static_cast(newValue));
+
+ if (_terminal->IsSelectionActive() && pointerPoint.Properties().IsLeftButtonPressed())
+ {
+ // If user is mouse selecting and scrolls, they then point at new character.
+ // Make sure selection reflects that immediately.
+ _SetEndSelectionPointAtCursor(pointerPoint.Position());
+ }
+ }
+
+ void TermControl::_ScrollbarChangeHandler(Windows::Foundation::IInspectable const& /*sender*/,
+ Controls::Primitives::RangeBaseValueChangedEventArgs const& args)
+ {
+ if (_isTerminalInitiatedScroll)
+ {
+ return;
+ }
+
+ const auto newValue = static_cast(args.NewValue());
+
+ // This is a scroll event that wasn't initiated by the terminal
+ // itself - it was initiated by the mouse wheel, or the scrollbar.
+ _terminal->UserScrollViewport(newValue);
+
+ // We've just told the terminal to update its viewport to reflect the
+ // new scroll value so the scroll bar matches the viewport now.
+ _willUpdateScrollBarToMatchViewport.store(false);
+ }
+
+ // Method Description:
+ // - captures the pointer so that none of the other XAML elements respond to pointer events
+ // Arguments:
+ // - sender: XAML element that is interacting with pointer
+ // - args: pointer data (i.e.: mouse, touch)
+ // Return Value:
+ // - true if we successfully capture the pointer, false otherwise.
+ bool TermControl::_CapturePointer(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& args)
+ {
+ IUIElement uielem;
+ if (sender.try_as(uielem))
+ {
+ uielem.CapturePointer(args.Pointer());
+ return true;
+ }
+ return false;
+ }
+
+ // Method Description:
+ // - releases the captured pointer because we're done responding to XAML pointer events
+ // Arguments:
+ // - sender: XAML element that is interacting with pointer
+ // - args: pointer data (i.e.: mouse, touch)
+ // Return Value:
+ // - true if we release capture of the pointer, false otherwise.
+ bool TermControl::_ReleasePointerCapture(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& args)
+ {
+ IUIElement uielem;
+ if (sender.try_as(uielem))
+ {
+ uielem.ReleasePointerCapture(args.Pointer());
+ return true;
+ }
+ return false;
+ }
+
+ // Method Description:
+ // - Starts new pointer related auto scroll behavior, or continues existing one.
+ // Does nothing when there is already auto scroll associated with another pointer.
+ // Arguments:
+ // - pointerPoint: info about pointer that causes auto scroll. Pointer's position
+ // is later used to update selection.
+ // - scrollVelocity: target velocity of scrolling in characters / sec
+ void TermControl::_TryStartAutoScroll(Windows::UI::Input::PointerPoint const& pointerPoint, const double scrollVelocity)
+ {
+ // Allow only one pointer at the time
+ if (!_autoScrollingPointerPoint.has_value() || _autoScrollingPointerPoint.value().PointerId() == pointerPoint.PointerId())
+ {
+ _autoScrollingPointerPoint = pointerPoint;
+ _autoScrollVelocity = scrollVelocity;
+
+ // If this is first time the auto scroll update is about to be called,
+ // kick-start it by initializing its time delta as if it started now
+ if (!_lastAutoScrollUpdateTime.has_value())
+ {
+ _lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
+ }
+
+ // Apparently this check is not necessary but greatly improves performance
+ if (!_autoScrollTimer.IsEnabled())
+ {
+ _autoScrollTimer.Start();
+ }
+ }
+ }
+
+ // Method Description:
+ // - Stops auto scroll if it's active and is associated with supplied pointer id.
+ // Arguments:
+ // - pointerId: id of pointer for which to stop auto scroll
+ void TermControl::_TryStopAutoScroll(const uint32_t pointerId)
+ {
+ if (_autoScrollingPointerPoint.has_value() && pointerId == _autoScrollingPointerPoint.value().PointerId())
+ {
+ _autoScrollingPointerPoint = std::nullopt;
+ _autoScrollVelocity = 0;
+ _lastAutoScrollUpdateTime = std::nullopt;
+
+ // Apparently this check is not necessary but greatly improves performance
+ if (_autoScrollTimer.IsEnabled())
+ {
+ _autoScrollTimer.Stop();
+ }
+ }
+ }
+
+ // Method Description:
+ // - Called continuously to gradually scroll viewport when user is
+ // mouse selecting outside it (to 'follow' the cursor).
+ // Arguments:
+ // - none
+ void TermControl::_UpdateAutoScroll(Windows::Foundation::IInspectable const& /* sender */,
+ Windows::Foundation::IInspectable const& /* e */)
+ {
+ if (_autoScrollVelocity != 0)
+ {
+ const auto timeNow = std::chrono::high_resolution_clock::now();
+
+ if (_lastAutoScrollUpdateTime.has_value())
+ {
+ static constexpr double microSecPerSec = 1000000.0;
+ const double deltaTime = std::chrono::duration_cast(timeNow - _lastAutoScrollUpdateTime.value()).count() / microSecPerSec;
+ _scrollBar.Value(_scrollBar.Value() + _autoScrollVelocity * deltaTime);
+
+ if (_autoScrollingPointerPoint.has_value())
+ {
+ _SetEndSelectionPointAtCursor(_autoScrollingPointerPoint.value().Position());
+ }
+ }
+
+ _lastAutoScrollUpdateTime = timeNow;
+ }
+ }
+
+ // Method Description:
+ // - Event handler for the GotFocus event. This is used to...
+ // - enable accessibility notifications for this TermControl
+ // - start blinking the cursor when the window is focused
+ // - update the number of lines to scroll to the value set in the system
+ void TermControl::_GotFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
+ RoutedEventArgs const& /* args */)
+ {
+ if (_closing)
+ {
+ return;
+ }
+ _focused = true;
+
+ if (_uiaEngine.get())
+ {
+ THROW_IF_FAILED(_uiaEngine->Enable());
+ }
+
+ if (_tsfInputControl != nullptr)
+ {
+ _tsfInputControl.NotifyFocusEnter();
+ }
+
+ if (_cursorTimer.has_value())
+ {
+ // When the terminal focuses, show the cursor immediately
+ _terminal->SetCursorVisible(true);
+ _cursorTimer.value().Start();
+ }
+ _rowsToScroll = _settings.RowsToScroll();
+ }
+
+ // Method Description:
+ // - Event handler for the LostFocus event. This is used to...
+ // - disable accessibility notifications for this TermControl
+ // - hide and stop blinking the cursor when the window loses focus.
+ void TermControl::_LostFocusHandler(Windows::Foundation::IInspectable const& /* sender */,
+ RoutedEventArgs const& /* args */)
+ {
+ if (_closing)
+ {
+ return;
+ }
+ _focused = false;
+
+ if (_uiaEngine.get())
+ {
+ THROW_IF_FAILED(_uiaEngine->Disable());
+ }
+
+ if (_tsfInputControl != nullptr)
+ {
+ _tsfInputControl.NotifyFocusLeave();
+ }
+
+ if (_cursorTimer.has_value())
+ {
+ _cursorTimer.value().Stop();
+ _terminal->SetCursorVisible(false);
+ }
+ }
+
+ // Method Description:
+ // - Writes the given sequence as input to the active terminal connection,
+ // Arguments:
+ // - wstr: the string of characters to write to the terminal connection.
+ // Return Value:
+ // -
+ void TermControl::_SendInputToConnection(const std::wstring& wstr)
+ {
+ _connection.WriteInput(wstr);
+ }
+
+ // Method Description:
+ // - Pre-process text pasted (presumably from the clipboard)
+ // before sending it over the terminal's connection, converting
+ // Windows-space \r\n line-endings to \r line-endings
+ void TermControl::_SendPastedTextToConnection(const std::wstring& wstr)
+ {
+ // Some notes on this implementation:
+ //
+ // - std::regex can do this in a single line, but is somewhat
+ // overkill for a simple search/replace operation (and its
+ // performance guarantees aren't exactly stellar)
+ // - The STL doesn't have a simple string search/replace method.
+ // This fact is lamentable.
+ // - This line-ending conversion is intentionally fairly
+ // conservative, to avoid stripping out lone \n characters
+ // where they could conceivably be intentional.
+
+ std::wstring stripped{ wstr };
+
+ std::wstring::size_type pos = 0;
+
+ while ((pos = stripped.find(L"\r\n", pos)) != std::wstring::npos)
+ {
+ stripped.replace(pos, 2, L"\r");
+ }
+
+ _connection.WriteInput(stripped);
+ _terminal->TrySnapOnInput();
+ }
+
+ // Method Description:
+ // - Update the font with the renderer. This will be called either when the
+ // font changes or the DPI changes, as DPI changes will necessitate a
+ // font change. This method will *not* change the buffer/viewport size
+ // to account for the new glyph dimensions. Callers should make sure to
+ // appropriately call _DoResize after this method is called.
+ // Arguments:
+ // - initialUpdate: whether this font update should be considered as being
+ // concerned with initialization process. Value forwarded to event handler.
+ void TermControl::_UpdateFont(const bool initialUpdate)
+ {
+ auto lock = _terminal->LockForWriting();
+
+ const int newDpi = static_cast(static_cast(USER_DEFAULT_SCREEN_DPI) * _swapChainPanel.CompositionScaleX());
+
+ // TODO: MSFT:20895307 If the font doesn't exist, this doesn't
+ // actually fail. We need a way to gracefully fallback.
+ _renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
+
+ const auto actualNewSize = _actualFont.GetSize();
+ _fontSizeChangedHandlers(actualNewSize.X, actualNewSize.Y, initialUpdate);
+ }
+
+ // Method Description:
+ // - Set the font size of the terminal control.
+ // Arguments:
+ // - fontSize: The size of the font.
+ void TermControl::_SetFontSize(int fontSize)
+ {
+ try
+ {
+ // Make sure we have a non-zero font size
+ const auto newSize = std::max(gsl::narrow(fontSize), static_cast(1));
+ const auto fontFace = _settings.FontFace();
+ _actualFont = { fontFace, 0, 10, { 0, newSize }, CP_UTF8, false };
+ _desiredFont = { _actualFont };
+
+ // Refresh our font with the renderer
+ _UpdateFont();
+ // Resize the terminal's BUFFER to match the new font size. This does
+ // NOT change the size of the window, because that can lead to more
+ // problems (like what happens when you change the font size while the
+ // window is maximized?)
+ auto lock = _terminal->LockForWriting();
+ _DoResize(_swapChainPanel.ActualWidth(), _swapChainPanel.ActualHeight());
+ }
+ CATCH_LOG();
+ }
+
+ // Method Description:
+ // - Triggered when the swapchain changes size. We use this to resize the
+ // terminal buffers to match the new visible size.
+ // Arguments:
+ // - e: a SizeChangedEventArgs with the new dimensions of the SwapChainPanel
+ void TermControl::_SwapChainSizeChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/,
+ SizeChangedEventArgs const& e)
+ {
+ if (!_initializedTerminal)
+ {
+ return;
+ }
+
+ auto lock = _terminal->LockForWriting();
+
+ const auto foundationSize = e.NewSize();
+
+ _DoResize(foundationSize.Width, foundationSize.Height);
+ }
+
+ void TermControl::_SwapChainScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
+ Windows::Foundation::IInspectable const& /*args*/)
+ {
+ if (_renderEngine)
+ {
+ const auto scale = sender.CompositionScaleX();
+ const auto dpi = (int)(scale * USER_DEFAULT_SCREEN_DPI);
+
+ // TODO: MSFT: 21169071 - Shouldn't this all happen through _renderer and trigger the invalidate automatically on DPI change?
+ THROW_IF_FAILED(_renderEngine->UpdateDpi(dpi));
+ _renderer->TriggerRedrawAll();
+ }
+ }
+
+ // Method Description:
+ // - Toggle the cursor on and off when called by the cursor blink timer.
+ // Arguments:
+ // - sender: not used
+ // - e: not used
+ void TermControl::_BlinkCursor(Windows::Foundation::IInspectable const& /* sender */,
+ Windows::Foundation::IInspectable const& /* e */)
+ {
+ if ((_closing) || (!_terminal->IsCursorBlinkingAllowed() && _terminal->IsCursorVisible()))
+ {
+ return;
+ }
+ _terminal->SetCursorVisible(!_terminal->IsCursorVisible());
+ }
+
+ // Method Description:
+ // - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
+ // Arguments:
+ // - cursorPosition: in pixels, relative to the origin of the control
+ void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition)
+ {
+ auto terminalPosition = _GetTerminalPosition(cursorPosition);
+
+ const short lastVisibleRow = std::max(_terminal->GetViewport().Height() - 1, 0);
+ const short lastVisibleCol = std::max(_terminal->GetViewport().Width() - 1, 0);
+
+ terminalPosition.Y = std::clamp(terminalPosition.Y, 0, lastVisibleRow);
+ terminalPosition.X = std::clamp(terminalPosition.X, 0, lastVisibleCol);
+
+ // save location (for rendering) + render
+ _terminal->SetEndSelectionPosition(terminalPosition);
+ _renderer->TriggerSelection();
+ }
+
+ // Method Description:
+ // - Process a resize event that was initiated by the user. This can either
+ // be due to the user resizing the window (causing the swapchain to
+ // resize) or due to the DPI changing (causing us to need to resize the
+ // buffer to match)
+ // Arguments:
+ // - newWidth: the new width of the swapchain, in pixels.
+ // - newHeight: the new height of the swapchain, in pixels.
+ void TermControl::_DoResize(const double newWidth, const double newHeight)
+ {
+ SIZE size;
+ size.cx = static_cast(newWidth);
+ size.cy = static_cast(newHeight);
+
+ // Don't actually resize so small that a single character wouldn't fit
+ // in either dimension. The buffer really doesn't like being size 0.
+ if (size.cx < _actualFont.GetSize().X || size.cy < _actualFont.GetSize().Y)
+ {
+ return;
+ }
+
+ // Tell the dx engine that our window is now the new size.
+ THROW_IF_FAILED(_renderEngine->SetWindowSize(size));
+
+ // Invalidate everything
+ _renderer->TriggerRedrawAll();
+
+ // Convert our new dimensions to characters
+ const auto viewInPixels = Viewport::FromDimensions({ 0, 0 },
+ { static_cast(size.cx), static_cast(size.cy) });
+ const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels);
+
+ // If this function succeeds with S_FALSE, then the terminal didn't
+ // actually change size. No need to notify the connection of this
+ // no-op.
+ // TODO: MSFT:20642295 Resizing the buffer will corrupt it
+ // I believe we'll need support for CSI 2J, and additionally I think
+ // we're resetting the viewport to the top
+ const HRESULT hr = _terminal->UserResize({ vp.Width(), vp.Height() });
+ if (SUCCEEDED(hr) && hr != S_FALSE)
+ {
+ _connection.Resize(vp.Height(), vp.Width());
+ }
+ }
+
+ void TermControl::_TerminalTitleChanged(const std::wstring_view& wstr)
+ {
+ _titleChangedHandlers(winrt::hstring{ wstr });
+ }
+
+ // Method Description:
+ // - Update the position and size of the scrollbar to match the given
+ // viewport top, viewport height, and buffer size.
+ // The change will be actually handled in _ScrollbarChangeHandler.
+ // This should be done on the UI thread. Make sure the caller is calling
+ // us in a RunAsync block.
+ // Arguments:
+ // - viewTop: the top of the visible viewport, in rows. 0 indicates the top
+ // of the buffer.
+ // - viewHeight: the height of the viewport in rows.
+ // - bufferSize: the length of the buffer, in rows
+ void TermControl::_ScrollbarUpdater(Controls::Primitives::ScrollBar scrollBar,
+ const int viewTop,
+ const int viewHeight,
+ const int bufferSize)
+ {
+ // The terminal is already in the scroll position it wants, so no need
+ // to tell it to scroll.
+ _isTerminalInitiatedScroll = true;
+
+ const auto hiddenContent = bufferSize - viewHeight;
+ scrollBar.Maximum(hiddenContent);
+ scrollBar.Minimum(0);
+ scrollBar.ViewportSize(viewHeight);
+ scrollBar.Value(viewTop);
+
+ _isTerminalInitiatedScroll = false;
+ }
+
+ // Method Description:
+ // - Update the position and size of the scrollbar to match the given
+ // viewport top, viewport height, and buffer size.
+ // Additionally fires a ScrollPositionChanged event for anyone who's
+ // registered an event handler for us.
+ // Arguments:
+ // - viewTop: the top of the visible viewport, in rows. 0 indicates the top
+ // of the buffer.
+ // - viewHeight: the height of the viewport in rows.
+ // - bufferSize: the length of the buffer, in rows
+ winrt::fire_and_forget TermControl::_TerminalScrollPositionChanged(const int viewTop,
+ const int viewHeight,
+ const int bufferSize)
+ {
+ // Since this callback fires from non-UI thread, we might be already
+ // closed/closing.
+ if (_closing.load())
+ {
+ return;
+ }
+
+ _scrollPositionChangedHandlers(viewTop, viewHeight, bufferSize);
+
+ auto weakThis{ get_weak() };
+
+ co_await winrt::resume_foreground(_scrollBar.Dispatcher());
+
+ // Even if we weren't closed/closing few lines above, we might be
+ // while waiting for this block of code to be dispatched.
+ // If 'weakThis' is locked, then we can safely work with 'this'
+ if (auto control{ weakThis.get() })
+ {
+ if (!_closing.load())
+ {
+ // Update our scrollbar
+ _ScrollbarUpdater(_scrollBar, viewTop, viewHeight, bufferSize);
+ }
+ }
+ }
+
+ hstring TermControl::Title()
+ {
+ if (!_initializedTerminal)
+ return L"";
+
+ hstring hstr(_terminal->GetConsoleTitle());
+ return hstr;
+ }
+
+ // Method Description:
+ // - Given a copy-able selection, get the selected text from the buffer and send it to the
+ // Windows Clipboard (CascadiaWin32:main.cpp).
+ // - CopyOnSelect does NOT clear the selection
+ // Arguments:
+ // - trimTrailingWhitespace: enable removing any whitespace from copied selection
+ // and get text to appear on separate lines.
+ bool TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace)
+ {
+ // no selection --> nothing to copy
+ if (_terminal == nullptr || !_terminal->IsSelectionActive())
+ {
+ return false;
+ }
+ // extract text from buffer
+ const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace);
+
+ // convert text: vector --> string
+ std::wstring textData;
+ for (const auto& text : bufferData.text)
+ {
+ textData += text;
+ }
+
+ // convert text to HTML format
+ const auto htmlData = TextBuffer::GenHTML(bufferData,
+ _actualFont.GetUnscaledSize().Y,
+ _actualFont.GetFaceName(),
+ _settings.DefaultBackground(),
+ "Windows Terminal");
+
+ // convert to RTF format
+ const auto rtfData = TextBuffer::GenRTF(bufferData,
+ _actualFont.GetUnscaledSize().Y,
+ _actualFont.GetFaceName(),
+ _settings.DefaultBackground());
+
+ if (!_terminal->IsCopyOnSelectActive())
+ {
+ _terminal->ClearSelection();
+ }
+
+ // send data up for clipboard
+ auto copyArgs = winrt::make_self(winrt::hstring(textData),
+ winrt::to_hstring(htmlData),
+ winrt::to_hstring(rtfData));
+ _clipboardCopyHandlers(*this, *copyArgs);
+ return true;
+ }
+
+ // Method Description:
+ // - Initiate a paste operation.
+ void TermControl::PasteTextFromClipboard()
+ {
+ // attach TermControl::_SendInputToConnection() as the clipboardDataHandler.
+ // This is called when the clipboard data is loaded.
+ auto clipboardDataHandler = std::bind(&TermControl::_SendPastedTextToConnection, this, std::placeholders::_1);
+ auto pasteArgs = winrt::make_self(clipboardDataHandler);
+
+ // send paste event up to TermApp
+ _clipboardPasteHandlers(*this, *pasteArgs);
+ }
+
+ void TermControl::Close()
+ {
+ if (!_closing.exchange(true))
+ {
+ // Stop accepting new output and state changes before we disconnect everything.
+ _connection.TerminalOutput(_connectionOutputEventToken);
+ _connectionStateChangedRevoker.revoke();
+
+ _tsfInputControl.Close(); // Disconnect the TSF input control so it doesn't receive EditContext events.
+
+ if (auto localConnection{ std::exchange(_connection, nullptr) })
+ {
+ localConnection.Close();
+ // connection is destroyed.
+ }
+
+ if (auto localRenderEngine{ std::exchange(_renderEngine, nullptr) })
+ {
+ if (auto localRenderer{ std::exchange(_renderer, nullptr) })
+ {
+ localRenderer->TriggerTeardown();
+ // renderer is destroyed
+ }
+ // renderEngine is destroyed
+ }
+
+ if (auto localTerminal{ std::exchange(_terminal, nullptr) })
+ {
+ _initializedTerminal = false;
+ // terminal is destroyed.
+ }
+ }
+ }
+
+ // Method Description:
+ // - Scrolls the viewport of the terminal and updates the scroll bar accordingly
+ // Arguments:
+ // - viewTop: the viewTop to scroll to
+ void TermControl::ScrollViewport(int viewTop)
+ {
+ _scrollBar.Value(viewTop);
+ }
+
+ int TermControl::GetScrollOffset()
+ {
+ return _terminal->GetScrollOffset();
+ }
+
+ // Function Description:
+ // - Gets the height of the terminal in lines of text
+ // Return Value:
+ // - The height of the terminal in lines of text
+ int TermControl::GetViewHeight() const
+ {
+ const auto viewPort = _terminal->GetViewport();
+ return viewPort.Height();
+ }
+
+ // Function Description:
+ // - Determines how much space (in pixels) an app would need to reserve to
+ // create a control with the settings stored in the settings param. This
+ // accounts for things like the font size and face, the initialRows and
+ // initialCols, and scrollbar visibility. The returned sized is based upon
+ // the provided DPI value
+ // Arguments:
+ // - settings: A IControlSettings with the settings to get the pixel size of.
+ // - dpi: The DPI we should create the terminal at. This affects things such
+ // as font size, scrollbar and other control scaling, etc. Make sure the
+ // caller knows what monitor the control is about to appear on.
+ // Return Value:
+ // - a point containing the requested dimensions in pixels.
+ winrt::Windows::Foundation::Point TermControl::GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi)
+ {
+ // Initialize our font information.
+ const auto fontFace = settings.FontFace();
+ const short fontHeight = gsl::narrow(settings.FontSize());
+ // The font width doesn't terribly matter, we'll only be using the
+ // height to look it up
+ // The other params here also largely don't matter.
+ // The family is only used to determine if the font is truetype or
+ // not, but DX doesn't use that info at all.
+ // The Codepage is additionally not actually used by the DX engine at all.
+ FontInfo actualFont = { fontFace, 0, 10, { 0, fontHeight }, CP_UTF8, false };
+ FontInfoDesired desiredFont = { actualFont };
+
+ // If the settings have negative or zero row or column counts, ignore those counts.
+ // (The lower TerminalCore layer also has upper bounds as well, but at this layer
+ // we may eventually impose different ones depending on how many pixels we can address.)
+ const auto cols = std::max(settings.InitialCols(), 1);
+ const auto rows = std::max(settings.InitialRows(), 1);
+
+ // Create a DX engine and initialize it with our font and DPI. We'll
+ // then use it to measure how much space the requested rows and columns
+ // will take up.
+ // TODO: MSFT:21254947 - use a static function to do this instead of
+ // instantiating a DxEngine
+ auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
+ THROW_IF_FAILED(dxEngine->UpdateDpi(dpi));
+ THROW_IF_FAILED(dxEngine->UpdateFont(desiredFont, actualFont));
+
+ const float scale = dxEngine->GetScaling();
+ const auto fontSize = actualFont.GetSize();
+
+ // Manually multiply by the scaling factor. The DX engine doesn't
+ // actually store the scaled font size in the fontInfo.GetSize()
+ // property when the DX engine is in Composition mode (which it is for
+ // the Terminal). At runtime, this is fine, as we'll transform
+ // everything by our scaling, so it'll work out. However, right now we
+ // need to get the exact pixel count.
+ const float fFontWidth = gsl::narrow(fontSize.X * scale);
+ const float fFontHeight = gsl::narrow(fontSize.Y * scale);
+
+ // UWP XAML scrollbars aren't guaranteed to be the same size as the
+ // ComCtl scrollbars, but it's certainly close enough.
+ const auto scrollbarSize = GetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
+
+ double width = cols * fFontWidth;
+
+ // Reserve additional space if scrollbar is intended to be visible
+ if (settings.ScrollState() == ScrollbarState::Visible)
+ {
+ width += scrollbarSize;
+ }
+
+ double height = rows * fFontHeight;
+ auto thickness = _ParseThicknessFromPadding(settings.Padding());
+ width += thickness.Left + thickness.Right;
+ height += thickness.Top + thickness.Bottom;
+
+ return { gsl::narrow_cast(width), gsl::narrow_cast(height) };
+ }
+
+ // Method Description:
+ // - Get the size of a single character of this control. The size is in
+ // DIPs. If you need it in _pixels_, you'll need to multiply by the
+ // current display scaling.
+ // Arguments:
+ // -
+ // Return Value:
+ // - The dimensions of a single character of this control, in DIPs
+ winrt::Windows::Foundation::Size TermControl::CharacterDimensions() const
+ {
+ const auto fontSize = _actualFont.GetSize();
+ return { gsl::narrow_cast(fontSize.X), gsl::narrow_cast(fontSize.Y) };
+ }
+
+ // Method Description:
+ // - Get the absolute minimum size that this control can be resized to and
+ // still have 1x1 character visible. This includes the space needed for
+ // the scrollbar and the padding.
+ // Arguments:
+ // -
+ // Return Value:
+ // - The minimum size that this terminal control can be resized to and still
+ // have a visible character.
+ winrt::Windows::Foundation::Size TermControl::MinimumSize() const
+ {
+ const auto fontSize = _actualFont.GetSize();
+ double width = fontSize.X;
+ double height = fontSize.Y;
+ // Reserve additional space if scrollbar is intended to be visible
+ if (_settings.ScrollState() == ScrollbarState::Visible)
+ {
+ width += _scrollBar.ActualWidth();
+ }
+
+ // Account for the size of any padding
+ const auto padding = _swapChainPanel.Margin();
+ width += padding.Left + padding.Right;
+ height += padding.Top + padding.Bottom;
+
+ return { gsl::narrow_cast(width), gsl::narrow_cast(height) };
+ }
+
+ // Method Description:
+ // - Adjusts given dimension (width or height) so that it aligns to the character grid.
+ // The snap is always downward.
+ // Arguments:
+ // - widthOrHeight: if true operates on width, otherwise on height
+ // - dimension: a dimension (width or height) to be snapped
+ // Return Value:
+ // - A dimension that would be aligned to the character grid.
+ float TermControl::SnapDimensionToGrid(const bool widthOrHeight, const float dimension) const
+ {
+ const auto fontSize = _actualFont.GetSize();
+ const auto fontDimension = widthOrHeight ? fontSize.X : fontSize.Y;
+
+ const auto padding = _swapChainPanel.Margin();
+ auto nonTerminalArea = gsl::narrow_cast(widthOrHeight ?
+ padding.Left + padding.Right :
+ padding.Top + padding.Bottom);
+
+ if (widthOrHeight && _settings.ScrollState() == ScrollbarState::Visible)
+ {
+ nonTerminalArea += gsl::narrow_cast(_scrollBar.ActualWidth());
+ }
+
+ const auto gridSize = dimension - nonTerminalArea;
+ const int cells = static_cast(gridSize / fontDimension);
+ return cells * fontDimension + nonTerminalArea;
+ }
+
+ // Method Description:
+ // - Create XAML Thickness object based on padding props provided.
+ // Used for controlling the TermControl XAML Grid container's Padding prop.
+ // Arguments:
+ // - padding: 2D padding values
+ // Single Double value provides uniform padding
+ // Two Double values provide isometric horizontal & vertical padding
+ // Four Double values provide independent padding for 4 sides of the bounding rectangle
+ // Return Value:
+ // - Windows::UI::Xaml::Thickness object
+ Windows::UI::Xaml::Thickness TermControl::_ParseThicknessFromPadding(const hstring padding)
+ {
+ const wchar_t singleCharDelim = L',';
+ std::wstringstream tokenStream(padding.c_str());
+ std::wstring token;
+ uint8_t paddingPropIndex = 0;
+ std::array thicknessArr = {};
+ size_t* idx = nullptr;
+
+ // Get padding values till we run out of delimiter separated values in the stream
+ // or we hit max number of allowable values (= 4) for the bounding rectangle
+ // Non-numeral values detected will default to 0
+ // std::getline will not throw exception unless flags are set on the wstringstream
+ // std::stod will throw invalid_argument exception if the input is an invalid double value
+ // std::stod will throw out_of_range exception if the input value is more than DBL_MAX
+ try
+ {
+ for (; std::getline(tokenStream, token, singleCharDelim) && (paddingPropIndex < thicknessArr.size()); paddingPropIndex++)
+ {
+ // std::stod internally calls wcstod which handles whitespace prefix (which is ignored)
+ // & stops the scan when first char outside the range of radix is encountered
+ // We'll be permissive till the extent that stod function allows us to be by default
+ // Ex. a value like 100.3#535w2 will be read as 100.3, but ;df25 will fail
+ thicknessArr[paddingPropIndex] = std::stod(token, idx);
+ }
+ }
+ catch (...)
+ {
+ // If something goes wrong, even if due to a single bad padding value, we'll reset the index & return default 0 padding
+ paddingPropIndex = 0;
+ LOG_CAUGHT_EXCEPTION();
+ }
+
+ switch (paddingPropIndex)
+ {
+ case 1:
+ return ThicknessHelper::FromUniformLength(thicknessArr[0]);
+ case 2:
+ return ThicknessHelper::FromLengths(thicknessArr[0], thicknessArr[1], thicknessArr[0], thicknessArr[1]);
+ // No case for paddingPropIndex = 3, since it's not a norm to provide just Left, Top & Right padding values leaving out Bottom
+ case 4:
+ return ThicknessHelper::FromLengths(thicknessArr[0], thicknessArr[1], thicknessArr[2], thicknessArr[3]);
+ default:
+ return Thickness();
+ }
+ }
+
+ // Method Description:
+ // - Get the modifier keys that are currently pressed. This can be used to
+ // find out which modifiers (ctrl, alt, shift) are pressed in events that
+ // don't necessarily include that state.
+ // Return Value:
+ // - The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states.
+ ControlKeyStates TermControl::_GetPressedModifierKeys() const
+ {
+ CoreWindow window = CoreWindow::GetForCurrentThread();
+ // DONT USE
+ // != CoreVirtualKeyStates::None
+ // OR
+ // == CoreVirtualKeyStates::Down
+ // Sometimes with the key down, the state is Down | Locked.
+ // Sometimes with the key up, the state is Locked.
+ // IsFlagSet(Down) is the only correct solution.
+
+ struct KeyModifier
+ {
+ VirtualKey vkey;
+ ControlKeyStates flags;
+ };
+
+ constexpr std::array modifiers{ {
+ { VirtualKey::RightMenu, ControlKeyStates::RightAltPressed },
+ { VirtualKey::LeftMenu, ControlKeyStates::LeftAltPressed },
+ { VirtualKey::RightControl, ControlKeyStates::RightCtrlPressed },
+ { VirtualKey::LeftControl, ControlKeyStates::LeftCtrlPressed },
+ { VirtualKey::Shift, ControlKeyStates::ShiftPressed },
+ } };
+
+ ControlKeyStates flags;
+
+ for (const auto& mod : modifiers)
+ {
+ const auto state = window.GetKeyState(mod.vkey);
+ const auto isDown = WI_IsFlagSet(state, CoreVirtualKeyStates::Down);
+
+ if (isDown)
+ {
+ flags |= mod.flags;
+ }
+ }
+
+ return flags;
+ }
+
+ // Method Description:
+ // - Gets the corresponding viewport terminal position for the cursor
+ // by excluding the padding and normalizing with the font size.
+ // This is used for selection.
+ // Arguments:
+ // - cursorPosition: the (x,y) position of a given cursor (i.e.: mouse cursor).
+ // NOTE: origin (0,0) is top-left.
+ // Return Value:
+ // - the corresponding viewport terminal position for the given Point parameter
+ const COORD TermControl::_GetTerminalPosition(winrt::Windows::Foundation::Point cursorPosition)
+ {
+ // Exclude padding from cursor position calculation
+ COORD terminalPosition = {
+ static_cast(cursorPosition.X - _swapChainPanel.Margin().Left),
+ static_cast(cursorPosition.Y - _swapChainPanel.Margin().Top)
+ };
+
+ const auto fontSize = _actualFont.GetSize();
+ FAIL_FAST_IF(fontSize.X == 0);
+ FAIL_FAST_IF(fontSize.Y == 0);
+
+ // Normalize to terminal coordinates by using font size
+ terminalPosition.X /= fontSize.X;
+ terminalPosition.Y /= fontSize.Y;
+
+ return terminalPosition;
+ }
+
+ // Method Description:
+ // - Composition Completion handler for the TSFInputControl that
+ // handles writing text out to TerminalConnection
+ // Arguments:
+ // - text: the text to write to TerminalConnection
+ // Return Value:
+ // -
+ void TermControl::_CompositionCompleted(winrt::hstring text)
+ {
+ _connection.WriteInput(text);
+ }
+
+ // Method Description:
+ // - CurrentCursorPosition handler for the TSFInputControl that
+ // handles returning current cursor position.
+ // Arguments:
+ // - eventArgs: event for storing the current cursor position
+ // Return Value:
+ // -
+ void TermControl::_CurrentCursorPositionHandler(const IInspectable& /*sender*/, const CursorPositionEventArgs& eventArgs)
+ {
+ const COORD cursorPos = _terminal->GetCursorPosition();
+ Windows::Foundation::Point p = { gsl::narrow(cursorPos.X), gsl::narrow(cursorPos.Y) };
+ eventArgs.CurrentPosition(p);
+ }
+
+ // Method Description:
+ // - FontInfo handler for the TSFInputControl that
+ // handles returning current font information
+ // Arguments:
+ // - eventArgs: event for storing the current font information
+ // Return Value:
+ // -
+ void TermControl::_FontInfoHandler(const IInspectable& /*sender*/, const FontInfoEventArgs& eventArgs)
+ {
+ eventArgs.FontSize(CharacterDimensions());
+ eventArgs.FontFace(_actualFont.GetFaceName());
+ }
+
+ // Method Description:
+ // - Returns the number of clicks that occurred (double and triple click support)
+ // Arguments:
+ // - clickPos: the (x,y) position of a given cursor (i.e.: mouse cursor).
+ // NOTE: origin (0,0) is top-left.
+ // - clickTime: the timestamp that the click occurred
+ // Return Value:
+ // - if the click is in the same position as the last click and within the timeout, the number of clicks within that time window
+ // - otherwise, 1
+ const unsigned int TermControl::_NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime)
+ {
+ // if click occurred at a different location or past the multiClickTimer...
+ Timestamp delta;
+ THROW_IF_FAILED(UInt64Sub(clickTime, _lastMouseClick, &delta));
+ if (clickPos != _lastMouseClickPos || delta > _multiClickTimer)
+ {
+ // exit early. This is a single click.
+ _multiClickCounter = 1;
+ }
+ else
+ {
+ _multiClickCounter++;
+ }
+ return _multiClickCounter;
+ }
+
+ // Method Description:
+ // - Calculates speed of single axis of auto scrolling. It has to allow for both
+ // fast and precise selection.
+ // Arguments:
+ // - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
+ // Return Value:
+ // - positive speed in characters / sec
+ double TermControl::_GetAutoScrollSpeed(double cursorDistanceFromBorder) const
+ {
+ // The numbers below just feel well, feel free to change.
+ // TODO: Maybe account for space beyond border that user has available
+ return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
+ }
+
+ // Method Description:
+ // - Async handler for the "Drop" event. If a file was dropped onto our
+ // root, we'll try to get the path of the file dropped onto us, and write
+ // the full path of the file to our terminal connection. Like conhost, if
+ // the path contains a space, we'll wrap the path in quotes.
+ // - Unlike conhost, if multiple files are dropped onto the terminal, we'll
+ // write all the paths to the terminal, separated by spaces.
+ // Arguments:
+ // - e: The DragEventArgs from the Drop event
+ // Return Value:
+ // -
+ winrt::fire_and_forget TermControl::_DoDragDrop(DragEventArgs const e)
+ {
+ if (e.DataView().Contains(StandardDataFormats::StorageItems()))
+ {
+ auto items = co_await e.DataView().GetStorageItemsAsync();
+ if (items.Size() > 0)
+ {
+ std::wstring allPaths;
+ for (auto item : items)
+ {
+ // Join the paths with spaces
+ if (!allPaths.empty())
+ {
+ allPaths += L" ";
+ }
+
+ std::wstring fullPath{ item.Path() };
+ const auto containsSpaces = std::find(fullPath.begin(),
+ fullPath.end(),
+ L' ') != fullPath.end();
+
+ auto lock = _terminal->LockForWriting();
+
+ if (containsSpaces)
+ {
+ fullPath.insert(0, L"\"");
+ fullPath += L"\"";
+ }
+
+ allPaths += fullPath;
+ }
+ _SendInputToConnection(allPaths);
+ }
+ }
+ }
+
+ // Method Description:
+ // - Synchronous handler for the "Drop" event. We'll dispatch the async
+ // _DoDragDrop method to handle this, because getting information about
+ // the file that was potentially dropped onto us must be done off the UI
+ // thread.
+ // Arguments:
+ // - e: The DragEventArgs from the Drop event
+ // Return Value:
+ // -
+ void TermControl::_DragDropHandler(Windows::Foundation::IInspectable const& /*sender*/,
+ DragEventArgs const& e)
+ {
+ // Dispatch an async method to handle the drop event.
+ _DoDragDrop(e);
+ }
+
+ // Method Description:
+ // - Handle the DragOver event. We'll signal that the drag operation we
+ // support is the "copy" operation, and we'll also customize the
+ // appearance of the drag-drop UI, by removing the preview and setting a
+ // custom caption. For more information, see
+ // https://docs.microsoft.com/en-us/windows/uwp/design/input/drag-and-drop#customize-the-ui
+ // Arguments:
+ // - e: The DragEventArgs from the DragOver event
+ // Return Value:
+ // -
+ void TermControl::_DragOverHandler(Windows::Foundation::IInspectable const& /*sender*/,
+ DragEventArgs const& e)
+ {
+ if (!e.DataView().Contains(StandardDataFormats::StorageItems()))
+ {
+ // We can't do anything for non-storageitems right now.
+ return;
+ }
+
+ // Make sure to set the AcceptedOperation, so that we can later receive the path in the Drop event
+ e.AcceptedOperation(DataPackageOperation::Copy);
+
+ // Sets custom UI text
+ e.DragUIOverride().Caption(RS_(L"DragFileCaption"));
+ // Sets if the caption is visible
+ e.DragUIOverride().IsCaptionVisible(true);
+ // Sets if the dragged content is visible
+ e.DragUIOverride().IsContentVisible(false);
+ // Sets if the glyph is visibile
+ e.DragUIOverride().IsGlyphVisible(false);
+ }
+
+ // -------------------------------- WinRT Events ---------------------------------
+ // Winrt events need a method for adding a callback to the event and removing the callback.
+ // These macros will define them both for you.
+ DEFINE_EVENT(TermControl, TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs);
+ DEFINE_EVENT(TermControl, FontSizeChanged, _fontSizeChangedHandlers, TerminalControl::FontSizeChangedEventArgs);
+ DEFINE_EVENT(TermControl, ScrollPositionChanged, _scrollPositionChangedHandlers, TerminalControl::ScrollPositionChangedEventArgs);
+
+ DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs);
+ DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs);
+ // clang-format on
+}
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index c566c440f35..a0290bccce8 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -162,6 +162,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Timestamp _lastMouseClick;
unsigned int _multiClickCounter;
std::optional _lastMouseClickPos;
+ std::optional _clickDragStartPos{ std::nullopt };
// Event revokers -- we need to deregister ourselves before we die,
// lest we get callbacks afterwards.
diff --git a/src/cascadia/TerminalControl/TermControlUiaProvider.cpp b/src/cascadia/TerminalControl/TermControlUiaProvider.cpp
index 353d30a28bf..b2a30e43511 100644
--- a/src/cascadia/TerminalControl/TermControlUiaProvider.cpp
+++ b/src/cascadia/TerminalControl/TermControlUiaProvider.cpp
@@ -95,6 +95,11 @@ const winrt::Windows::UI::Xaml::Thickness TermControlUiaProvider::GetPadding() c
return _termControl->GetPadding();
}
+void TermControlUiaProvider::ChangeViewport(const SMALL_RECT NewWindow)
+{
+ _termControl->ScrollViewport(NewWindow.Top);
+}
+
HRESULT TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque>& result)
{
try
diff --git a/src/cascadia/TerminalControl/TermControlUiaProvider.hpp b/src/cascadia/TerminalControl/TermControlUiaProvider.hpp
index c90d3bf6ca3..0c41fa53d21 100644
--- a/src/cascadia/TerminalControl/TermControlUiaProvider.hpp
+++ b/src/cascadia/TerminalControl/TermControlUiaProvider.hpp
@@ -45,6 +45,7 @@ namespace Microsoft::Terminal
const COORD GetFontSize() const;
const winrt::Windows::UI::Xaml::Thickness GetPadding() const;
+ void ChangeViewport(const SMALL_RECT NewWindow) override;
protected:
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque>& selectionRanges) override;
diff --git a/src/cascadia/TerminalControl/UiaTextRange.cpp b/src/cascadia/TerminalControl/UiaTextRange.cpp
index 45e907bd856..35169adcc36 100644
--- a/src/cascadia/TerminalControl/UiaTextRange.cpp
+++ b/src/cascadia/TerminalControl/UiaTextRange.cpp
@@ -8,6 +8,7 @@
using namespace Microsoft::Terminal;
using namespace Microsoft::Console::Types;
using namespace Microsoft::WRL;
+using namespace winrt::Windows::Graphics::Display;
HRESULT UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData,
_In_ IRawElementProviderSimple* pProvider,
@@ -105,9 +106,10 @@ IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider*
return S_OK;
}
-void UiaTextRange::_ChangeViewport(const SMALL_RECT /*NewWindow*/)
+void UiaTextRange::_ChangeViewport(const SMALL_RECT NewWindow)
{
- // TODO GitHub #2361: Update viewport when calling UiaTextRangeBase::ScrollIntoView()
+ auto provider = static_cast(_pProvider);
+ provider->ChangeViewport(NewWindow);
}
// Method Description:
@@ -121,21 +123,59 @@ void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const
{
auto provider = static_cast(_pProvider);
+ auto includeOffsets = [](long clientPos, double termControlPos, double padding, double scaleFactor) {
+ auto result = base::ClampedNumeric(clientPos);
+ result += padding;
+ result *= scaleFactor;
+ result += termControlPos;
+ return result;
+ };
+
// update based on TermControl location (important for Panes)
UiaRect boundingRect;
THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect));
- clientPoint->x += gsl::narrow(boundingRect.left);
- clientPoint->y += gsl::narrow(boundingRect.top);
// update based on TermControl padding
- auto padding = provider->GetPadding();
- clientPoint->x += gsl::narrow(padding.Left);
- clientPoint->y += gsl::narrow(padding.Top);
+ const auto padding = provider->GetPadding();
+
+ // Get scale factor for display
+ const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
+
+ clientPoint->x = includeOffsets(clientPoint->x, boundingRect.left, padding.Left, scaleFactor);
+ clientPoint->y = includeOffsets(clientPoint->y, boundingRect.top, padding.Top, scaleFactor);
}
-void UiaTextRange::_TranslatePointFromScreen(LPPOINT /*screenPoint*/) const
+// Method Description:
+// - Transform coordinates relative to the screen to relative to the client
+// Arguments:
+// - screenPoint: coordinates relative to the screen where
+// (0,0) is the top-left of the screen
+// Return Value:
+// -
+void UiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) const
{
- // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF SCREENTOCLIENT()
+ auto provider = static_cast(_pProvider);
+
+ auto includeOffsets = [](long screenPos, double termControlPos, double padding, double scaleFactor) {
+ auto result = base::ClampedNumeric(screenPos);
+ result -= termControlPos;
+ result /= scaleFactor;
+ result -= padding;
+ return result;
+ };
+
+ // update based on TermControl location (important for Panes)
+ UiaRect boundingRect;
+ THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect));
+
+ // update based on TermControl padding
+ const auto padding = provider->GetPadding();
+
+ // Get scale factor for display
+ const auto scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
+
+ screenPoint->x = includeOffsets(screenPoint->x, boundingRect.left, padding.Left, scaleFactor);
+ screenPoint->y = includeOffsets(screenPoint->y, boundingRect.top, padding.Top, scaleFactor);
}
const COORD UiaTextRange::_getScreenFontSize() const
diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
index e6b4be530b7..112cf396432 100644
--- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
+++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
@@ -128,6 +128,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
g.EnableConptyModeForTests();
expectedOutput.clear();
+ _checkConptyOutput = true;
+ _logConpty = false;
return true;
}
@@ -152,6 +154,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
TEST_METHOD(WriteAFewSimpleLines);
TEST_METHOD(PassthroughClearScrollback);
+ TEST_METHOD(MoveCursorAtEOL);
+
private:
bool _writeCallback(const char* const pch, size_t const cch);
void _flushFirstFrame();
@@ -344,6 +348,70 @@ void ConptyRoundtripTests::WriteAFewSimpleLines()
verifyData(termTb);
}
+void ConptyRoundtripTests::MoveCursorAtEOL()
+{
+ // This is a test for GH#1245
+ VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());
+
+ auto& g = ServiceLocator::LocateGlobals();
+ auto& renderer = *g.pRender;
+ auto& gci = g.getConsoleInformation();
+ auto& si = gci.GetActiveOutputBuffer();
+ auto& hostSm = si.GetStateMachine();
+ auto& hostTb = si.GetTextBuffer();
+ auto& termTb = *term->_buffer;
+
+ _flushFirstFrame();
+
+ Log::Comment(NoThrowString().Format(
+ L"Write exactly a full line of text"));
+ hostSm.ProcessString(std::wstring(TerminalViewWidth, L'A'));
+
+ auto verifyData0 = [](TextBuffer& tb) {
+ auto iter = tb.GetCellDataAt({ 0, 0 });
+ TestUtils::VerifySpanOfText(L"A", iter, 0, TerminalViewWidth);
+ TestUtils::VerifySpanOfText(L" ", iter, 0, TerminalViewWidth);
+ };
+
+ verifyData0(hostTb);
+
+ // TODO: GH#405/#4415 - Before #405 merges, the VT sequences conpty emits
+ // might change, but the buffer contents shouldn't.
+ // If they do change and these tests break, that's to be expected.
+ expectedOutput.push_back(std::string(TerminalViewWidth, 'A'));
+ expectedOutput.push_back("\x1b[1;80H");
+
+ VERIFY_SUCCEEDED(renderer.PaintFrame());
+
+ verifyData0(termTb);
+
+ Log::Comment(NoThrowString().Format(
+ L"Emulate backspacing at a bash prompt when the previous line wrapped.\n"
+ L"We'll move the cursor up to the last char of the prev line, and erase it."));
+ hostSm.ProcessString(L"\x1b[1;80H");
+ hostSm.ProcessString(L"\x1b[K");
+
+ auto verifyData1 = [](TextBuffer& tb) {
+ auto iter = tb.GetCellDataAt({ 0, 0 });
+ // There should be 79 'A's, followed by a space, and the following line should be blank.
+ TestUtils::VerifySpanOfText(L"A", iter, 0, TerminalViewWidth - 1);
+ TestUtils::VerifySpanOfText(L" ", iter, 0, 1);
+ TestUtils::VerifySpanOfText(L" ", iter, 0, TerminalViewWidth);
+
+ auto& cursor = tb.GetCursor();
+ VERIFY_ARE_EQUAL(TerminalViewWidth - 1, cursor.GetPosition().X);
+ VERIFY_ARE_EQUAL(0, cursor.GetPosition().Y);
+ };
+
+ verifyData1(hostTb);
+
+ expectedOutput.push_back(" ");
+ expectedOutput.push_back("\x1b[1;80H");
+ VERIFY_SUCCEEDED(renderer.PaintFrame());
+
+ verifyData1(termTb);
+}
+
void ConptyRoundtripTests::PassthroughClearScrollback()
{
Log::Comment(NoThrowString().Format(
diff --git a/src/interactivity/onecore/BgfxEngine.cpp b/src/interactivity/onecore/BgfxEngine.cpp
index b73c769c7a5..32d52760bdf 100644
--- a/src/interactivity/onecore/BgfxEngine.cpp
+++ b/src/interactivity/onecore/BgfxEngine.cpp
@@ -138,7 +138,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
for (SHORT j = 0; j < _displayWidth; j++)
{
NewRun[j].Character = L' ';
- NewRun[j].Atribute = 0;
+ NewRun[j].Attribute = 0;
}
}
@@ -157,7 +157,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
for (size_t i = 0; i < clusters.size() && i < (size_t)_displayWidth; i++)
{
NewRun[coord.X + i].Character = clusters.at(i).GetTextAsSingle();
- NewRun[coord.X + i].Atribute = _currentLegacyColorAttribute;
+ NewRun[coord.X + i].Attribute = _currentLegacyColorAttribute;
}
return S_OK;
diff --git a/src/interactivity/win32/screenInfoUiaProvider.hpp b/src/interactivity/win32/screenInfoUiaProvider.hpp
index 3b39a32eec8..7984621835d 100644
--- a/src/interactivity/win32/screenInfoUiaProvider.hpp
+++ b/src/interactivity/win32/screenInfoUiaProvider.hpp
@@ -41,7 +41,7 @@ namespace Microsoft::Console::Interactivity::Win32
IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override;
HWND GetWindowHandle() const;
- void ChangeViewport(const SMALL_RECT NewWindow);
+ void ChangeViewport(const SMALL_RECT NewWindow) override;
protected:
HRESULT GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider, const std::wstring_view wordDelimiters, _Out_ std::deque>& selectionRanges) override;
diff --git a/src/renderer/base/thread.cpp b/src/renderer/base/thread.cpp
index 8dd6784cd9a..99efd4b2e35 100644
--- a/src/renderer/base/thread.cpp
+++ b/src/renderer/base/thread.cpp
@@ -17,6 +17,8 @@ RenderThread::RenderThread() :
_fKeepRunning(true),
_hPaintEnabledEvent(nullptr)
{
+ _fNextFrameRequested.clear();
+ _fPainting.clear();
}
RenderThread::~RenderThread()
@@ -158,10 +160,21 @@ DWORD WINAPI RenderThread::_ThreadProc()
while (_fKeepRunning)
{
WaitForSingleObject(_hPaintEnabledEvent, INFINITE);
- WaitForSingleObject(_hEvent, INFINITE);
+
+ // Skip waiting if next frame is requested.
+ if (_fNextFrameRequested.test_and_set(std::memory_order_relaxed))
+ {
+ _fNextFrameRequested.clear(std::memory_order_relaxed);
+ }
+ else
+ {
+ WaitForSingleObject(_hEvent, INFINITE);
+ }
ResetEvent(_hPaintCompletedEvent);
+ _fPainting.test_and_set(std::memory_order_acquire);
+
LOG_IF_FAILED(_pRenderer->PaintFrame());
SetEvent(_hPaintCompletedEvent);
@@ -171,6 +184,8 @@ DWORD WINAPI RenderThread::_ThreadProc()
{
Sleep(s_FrameLimitMilliseconds);
}
+
+ _fPainting.clear(std::memory_order_release);
}
return S_OK;
@@ -178,6 +193,14 @@ DWORD WINAPI RenderThread::_ThreadProc()
void RenderThread::NotifyPaint()
{
+ // If we are currently painting a frame, set _fNextFrameRequested flag
+ // to indicate we want to paint next frame immediately.
+ if (_fPainting.test_and_set(std::memory_order_acquire))
+ {
+ _fNextFrameRequested.test_and_set(std::memory_order_relaxed);
+ return;
+ }
+
SetEvent(_hEvent);
}
diff --git a/src/renderer/base/thread.hpp b/src/renderer/base/thread.hpp
index 7dd20535c0e..3e991f61836 100644
--- a/src/renderer/base/thread.hpp
+++ b/src/renderer/base/thread.hpp
@@ -47,5 +47,7 @@ namespace Microsoft::Console::Render
IRenderer* _pRenderer; // Non-ownership pointer
bool _fKeepRunning;
+ std::atomic_flag _fNextFrameRequested;
+ std::atomic_flag _fPainting;
};
}
diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp
index 9ea3cdf3bf6..d00700d274a 100644
--- a/src/renderer/dx/CustomTextLayout.cpp
+++ b/src/renderer/dx/CustomTextLayout.cpp
@@ -404,30 +404,8 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory
// Offsets is how far to move the origin (in pixels) from where it is
auto& offset = _glyphOffsets.at(i);
- // Get how many columns we expected the glyph to have and multiply into pixels.
- UINT16 columns = 0;
- {
- // Because of typographic features such as ligatures, it is well possible for a glyph to represent
- // multiple code points. Previous calls to IDWriteTextAnalyzer::GetGlyphs stores the mapping
- // information between code points and glyphs in _glyphClusters.
- // To properly allocate the columns for such glyphs, we need to find all characters that this glyph
- // is representing and add column counts for all the characters together.
-
- // Find the range for current glyph run in _glyphClusters.
- const auto runStartIterator = _glyphClusters.begin() + run.textStart;
- const auto runEndIterator = _glyphClusters.begin() + run.textStart + run.textLength;
-
- // Find the range of characters that the current glyph is representing.
- const auto firstIterator = std::find(runStartIterator, runEndIterator, i - run.glyphStart);
- const auto lastIterator = std::find(runStartIterator, runEndIterator, i - run.glyphStart + 1);
-
- // Add all allocated column counts together.
- for (auto j = firstIterator; j < lastIterator; j++)
- {
- const auto charIndex = std::distance(_glyphClusters.begin(), j);
- columns += _textClusterColumns.at(charIndex);
- }
- }
+ // Get how many columns we expected the glyph to have and mutiply into pixels.
+ const auto columns = _textClusterColumns.at(i);
const auto advanceExpected = static_cast(columns * _width);
// If what we expect is bigger than what we have... pad it out.
diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp
index 2c1d8a08ed1..dfbe434f2d2 100644
--- a/src/renderer/vt/XtermEngine.cpp
+++ b/src/renderer/vt/XtermEngine.cpp
@@ -258,6 +258,20 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
hr = _Write(seq);
}
}
+ else if (_delayedEolWrap)
+ {
+ // GH#1245, GH#357 - If we were in the delayed EOL wrap state, make
+ // sure to _manually_ position the cursor now, with a full CUP
+ // sequence, don't try and be clever with \b or \r or other control
+ // sequences. Different terminals (conhost, gnome-terminal, wt) all
+ // behave differently with how the cursor behaves at an end of line.
+ // This is the only solution that works in all of them, and also
+ // works wrapped lines emitted by conpty.
+ //
+ // Make sure to do this _after_ the possible \r\n branch above,
+ // otherwise we might accidentally break wrapped lines (GH#405)
+ hr = _CursorPosition(coord);
+ }
else if (coord.X == 0 && coord.Y == _lastText.Y)
{
// Start of this line
@@ -298,6 +312,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
_newBottomLine = false;
}
_deferredCursorPos = INVALID_COORDS;
+ _delayedEolWrap = false;
return hr;
}
diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp
index 10642d158cc..1c20a85a1a9 100644
--- a/src/renderer/vt/paint.cpp
+++ b/src/renderer/vt/paint.cpp
@@ -450,7 +450,7 @@ using namespace Microsoft::Console::Types;
RETURN_IF_FAILED(VtEngine::_WriteTerminalUtf8(wstr));
// Update our internal tracker of the cursor's position.
- // See MSFT:20266233
+ // See MSFT:20266233 (which is also GH#357)
// If the cursor is at the rightmost column of the terminal, and we write a
// space, the cursor won't actually move to the next cell (which would
// be {0, _lastText.Y++}). The cursor will stay visibly in that last
@@ -461,10 +461,24 @@ using namespace Microsoft::Console::Types;
// we'll determine that we need to emit a \b to put the cursor in the
// right position. This is wrong, and will cause us to move the cursor
// back one character more than we wanted.
- if (_lastText.X < _lastViewport.RightInclusive())
+ //
+ // GH#1245: This needs to be RightExclusive, _not_ inclusive. Otherwise, we
+ // won't update our internal cursor position tracker correctly at the last
+ // character of the row.
+ if (_lastText.X < _lastViewport.RightExclusive())
{
_lastText.X += static_cast(columnsActual);
}
+ // GH#1245: If we wrote the exactly last char of the row, then we're in the
+ // "delayed EOL wrap" state. Different terminals (conhost, gnome-terminal,
+ // wt) all behave differently with how the cursor behaves at an end of line.
+ // Mark that we're in the delayed EOL wrap state - we don't want to be
+ // clever about how we move the cursor in this state, since different
+ // terminals will handle a backspace differently in this state.
+ if (_lastText.X >= _lastViewport.RightInclusive())
+ {
+ _delayedEolWrap = true;
+ }
short sNumSpaces;
try
diff --git a/src/renderer/vt/tracing.cpp b/src/renderer/vt/tracing.cpp
index 3c5f4e0aa63..f03918d47d4 100644
--- a/src/renderer/vt/tracing.cpp
+++ b/src/renderer/vt/tracing.cpp
@@ -67,12 +67,15 @@ std::string toPrintableString(const std::string_view& inString)
void RenderTracing::TraceString(const std::string_view& instr) const
{
#ifndef UNIT_TESTING
- const std::string _seq = toPrintableString(instr);
- const char* const seq = _seq.c_str();
- TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
- "VtEngine_TraceString",
- TraceLoggingString(seq),
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
+ {
+ const std::string _seq = toPrintableString(instr);
+ const char* const seq = _seq.c_str();
+ TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
+ "VtEngine_TraceString",
+ TraceLoggingString(seq),
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ }
#else
UNREFERENCED_PARAMETER(instr);
#endif UNIT_TESTING
@@ -117,12 +120,15 @@ std::string _CoordToString(const COORD& c)
void RenderTracing::TraceInvalidate(const Viewport invalidRect) const
{
#ifndef UNIT_TESTING
- const auto invalidatedStr = _ViewportToString(invalidRect);
- const auto invalidated = invalidatedStr.c_str();
- TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
- "VtEngine_TraceInvalidate",
- TraceLoggingString(invalidated),
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
+ {
+ const auto invalidatedStr = _ViewportToString(invalidRect);
+ const auto invalidated = invalidatedStr.c_str();
+ TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
+ "VtEngine_TraceInvalidate",
+ TraceLoggingString(invalidated),
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ }
#else
UNREFERENCED_PARAMETER(invalidRect);
#endif UNIT_TESTING
@@ -131,12 +137,15 @@ void RenderTracing::TraceInvalidate(const Viewport invalidRect) const
void RenderTracing::TraceInvalidateAll(const Viewport viewport) const
{
#ifndef UNIT_TESTING
- const auto invalidatedStr = _ViewportToString(viewport);
- const auto invalidatedAll = invalidatedStr.c_str();
- TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
- "VtEngine_TraceInvalidateAll",
- TraceLoggingString(invalidatedAll),
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
+ {
+ const auto invalidatedStr = _ViewportToString(viewport);
+ const auto invalidatedAll = invalidatedStr.c_str();
+ TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
+ "VtEngine_TraceInvalidateAll",
+ TraceLoggingString(invalidatedAll),
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ }
#else
UNREFERENCED_PARAMETER(viewport);
#endif UNIT_TESTING
@@ -162,21 +171,24 @@ void RenderTracing::TraceStartPaint(const bool quickReturn,
const bool cursorMoved) const
{
#ifndef UNIT_TESTING
- const auto invalidatedStr = _ViewportToString(invalidRect);
- const auto invalidated = invalidatedStr.c_str();
- const auto lastViewStr = _ViewportToString(lastViewport);
- const auto lastView = lastViewStr.c_str();
- const auto scrollDeltaStr = _CoordToString(scrollDelt);
- const auto scrollDelta = scrollDeltaStr.c_str();
- TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
- "VtEngine_TraceStartPaint",
- TraceLoggingBool(quickReturn),
- TraceLoggingBool(invalidRectUsed),
- TraceLoggingString(invalidated),
- TraceLoggingString(lastView),
- TraceLoggingString(scrollDelta),
- TraceLoggingBool(cursorMoved),
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
+ {
+ const auto invalidatedStr = _ViewportToString(invalidRect);
+ const auto invalidated = invalidatedStr.c_str();
+ const auto lastViewStr = _ViewportToString(lastViewport);
+ const auto lastView = lastViewStr.c_str();
+ const auto scrollDeltaStr = _CoordToString(scrollDelt);
+ const auto scrollDelta = scrollDeltaStr.c_str();
+ TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
+ "VtEngine_TraceStartPaint",
+ TraceLoggingBool(quickReturn),
+ TraceLoggingBool(invalidRectUsed),
+ TraceLoggingString(invalidated),
+ TraceLoggingString(lastView),
+ TraceLoggingString(scrollDelta),
+ TraceLoggingBool(cursorMoved),
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ }
#else
UNREFERENCED_PARAMETER(quickReturn);
UNREFERENCED_PARAMETER(invalidRectUsed);
@@ -200,12 +212,15 @@ void RenderTracing::TraceEndPaint() const
void RenderTracing::TraceLastText(const COORD lastTextPos) const
{
#ifndef UNIT_TESTING
- const auto lastTextStr = _CoordToString(lastTextPos);
- const auto lastText = lastTextStr.c_str();
- TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
- "VtEngine_TraceLastText",
- TraceLoggingString(lastText),
- TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, 0))
+ {
+ const auto lastTextStr = _CoordToString(lastTextPos);
+ const auto lastText = lastTextStr.c_str();
+ TraceLoggingWrite(g_hConsoleVtRendererTraceProvider,
+ "VtEngine_TraceLastText",
+ TraceLoggingString(lastText),
+ TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE));
+ }
#else
UNREFERENCED_PARAMETER(lastTextPos);
#endif UNIT_TESTING
diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp
index 27658e7f9c8..5e9cdb1e716 100644
--- a/src/renderer/vt/vtrenderer.hpp
+++ b/src/renderer/vt/vtrenderer.hpp
@@ -144,6 +144,8 @@ namespace Microsoft::Console::Render
Microsoft::Console::VirtualTerminal::RenderTracing _trace;
bool _inResizeRequest{ false };
+ bool _delayedEolWrap{ false };
+
[[nodiscard]] HRESULT _Write(std::string_view const str) noexcept;
[[nodiscard]] HRESULT _WriteFormattedString(const std::string* const pFormat, ...) noexcept;
[[nodiscard]] HRESULT _Flush() noexcept;
diff --git a/src/renderer/wddmcon/WddmConRenderer.cpp b/src/renderer/wddmcon/WddmConRenderer.cpp
index 23804ec31f2..d9404f9dade 100644
--- a/src/renderer/wddmcon/WddmConRenderer.cpp
+++ b/src/renderer/wddmcon/WddmConRenderer.cpp
@@ -248,10 +248,10 @@ bool WddmConEngine::IsInitialized()
NewChar = &_displayState[rowIndex]->New[colIndex];
OldChar->Character = NewChar->Character;
- OldChar->Atribute = NewChar->Atribute;
+ OldChar->Attribute = NewChar->Attribute;
NewChar->Character = L' ';
- NewChar->Atribute = 0x0;
+ NewChar->Attribute = 0x0;
}
}
@@ -275,10 +275,10 @@ bool WddmConEngine::IsInitialized()
NewChar = &_displayState[coord.Y]->New[coord.X + i];
OldChar->Character = NewChar->Character;
- OldChar->Atribute = NewChar->Atribute;
+ OldChar->Attribute = NewChar->Attribute;
NewChar->Character = clusters.at(i).GetTextAsSingle();
- NewChar->Atribute = _currentLegacyColorAttribute;
+ NewChar->Attribute = _currentLegacyColorAttribute;
}
return WDDMConUpdateDisplay(_hWddmConCtx, _displayState[coord.Y], FALSE);
diff --git a/src/renderer/wddmcon/main.cxx b/src/renderer/wddmcon/main.cxx
index 11ca30730fe..b4d585603ef 100644
--- a/src/renderer/wddmcon/main.cxx
+++ b/src/renderer/wddmcon/main.cxx
@@ -617,7 +617,7 @@ WDDMConUpdateDisplay(
pCtx->pwszGlyphRunAccel[ColumnIndex] = pRowInformation->New[ColumnIndex].Character;
if (ColumnIndexReadAhead != pCtx->DisplaySize.Width) {
- while (pRowInformation->New[ColumnIndex].Atribute == pRowInformation->New[ColumnIndexReadAhead].Atribute) {
+ while (pRowInformation->New[ColumnIndex].Attribute == pRowInformation->New[ColumnIndexReadAhead].Attribute) {
if (memcmp(&pRowInformation->New[ColumnIndexReadAhead],
&pRowInformation->Old[ColumnIndexReadAhead],
sizeof(CD_IO_CHARACTER)) == 0) {
@@ -667,11 +667,11 @@ WDDMConUpdateDisplay(
}
pCtx->pD2DColorBrush->SetColor(
- ConsoleColors[(pCharacter->Atribute >> 4) & 0xF]);
+ ConsoleColors[(pCharacter->Attribute >> 4) & 0xF]);
pCtx->pD2DSwapChainRT->FillRectangle(&GlyphRectangle,
pCtx->pD2DColorBrush);
- pCtx->pD2DColorBrush->SetColor(ConsoleColors[pCharacter->Atribute & 0xF]);
+ pCtx->pD2DColorBrush->SetColor(ConsoleColors[pCharacter->Attribute & 0xF]);
pCtx->pD2DSwapChainRT->DrawTextLayout(Origin,
pTextLayout,
pCtx->pD2DColorBrush,
diff --git a/src/types/ScreenInfoUiaProviderBase.h b/src/types/ScreenInfoUiaProviderBase.h
index 82bdd31fcb0..97c7a8f698c 100644
--- a/src/types/ScreenInfoUiaProviderBase.h
+++ b/src/types/ScreenInfoUiaProviderBase.h
@@ -47,6 +47,7 @@ namespace Microsoft::Console::Types
~ScreenInfoUiaProviderBase() = default;
[[nodiscard]] HRESULT Signal(_In_ EVENTID id);
+ virtual void ChangeViewport(const SMALL_RECT NewWindow) = 0;
// IRawElementProviderSimple methods
IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions) noexcept override;