From 094273b995352ff9d80f36279c297488b756df1d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 1 Dec 2021 13:33:51 -0600 Subject: [PATCH] Change the `ControlCore` layer to own a copy of its settings (#11619) ## Summary of the Pull Request Currently, the TermControl and ControlCore recieve a settings object that implements `IControlSettings`. They use for this for both reading the settings they should use, and also storing some runtime overrides to those settings (namely, `Opacity`). The object they recieve currently is a `T.S.M.TerminalSettings` object, as well as another `TerminalSettings` object if the user wants to have an `unfocusedAppearance`. All these are all hosted in the same process, so everything is fine and dandy. With the upcoming move to having the Terminal split into multiple processes, this will no longer work. If the `ControlCore` in the Content Process is given a pointer to a `TerminalSettings` in a certain Window Process, and that control is subsequently moved to another window, then there's no guarantee that the original `TerminalSettings` object continues to exist. In this scenario, when window 1 is closed, now the Core is unable to read any settings, because the process that owned that object no longer exists. The solution to this issue is to have the `ControlCore`'s own their own copy of the settings they were created with. that way, they can be confident those settings will always exist. Enter `ControlSettings`, a dumb struct for just storing all the contents of the Settings. I used x-macros for this, so that we don't need to copy-paste into this file every time we add a setting. Changing this has all sorts of other fallout effects: * Previewing a scheme/anything is a tad bit more annoying. Before, we could just sneak the previewed scheme into a `TerminalSettings` that lived between the settings we created the control with, and the settings they were actually using, and it would _just work_. Even explaining that here, it sounds like magic, because it was. However, now, the TermControl can't use a layered `TerminalSettings` for the settings anymore. Now we need to actually read out the current color table, and set the whole scheme when we change it. So now there's also a `Microsoft.Terminal.Core.Scheme` _struct_ for holding that data. - Why a `struct`? Because that will go across the process boundary as a blob, rather than as a pointer to an object in the other process. That way we can transit the whole struct from window to core safely. * A TermControl doesn't have a `IControlSettings` at all anymore - it initalizes itself via the settings in the `Core`. This will be useful for tear-out, when we need to have the `TermControl` initialize itself from just a `ControlCore`, without being able to rebuild the settings from scratch. * The `TabTests` that were written under the assumption that the Control had a layered `TerminalSettings` obviously broke, as they were designed to. They've been modified to reflect the new reality. * When we initialize the Control, we give it the settings and the `UnfocusedAppearance` all at once. If we don't give it an `unfocusedAppearance`, it will just use the focused appearance as the unfocused appearance. * The Control no longer can _write_ settings to the `ControlSettings`. We don't want to be storing things in there. Pretty much everything we set in the control, we store somewhere other than in the settings object itself. However, `opacity` and `useAcrylic`, we need to store in a handy new `RUNTIME_SETTING` property. We can write those runtime overrides to those properties. * We no longer store the color scheme for a pane in the persisted state. I'm tracking that in #9800. I don't think it's too hard to add back, but I wanted this in front of eyes sooner than later. ## References * #1256 * #5000 * #9794 has the scheme previewing in it. * #9818 is WAY more possible now. ## PR Checklist * [x] Surprisingly there wasn't ever a card or issue for this one. This was only ever a bullet point in #5000. * A bunch of these issues were fixed along the way, though I never intended to fix them: * [x] Closes #11571 * [x] Closes #11586 * [x] Closes #7219 * [x] Closes #11067 * [x] I think #11623 actually ended up resolving this one, but I'm double tapping on it here: Closes #5703 * [x] I work here * [x] Tests added/passed * [n/a] Requires documentation to be updated ## Detailed Description of the Pull Request / Additional comments Along the way I tried to clean up code where possible, but not too agressively. I didn't end up converting the various `MockTerminalSettings` classes used in tests to the x macros quite yet. I wanted to merge this with #11416 in `main` before I went too crazy. ## Validation Steps Performed * [x] Scheme previewing works * [x] Adjusting the font size works * [x] focused/unfocused appearances still work * [x] mouse-wheeling opacity still works * [x] acrylic & cleartype still does the right thing * [x] saving the settings still works * [x] going wild on sliding the opacity slider in the settings doesn't crash the terminal * [x] toggling retro effects with a keybinding still works * [x] toggling retro effects with the command palette works * [x] The matrix of (`useAcrylic(true,false)`)x(`opacity(50,100)`)x(`antialiasingMode(cleartype, grayscale)`) works as expected. Slightly changed, falls back to grayscale more often, but looks more right. --- .../TerminalSettingsTests.cpp | 8 +- .../LocalTests_TerminalApp/TabTests.cpp | 108 ++------ .../TerminalApp/ActionPreviewHandlers.cpp | 63 +---- .../TerminalApp/AppActionHandlers.cpp | 23 +- src/cascadia/TerminalApp/Pane.cpp | 36 +-- src/cascadia/TerminalApp/TerminalPage.cpp | 11 +- .../TerminalControl/ControlAppearance.h | 49 ++++ src/cascadia/TerminalControl/ControlCore.cpp | 247 +++++++++++++----- src/cascadia/TerminalControl/ControlCore.h | 36 ++- src/cascadia/TerminalControl/ControlCore.idl | 12 +- .../TerminalControl/ControlInteractivity.cpp | 14 +- .../TerminalControl/ControlInteractivity.h | 2 + .../TerminalControl/ControlInteractivity.idl | 3 + .../TerminalControl/ControlSettings.h | 81 ++++++ .../TerminalControl/IControlAppearance.idl | 20 +- .../TerminalControl/IControlSettings.idl | 41 +-- src/cascadia/TerminalControl/ICoreState.idl | 2 + src/cascadia/TerminalControl/TermControl.cpp | 226 ++++++++++------ src/cascadia/TerminalControl/TermControl.h | 30 ++- src/cascadia/TerminalControl/TermControl.idl | 7 +- src/cascadia/TerminalCore/ICoreAppearance.idl | 31 +++ src/cascadia/TerminalCore/Terminal.cpp | 67 +++++ src/cascadia/TerminalCore/Terminal.hpp | 4 + .../TerminalSettingsEditor/Profiles.cpp | 11 +- .../TerminalSettingsModel/ColorScheme.cpp | 27 ++ .../TerminalSettingsModel/ColorScheme.h | 2 + .../TerminalSettingsModel/ColorScheme.idl | 2 + .../TerminalSettings.cpp | 53 ---- .../TerminalSettingsModel/TerminalSettings.h | 7 - .../TerminalSettings.idl | 10 +- .../UnitTests_Control/ControlCoreTests.cpp | 13 +- .../ControlInteractivityTests.cpp | 11 +- .../UnitTests_Control/MockControlSettings.h | 1 - src/cascadia/inc/ControlProperties.h | 70 +++++ src/renderer/atlas/AtlasEngine.api.cpp | 4 +- src/renderer/atlas/AtlasEngine.h | 2 +- src/renderer/dx/DxRenderer.cpp | 31 +-- src/renderer/dx/DxRenderer.hpp | 4 +- src/renderer/inc/IRenderEngine.hpp | 2 +- 39 files changed, 871 insertions(+), 500 deletions(-) create mode 100644 src/cascadia/TerminalControl/ControlAppearance.h create mode 100644 src/cascadia/TerminalControl/ControlSettings.h create mode 100644 src/cascadia/inc/ControlProperties.h diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp index d830a65efb8..d5ced6f3e17 100644 --- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp @@ -55,8 +55,12 @@ namespace SettingsModelLocalTests TerminalSettings settings; VERIFY_IS_NOT_NULL(settings); auto oldFontSize = settings.FontSize(); - settings.FontSize(oldFontSize + 5); - auto newFontSize = settings.FontSize(); + + auto selfSettings = winrt::make_self(); + VERIFY_ARE_EQUAL(oldFontSize, selfSettings->FontSize()); + + selfSettings->FontSize(oldFontSize + 5); + auto newFontSize = selfSettings->FontSize(); VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); } diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index 4afa3a03b93..0deb0570e3c 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -90,7 +90,6 @@ namespace TerminalAppLocalTests TEST_METHOD(TestWindowRenameSuccessful); TEST_METHOD(TestWindowRenameFailure); - TEST_METHOD(TestControlSettingsHasParent); TEST_METHOD(TestPreviewCommitScheme); TEST_METHOD(TestPreviewDismissScheme); TEST_METHOD(TestPreviewSchemeWhilePreviewing); @@ -134,10 +133,6 @@ namespace TerminalAppLocalTests // Just creating it is enough to know that everything is working. TerminalSettings settings; VERIFY_IS_NOT_NULL(settings); - auto oldFontSize = settings.FontSize(); - settings.FontSize(oldFontSize + 5); - auto newFontSize = settings.FontSize(); - VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); } void TabTests::TryCreateConnectionType() @@ -1297,25 +1292,6 @@ namespace TerminalAppLocalTests }); } - void TabTests::TestControlSettingsHasParent() - { - Log::Comment(L"Ensure that when we create a control, it always has a parent TerminalSettings"); - - auto page = _commonSetup(); - VERIFY_IS_NOT_NULL(page); - - TestOnUIThread([&page]() { - const auto& activeControl{ page->_GetActiveControl() }; - VERIFY_IS_NOT_NULL(activeControl); - - const auto& controlSettings = activeControl.Settings().as(); - VERIFY_IS_NOT_NULL(controlSettings); - - const auto& originalSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - }); - } - void TabTests::TestPreviewCommitScheme() { Log::Comment(L"Preview a color scheme. Make sure it's applied, then committed accordingly"); @@ -1327,12 +1303,9 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& originalSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); }); @@ -1346,17 +1319,12 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& previewSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(previewSettings); - - const auto& originalSettings = previewSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - Log::Comment(L"Color should be changed to the preview"); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); + // And we should have stored a function to revert the change. VERIFY_ARE_EQUAL(1u, page->_restorePreviewFuncs.size()); }); @@ -1373,20 +1341,22 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& originalSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - - const auto& grandparentSettings = originalSettings.GetParent(); - VERIFY_IS_NULL(grandparentSettings); - Log::Comment(L"Color should be changed"); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); + // After preview there should be no more restore functions to execute. VERIFY_ARE_EQUAL(0u, page->_restorePreviewFuncs.size()); }); + + Log::Comment(L"Sleep to let events propagate"); + // If you don't do this, we will _sometimes_ crash as we're tearing down + // the control from this test as we start the next one. We crash + // somewhere in the CursorPositionChanged handler. It's annoying, but + // this works. + Sleep(250); } void TabTests::TestPreviewDismissScheme() @@ -1400,12 +1370,9 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& originalSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); }); @@ -1419,15 +1386,9 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& previewSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(previewSettings); - - const auto& originalSettings = previewSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - Log::Comment(L"Color should be changed to the preview"); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); }); @@ -1441,18 +1402,14 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& originalSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - - const auto& grandparentSettings = originalSettings.GetParent(); - VERIFY_IS_NULL(grandparentSettings); - Log::Comment(L"Color should be the same as it originally was"); VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); }); + Log::Comment(L"Sleep to let events propagate"); + Sleep(250); } void TabTests::TestPreviewSchemeWhilePreviewing() @@ -1468,12 +1425,9 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& originalSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - VERIFY_ARE_EQUAL(til::color{ 0xff0c0c0c }, controlSettings.DefaultBackground()); }); @@ -1487,15 +1441,9 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& previewSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(previewSettings); - - const auto& originalSettings = previewSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - Log::Comment(L"Color should be changed to the preview"); VERIFY_ARE_EQUAL(til::color{ 0xff000000 }, controlSettings.DefaultBackground()); }); @@ -1510,15 +1458,9 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& previewSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(previewSettings); - - const auto& originalSettings = previewSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - Log::Comment(L"Color should be changed to the preview"); VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground()); }); @@ -1535,18 +1477,14 @@ namespace TerminalAppLocalTests const auto& activeControl{ page->_GetActiveControl() }; VERIFY_IS_NOT_NULL(activeControl); - const auto& controlSettings = activeControl.Settings().as(); + const auto& controlSettings = activeControl.Settings(); VERIFY_IS_NOT_NULL(controlSettings); - const auto& originalSettings = controlSettings.GetParent(); - VERIFY_IS_NOT_NULL(originalSettings); - - const auto& grandparentSettings = originalSettings.GetParent(); - VERIFY_IS_NULL(grandparentSettings); - Log::Comment(L"Color should be changed"); VERIFY_ARE_EQUAL(til::color{ 0xffFAFAFA }, controlSettings.DefaultBackground()); }); + Log::Comment(L"Sleep to let events propagate"); + Sleep(250); } void TabTests::TestClampSwitchToTab() diff --git a/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp b/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp index 3fc4ff2ae9d..9965674f4e0 100644 --- a/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp +++ b/src/cascadia/TerminalApp/ActionPreviewHandlers.cpp @@ -67,8 +67,12 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_EndPreviewColorScheme() { - for (const auto& f : _restorePreviewFuncs) + // Apply the reverts in reverse order - If we had multiple previews + // stacked on top of each other, then this will ensure the first one in + // is the last one out. + for (auto i{ _restorePreviewFuncs.rbegin() }; i < _restorePreviewFuncs.rend(); i++) { + auto f = *i; f(); } _restorePreviewFuncs.clear(); @@ -90,59 +94,18 @@ namespace winrt::TerminalApp::implementation { if (const auto& scheme{ _settings.GlobalSettings().ColorSchemes().TryLookup(args.SchemeName()) }) { - // Clear the saved preview funcs because we don't need to add a restore each time - // the preview color changes, we only need to be able to restore the last one. - _restorePreviewFuncs.clear(); - _ApplyToActiveControls([&](const auto& control) { - // Get the settings of the focused control and stash them - const auto& controlSettings = control.Settings().as(); - // Make sure to recurse up to the root - if you're doing - // this while you're currently previewing a SetColorScheme - // action, then the parent of the control's settings is _the - // last preview TerminalSettings we inserted! We don't want - // to save that one! - auto originalSettings = controlSettings.GetParent(); - while (originalSettings.GetParent() != nullptr) - { - originalSettings = originalSettings.GetParent(); - } - // Create a new child for those settings - TerminalSettingsCreateResult fake{ originalSettings }; - const auto& childStruct = TerminalSettings::CreateWithParent(fake); - // Modify the child to have the applied color scheme - childStruct.DefaultSettings().ApplyColorScheme(scheme); + // Stash a copy of the current scheme. + auto originalScheme{ control.ColorScheme() }; - // Insert that new child as the parent of the control's settings - controlSettings.SetParent(childStruct.DefaultSettings()); - control.UpdateSettings(); + // Apply the new scheme. + control.ColorScheme(scheme.ToCoreScheme()); - // Take a copy of the inputs, since they are pointers anyways. + // Each control will emplace a revert into the + // _restorePreviewFuncs for itself. _restorePreviewFuncs.emplace_back([=]() { - // Get the runtime settings of the focused control - const auto& controlSettings{ control.Settings().as() }; - - // Get the control's root settings, the ones that we actually - // assigned to it. - auto parentSettings{ controlSettings.GetParent() }; - while (parentSettings.GetParent() != nullptr) - { - parentSettings = parentSettings.GetParent(); - } - - // If the root settings are the same as the ones we stashed, - // then reset the parent of the runtime settings to the stashed - // settings. This condition might be false if the settings - // hot-reloaded while the palette was open. In that case, we - // don't want to reset the settings to what they were _before_ - // the hot-reload. - if (originalSettings == parentSettings) - { - // Set the original settings as the parent of the control's settings - control.Settings().as().SetParent(originalSettings); - } - - control.UpdateSettings(); + // On dismiss, restore the original scheme. + control.ColorScheme(originalScheme); }); }); } diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 3feed58a0a0..f734425cc02 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -451,28 +451,7 @@ namespace winrt::TerminalApp::implementation if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName())) { const auto res = _ApplyToActiveControls([&](auto& control) { - // Start by getting the current settings of the control - auto controlSettings = control.Settings().as(); - auto parentSettings = controlSettings; - // Those are the _runtime_ settings however. What we - // need to do is: - // - // 1. Blow away any colors set in the runtime settings. - // 2. Apply the color scheme to the parent settings. - // - // 1 is important to make sure that the effects of - // something like `colortool` are cleared when setting - // the scheme. - if (controlSettings.GetParent() != nullptr) - { - parentSettings = controlSettings.GetParent(); - } - - // ApplyColorScheme(nullptr) will clear the old color scheme. - controlSettings.ApplyColorScheme(nullptr); - parentSettings.ApplyColorScheme(scheme); - - control.UpdateSettings(); + control.ColorScheme(scheme.ToCoreScheme()); }); args.Handled(res); } diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index ef8e125228a..a9971a420a2 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -125,7 +125,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const assert(_IsLeaf()); NewTerminalArgs args{}; - auto controlSettings = _control.Settings().as(); + auto controlSettings = _control.Settings(); args.Profile(controlSettings.ProfileName()); // If we know the user's working directory use it instead of the profile. @@ -156,16 +156,13 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const args.TabColor(winrt::Windows::Foundation::IReference(c)); } - if (controlSettings.AppliedColorScheme()) - { - auto name = controlSettings.AppliedColorScheme().Name(); - // Only save the color scheme if it is different than the profile color - // scheme to not override any other profile appearance choices. - if (_profile.DefaultAppearance().ColorSchemeName() != name) - { - args.ColorScheme(name); - } - } + // TODO:GH#9800 - we used to be able to persist the color scheme that a + // TermControl was initialized with, by name. With the change to having the + // control own its own copy of its settings, this isn't possible anymore. + // + // We may be able to get around this by storing the Name in the Core::Scheme + // object. That would work for schemes set by the Terminal, but not ones set + // by VT, but that seems good enough. return args; } @@ -1459,21 +1456,8 @@ void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Pr assert(_IsLeaf()); _profile = profile; - auto controlSettings = _control.Settings().as(); - // Update the parent of the control's settings object (and not the object itself) so - // that any overrides made by the control don't get affected by the reload - controlSettings.SetParent(settings.DefaultSettings()); - auto unfocusedSettings{ settings.UnfocusedSettings() }; - if (unfocusedSettings) - { - // Note: the unfocused settings needs to be entirely unchanged _except_ we need to - // set its parent to the settings object that lives in the control. This is because - // the overrides made by the control live in that settings object, so we want to make - // sure the unfocused settings inherit from that. - unfocusedSettings.SetParent(controlSettings); - } - _control.UnfocusedAppearance(unfocusedSettings); - _control.UpdateSettings(); + + _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings()); } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index bec40616947..92f853d537c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2247,13 +2247,10 @@ namespace winrt::TerminalApp::implementation TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const ITerminalConnection& connection) { - // Give term control a child of the settings so that any overrides go in the child - // This way, when we do a settings reload we just update the parent and the overrides remain - const auto child = TerminalSettings::CreateWithParent(settings); - TermControl term{ child.DefaultSettings(), connection }; - - term.UnfocusedAppearance(child.UnfocusedSettings()); // It is okay for the unfocused settings to be null - + // Do any initialization that needs to apply to _every_ TermControl we + // create here. + // TermControl will copy the settings out of the settings passed to it. + TermControl term{ settings.DefaultSettings(), settings.UnfocusedSettings(), connection }; return term; } diff --git a/src/cascadia/TerminalControl/ControlAppearance.h b/src/cascadia/TerminalControl/ControlAppearance.h new file mode 100644 index 00000000000..aeb4641f885 --- /dev/null +++ b/src/cascadia/TerminalControl/ControlAppearance.h @@ -0,0 +1,49 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ +#pragma once +#include "../../inc/cppwinrt_utils.h" +#include "../../inc/ControlProperties.h" + +#include +#include + +namespace winrt::Microsoft::Terminal::Control::implementation +{ + struct ControlAppearance : public winrt::implements + { +#define SETTINGS_GEN(type, name, ...) WINRT_PROPERTY(type, name, __VA_ARGS__); + CORE_APPEARANCE_SETTINGS(SETTINGS_GEN) + CONTROL_APPEARANCE_SETTINGS(SETTINGS_GEN) +#undef SETTINGS_GEN + + private: + // Color Table is special because it's an array + std::array _ColorTable; + + public: + winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept + { + return _ColorTable.at(index); + } + void SetColorTableEntry(int32_t index, + winrt::Microsoft::Terminal::Core::Color color) noexcept + { + _ColorTable.at(index) = color; + } + + ControlAppearance(Control::IControlAppearance appearance) + { +#define COPY_SETTING(type, name, ...) _##name = appearance.name(); + CORE_APPEARANCE_SETTINGS(COPY_SETTING) + CONTROL_APPEARANCE_SETTINGS(COPY_SETTING) +#undef COPY_SETTING + + for (size_t i = 0; i < _ColorTable.size(); i++) + { + _ColorTable[i] = appearance.GetColorTableEntry(static_cast(i)); + } + } + }; +} diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 5f4dcb91594..62ddc20244a 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -56,15 +56,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation return initialized; } - ControlCore::ControlCore(IControlSettings settings, + ControlCore::ControlCore(Control::IControlSettings settings, + Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) : _connection{ connection }, - _settings{ settings }, _desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8 }, _actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false } { _EnsureStaticInitialization(); + _settings = winrt::make_self(settings, unfocusedAppearance); + _terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>(); // Subscribe to the connection's disconnected event and call our connection closed handlers. @@ -80,7 +82,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation }); // GH#8969: pre-seed working directory to prevent potential races - _terminal->SetWorkingDirectory(_settings.StartingDirectory()); + _terminal->SetWorkingDirectory(_settings->StartingDirectory()); auto pfnCopyToClipboard = std::bind(&ControlCore::_terminalCopyToClipboard, this, std::placeholders::_1); _terminal->SetCopyToClipboardCallback(pfnCopyToClipboard); @@ -187,7 +189,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } }); - UpdateSettings(settings); + UpdateSettings(settings, unfocusedAppearance); } ControlCore::~ControlCore() @@ -226,7 +228,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation return false; } - if (Feature_AtlasEngine::IsEnabled() && _settings.UseAtlasEngine()) + if (Feature_AtlasEngine::IsEnabled() && _settings->UseAtlasEngine()) { _renderEngine = std::make_unique<::Microsoft::Console::Render::AtlasEngine>(); } @@ -252,7 +254,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation LOG_IF_FAILED(_renderEngine->SetWindowSize({ viewInPixels.Width(), viewInPixels.Height() })); // Update DxEngine's SelectionBackground - _renderEngine->SetSelectionBackground(til::color{ _settings.SelectionBackground() }); + _renderEngine->SetSelectionBackground(til::color{ _settings->SelectionBackground() }); const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels); const auto width = vp.Width(); @@ -260,10 +262,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation _connection.Resize(height, width); // Override the default width and height to match the size of the swapChainPanel - _settings.InitialCols(width); - _settings.InitialRows(height); + _settings->InitialCols(width); + _settings->InitialRows(height); - _terminal->CreateFromSettings(_settings, *_renderer); + _terminal->CreateFromSettings(*_settings, *_renderer); // IMPORTANT! Set this callback up sooner than later. If we do it // after Enable, then it'll be possible to paint the frame once @@ -275,18 +277,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation // We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems) _renderEngine->SetCallback(std::bind(&ControlCore::_renderEngineSwapChainChanged, this)); - _renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect()); - _renderEngine->SetPixelShaderPath(_settings.PixelShaderPath()); - _renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering()); - _renderEngine->SetSoftwareRendering(_settings.SoftwareRendering()); - _renderEngine->SetIntenseIsBold(_settings.IntenseIsBold()); + _renderEngine->SetRetroTerminalEffect(_settings->RetroTerminalEffect()); + _renderEngine->SetPixelShaderPath(_settings->PixelShaderPath()); + _renderEngine->SetForceFullRepaintRendering(_settings->ForceFullRepaintRendering()); + _renderEngine->SetSoftwareRendering(_settings->SoftwareRendering()); + _renderEngine->SetIntenseIsBold(_settings->IntenseIsBold()); _updateAntiAliasingMode(); // GH#5098: Inform the engine of the opacity of the default text background. // GH#11315: Always do this, even if they don't have acrylic on. - const auto backgroundIsOpaque = _settings.Opacity() == 1.0 && _settings.BackgroundImage().empty(); - _renderEngine->SetDefaultTextBackgroundOpacity(static_cast(backgroundIsOpaque)); + _renderEngine->EnableTransparentBackground(_isBackgroundTransparent()); THROW_IF_FAILED(_renderEngine->Enable()); @@ -444,14 +445,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - auto newOpacity = std::clamp(_settings.Opacity() + adjustment, + auto newOpacity = std::clamp(Opacity() + adjustment, 0.0, 1.0); - // GH#5098: Inform the engine of the new opacity of the default text background. - SetBackgroundOpacity(::base::saturated_cast(newOpacity)); - - _settings.Opacity(newOpacity); + auto lock = _terminal->LockForWriting(); + // Update our runtime opacity value + Opacity(newOpacity); // GH#11285 - If the user is on Windows 10, and they changed the // transparency of the control s.t. it should be partially opaque, then @@ -461,7 +461,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation // is what the Terminal did prior to 1.12. if (!IsVintageOpacityAvailable()) { - _settings.UseAcrylic(newOpacity < 1.0); + _runtimeUseAcrylic = newOpacity < 1.0; + } + + // Update the renderer as well. It might need to fall back from + // cleartype -> grayscale if the BG is transparent / acrylic. + if (_renderEngine) + { + _renderEngine->EnableTransparentBackground(_isBackgroundTransparent()); } auto eventArgs = winrt::make_self(newOpacity); @@ -476,7 +483,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // specify a custom pixel shader, manually enable the legacy retro // effect first. This will ensure that a toggle off->on will still work, // even if they currently have retro effect off. - if (_settings.PixelShaderPath().empty() && !_renderEngine->GetRetroTerminalEffect()) + if (_settings->PixelShaderPath().empty() && !_renderEngine->GetRetroTerminalEffect()) { // SetRetroTerminalEffect to true will enable the effect. In this // case, the shader effect will already be disabled (because neither @@ -586,24 +593,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Method Description: // - Updates the settings of the current terminal. // - INVARIANT: This method can only be called if the caller DOES NOT HAVE writing lock on the terminal. - void ControlCore::UpdateSettings(const IControlSettings& settings) + void ControlCore::UpdateSettings(const IControlSettings& settings, const IControlAppearance& newAppearance) { + _settings = winrt::make_self(settings, newAppearance); + auto lock = _terminal->LockForWriting(); - _settings = settings; + _runtimeOpacity = std::nullopt; + _runtimeUseAcrylic = std::nullopt; // GH#11285 - If the user is on Windows 10, and they wanted opacity, but // didn't explicitly request acrylic, then opt them in to acrylic. // On Windows 11+, this isn't needed, because we can have vintage opacity. - if (!IsVintageOpacityAvailable() && _settings.Opacity() < 1.0 && !_settings.UseAcrylic()) + if (!IsVintageOpacityAvailable() && _settings->Opacity() < 1.0 && !_settings->UseAcrylic()) { - _settings.UseAcrylic(true); + _runtimeUseAcrylic = true; } // Initialize our font information. - const auto fontFace = _settings.FontFace(); - const short fontHeight = ::base::saturated_cast(_settings.FontSize()); - const auto fontWeight = _settings.FontWeight(); + const auto fontFace = _settings->FontFace(); + const short fontHeight = ::base::saturated_cast(_settings->FontSize()); + const auto fontWeight = _settings->FontWeight(); // 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. @@ -615,7 +625,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _desiredFont = { _actualFont }; // Update the terminal core with its new Core settings - _terminal->UpdateSettings(_settings); + _terminal->UpdateSettings(*_settings); if (!_initializedTerminal) { @@ -624,8 +634,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - _renderEngine->SetForceFullRepaintRendering(_settings.ForceFullRepaintRendering()); - _renderEngine->SetSoftwareRendering(_settings.SoftwareRendering()); + _renderEngine->SetForceFullRepaintRendering(_settings->ForceFullRepaintRendering()); + _renderEngine->SetSoftwareRendering(_settings->SoftwareRendering()); + // Inform the renderer of our opacity + _renderEngine->EnableTransparentBackground(_isBackgroundTransparent()); _updateAntiAliasingMode(); @@ -642,21 +654,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Method Description: // - Updates the appearance of the current terminal. // - INVARIANT: This method can only be called if the caller DOES NOT HAVE writing lock on the terminal. - void ControlCore::UpdateAppearance(const IControlAppearance& newAppearance) + void ControlCore::ApplyAppearance(const bool& focused) { auto lock = _terminal->LockForWriting(); - + const auto& newAppearance{ focused ? _settings->FocusedAppearance() : _settings->UnfocusedAppearance() }; // Update the terminal core with its new Core settings - _terminal->UpdateAppearance(newAppearance); + _terminal->UpdateAppearance(*newAppearance); // Update DxEngine settings under the lock if (_renderEngine) { // Update DxEngine settings under the lock - _renderEngine->SetSelectionBackground(til::color{ newAppearance.SelectionBackground() }); - _renderEngine->SetRetroTerminalEffect(newAppearance.RetroTerminalEffect()); - _renderEngine->SetPixelShaderPath(newAppearance.PixelShaderPath()); - _renderEngine->SetIntenseIsBold(_settings.IntenseIsBold()); + _renderEngine->SetSelectionBackground(til::color{ newAppearance->SelectionBackground() }); + _renderEngine->SetRetroTerminalEffect(newAppearance->RetroTerminalEffect()); + _renderEngine->SetPixelShaderPath(newAppearance->PixelShaderPath()); + _renderEngine->SetIntenseIsBold(_settings->IntenseIsBold()); _renderer->TriggerRedrawAll(); } } @@ -664,8 +676,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_updateAntiAliasingMode() { D2D1_TEXT_ANTIALIAS_MODE mode; - - switch (_settings.AntialiasingMode()) + // Update DxEngine's AntialiasingMode + switch (_settings->AntialiasingMode()) { case TextAntialiasingMode::Cleartype: mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; @@ -701,7 +713,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (_renderEngine) { std::unordered_map featureMap; - if (const auto fontFeatures = _settings.FontFeatures()) + if (const auto fontFeatures = _settings->FontFeatures()) { featureMap.reserve(fontFeatures.Size()); @@ -711,7 +723,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } std::unordered_map axesMap; - if (const auto fontAxes = _settings.FontAxes()) + if (const auto fontAxes = _settings->FontAxes()) { axesMap.reserve(fontAxes.Size()); @@ -753,8 +765,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { // Make sure we have a non-zero font size const auto newSize = std::max(gsl::narrow_cast(fontSize), 1); - const auto fontFace = _settings.FontFace(); - const auto fontWeight = _settings.FontWeight(); + const auto fontFace = _settings->FontFace(); + const auto fontWeight = _settings->FontWeight(); _actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false }; _actualFontFaceName = { fontFace }; _desiredFont = { _actualFont }; @@ -779,7 +791,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - none void ControlCore::ResetFontSize() { - _setFontSize(_settings.FontSize()); + _setFontSize(_settings->FontSize()); } // Method Description: @@ -991,7 +1003,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TextBuffer::GenHTML(bufferData, _actualFont.GetUnscaledSize().Y, _actualFont.GetFaceName(), - til::color{ _settings.DefaultBackground() }) : + til::color{ _settings->DefaultBackground() }) : ""; // convert to RTF format @@ -999,10 +1011,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation TextBuffer::GenRTF(bufferData, _actualFont.GetUnscaledSize().Y, _actualFont.GetFaceName(), - til::color{ _settings.DefaultBackground() }) : + til::color{ _settings->DefaultBackground() }) : ""; - if (!_settings.CopyOnSelect()) + if (!_settings->CopyOnSelect()) { _terminal->ClearSelection(); _renderer->TriggerSelection(); @@ -1089,7 +1101,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::color ControlCore::BackgroundColor() const { - return _terminal->GetColorTableEntry(TextColor::DEFAULT_BACKGROUND); + return til::color{ _terminal->GetColorTableEntry(TextColor::DEFAULT_BACKGROUND) }.with_alpha(0xff); } // Method Description: @@ -1246,7 +1258,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool ControlCore::CopyOnSelect() const { - return _settings.CopyOnSelect(); + return _settings->CopyOnSelect(); } Windows::Foundation::Collections::IVector ControlCore::SelectedText(bool trimTrailingWhitespace) const @@ -1304,16 +1316,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void ControlCore::SetBackgroundOpacity(const double opacity) - { - if (_renderEngine) - { - auto lock = _terminal->LockForWriting(); - const auto backgroundIsOpaque = opacity == 1.0 && _settings.BackgroundImage().empty(); - _renderEngine->SetDefaultTextBackgroundOpacity(static_cast(backgroundIsOpaque)); - } - } - // Method Description: // - Asynchronously close our connection. The Connection will likely wait // until the attached process terminates before Close returns. If that's @@ -1599,4 +1601,125 @@ namespace winrt::Microsoft::Terminal::Control::implementation return VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE; } + + Core::Scheme ControlCore::ColorScheme() const noexcept + { + Core::Scheme s; + + // This part is definitely a hack. + // + // This function is usually used by the "Preview Color Scheme" + // functionality in TerminalPage. If we've got an unfocused appearance, + // then we've applied that appearance before this is even getting called + // (because the command palette is open with focus on top of us). If we + // return the _current_ colors now, we'll return out the _unfocused_ + // colors. If we do that, and the user dismisses the command palette, + // then the scheme that will get restored is the _unfocused_ one, which + // is not what we want. + // + // So if that's the case, then let's grab the colors from the focused + // appearance as the scheme instead. We'll lose any current runtime + // changes to the color table, but those were already blown away when we + // switched to an unfocused appearance. + // + // IF WE DON'T HAVE AN UNFOCUSED APPEARANCE: then just ask the Terminal + // for it's current color table. That way, we can restore those colors + // back. + if (HasUnfocusedAppearance()) + { + s.Foreground = _settings->FocusedAppearance()->DefaultForeground(); + s.Background = _settings->FocusedAppearance()->DefaultBackground(); + + s.CursorColor = _settings->FocusedAppearance()->CursorColor(); + + s.Black = _settings->FocusedAppearance()->GetColorTableEntry(0); + s.Red = _settings->FocusedAppearance()->GetColorTableEntry(1); + s.Green = _settings->FocusedAppearance()->GetColorTableEntry(2); + s.Yellow = _settings->FocusedAppearance()->GetColorTableEntry(3); + s.Blue = _settings->FocusedAppearance()->GetColorTableEntry(4); + s.Purple = _settings->FocusedAppearance()->GetColorTableEntry(5); + s.Cyan = _settings->FocusedAppearance()->GetColorTableEntry(6); + s.White = _settings->FocusedAppearance()->GetColorTableEntry(7); + s.BrightBlack = _settings->FocusedAppearance()->GetColorTableEntry(8); + s.BrightRed = _settings->FocusedAppearance()->GetColorTableEntry(9); + s.BrightGreen = _settings->FocusedAppearance()->GetColorTableEntry(10); + s.BrightYellow = _settings->FocusedAppearance()->GetColorTableEntry(11); + s.BrightBlue = _settings->FocusedAppearance()->GetColorTableEntry(12); + s.BrightPurple = _settings->FocusedAppearance()->GetColorTableEntry(13); + s.BrightCyan = _settings->FocusedAppearance()->GetColorTableEntry(14); + s.BrightWhite = _settings->FocusedAppearance()->GetColorTableEntry(15); + } + else + { + s = _terminal->GetColorScheme(); + } + + // This might be a tad bit of a hack. This event only gets called by set + // color scheme / preview color scheme, and in that case, we know the + // control _is_ focused. + s.SelectionBackground = _settings->FocusedAppearance()->SelectionBackground(); + + return s; + } + + // Method Description: + // - Apply the given color scheme to this control. We'll take the colors out + // of it and apply them to our focused appearance, and update the terminal + // buffer with the new color table. + // - This is here to support the Set Color Scheme action, and the ability to + // preview schemes in the control. + // Arguments: + // - scheme: the collection of colors to apply. + // Return Value: + // - + void ControlCore::ColorScheme(const Core::Scheme& scheme) + { + auto l{ _terminal->LockForWriting() }; + + _settings->FocusedAppearance()->DefaultForeground(scheme.Foreground); + _settings->FocusedAppearance()->DefaultBackground(scheme.Background); + _settings->FocusedAppearance()->CursorColor(scheme.CursorColor); + _settings->FocusedAppearance()->SelectionBackground(scheme.SelectionBackground); + + _settings->FocusedAppearance()->SetColorTableEntry(0, scheme.Black); + _settings->FocusedAppearance()->SetColorTableEntry(1, scheme.Red); + _settings->FocusedAppearance()->SetColorTableEntry(2, scheme.Green); + _settings->FocusedAppearance()->SetColorTableEntry(3, scheme.Yellow); + _settings->FocusedAppearance()->SetColorTableEntry(4, scheme.Blue); + _settings->FocusedAppearance()->SetColorTableEntry(5, scheme.Purple); + _settings->FocusedAppearance()->SetColorTableEntry(6, scheme.Cyan); + _settings->FocusedAppearance()->SetColorTableEntry(7, scheme.White); + _settings->FocusedAppearance()->SetColorTableEntry(8, scheme.BrightBlack); + _settings->FocusedAppearance()->SetColorTableEntry(9, scheme.BrightRed); + _settings->FocusedAppearance()->SetColorTableEntry(10, scheme.BrightGreen); + _settings->FocusedAppearance()->SetColorTableEntry(11, scheme.BrightYellow); + _settings->FocusedAppearance()->SetColorTableEntry(12, scheme.BrightBlue); + _settings->FocusedAppearance()->SetColorTableEntry(13, scheme.BrightPurple); + _settings->FocusedAppearance()->SetColorTableEntry(14, scheme.BrightCyan); + _settings->FocusedAppearance()->SetColorTableEntry(15, scheme.BrightWhite); + + _terminal->ApplyScheme(scheme); + _renderEngine->SetSelectionBackground(til::color{ _settings->SelectionBackground() }); + + _renderer->TriggerRedrawAll(); + _BackgroundColorChangedHandlers(*this, nullptr); + } + + bool ControlCore::HasUnfocusedAppearance() const + { + return _settings->HasUnfocusedAppearance(); + } + + bool ControlCore::_isBackgroundTransparent() + { + // If we're: + // * Not fully opaque + // * On an acrylic background (of any opacity) + // * rendering on top of an image + // + // then the renderer should not render "default background" text with a + // fully opaque background. Doing that would cover up our nice + // transparency, or our acrylic, or our image. + return Opacity() < 1.0f || UseAcrylic() || !_settings->BackgroundImage().empty(); + } } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index e9fca464446..74c29225f7b 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -16,23 +16,35 @@ #pragma once #include "ControlCore.g.h" +#include "ControlSettings.h" #include "../../renderer/base/Renderer.hpp" #include "../../cascadia/TerminalCore/Terminal.hpp" #include "../buffer/out/search.h" #include "cppwinrt_utils.h" +#include + namespace ControlUnitTests { class ControlCoreTests; class ControlInteractivityTests; }; +#define RUNTIME_SETTING(type, name, setting) \ +private: \ + std::optional _runtime##name{ std::nullopt }; \ + void name(const type newValue) { _runtime##name = newValue; } \ + \ +public: \ + type name() const { return til::coalesce_value(_runtime##name, setting); } + namespace winrt::Microsoft::Terminal::Control::implementation { struct ControlCore : ControlCoreT { public: - ControlCore(IControlSettings settings, + ControlCore(Control::IControlSettings settings, + Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection); ~ControlCore(); @@ -41,8 +53,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation const double compositionScale); void EnablePainting(); - void UpdateSettings(const IControlSettings& settings); - void UpdateAppearance(const IControlAppearance& newAppearance); + void UpdateSettings(const Control::IControlSettings& settings, const IControlAppearance& newAppearance); + void ApplyAppearance(const bool& focused); + Control::IControlSettings Settings() { return *_settings; }; + Control::IControlAppearance FocusedAppearance() const { return *_settings->FocusedAppearance(); }; + Control::IControlAppearance UnfocusedAppearance() const { return *_settings->UnfocusedAppearance(); }; + bool HasUnfocusedAppearance() const; + + winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; + void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme); + void SizeChanged(const double width, const double height); void ScaleChanged(const double scale); uint64_t SwapChainHandle() const; @@ -57,7 +77,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation uint16_t FontWeight() const noexcept; til::color BackgroundColor() const; - void SetBackgroundOpacity(const double opacity); void SendInput(const winrt::hstring& wstr); void PasteText(const winrt::hstring& hstr); @@ -148,6 +167,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation static bool IsVintageOpacityAvailable() noexcept; + RUNTIME_SETTING(double, Opacity, _settings->Opacity()); + RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic()); + // -------------------------------- WinRT Events --------------------------------- // clang-format off WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); @@ -178,6 +200,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation event_token _connectionOutputEventToken; TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker; + winrt::com_ptr _settings{ nullptr }; + std::unique_ptr<::Microsoft::Terminal::Core::Terminal> _terminal{ nullptr }; // NOTE: _renderEngine must be ordered before _renderer. @@ -188,8 +212,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::unique_ptr<::Microsoft::Console::Render::IRenderEngine> _renderEngine{ nullptr }; std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer{ nullptr }; - IControlSettings _settings{ nullptr }; - FontInfoDesired _desiredFont; FontInfo _actualFont; winrt::hstring _actualFontFaceName; @@ -249,6 +271,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _connectionOutputHandler(const hstring& hstr); void _updateHoveredCell(const std::optional terminalPosition); + bool _isBackgroundTransparent(); + inline bool _IsClosing() const noexcept { #ifndef NDEBUG diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 84cb83e80f2..2a4287f5a83 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -33,20 +33,27 @@ namespace Microsoft.Terminal.Control [default_interface] runtimeclass ControlCore : ICoreState { ControlCore(IControlSettings settings, + IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); Boolean Initialize(Double actualWidth, Double actualHeight, Double compositionScale); - void UpdateSettings(IControlSettings settings); - void UpdateAppearance(IControlAppearance appearance); + void UpdateSettings(IControlSettings settings, IControlAppearance appearance); + void ApplyAppearance(Boolean focused); + + IControlSettings Settings { get; }; + IControlAppearance FocusedAppearance { get; }; + IControlAppearance UnfocusedAppearance { get; }; + Boolean HasUnfocusedAppearance(); UInt64 SwapChainHandle { get; }; Windows.Foundation.Size FontSize { get; }; String FontFaceName { get; }; UInt16 FontWeight { get; }; + Double Opacity { get; }; Boolean TrySendKeyEvent(Int16 vkey, Int16 scanCode, @@ -75,7 +82,6 @@ namespace Microsoft.Terminal.Control void BlinkAttributeTick(); void UpdatePatternLocations(); void Search(String text, Boolean goForward, Boolean caseSensitive); - void SetBackgroundOpacity(Double opacity); Microsoft.Terminal.Core.Color BackgroundColor { get; }; Boolean HasSelection { get; }; diff --git a/src/cascadia/TerminalControl/ControlInteractivity.cpp b/src/cascadia/TerminalControl/ControlInteractivity.cpp index 6991d1b400c..d24d443d1f7 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.cpp +++ b/src/cascadia/TerminalControl/ControlInteractivity.cpp @@ -39,13 +39,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation } ControlInteractivity::ControlInteractivity(IControlSettings settings, + Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) : _touchAnchor{ std::nullopt }, _lastMouseClickTimestamp{}, _lastMouseClickPos{}, _selectionNeedsToBeCopied{ false } { - _core = winrt::make_self(settings, connection); + _core = winrt::make_self(settings, unfocusedAppearance, connection); } // Method Description: @@ -641,4 +642,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core->GetUiaData(); } + // Method Description: + // - Used by the TermControl to know if it should translate drag-dropped + // paths into WSL-friendly paths. + // Arguments: + // - + // Return Value: + // - true if the connection we were created with was a WSL profile. + bool ControlInteractivity::ManglePathsForWsl() + { + return _core->Settings().ProfileSource() == L"Windows.Terminal.Wsl"; + } } diff --git a/src/cascadia/TerminalControl/ControlInteractivity.h b/src/cascadia/TerminalControl/ControlInteractivity.h index 5dd818d9d62..2c57bf9bea1 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.h +++ b/src/cascadia/TerminalControl/ControlInteractivity.h @@ -36,6 +36,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { public: ControlInteractivity(IControlSettings settings, + Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection); void GotFocus(); @@ -83,6 +84,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const Windows::Foundation::IReference& formats); void RequestPasteTextFromClipboard(); void SetEndSelectionPoint(const til::point pixelPosition); + bool ManglePathsForWsl(); TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs); TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); diff --git a/src/cascadia/TerminalControl/ControlInteractivity.idl b/src/cascadia/TerminalControl/ControlInteractivity.idl index 6261b773d89..aada01ee0ad 100644 --- a/src/cascadia/TerminalControl/ControlInteractivity.idl +++ b/src/cascadia/TerminalControl/ControlInteractivity.idl @@ -14,6 +14,7 @@ namespace Microsoft.Terminal.Control [default_interface] runtimeclass ControlInteractivity { ControlInteractivity(IControlSettings settings, + IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); ControlCore Core { get; }; @@ -58,6 +59,8 @@ namespace Microsoft.Terminal.Control void UpdateScrollbar(Double newValue); + Boolean ManglePathsForWsl { get; }; + event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler ScrollPositionChanged; event Windows.Foundation.TypedEventHandler PasteFromClipboard; diff --git a/src/cascadia/TerminalControl/ControlSettings.h b/src/cascadia/TerminalControl/ControlSettings.h new file mode 100644 index 00000000000..6dc9a1f639e --- /dev/null +++ b/src/cascadia/TerminalControl/ControlSettings.h @@ -0,0 +1,81 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ +#pragma once +#include "../../inc/cppwinrt_utils.h" +#include "../../inc/ControlProperties.h" + +#include +#include +#include "ControlAppearance.h" + +using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap; +using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap; + +namespace winrt::Microsoft::Terminal::Control::implementation +{ + struct ControlSettings : public winrt::implements + { + // Getters and setters for each *Setting member. We're not using + // WINRT_PROPERTY for these, because they actually exist inside the + // _focusedAppearance member. We don't need to reserve another member to + // hold them. +#define SETTINGS_GEN(type, name, ...) WINRT_PROPERTY(type, name, __VA_ARGS__); + CORE_SETTINGS(SETTINGS_GEN) + CONTROL_SETTINGS(SETTINGS_GEN) +#undef SETTINGS_GEN + + private: + winrt::com_ptr _unfocusedAppearance{ nullptr }; + winrt::com_ptr _focusedAppearance{ nullptr }; + bool _hasUnfocusedAppearance{ false }; + + public: + ControlSettings(const Control::IControlSettings& settings, + const Control::IControlAppearance& unfocusedAppearance) + { + _hasUnfocusedAppearance = unfocusedAppearance != nullptr; + + _focusedAppearance = winrt::make_self(settings); + _unfocusedAppearance = unfocusedAppearance ? + winrt::make_self(unfocusedAppearance) : + _focusedAppearance; + + // Copy every value from the passed in settings, into us. +#define COPY_SETTING(type, name, ...) _##name = settings.name(); + CORE_SETTINGS(COPY_SETTING) + CONTROL_SETTINGS(COPY_SETTING) +#undef COPY_SETTING + } + + winrt::com_ptr UnfocusedAppearance() { return _unfocusedAppearance; } + winrt::com_ptr FocusedAppearance() { return _focusedAppearance; } + bool HasUnfocusedAppearance() { return _hasUnfocusedAppearance; } + + // Getters and setters for each Appearance member. We're not using + // WINRT_PROPERTY for these, because they actually exist inside the + // _focusedAppearance member. We don't need to reserve another member to + // hold them. + // + // The Appearance members (including GetColorTableEntry below) are used + // when this ControlSettings is cast to a IControlAppearance or + // ICoreAppearance. In those cases, we'll always return the Focused + // appearance's version of the member. Callers who care about which + // appearance is being used should be more careful. Fortunately, this + // situation is generally only used when a control is first created, or + // when calling UpdateSettings. +#define APPEARANCE_GEN(type, name, ...) \ + type name() const noexcept { return _focusedAppearance->name(); } \ + void name(const type& value) noexcept { _focusedAppearance->name(value); } + + CORE_APPEARANCE_SETTINGS(APPEARANCE_GEN) + CONTROL_APPEARANCE_SETTINGS(APPEARANCE_GEN) +#undef APPEARANCE_GEN + + winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept + { + return _focusedAppearance->GetColorTableEntry(index); + } + }; +} diff --git a/src/cascadia/TerminalControl/IControlAppearance.idl b/src/cascadia/TerminalControl/IControlAppearance.idl index fd06bc853c3..15269cccf2e 100644 --- a/src/cascadia/TerminalControl/IControlAppearance.idl +++ b/src/cascadia/TerminalControl/IControlAppearance.idl @@ -5,18 +5,18 @@ namespace Microsoft.Terminal.Control { interface IControlAppearance requires Microsoft.Terminal.Core.ICoreAppearance { - Microsoft.Terminal.Core.Color SelectionBackground; - String BackgroundImage; - Double BackgroundImageOpacity; - Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode; - Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment; - Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment; - Boolean IntenseIsBold; + Microsoft.Terminal.Core.Color SelectionBackground { get; }; + String BackgroundImage { get; }; + Double BackgroundImageOpacity { get; }; + Windows.UI.Xaml.Media.Stretch BackgroundImageStretchMode { get; }; + Windows.UI.Xaml.HorizontalAlignment BackgroundImageHorizontalAlignment { get; }; + Windows.UI.Xaml.VerticalAlignment BackgroundImageVerticalAlignment { get; }; + Boolean IntenseIsBold { get; }; // IntenseIsBright is in Core Appearance - Double Opacity; + Double Opacity { get; }; // Experimental settings - Boolean RetroTerminalEffect; - String PixelShaderPath; + Boolean RetroTerminalEffect { get; }; + String PixelShaderPath { get; }; }; } diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index e51a34c561d..8f0c31f58fc 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -24,34 +24,37 @@ namespace Microsoft.Terminal.Control // TermControl's behavior. In these settings there is both the entirety // of the Core ITerminalSettings interface, and any additional settings // for specifically the control. - interface IControlSettings requires Microsoft.Terminal.Core.ICoreSettings, Microsoft.Terminal.Control.IControlAppearance + interface IControlSettings requires Microsoft.Terminal.Core.ICoreSettings, + Microsoft.Terminal.Control.IControlAppearance { String ProfileName; String ProfileSource; - Boolean UseAcrylic; - ScrollbarState ScrollState; - Boolean UseAtlasEngine; - String FontFace; - Int32 FontSize; - Windows.UI.Text.FontWeight FontWeight; - String Padding; - Windows.Foundation.Collections.IMap FontFeatures; - Windows.Foundation.Collections.IMap FontAxes; + Boolean UseAcrylic { get; }; + ScrollbarState ScrollState { get; }; - Microsoft.Terminal.Control.IKeyBindings KeyBindings; + Boolean UseAtlasEngine { get; }; - Boolean CopyOnSelect; - Boolean FocusFollowMouse; + String FontFace { get; }; + Int32 FontSize { get; }; + Windows.UI.Text.FontWeight FontWeight { get; }; + String Padding { get; }; + Windows.Foundation.Collections.IMap FontFeatures { get; }; + Windows.Foundation.Collections.IMap FontAxes { get; }; - String Commandline; - String StartingDirectory; - String EnvironmentVariables; + Microsoft.Terminal.Control.IKeyBindings KeyBindings { get; }; - TextAntialiasingMode AntialiasingMode; + Boolean CopyOnSelect { get; }; + Boolean FocusFollowMouse { get; }; + + String Commandline { get; }; + String StartingDirectory { get; }; + String EnvironmentVariables { get; }; + + TextAntialiasingMode AntialiasingMode { get; }; // Experimental Settings - Boolean ForceFullRepaintRendering; - Boolean SoftwareRendering; + Boolean ForceFullRepaintRendering { get; }; + Boolean SoftwareRendering { get; }; }; } diff --git a/src/cascadia/TerminalControl/ICoreState.idl b/src/cascadia/TerminalControl/ICoreState.idl index ad5217bf666..ac9a2a3465c 100644 --- a/src/cascadia/TerminalControl/ICoreState.idl +++ b/src/cascadia/TerminalControl/ICoreState.idl @@ -22,5 +22,7 @@ namespace Microsoft.Terminal.Control Boolean BracketedPasteEnabled { get; }; Microsoft.Terminal.TerminalConnection.ConnectionState ConnectionState { get; }; + + Microsoft.Terminal.Core.Scheme ColorScheme { get; set; }; }; } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index d78d88ab517..14324e8565c 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -48,8 +48,8 @@ DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState namespace winrt::Microsoft::Terminal::Control::implementation { TermControl::TermControl(IControlSettings settings, + Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection) : - _settings{ settings }, _isInternalScrollBarUpdate{ false }, _autoScrollVelocity{ 0 }, _autoScrollingPointerPoint{ std::nullopt }, @@ -61,7 +61,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { InitializeComponent(); - _interactivity = winrt::make(settings, connection); + _interactivity = winrt::make(settings, unfocusedAppearance, connection); _core = _interactivity.Core(); // These events might all be triggered by the connection, but that @@ -77,7 +77,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // These callbacks can only really be triggered by UI interactions. So // they don't need weak refs - they can't be triggered unless we're // alive. - _core.BackgroundColorChanged({ this, &TermControl::_BackgroundColorChangedHandler }); + _core.BackgroundColorChanged({ this, &TermControl::_coreBackgroundColorChanged }); _core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged }); _core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged }); _core.RaiseNotice({ this, &TermControl::_coreRaisedNotice }); @@ -144,7 +144,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _autoScrollTimer.Interval(AutoScrollUpdateInterval); _autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll }); - _ApplyUISettings(_settings); + _ApplyUISettings(); } // Method Description: @@ -229,11 +229,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation this->Focus(FocusState::Programmatic); } + winrt::fire_and_forget TermControl::UpdateControlSettings(IControlSettings settings) + { + return UpdateControlSettings(settings, _core.UnfocusedAppearance()); + } // Method Description: // - Given Settings having been updated, applies the settings to the current terminal. // Return Value: // - - winrt::fire_and_forget TermControl::UpdateSettings() + winrt::fire_and_forget TermControl::UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance) { auto weakThis{ get_weak() }; @@ -241,21 +245,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation // terminal. co_await winrt::resume_foreground(Dispatcher()); - _UpdateSettingsFromUIThread(_settings); + _core.UpdateSettings(settings, unfocusedAppearance); - auto appearance = _settings.try_as(); - if (!_focused && _UnfocusedAppearance) - { - appearance = _UnfocusedAppearance; - } - _UpdateAppearanceFromUIThread(appearance); + _UpdateSettingsFromUIThread(); + + _UpdateAppearanceFromUIThread(_focused ? _core.FocusedAppearance() : _core.UnfocusedAppearance()); } // Method Description: // - Dispatches a call to the UI thread and updates the appearance // Arguments: // - newAppearance: the new appearance to set - winrt::fire_and_forget TermControl::UpdateAppearance(const IControlAppearance newAppearance) + winrt::fire_and_forget TermControl::UpdateAppearance(IControlAppearance newAppearance) { // Dispatch a call to the UI thread co_await winrt::resume_foreground(Dispatcher()); @@ -271,17 +272,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - INVARIANT: This method must be called from the UI thread. // Arguments: // - newSettings: the new settings to set - void TermControl::_UpdateSettingsFromUIThread(IControlSettings newSettings) + void TermControl::_UpdateSettingsFromUIThread() { if (_IsClosing()) { return; } - _core.UpdateSettings(_settings); - // Update our control settings - _ApplyUISettings(_settings); + _ApplyUISettings(); } // Method Description: @@ -289,7 +288,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - INVARIANT: This method must be called from the UI thread. // Arguments: // - newAppearance: the new appearance to set - void TermControl::_UpdateAppearanceFromUIThread(IControlAppearance newAppearance) + void TermControl::_UpdateAppearanceFromUIThread(Control::IControlAppearance newAppearance) { if (_IsClosing()) { @@ -300,6 +299,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Update our control settings const auto bg = newAppearance.DefaultBackground(); + + // In the future, this might need to be changed to a + // _InitializeBackgroundBrush call instead, because we may need to + // switch from a solid color brush to an acrylic one. _changeBackgroundColor(bg); // Set TSF Foreground @@ -307,7 +310,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation foregroundBrush.Color(static_cast(newAppearance.DefaultForeground())); TSFInputControl().Foreground(foregroundBrush); - _core.UpdateAppearance(newAppearance); + _core.ApplyAppearance(_focused); } // Method Description: @@ -331,7 +334,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Method Description: - // - Style our UI elements based on the values in our _settings, and set up + // - 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 @@ -342,21 +345,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - // Return Value: // - - void TermControl::_ApplyUISettings(const IControlSettings& newSettings) + void TermControl::_ApplyUISettings() { _InitializeBackgroundBrush(); - const auto bg = newSettings.DefaultBackground(); - _changeBackgroundColor(bg); + // settings might be out-of-proc in the future + auto settings{ _core.Settings() }; // Apply padding as swapChainPanel's margin - const auto newMargin = ParseThicknessFromPadding(newSettings.Padding()); + const auto newMargin = ParseThicknessFromPadding(settings.Padding()); SwapChainPanel().Margin(newMargin); TSFInputControl().Margin(newMargin); // Apply settings for scrollbar - if (newSettings.ScrollState() == ScrollbarState::Hidden) + if (settings.ScrollState() == ScrollbarState::Hidden) { // In the scenario where the user has turned off the OS setting to automatically hide scrollbars, the // Terminal scrollbar would still be visible; so, we need to set the control's visibility accordingly to @@ -440,16 +443,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation // 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() { - auto appearance = _settings.try_as(); - if (_settings.UseAcrylic()) + auto settings{ _core.Settings() }; + auto bgColor = til::color{ _core.FocusedAppearance().DefaultBackground() }.with_alpha(0xff); + if (settings.UseAcrylic()) { // See if we've already got an acrylic background brush // to avoid the flicker when setting up a new one @@ -464,65 +466,83 @@ namespace winrt::Microsoft::Terminal::Control::implementation // see GH#1082: Initialize background color so we don't get a // fade/flash when _BackgroundColorChanged is called - auto bgColor = til::color{ _settings.DefaultBackground() }.with_alpha(0xff); - acrylic.FallbackColor(bgColor); acrylic.TintColor(bgColor); // Apply brush settings - acrylic.TintOpacity(appearance.Opacity()); + acrylic.TintOpacity(_core.Opacity()); // Apply brush to control if it's not already there if (RootGrid().Background() != acrylic) { RootGrid().Background(acrylic); } - - // GH#5098: Inform the engine of the new opacity of the default text background. - _core.SetBackgroundOpacity(appearance.Opacity()); } else { Media::SolidColorBrush solidColor{}; - solidColor.Opacity(_settings.Opacity()); - RootGrid().Background(solidColor); + solidColor.Opacity(_core.Opacity()); + solidColor.Color(bgColor); - // GH#5098: Inform the engine of the new opacity of the default text background. - _core.SetBackgroundOpacity(appearance.Opacity()); + RootGrid().Background(solidColor); } } // Method Description: - // - Style the background of the control with the provided background color + // - Handler for the core's BackgroundColorChanged event. Updates the color + // of our background brush to match. + // - Hops over to the UI thread to do this work. // Arguments: - // - color: The background color to use as a uint32 (aka DWORD COLORREF) + // // Return Value: // - - void TermControl::_BackgroundColorChangedHandler(const IInspectable& /*sender*/, - const IInspectable& /*args*/) + winrt::fire_and_forget TermControl::_coreBackgroundColorChanged(const IInspectable& /*sender*/, + const IInspectable& /*args*/) { - til::color newBgColor{ _core.BackgroundColor() }; - _changeBackgroundColor(newBgColor); + auto weakThis{ get_weak() }; + co_await winrt::resume_foreground(Dispatcher()); + if (auto control{ weakThis.get() }) + { + til::color newBgColor{ _core.BackgroundColor() }; + _changeBackgroundColor(newBgColor); + } } - winrt::fire_and_forget TermControl::_changeBackgroundColor(const til::color bg) + // Method Description: + // - Update the color of the background brush we're using. This does _not_ + // update the opacity, or what type of brush it is. + // - INVARIANT: This needs to be called on the UI thread. + // Arguments: + // - bg: the new color to use as the background color. + void TermControl::_changeBackgroundColor(const til::color bg) { - auto weakThis{ get_weak() }; - co_await winrt::resume_foreground(Dispatcher()); + if (auto acrylic = RootGrid().Background().try_as()) + { + acrylic.FallbackColor(bg); + acrylic.TintColor(bg); + } + else if (auto solidColor = RootGrid().Background().try_as()) + { + const auto originalOpacity = solidColor.Opacity(); + solidColor.Color(bg); + solidColor.Opacity(originalOpacity); + } + } - if (auto control{ weakThis.get() }) + // Method Description: + // - Update the opacity of the background brush we're using. This does _not_ + // update the color, or what type of brush it is. + // - INVARIANT: This needs to be called on the UI thread. + void TermControl::_changeBackgroundOpacity() + { + const auto opacity{ _core.Opacity() }; + if (auto acrylic = RootGrid().Background().try_as()) { - if (auto acrylic = RootGrid().Background().try_as()) - { - acrylic.FallbackColor(bg); - acrylic.TintColor(bg); - } - else if (auto solidColor = RootGrid().Background().try_as()) - { - const auto originalOpacity = solidColor.Opacity(); - solidColor.Color(bg); - solidColor.Opacity(originalOpacity); - } + acrylic.TintOpacity(opacity); + } + else if (auto solidColor = RootGrid().Background().try_as()) + { + solidColor.Opacity(opacity); } } @@ -618,7 +638,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) { message = { fmt::format(std::wstring_view{ RS_(L"PixelShaderNotFound") }, - _settings.PixelShaderPath()) }; + (_focused ? _core.FocusedAppearance() : _core.UnfocusedAppearance()).PixelShaderPath()) }; } else if (D2DERR_SHADER_COMPILE_FAILED == hr) { @@ -740,7 +760,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } // Now that the renderer is set up, update the appearance for initialization - _UpdateAppearanceFromUIThread(_settings); + _UpdateAppearanceFromUIThread(_core.FocusedAppearance()); _initializedTerminal = true; @@ -815,7 +835,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // This is required as part of GH#638. // Or do so for alt+space; only send to terminal when explicitly unbound // That is part of #GH7125 - auto bindings{ _settings.KeyBindings() }; + auto bindings{ _core.Settings().KeyBindings() }; bool isUnbound = false; const KeyChord kc = { modifiers.IsCtrlPressed(), @@ -960,7 +980,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - modifiers: The ControlKeyStates representing the modifier key states. bool TermControl::_TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const { - auto bindings = _settings.KeyBindings(); + // TODO: GH#5000 + // The Core owning the keybindings is weird. That's for sure. In the + // future, we may want to pass the keybindings into the control + // separately, so the control can have a pointer to an in-proc + // Keybindings object, rather than routing through the ControlCore. + // (see GH#5000) + auto bindings = _core.Settings().KeyBindings(); if (!bindings) { return false; @@ -1139,7 +1165,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const auto pixelPosition = _toTerminalOrigin(cursorPosition); const auto type = ptr.PointerDeviceType(); - if (!_focused && _settings.FocusFollowMouse()) + if (!_focused && _core.Settings().FocusFollowMouse()) { _FocusFollowMouseRequestedHandlers(*this, nullptr); } @@ -1307,7 +1333,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - Called in response to the core's TransparencyChanged event. We'll use // this to update our background brush. // - The Core should have already updated the TintOpacity and UseAcrylic - // properties in the _settings. + // properties in the _settings-> // Arguments: // - // Return Value: @@ -1318,9 +1344,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation co_await resume_foreground(Dispatcher()); try { - _InitializeBackgroundBrush(); - const auto bg = _settings.DefaultBackground(); - _changeBackgroundColor(bg); + _changeBackgroundOpacity(); } CATCH_LOG(); } @@ -1527,12 +1551,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation _blinkTimer->Start(); } - // Only update the appearance here if an unfocused config exists - - // if an unfocused config does not exist then we never would have switched - // appearances anyway so there's no need to switch back upon gaining focus - if (_UnfocusedAppearance) + // Only update the appearance here if an unfocused config exists - if an + // unfocused config does not exist then we never would have switched + // appearances anyway so there's no need to switch back upon gaining + // focus + if (_core.HasUnfocusedAppearance()) { - UpdateAppearance(_settings); + UpdateAppearance(_core.FocusedAppearance()); } } @@ -1574,9 +1599,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // Check if there is an unfocused config we should set the appearance to // upon losing focus - if (_UnfocusedAppearance) + if (_core.HasUnfocusedAppearance()) { - UpdateAppearance(_UnfocusedAppearance); + UpdateAppearance(_core.UnfocusedAppearance()); } } @@ -1725,7 +1750,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring TermControl::GetProfileName() const { - return _settings.ProfileName(); + return _core.Settings().ProfileName(); } hstring TermControl::WorkingDirectory() const @@ -1947,7 +1972,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation double width = fontSize.Width; double height = fontSize.Height; // Reserve additional space if scrollbar is intended to be visible - if (_settings.ScrollState() == ScrollbarState::Visible) + if (_core.Settings().ScrollState() == ScrollbarState::Visible) { width += ScrollBar().ActualWidth(); } @@ -1968,7 +1993,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation const winrt::Windows::Foundation::Size minSize{ 1, 1 }; const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel(); const auto dpi = ::base::saturated_cast(USER_DEFAULT_SCREEN_DPI * scaleFactor); - return GetProposedDimensions(_settings, dpi, minSize); + + return GetProposedDimensions(_core.Settings(), dpi, minSize); } } @@ -1990,7 +2016,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation padding.Left + padding.Right : padding.Top + padding.Bottom); - if (widthOrHeight && _settings.ScrollState() == ScrollbarState::Visible) + if (widthOrHeight && _core.Settings().ScrollState() == ScrollbarState::Visible) { nonTerminalArea += gsl::narrow_cast(ScrollBar().ActualWidth()); } @@ -2273,7 +2299,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation std::wstring fullPath{ item.Path() }; // Fix path for WSL - if (_settings.ProfileSource() == L"Windows.Terminal.Wsl") + // In the fullness of time, we should likely plumb this up + // to the TerminalApp layer, and have it make the decision + // if this control should have it's path mangled (and do the + // mangling), rather than exposing the source concept to the + // Control layer. + // + // However, it's likely that the control layer may need to + // know about the source anyways in the future, to support + // GH#3158 + if (_interactivity.ManglePathsForWsl()) { std::replace(fullPath.begin(), fullPath.end(), L'\\', L'/'); @@ -2420,12 +2455,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation IControlSettings TermControl::Settings() const { - return _settings; - } - - void TermControl::Settings(IControlSettings newSettings) - { - _settings = newSettings; + // TODO: GH#5000 + // We still need this in a couple places: + // - Pane.cpp uses this for parsing out the StartingTitle, Commandline, + // etc for Pane::GetTerminalArgsForPane. + // - TerminalTab::_CreateToolTipTitle uses the ProfileName for the + // tooltip for the tab. + // + // These both happen on the UI thread right now. In the future, when we + // have to hop across the process boundary to get at the core settings, + // it may make sense to cache these values inside the TermControl + // itself, so it can do the hop once when it's first setup, rather than + // when it's needed by the UI thread. + return _core.Settings(); } Windows::Foundation::IReference TermControl::TabColor() noexcept @@ -2648,4 +2690,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation { return _core.ReadEntireBuffer(); } + + Core::Scheme TermControl::ColorScheme() const noexcept + { + return _core.ColorScheme(); + } + + void TermControl::ColorScheme(const Core::Scheme& scheme) const noexcept + { + _core.ColorScheme(scheme); + } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 441e947b4af..dc6a8cdb8bf 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -25,10 +25,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation { struct TermControl : TermControlT { - TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection); + TermControl(IControlSettings settings, + Control::IControlAppearance unfocusedAppearance, + TerminalConnection::ITerminalConnection connection); - winrt::fire_and_forget UpdateSettings(); - winrt::fire_and_forget UpdateAppearance(const IControlAppearance newAppearance); + winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings); + winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance); + IControlSettings Settings() const; hstring GetProfileName() const; @@ -88,9 +91,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer(); const Windows::UI::Xaml::Thickness GetPadding(); - IControlSettings Settings() const; - void Settings(IControlSettings newSettings); - static Windows::Foundation::Size GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi); static Windows::Foundation::Size GetProposedDimensions(IControlSettings const& settings, const uint32_t dpi, const winrt::Windows::Foundation::Size& initialSizeInChars); @@ -105,6 +105,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring ReadEntireBuffer() const; + winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; + void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept; + // -------------------------------- WinRT Events --------------------------------- // clang-format off WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs); @@ -127,8 +130,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(WarningBell, IInspectable, IInspectable); // clang-format on - WINRT_PROPERTY(IControlAppearance, UnfocusedAppearance); - private: friend struct TermControlT; // friend our parent so it can bind private event handlers @@ -146,7 +147,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::com_ptr _searchBox; - IControlSettings _settings; bool _closing{ false }; bool _focused{ false }; bool _initializedTerminal{ false }; @@ -193,14 +193,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _closing; } - void _UpdateSettingsFromUIThread(IControlSettings newSettings); - void _UpdateAppearanceFromUIThread(IControlAppearance newAppearance); - void _ApplyUISettings(const IControlSettings&); + void _UpdateSettingsFromUIThread(); + void _UpdateAppearanceFromUIThread(Control::IControlAppearance newAppearance); + void _ApplyUISettings(); + winrt::fire_and_forget UpdateAppearance(Control::IControlAppearance newAppearance); void _SetBackgroundImage(const IControlAppearance& newAppearance); void _InitializeBackgroundBrush(); - void _BackgroundColorChangedHandler(const IInspectable& sender, const IInspectable& args); - winrt::fire_and_forget _changeBackgroundColor(const til::color bg); + winrt::fire_and_forget _coreBackgroundColorChanged(const IInspectable& sender, const IInspectable& args); + void _changeBackgroundColor(const til::color bg); + void _changeBackgroundOpacity(); bool _InitializeTerminal(); void _SetFontSize(int fontSize); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 26db0862cac..a87cdc8edbe 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -17,14 +17,15 @@ namespace Microsoft.Terminal.Control ICoreState { TermControl(IControlSettings settings, + IControlAppearance unfocusedAppearance, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); static Windows.Foundation.Size GetProposedDimensions(IControlSettings settings, UInt32 dpi); - void UpdateSettings(); + void UpdateControlSettings(IControlSettings settings); + void UpdateControlSettings(IControlSettings settings, IControlAppearance unfocusedAppearance); - Microsoft.Terminal.Control.IControlSettings Settings; - Microsoft.Terminal.Control.IControlAppearance UnfocusedAppearance; + Microsoft.Terminal.Control.IControlSettings Settings { get; }; event FontSizeChangedEventArgs FontSizeChanged; event Windows.Foundation.TypedEventHandler TitleChanged; diff --git a/src/cascadia/TerminalCore/ICoreAppearance.idl b/src/cascadia/TerminalCore/ICoreAppearance.idl index ddeb5b83825..30813644f07 100644 --- a/src/cascadia/TerminalCore/ICoreAppearance.idl +++ b/src/cascadia/TerminalCore/ICoreAppearance.idl @@ -50,6 +50,37 @@ namespace Microsoft.Terminal.Core UInt32 Value; }; + struct Scheme + { + Microsoft.Terminal.Core.Color Foreground; + Microsoft.Terminal.Core.Color Background; + + Microsoft.Terminal.Core.Color SelectionBackground; + + Microsoft.Terminal.Core.Color CursorColor; + + // Table: A WinRT struct doesn't allow pointers (READ: doesn't allow + // array members) in structs, but we very much would like this object to + // be a struct. So we'll call out each color individually. There's only + // 16, it's not that bad. + Microsoft.Terminal.Core.Color Black; + Microsoft.Terminal.Core.Color Red; + Microsoft.Terminal.Core.Color Green; + Microsoft.Terminal.Core.Color Yellow; + Microsoft.Terminal.Core.Color Blue; + Microsoft.Terminal.Core.Color Purple; + Microsoft.Terminal.Core.Color Cyan; + Microsoft.Terminal.Core.Color White; + Microsoft.Terminal.Core.Color BrightBlack; + Microsoft.Terminal.Core.Color BrightRed; + Microsoft.Terminal.Core.Color BrightGreen; + Microsoft.Terminal.Core.Color BrightYellow; + Microsoft.Terminal.Core.Color BrightBlue; + Microsoft.Terminal.Core.Color BrightPurple; + Microsoft.Terminal.Core.Color BrightCyan; + Microsoft.Terminal.Core.Color BrightWhite; + }; + declare { // Forward declare this parameterized specialization so that it lives diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index b622e40e90b..29c613dc000 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -180,6 +180,9 @@ void Terminal::UpdateSettings(ICoreSettings settings) // - appearance: an ICoreAppearance with new settings values for us to use. void Terminal::UpdateAppearance(const ICoreAppearance& appearance) { + _intenseIsBright = appearance.IntenseIsBright(); + _adjustIndistinguishableColors = appearance.AdjustIndistinguishableColors(); + // Set the default background as transparent to prevent the // DX layer from overwriting the background image or acrylic effect const til::color newBackgroundColor{ appearance.DefaultBackground() }; @@ -1302,3 +1305,67 @@ const size_t Microsoft::Terminal::Core::Terminal::GetTaskbarProgress() const noe { return _taskbarProgress; } + +Scheme Terminal::GetColorScheme() const noexcept +{ + Scheme s; + + s.Foreground = til::color{ _colorTable.at(TextColor::DEFAULT_FOREGROUND) }; + // Don't leak the implementation detail that our _defaultBg is stored + // internally without alpha. + s.Background = til::color{ _colorTable.at(TextColor::DEFAULT_BACKGROUND) }.with_alpha(0xff); + + // SelectionBackground is stored in the ControlAppearance + s.CursorColor = til::color{ _colorTable.at(TextColor::CURSOR_COLOR) }; + + s.Black = til::color{ _colorTable[0] }; + s.Red = til::color{ _colorTable[1] }; + s.Green = til::color{ _colorTable[2] }; + s.Yellow = til::color{ _colorTable[3] }; + s.Blue = til::color{ _colorTable[4] }; + s.Purple = til::color{ _colorTable[5] }; + s.Cyan = til::color{ _colorTable[6] }; + s.White = til::color{ _colorTable[7] }; + s.BrightBlack = til::color{ _colorTable[8] }; + s.BrightRed = til::color{ _colorTable[9] }; + s.BrightGreen = til::color{ _colorTable[10] }; + s.BrightYellow = til::color{ _colorTable[11] }; + s.BrightBlue = til::color{ _colorTable[12] }; + s.BrightPurple = til::color{ _colorTable[13] }; + s.BrightCyan = til::color{ _colorTable[14] }; + s.BrightWhite = til::color{ _colorTable[15] }; + return s; +} + +void Terminal::ApplyScheme(const Scheme& colorScheme) +{ + _colorTable.at(TextColor::DEFAULT_FOREGROUND) = til::color{ colorScheme.Foreground }; + // Set the default background as transparent to prevent the + // DX layer from overwriting the background image or acrylic effect + til::color newBackgroundColor{ colorScheme.Background }; + _colorTable.at(TextColor::DEFAULT_BACKGROUND) = newBackgroundColor.with_alpha(0); + + _colorTable[0] = til::color{ colorScheme.Black }; + _colorTable[1] = til::color{ colorScheme.Red }; + _colorTable[2] = til::color{ colorScheme.Green }; + _colorTable[3] = til::color{ colorScheme.Yellow }; + _colorTable[4] = til::color{ colorScheme.Blue }; + _colorTable[5] = til::color{ colorScheme.Purple }; + _colorTable[6] = til::color{ colorScheme.Cyan }; + _colorTable[7] = til::color{ colorScheme.White }; + _colorTable[8] = til::color{ colorScheme.BrightBlack }; + _colorTable[9] = til::color{ colorScheme.BrightRed }; + _colorTable[10] = til::color{ colorScheme.BrightGreen }; + _colorTable[11] = til::color{ colorScheme.BrightYellow }; + _colorTable[12] = til::color{ colorScheme.BrightBlue }; + _colorTable[13] = til::color{ colorScheme.BrightPurple }; + _colorTable[14] = til::color{ colorScheme.BrightCyan }; + _colorTable[15] = til::color{ colorScheme.BrightWhite }; + + _colorTable.at(TextColor::CURSOR_COLOR) = til::color{ colorScheme.CursorColor }; + + if (_adjustIndistinguishableColors) + { + _MakeAdjustedColorArray(); + } +} diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 1f8a6744767..6369d27e68d 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -30,6 +30,7 @@ namespace winrt::Microsoft::Terminal::Core { struct ICoreSettings; struct ICoreAppearance; + struct Scheme; } namespace Microsoft::Terminal::Core @@ -211,6 +212,9 @@ class Microsoft::Terminal::Core::Terminal final : const std::optional GetTabColor() const noexcept; + winrt::Microsoft::Terminal::Core::Scheme GetColorScheme() const noexcept; + void ApplyScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme); + Microsoft::Console::Render::BlinkingState& GetBlinkingState() const noexcept; const size_t GetTaskbarState() const noexcept; diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.cpp b/src/cascadia/TerminalSettingsEditor/Profiles.cpp index dc377384293..b2e7e322006 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles.cpp @@ -398,7 +398,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } Profiles::Profiles() : - _previewControl{ Control::TermControl(Model::TerminalSettings{}, make()) } + _previewControl{ Control::TermControl(Model::TerminalSettings{}, nullptr, make()) } { InitializeComponent(); @@ -453,26 +453,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { _PropertyChangedHandlers(*this, PropertyChangedEventArgs{ L"CurrentScrollState" }); } - _previewControl.Settings(_State.Profile().TermSettings()); - _previewControl.UpdateSettings(); + _previewControl.UpdateControlSettings(_State.Profile().TermSettings()); }); // The Appearances object handles updating the values in the settings UI, but // we still need to listen to the changes here just to update the preview control _AppearanceViewModelChangedRevoker = _State.Profile().DefaultAppearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) { - _previewControl.Settings(_State.Profile().TermSettings()); - _previewControl.UpdateSettings(); + _previewControl.UpdateControlSettings(_State.Profile().TermSettings()); }); // Navigate to the pivot in the provided navigation state ProfilesPivot().SelectedIndex(static_cast(_State.LastActivePivot())); - _previewControl.Settings(_State.Profile().TermSettings()); // There is a possibility that the control has not fully initialized yet, // so wait for it to initialize before updating the settings (so we know // that the renderer is set up) _previewControl.Initialized([&](auto&& /*s*/, auto&& /*e*/) { - _previewControl.UpdateSettings(); + _previewControl.UpdateControlSettings(_State.Profile().TermSettings()); }); } diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.cpp b/src/cascadia/TerminalSettingsModel/ColorScheme.cpp index 76ba3de6f5f..0d55c405b79 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.cpp +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.cpp @@ -147,3 +147,30 @@ void ColorScheme::SetColorTableEntry(uint8_t index, const Core::Color& value) no THROW_HR_IF(E_INVALIDARG, index >= _table.size()); _table[index] = value; } + +winrt::Microsoft::Terminal::Core::Scheme ColorScheme::ToCoreScheme() const noexcept +{ + winrt::Microsoft::Terminal::Core::Scheme coreScheme{}; + + coreScheme.Foreground = Foreground(); + coreScheme.Background = Background(); + coreScheme.CursorColor = CursorColor(); + coreScheme.SelectionBackground = SelectionBackground(); + coreScheme.Black = Table()[0]; + coreScheme.Red = Table()[1]; + coreScheme.Green = Table()[2]; + coreScheme.Yellow = Table()[3]; + coreScheme.Blue = Table()[4]; + coreScheme.Purple = Table()[5]; + coreScheme.Cyan = Table()[6]; + coreScheme.White = Table()[7]; + coreScheme.BrightBlack = Table()[8]; + coreScheme.BrightRed = Table()[9]; + coreScheme.BrightGreen = Table()[10]; + coreScheme.BrightYellow = Table()[11]; + coreScheme.BrightBlue = Table()[12]; + coreScheme.BrightPurple = Table()[13]; + coreScheme.BrightCyan = Table()[14]; + coreScheme.BrightWhite = Table()[15]; + return coreScheme; +} diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.h b/src/cascadia/TerminalSettingsModel/ColorScheme.h index 4d5e580b074..63bfaf45dd7 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.h +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.h @@ -47,6 +47,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static com_ptr FromJson(const Json::Value& json); Json::Value ToJson() const; + winrt::Microsoft::Terminal::Core::Scheme ToCoreScheme() const noexcept; + com_array Table() const noexcept; void SetColorTableEntry(uint8_t index, const Core::Color& value) noexcept; diff --git a/src/cascadia/TerminalSettingsModel/ColorScheme.idl b/src/cascadia/TerminalSettingsModel/ColorScheme.idl index 418fbb531f2..8e758cfa6b2 100644 --- a/src/cascadia/TerminalSettingsModel/ColorScheme.idl +++ b/src/cascadia/TerminalSettingsModel/ColorScheme.idl @@ -19,5 +19,7 @@ namespace Microsoft.Terminal.Settings.Model // we expose the getter as a function. Microsoft.Terminal.Core.Color[] Table(); void SetColorTableEntry(UInt8 index, Microsoft.Terminal.Core.Color value); + + Microsoft.Terminal.Core.Scheme ToCoreScheme(); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 9645b09836b..bf854ab2e56 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -206,59 +206,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _Opacity = appearance.Opacity(); } - // Method Description: - // - Creates a TerminalSettingsCreateResult from a parent TerminalSettingsCreateResult - // - The returned defaultSettings inherits from the parent's defaultSettings, and the - // returned unfocusedSettings inherits from the returned defaultSettings - // - Note that the unfocused settings needs to be entirely unchanged _except_ we need to - // set its parent to the other settings object that we return. This is because the overrides - // made by the control will live in that other settings object, so we want to make - // sure the unfocused settings inherit from that. - // - Another way to think about this is that initially we have UnfocusedSettings inherit - // from DefaultSettings. This function simply adds another TerminalSettings object - // in the middle of these two, so UnfocusedSettings now inherits from the new object - // and the new object inherits from the DefaultSettings. And this new object is what - // the control can put overrides in. - // Arguments: - // - parent: the TerminalSettingsCreateResult that we create a new one from - // Return Value: - // - A TerminalSettingsCreateResult object that contains a defaultSettings that inherits - // from parent's defaultSettings, and contains an unfocusedSettings that inherits from - // its defaultSettings - Model::TerminalSettingsCreateResult TerminalSettings::CreateWithParent(const Model::TerminalSettingsCreateResult& parent) - { - THROW_HR_IF_NULL(E_INVALIDARG, parent); - - auto defaultImpl{ get_self(parent.DefaultSettings()) }; - auto defaultChild = defaultImpl->CreateChild(); - if (parent.UnfocusedSettings()) - { - parent.UnfocusedSettings().SetParent(*defaultChild); - } - return winrt::make(*defaultChild, parent.UnfocusedSettings()); - } - - // Method Description: - // - Sets our parent to the provided TerminalSettings - // Arguments: - // - parent: our new parent - void TerminalSettings::SetParent(const Model::TerminalSettings& parent) - { - ClearParents(); - com_ptr parentImpl; - parentImpl.copy_from(get_self(parent)); - AddLeastImportantParent(parentImpl); - } - - Model::TerminalSettings TerminalSettings::GetParent() - { - if (_parents.size() > 0) - { - return *_parents.at(0); - } - return nullptr; - } - // Method Description: // - Apply Profile settings, as well as any colors from our color scheme, if we have one. // Arguments: diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 12b318d70b2..1a42dcde45f 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -64,12 +64,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation const Model::NewTerminalArgs& newTerminalArgs, const Control::IKeyBindings& keybindings); - static Model::TerminalSettingsCreateResult CreateWithParent(const Model::TerminalSettingsCreateResult& parent); - - Model::TerminalSettings GetParent(); - - void SetParent(const Model::TerminalSettings& parent); - void ApplyColorScheme(const Model::ColorScheme& scheme); // --------------------------- Core Settings --------------------------- @@ -95,7 +89,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); INHERITABLE_SETTING(Model::TerminalSettings, hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); INHERITABLE_SETTING(Model::TerminalSettings, bool, CopyOnSelect, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, InputServiceWarning, true); INHERITABLE_SETTING(Model::TerminalSettings, bool, FocusFollowMouse, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, TrimBlockSelection, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, DetectURLs, true); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl index 80231906e8f..d6b6e395d6d 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl @@ -28,12 +28,16 @@ namespace Microsoft.Terminal.Settings.Model static TerminalSettingsCreateResult CreateWithProfile(CascadiaSettings appSettings, Profile profile, Microsoft.Terminal.Control.IKeyBindings keybindings); static TerminalSettingsCreateResult CreateWithNewTerminalArgs(CascadiaSettings appSettings, NewTerminalArgs newTerminalArgs, Microsoft.Terminal.Control.IKeyBindings keybindings); - static TerminalSettingsCreateResult CreateWithParent(TerminalSettingsCreateResult parent); - void SetParent(TerminalSettings parent); - TerminalSettings GetParent(); void ApplyColorScheme(ColorScheme scheme); ColorScheme AppliedColorScheme; + + // The getters for these are already defined in IControlSettings. So + // we're just adding the setters here, because TerminalApp likes to be + // able to change these at runtime (e.g. when duplicating a pane). + String Commandline { set; }; + String StartingDirectory { set; }; + String EnvironmentVariables { set; }; }; } diff --git a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp index 4c090b27e71..a936d046eba 100644 --- a/src/cascadia/UnitTests_Control/ControlCoreTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlCoreTests.cpp @@ -67,7 +67,7 @@ namespace ControlUnitTests { Log::Comment(L"Create ControlCore object"); - auto core = winrt::make_self(settings, conn); + auto core = winrt::make_self(settings, settings, conn); core->_inUnitTests = true; return core; } @@ -128,13 +128,14 @@ namespace ControlUnitTests double expectedOpacity = 0.5; auto opacityCallback = [&](auto&&, Control::TransparencyChangedEventArgs args) mutable { VERIFY_ARE_EQUAL(expectedOpacity, args.Opacity()); - VERIFY_ARE_EQUAL(expectedOpacity, settings->Opacity()); - VERIFY_ARE_EQUAL(expectedOpacity, core->_settings.Opacity()); + VERIFY_ARE_EQUAL(expectedOpacity, core->Opacity()); + // The Settings object's opacity shouldn't be changed + VERIFY_ARE_EQUAL(0.5, settings->Opacity()); if (expectedOpacity < 1.0) { VERIFY_IS_TRUE(settings->UseAcrylic()); - VERIFY_IS_TRUE(core->_settings.UseAcrylic()); + VERIFY_IS_TRUE(core->_settings->UseAcrylic()); } // GH#603: Adjusting opacity shouldn't change whether or not we @@ -142,8 +143,8 @@ namespace ControlUnitTests auto expectedUseAcrylic = winrt::Microsoft::Terminal::Control::implementation::ControlCore::IsVintageOpacityAvailable() ? true : (expectedOpacity < 1.0 ? true : false); - VERIFY_ARE_EQUAL(expectedUseAcrylic, settings->UseAcrylic()); - VERIFY_ARE_EQUAL(expectedUseAcrylic, core->_settings.UseAcrylic()); + VERIFY_ARE_EQUAL(expectedUseAcrylic, core->UseAcrylic()); + VERIFY_ARE_EQUAL(true, core->_settings->UseAcrylic()); }; core->TransparencyChanged(opacityCallback); diff --git a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp index 51a2215e915..9f885a94f9f 100644 --- a/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp +++ b/src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp @@ -71,7 +71,7 @@ namespace ControlUnitTests TerminalConnection::ITerminalConnection conn) { Log::Comment(L"Create ControlInteractivity object"); - auto interactivity = winrt::make_self(settings, conn); + auto interactivity = winrt::make_self(settings, settings, conn); VERIFY_IS_NOT_NULL(interactivity); auto core = interactivity->_core; core->_inUnitTests = true; @@ -116,13 +116,14 @@ namespace ControlUnitTests double expectedOpacity = 0.5; auto opacityCallback = [&](auto&&, Control::TransparencyChangedEventArgs args) mutable { VERIFY_ARE_EQUAL(expectedOpacity, args.Opacity()); - VERIFY_ARE_EQUAL(expectedOpacity, settings->Opacity()); - VERIFY_ARE_EQUAL(expectedOpacity, core->_settings.Opacity()); + VERIFY_ARE_EQUAL(expectedOpacity, core->Opacity()); + // The Settings object's opacity shouldn't be changed + VERIFY_ARE_EQUAL(0.5, settings->Opacity()); auto expectedUseAcrylic = winrt::Microsoft::Terminal::Control::implementation::ControlCore::IsVintageOpacityAvailable() ? useAcrylic : (expectedOpacity < 1.0 ? true : false); - VERIFY_ARE_EQUAL(expectedUseAcrylic, settings->UseAcrylic()); - VERIFY_ARE_EQUAL(expectedUseAcrylic, core->_settings.UseAcrylic()); + VERIFY_ARE_EQUAL(useAcrylic, settings->UseAcrylic()); + VERIFY_ARE_EQUAL(expectedUseAcrylic, core->UseAcrylic()); }; core->TransparencyChanged(opacityCallback); diff --git a/src/cascadia/UnitTests_Control/MockControlSettings.h b/src/cascadia/UnitTests_Control/MockControlSettings.h index 9a5767946e2..5a02a54ab04 100644 --- a/src/cascadia/UnitTests_Control/MockControlSettings.h +++ b/src/cascadia/UnitTests_Control/MockControlSettings.h @@ -35,7 +35,6 @@ namespace ControlUnitTests WINRT_PROPERTY(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); WINRT_PROPERTY(winrt::hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); WINRT_PROPERTY(bool, CopyOnSelect, false); - WINRT_PROPERTY(bool, InputServiceWarning, true); WINRT_PROPERTY(bool, FocusFollowMouse, false); WINRT_PROPERTY(winrt::Windows::Foundation::IReference, TabColor, nullptr); diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h new file mode 100644 index 00000000000..b45d3f89015 --- /dev/null +++ b/src/cascadia/inc/ControlProperties.h @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +// --------------------------- Core Appearance --------------------------- +// All of these settings are defined in ICoreAppearance. +#define CORE_APPEARANCE_SETTINGS(X) \ + X(til::color, DefaultForeground, DEFAULT_FOREGROUND) \ + X(til::color, DefaultBackground, DEFAULT_BACKGROUND) \ + X(til::color, CursorColor, DEFAULT_CURSOR_COLOR) \ + X(winrt::Microsoft::Terminal::Core::CursorStyle, CursorShape, winrt::Microsoft::Terminal::Core::CursorStyle::Vintage) \ + X(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT) \ + X(bool, IntenseIsBright, true) \ + X(bool, AdjustIndistinguishableColors, true) + +// --------------------------- Control Appearance --------------------------- +// All of these settings are defined in IControlSettings. +#define CONTROL_APPEARANCE_SETTINGS(X) \ + X(til::color, SelectionBackground, DEFAULT_FOREGROUND) \ + X(double, Opacity, 1.0) \ + X(winrt::hstring, BackgroundImage) \ + X(double, BackgroundImageOpacity, 1.0) \ + X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \ + X(winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center) \ + X(winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center) \ + X(bool, IntenseIsBold) \ + X(bool, RetroTerminalEffect, false) \ + X(winrt::hstring, PixelShaderPath) + +// --------------------------- Core Settings --------------------------- +// All of these settings are defined in ICoreSettings. +#define CORE_SETTINGS(X) \ + X(int32_t, HistorySize, DEFAULT_HISTORY_SIZE) \ + X(int32_t, InitialRows, 30) \ + X(int32_t, InitialCols, 80) \ + X(bool, SnapOnInput, true) \ + X(bool, AltGrAliasing, true) \ + X(winrt::hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS) \ + X(bool, CopyOnSelect, false) \ + X(bool, FocusFollowMouse, false) \ + X(winrt::Windows::Foundation::IReference, TabColor, nullptr) \ + X(winrt::Windows::Foundation::IReference, StartingTabColor, nullptr) \ + X(bool, TrimBlockSelection, false) \ + X(bool, DetectURLs, true) + +// --------------------------- Control Settings --------------------------- +// All of these settings are defined in IControlSettings. +#define CONTROL_SETTINGS(X) \ + X(winrt::hstring, ProfileName) \ + X(winrt::hstring, ProfileSource) \ + X(bool, UseAcrylic, false) \ + X(winrt::hstring, Padding, DEFAULT_PADDING) \ + X(winrt::hstring, FontFace, L"Consolas") \ + X(int32_t, FontSize, DEFAULT_FONT_SIZE) \ + X(winrt::Windows::UI::Text::FontWeight, FontWeight) \ + X(IFontFeatureMap, FontFeatures) \ + X(IFontAxesMap, FontAxes) \ + X(winrt::Microsoft::Terminal::Control::IKeyBindings, KeyBindings, nullptr) \ + X(winrt::hstring, Commandline) \ + X(winrt::hstring, StartingDirectory) \ + X(winrt::hstring, StartingTitle) \ + X(bool, SuppressApplicationTitle) \ + X(winrt::hstring, EnvironmentVariables) \ + X(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible) \ + X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \ + X(bool, ForceFullRepaintRendering, false) \ + X(bool, SoftwareRendering, false) \ + X(bool, ForceVTInput, false) \ + X(bool, UseAtlasEngine, false) diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index f4b3ac9e199..95c6a9a5ffd 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -324,9 +324,9 @@ void AtlasEngine::SetCallback(std::function pfn) noexcept _api.swapChainChangedCallback = std::move(pfn); } -void AtlasEngine::SetDefaultTextBackgroundOpacity(const float opacity) noexcept +void AtlasEngine::EnableTransparentBackground(const bool isTransparent) noexcept { - const auto mixin = opacity == 1.0f ? 0xff000000 : 0x00000000; + const auto mixin = !isTransparent ? 0xff000000 : 0x00000000; if (_api.backgroundOpaqueMixin != mixin) { _api.backgroundOpaqueMixin = mixin; diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 8b9b6d0caa0..7872f1d6b83 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -64,7 +64,7 @@ namespace Microsoft::Console::Render // DxRenderer - setter void SetAntialiasingMode(D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override; void SetCallback(std::function pfn) noexcept override; - void SetDefaultTextBackgroundOpacity(float opacity) noexcept override; + void EnableTransparentBackground(const bool isTransparent) noexcept override; void SetForceFullRepaintRendering(bool enable) noexcept override; [[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override; void SetPixelShaderPath(std::wstring_view value) noexcept override; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 4a99dbfac72..aae67a7bef9 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -88,7 +88,7 @@ DxEngine::DxEngine() : _forceFullRepaintRendering{ false }, _softwareRendering{ false }, _antialiasingMode{ D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE }, - _defaultTextBackgroundOpacity{ 1.0f }, + _defaultBackgroundIsTransparent{ true }, _hwndTarget{ static_cast(INVALID_HANDLE_VALUE) }, _sizeTarget{}, _dpi{ USER_DEFAULT_SCREEN_DPI }, @@ -911,7 +911,7 @@ void DxEngine::_ReleaseDeviceResources() noexcept // someone has chosen the slower ClearType antialiasing (versus the faster // grayscale antialiasing) const bool usingCleartype = _antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - const bool usingTransparency = _defaultTextBackgroundOpacity != 1.0f; + const bool usingTransparency = _defaultBackgroundIsTransparent; // Another way of naming "bgIsDefault" is "bgHasTransparency" const auto bgIsDefault = (_backgroundColor.a == _defaultBackgroundColor.a) && (_backgroundColor.r == _defaultBackgroundColor.r) && @@ -1927,20 +1927,16 @@ CATCH_RETURN() const bool /*usingSoftFont*/, const bool isSettingDefaultBrushes) noexcept { - // GH#5098: If we're rendering with cleartype text, we need to always render - // onto an opaque background. If our background's opacity is 1.0f, that's - // great, we can actually use cleartype in that case. In that scenario - // (cleartype && opacity == 1.0), we'll force the opacity bits of the - // COLORREF to 0xff so we draw as cleartype. In any other case, leave the - // opacity bits unchanged. PaintBufferLine will later do some logic to - // determine if we should paint the text as grayscale or not. + const auto [colorForeground, colorBackground] = pData->GetAttributeColors(textAttributes); + const bool usingCleartype = _antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - const bool usingTransparency = _defaultTextBackgroundOpacity != 1.0f; + const bool usingTransparency = _defaultBackgroundIsTransparent; const bool forceOpaqueBG = usingCleartype && !usingTransparency; - const auto [colorForeground, colorBackground] = pData->GetAttributeColors(textAttributes); - _foregroundColor = _ColorFFromColorRef(OPACITY_OPAQUE | colorForeground); + // October 2021: small changes were made to the way BG color interacts with + // grayscale AA, esp. with regards to acrylic and GH#5098. See comment in + // _ShouldForceGrayscaleAA for more details. _backgroundColor = _ColorFFromColorRef((forceOpaqueBG ? OPACITY_OPAQUE : 0) | colorBackground); _d2dBrushForeground->SetColor(_foregroundColor); @@ -2237,14 +2233,19 @@ CATCH_LOG() // rendering onto a transparent surface (like acrylic), then cleartype won't // work correctly, and will actually just additively blend with the // background. This is here to support GH#5098. +// - We'll use this, along with whether cleartype was requested, to manually set +// the alpha channel of the background brush to 1.0. We need to do that to +// make cleartype work without blending. However, we don't want to do that too +// often - if we do that on top of a transparent BG, then the entire swap +// chain will be fully opaque. // Arguments: -// - opacity: the new opacity of our background, on [0.0f, 1.0f] +// - isTransparent: true if our BG is transparent (acrylic, or anything that's not fully opaque) // Return Value: // - -void DxEngine::SetDefaultTextBackgroundOpacity(const float opacity) noexcept +void DxEngine::EnableTransparentBackground(const bool isTransparent) noexcept try { - _defaultTextBackgroundOpacity = opacity; + _defaultBackgroundIsTransparent = isTransparent; // Make sure we redraw all the cells, to update whether they're actually // drawn with cleartype or not. diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 22aa0132cb4..000cb23f749 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -128,7 +128,7 @@ namespace Microsoft::Console::Render void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept override; void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept override; - void SetDefaultTextBackgroundOpacity(const float opacity) noexcept override; + void EnableTransparentBackground(const bool isTransparent) noexcept override; void SetIntenseIsBold(const bool opacity) noexcept override; void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept override; @@ -257,7 +257,7 @@ namespace Microsoft::Console::Render D2D1_TEXT_ANTIALIAS_MODE _antialiasingMode; - float _defaultTextBackgroundOpacity; + bool _defaultBackgroundIsTransparent; bool _intenseIsBold; // DirectX constant buffers need to be a multiple of 16; align to pad the size. diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 30844b217d4..3397cfa6e75 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -104,7 +104,7 @@ namespace Microsoft::Console::Render // DxRenderer - setter virtual void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept {} virtual void SetCallback(std::function pfn) noexcept {} - virtual void SetDefaultTextBackgroundOpacity(const float opacity) noexcept {} + virtual void EnableTransparentBackground(const bool isTransparent) noexcept {} virtual void SetForceFullRepaintRendering(bool enable) noexcept {} virtual [[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; } virtual void SetPixelShaderPath(std::wstring_view value) noexcept {}