Repository: microsoft/terminal (language: cpp)

Issue:
Feature request: Background image for tab, not per split pane
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

# Description of the new feature/enhancement

Currently, when I have a tab opened in terminal, it uses background image of specific profile. If I add more split panes from the same profile or from different ones, background image tries to fit pane for specific profile.

So it looks like this for single pane in tab:
![single_pane](https://user-images.githubusercontent.com/1774613/82426936-bddf9e00-9a88-11ea-8902-8b67f2b106a4.jpg)

And like this for multiple panes in tab:
![multiple_panes](https://user-images.githubusercontent.com/1774613/82427435-6857c100-9a89-11ea-8f11-d510d22f7bf2.jpg)

I'd like to have single background image for tab, even if it has multiple panes (image would be just "cut" by panes borders). So I'd like to have only one background image like in first screenshot, but still use many panes, like in second.

For this scenario, background images of specific profiles would be ignored or only background image of first pane opened in tab would be taken into consideration and shown.

Lots of other configuration options which control how terminal looks like could be also configurable per tab.
Feature request: Background image for tab, not per split pane
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

# Description of the new feature/enhancement

Currently, when I have a tab opened in terminal, it uses background image of specific profile. If I add more split panes from the same profile or from different ones, background image tries to fit pane for specific profile.

So it looks like this for single pane in tab:
![single_pane](https://user-images.githubusercontent.com/1774613/82426936-bddf9e00-9a88-11ea-8902-8b67f2b106a4.jpg)

And like this for multiple panes in tab:
![multiple_panes](https://user-images.githubusercontent.com/1774613/82427435-6857c100-9a89-11ea-8f11-d510d22f7bf2.jpg)

I'd like to have single background image for tab, even if it has multiple panes (image would be just "cut" by panes borders). So I'd like to have only one background image like in first screenshot, but still use many panes, like in second.

For this scenario, background images of specific profiles would be ignored or only background image of first pane opened in tab would be taken into consideration and shown.

Lots of other configuration options which control how terminal looks like could be also configurable per tab.


Discussion / hints:
This would make a neat extension idea. I've tagged it up as such and added it to our mega extensions list, thanks!
I'd love this, even if it were in core.
@DHowett Do you think this idea could make it through to the core?
If yes, I can start thinking, how it should work.
It'd be easy to have only one "tab profile", which would simply overwrite view-related profiles settings for all tabs if enabled. More hardcore scenario is having several "tab profiles" and having possibility to switch between them somehow (every tab can use different profile).

Extensions system will probably start working in two years perspective, cause there are some Windows changes required.
@maciejmiara The solution to this could be made much simpler from a user's point of view: give the user another option for `backgroundImageStretchMode`, or another setting entirely, that makes the background image always align to the tab instead of the pane. The pane will then just show the portion of that tab aligned background image inside its boundaries. This way, if all panes are of the same profile, they will all show the full background image together. And if one pane is of a different profile, it will show a portion of its background image instead of the full one (assuming that profile's background image is also configured like that).
It's also possible to have the background of the profile that is _in focus_ be the tab background. Just throwing an idea here. Either way, I would love being able to toggle 'per tab'/'per pane' background image behavior. 
> I'd love this, even if it were in core.

@DHowett I tried to start implementing this, adding a Grid.Background ImageBrush to TerminalPage's Root.TabContent, but I'm running into some trouble finding the correct settings to set the image with.
This snippet: https://github.com/microsoft/terminal/blob/b64fd774ce48d8f10c88ac171b134a43cc2791a2/src/cascadia/TerminalApp/TerminalPage.cpp#L2555-L2573
Seems to imply that *each pane/split within the same tab* can have a different profile/settings. Am I understanding that correctly? What would be the correct background image to use, if that's the case?
> Seems to imply that _each pane/split within the same tab_ can have a different profile/settings

Yep, that's exactly correct. And probably what would make this particular feature a bit more challenging. Theoretically, you could just change the image based on what the active profile was. Like, always use the active pane's BG as the whole window BG. But I personally think this needs to be an entirely new setting. 

I've discussed this _a bit_ in #12530, as it pertained to the mockup here: https://user-images.githubusercontent.com/6630660/105511648-17305900-5d0b-11eb-8bba-3502468e2110.png

> **FOR DISCUSSION**: In [this comment], the poster shows a pretty cool mockup of what a backgound image for the _window_ might look like. This kinda fills a similar problem space as Mica. If there were a background image for the whole Terminal window, then in-app acrylic would apply on top of the image. Vintage transparency wouldn't work through the image, it would apply on _top_ of the image. Perhaps, because of these related concerns, there should be a singular `window.background` property, that contains an object of settings. Or, to follow the solely dot notation from before
> ```jsonc
> {
>     "theme": "My Background Theme",
>     "themes": [
>         {
>             "name": "My Background Theme",
>             "window.background.useMica": true, // Use mica behind the window
>             "window.background.image": "some/path/to/image", // Use a BG image
>             "window.background.imageOpacity": 25, // Image is mostly transparent
>             "tabRow.background": "#00000000", // Make the TabView Transparent
>         }
>     ],
>     "profiles": {
>         "defaults": {
>             "opacity": 0 // Panes are all totally transparent
>         }
>     }
> }
> ```
> 
> Perhaps, buy nesting Mica usage under the `"window.background"`, then it'll be clearer that it's something that applies to the whole window, as the background for everything.

Now, proper theming support is more than a little while off, but if you're interested in doing this now, I think we could find a way to make it work. I think we could add this setting as a Global property, like `useAcrylicInTabRow`, and [migrating it later](ttps://github.com/microsoft/terminal/pull/10864#pullrequestreview-726403388). (i'd personally want to toss it in as an `experimental.` setting, just so we don't need to worry about migrating it in the future, but we didn't do that for the acrylic one so 🤷)

Sorry for maybe rambling a bit to get my thoughts in order. I'd asy go ahead and add the setting (name it something like `window.backgroundImage`) to the `GlobalAppSettings`, and then use _that image_ to set the BG of the `Root.TabContent`. Add the setting to:
1. `MTSM_GLOBAL_SETTINGS` in `src\cascadia\TerminalSettingsModel\MTSMSettings.h`
2. `GlobalAppSettings` in `src\cascadia\TerminalSettingsModel\GlobalAppSettings.idl`

After that, you should be able to get it out of `_settings.GlobalSettings()` in `TerminalPage`.
Small; corrections to the above following discussion with the team today:

1. We concurred on the design of `experimental.useBackgroundImageForWindow`, as a global `boolean` setting. When that's true, set the `Root.TabContent` to the background image. (You may also need to plumb that information down through `IControlAppearance` to prevent the control setting it's BG image when that setting is enabled.)
  * a single boolean would make it easier to migrate this in the future. 
  * It didn't seem like folks wanted to be able to layer the pane background on top of the window background
  * Folks did want it in the `experimental.` namespace because we were REALLY unsure as to how this would blend with the bigger picture of themes, which aren't _that_ far down the backlog. 
2. There was majority consensus that the image that should be used was the image of the first pane (the top-leftmost one). We also agreed _that might be hard to implement_, so we were fine with just using the "active pane"'s BG image (which would be quite a bit easier to set).
>  (You may also need to plumb that information down through IControlAppearance to prevent the control setting it's BG image when that setting is enabled.)

How would I go about doing this? I tried adding it to the `.idl` file but that results in errors, and having to add it to ControlSettings and TerminalSettings, which seem to require adding it to macros a second time, with another JSON key, which I assume is wrong since it should canonically be a single global key. I could try plumbing the IGlobalAppSettings through `UpdateSettings->UpdateControlSettings->_UpdateAppearanceFromUIThread->_SetBackgroundImage` but I'm not sure if that's what you meant.
I have it *somewhat* working, just need to figure out how to get the global boolean setting in TermControl to know if the pane backgrounds need to be transparent (This image is hardcoded to always make them transparent):
![imagen](https://user-images.githubusercontent.com/24706838/162854334-ee1d199d-a542-4cc3-86a0-38cbc91b257c.png)

> How would I go about doing this? I tried adding it to the `.idl` file but that results in errors, and having to add it to ControlSettings and TerminalSettings

Yea, there is another place that does need to get added. There are kinda two halves to the settings:
1. Whether a setting is global or per-profile. These are defined in GLobalAppSettings and Profile, and in the MTSMSettings.h macros
2. Whether a setting applies to the Control or the "core" of the Terminal. This one's a little trickier to wrap the head around, but for this case, since the BG image only affects the actual XAML TermControl, it's a ControlSetting. Those are defined in `CONTROL_SETTINGS` in `src\cascadia\inc\ControlProperties.h`

You will also need to add it to `IControlSettings.idl` and `TerminalSettings.h` (haven't figured how to get those generated by the macro yet), and the also set it in `TerminalSettings::_ApplyGlobalSettings`. 

There's a lot of plumbing to deal with, I know. That _should_ be everything. 
This would make a neat extension idea. I've tagged it up as such and added it to our mega extensions list, thanks!
I'd love this, even if it were in core.
@DHowett Do you think this idea could make it through to the core?
If yes, I can start thinking, how it should work.
It'd be easy to have only one "tab profile", which would simply overwrite view-related profiles settings for all tabs if enabled. More hardcore scenario is having several "tab profiles" and having possibility to switch between them somehow (every tab can use different profile).

Extensions system will probably start working in two years perspective, cause there are some Windows changes required.
@maciejmiara The solution to this could be made much simpler from a user's point of view: give the user another option for `backgroundImageStretchMode`, or another setting entirely, that makes the background image always align to the tab instead of the pane. The pane will then just show the portion of that tab aligned background image inside its boundaries. This way, if all panes are of the same profile, they will all show the full background image together. And if one pane is of a different profile, it will show a portion of its background image instead of the full one (assuming that profile's background image is also configured like that).
It's also possible to have the background of the profile that is _in focus_ be the tab background. Just throwing an idea here. Either way, I would love being able to toggle 'per tab'/'per pane' background image behavior. 
> I'd love this, even if it were in core.

@DHowett I tried to start implementing this, adding a Grid.Background ImageBrush to TerminalPage's Root.TabContent, but I'm running into some trouble finding the correct settings to set the image with.
This snippet: https://github.com/microsoft/terminal/blob/b64fd774ce48d8f10c88ac171b134a43cc2791a2/src/cascadia/TerminalApp/TerminalPage.cpp#L2555-L2573
Seems to imply that *each pane/split within the same tab* can have a different profile/settings. Am I understanding that correctly? What would be the correct background image to use, if that's the case?
> Seems to imply that _each pane/split within the same tab_ can have a different profile/settings

Yep, that's exactly correct. And probably what would make this particular feature a bit more challenging. Theoretically, you could just change the image based on what the active profile was. Like, always use the active pane's BG as the whole window BG. But I personally think this needs to be an entirely new setting. 

I've discussed this _a bit_ in #12530, as it pertained to the mockup here: https://user-images.githubusercontent.com/6630660/105511648-17305900-5d0b-11eb-8bba-3502468e2110.png

> **FOR DISCUSSION**: In [this comment], the poster shows a pretty cool mockup of what a backgound image for the _window_ might look like. This kinda fills a similar problem space as Mica. If there were a background image for the whole Terminal window, then in-app acrylic would apply on top of the image. Vintage transparency wouldn't work through the image, it would apply on _top_ of the image. Perhaps, because of these related concerns, there should be a singular `window.background` property, that contains an object of settings. Or, to follow the solely dot notation from before
> ```jsonc
> {
>     "theme": "My Background Theme",
>     "themes": [
>         {
>             "name": "My Background Theme",
>             "window.background.useMica": true, // Use mica behind the window
>             "window.background.image": "some/path/to/image", // Use a BG image
>             "window.background.imageOpacity": 25, // Image is mostly transparent
>             "tabRow.background": "#00000000", // Make the TabView Transparent
>         }
>     ],
>     "profiles": {
>         "defaults": {
>             "opacity": 0 // Panes are all totally transparent
>         }
>     }
> }
> ```
> 
> Perhaps, buy nesting Mica usage under the `"window.background"`, then it'll be clearer that it's something that applies to the whole window, as the background for everything.

Now, proper theming support is more than a little while off, but if you're interested in doing this now, I think we could find a way to make it work. I think we could add this setting as a Global property, like `useAcrylicInTabRow`, and [migrating it later](ttps://github.com/microsoft/terminal/pull/10864#pullrequestreview-726403388). (i'd personally want to toss it in as an `experimental.` setting, just so we don't need to worry about migrating it in the future, but we didn't do that for the acrylic one so 🤷)

Sorry for maybe rambling a bit to get my thoughts in order. I'd asy go ahead and add the setting (name it something like `window.backgroundImage`) to the `GlobalAppSettings`, and then use _that image_ to set the BG of the `Root.TabContent`. Add the setting to:
1. `MTSM_GLOBAL_SETTINGS` in `src\cascadia\TerminalSettingsModel\MTSMSettings.h`
2. `GlobalAppSettings` in `src\cascadia\TerminalSettingsModel\GlobalAppSettings.idl`

After that, you should be able to get it out of `_settings.GlobalSettings()` in `TerminalPage`.
Small; corrections to the above following discussion with the team today:

1. We concurred on the design of `experimental.useBackgroundImageForWindow`, as a global `boolean` setting. When that's true, set the `Root.TabContent` to the background image. (You may also need to plumb that information down through `IControlAppearance` to prevent the control setting it's BG image when that setting is enabled.)
  * a single boolean would make it easier to migrate this in the future. 
  * It didn't seem like folks wanted to be able to layer the pane background on top of the window background
  * Folks did want it in the `experimental.` namespace because we were REALLY unsure as to how this would blend with the bigger picture of themes, which aren't _that_ far down the backlog. 
2. There was majority consensus that the image that should be used was the image of the first pane (the top-leftmost one). We also agreed _that might be hard to implement_, so we were fine with just using the "active pane"'s BG image (which would be quite a bit easier to set).
>  (You may also need to plumb that information down through IControlAppearance to prevent the control setting it's BG image when that setting is enabled.)

How would I go about doing this? I tried adding it to the `.idl` file but that results in errors, and having to add it to ControlSettings and TerminalSettings, which seem to require adding it to macros a second time, with another JSON key, which I assume is wrong since it should canonically be a single global key. I could try plumbing the IGlobalAppSettings through `UpdateSettings->UpdateControlSettings->_UpdateAppearanceFromUIThread->_SetBackgroundImage` but I'm not sure if that's what you meant.
I have it *somewhat* working, just need to figure out how to get the global boolean setting in TermControl to know if the pane backgrounds need to be transparent (This image is hardcoded to always make them transparent):
![imagen](https://user-images.githubusercontent.com/24706838/162854334-ee1d199d-a542-4cc3-86a0-38cbc91b257c.png)

> How would I go about doing this? I tried adding it to the `.idl` file but that results in errors, and having to add it to ControlSettings and TerminalSettings

Yea, there is another place that does need to get added. There are kinda two halves to the settings:
1. Whether a setting is global or per-profile. These are defined in GLobalAppSettings and Profile, and in the MTSMSettings.h macros
2. Whether a setting applies to the Control or the "core" of the Terminal. This one's a little trickier to wrap the head around, but for this case, since the BG image only affects the actual XAML TermControl, it's a ControlSetting. Those are defined in `CONTROL_SETTINGS` in `src\cascadia\inc\ControlProperties.h`

You will also need to add it to `IControlSettings.idl` and `TerminalSettings.h` (haven't figured how to get those generated by the macro yet), and the also set it in `TerminalSettings::_ApplyGlobalSettings`. 

There's a lot of plumbing to deal with, I know. That _should_ be everything. 

Fix patch:
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index 144e48b7a57..60ef09e5123 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -152,6 +152,9 @@ namespace winrt::TerminalApp::implementation
                 // SetTaskbarProgress event here, to get tell the hosting
                 // application to re-query this value from us.
                 page->_SetTaskbarProgressHandlers(*page, nullptr);
+
+                auto profile = tab->GetFocusedProfile();
+                page->_UpdateBackground(profile);
             }
         });
 
@@ -905,10 +908,25 @@ namespace winrt::TerminalApp::implementation
             {
                 _TitleChangedHandlers(*this, tab.Title());
             }
+
+            auto tab_impl = _GetTerminalTabImpl(tab);
+            if (tab_impl)
+            {
+                auto profile = tab_impl->GetFocusedProfile();
+                _UpdateBackground(profile);
+            }
         }
         CATCH_LOG();
     }
 
+    void TerminalPage::_UpdateBackground(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile)
+    {
+        if (profile && _settings.GlobalSettings().UseBackgroundImageForWindow())
+        {
+            _SetBackgroundImage(profile.DefaultAppearance());
+        }
+    }
+
     // Method Description:
     // - Responds to the TabView control's Selection Changed event (to move a
     //      new terminal control into focus) when not in in the middle of a tab rearrangement.
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 62c8885fffa..fc64418b997 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -2514,6 +2514,57 @@ namespace winrt::TerminalApp::implementation
         return resultPane;
     }
 
+    // Method Description:
+    // - Sets background image and applies its settings (stretch, opacity and alignment)
+    // - Checks path validity
+    // Arguments:
+    // - newAppearance
+    // Return Value:
+    // - <none>
+    void TerminalPage::_SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance)
+    {
+        if (newAppearance.BackgroundImagePath().empty())
+        {
+            _tabContent.Background(nullptr);
+            return;
+        }
+
+        Windows::Foundation::Uri imageUri{ nullptr };
+        try
+        {
+            imageUri = Windows::Foundation::Uri{ newAppearance.BackgroundImagePath() };
+        }
+        catch (...)
+        {
+            LOG_CAUGHT_EXCEPTION();
+            _tabContent.Background(nullptr);
+            return;
+        }
+        // Check if the image brush is already pointing to the image
+        // in the modified settings; if it isn't (or isn't there),
+        // set a new image source for the brush
+
+        auto brush = _tabContent.Background().try_as<Media::ImageBrush>();
+        Media::Imaging::BitmapImage imageSource = brush == nullptr ? nullptr : brush.ImageSource().try_as<Media::Imaging::BitmapImage>();
+
+        if (imageSource == nullptr ||
+            imageSource.UriSource() == nullptr ||
+            imageSource.UriSource().RawUri() != imageUri.RawUri())
+        {
+            Media::ImageBrush b{};
+            // Note that BitmapImage handles the image load asynchronously,
+            // which is especially important since the image
+            // may well be both large and somewhere out on the
+            // internet.
+            Media::Imaging::BitmapImage image(imageUri);
+            b.ImageSource(image);
+            _tabContent.Background(b);
+
+            b.Stretch(newAppearance.BackgroundImageStretchMode());
+            b.Opacity(newAppearance.BackgroundImageOpacity());
+        }
+    }
+
     // Method Description:
     // - Hook up keybindings, and refresh the UI of the terminal.
     //   This includes update the settings of all the tabs according
@@ -2544,6 +2595,18 @@ namespace winrt::TerminalApp::implementation
             profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
         }
 
+        if (_settings.GlobalSettings().UseBackgroundImageForWindow())
+        {
+            const auto focusedTab{ _GetFocusedTabImpl() };
+            if (focusedTab)
+            {
+                auto profile = focusedTab->GetFocusedProfile();
+                if (profile)
+                {
+                    _SetBackgroundImage(profile.DefaultAppearance());
+                }
+            }
+        }
         for (const auto& tab : _tabs)
         {
             if (auto terminalTab{ _GetTerminalTabImpl(tab) })
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index f1be2b6177d..a0e81afd4e4 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -255,6 +255,7 @@ namespace winrt::TerminalApp::implementation
         void _UpdateTabView();
         void _UpdateTabWidthMode();
         void _UpdateCommandsForPalette();
+        void _SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance);
 
         static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, Microsoft::Terminal::Settings::Model::Command> _ExpandCommands(Windows::Foundation::Collections::IMapView<winrt::hstring, Microsoft::Terminal::Settings::Model::Command> commandsToExpand,
                                                                                                                                             Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::Profile> profiles,
@@ -362,6 +363,7 @@ namespace winrt::TerminalApp::implementation
         void _OnTabCloseRequested(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabCloseRequestedEventArgs& eventArgs);
         void _OnFirstLayout(const IInspectable& sender, const IInspectable& eventArgs);
         void _UpdatedSelectedTab(const winrt::TerminalApp::TabBase& tab);
+        void _UpdateBackground(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
 
         void _OnDispatchCommandRequested(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::Command& command);
         void _OnCommandLineExecutionRequested(const IInspectable& sender, const winrt::hstring& commandLine);
diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml
index 9270e383d56..70a64550ee0 100644
--- a/src/cascadia/TerminalApp/TerminalPage.xaml
+++ b/src/cascadia/TerminalApp/TerminalPage.xaml
@@ -70,7 +70,11 @@
         <Grid x:Name="TabContent"
               Grid.Row="2"
               HorizontalAlignment="Stretch"
-              VerticalAlignment="Stretch" />
+              VerticalAlignment="Stretch">
+            <Grid.Background>
+                <ImageBrush />
+            </Grid.Background>
+        </Grid>
 
         <!--
             GH#12775 et. al: After switching to ControlsV2, it seems that
diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h
index 0ba3fd6fbfe..9946783ab83 100644
--- a/src/cascadia/TerminalApp/pch.h
+++ b/src/cascadia/TerminalApp/pch.h
@@ -44,6 +44,7 @@
 #include <winrt/Windows.UI.Xaml.Input.h>
 #include <winrt/Windows.UI.Xaml.Markup.h>
 #include <winrt/Windows.UI.Xaml.Media.h>
+#include <winrt/Windows.UI.Xaml.Media.Imaging.h>
 #include <winrt/Windows.UI.Xaml.Media.Animation.h>
 #include <winrt/Windows.Media.h>
 #include <winrt/Windows.Media.Core.h>
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index 820686d322b..9570639f25f 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -1693,6 +1693,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         // 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();
+        return Opacity() < 1.0f || UseAcrylic() || !_settings->BackgroundImage().empty() || _settings->UseBackgroundImageForWindow();
     }
 }
diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl
index 8f0c31f58fc..2fb8c598519 100644
--- a/src/cascadia/TerminalControl/IControlSettings.idl
+++ b/src/cascadia/TerminalControl/IControlSettings.idl
@@ -56,5 +56,6 @@ namespace Microsoft.Terminal.Control
         // Experimental Settings
         Boolean ForceFullRepaintRendering { get; };
         Boolean SoftwareRendering { get; };
+        Boolean UseBackgroundImageForWindow { get; };
     };
 }
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 818a10c9b4b..527d40e6c42 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -308,7 +308,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
         // Set TSF Foreground
         Media::SolidColorBrush foregroundBrush{};
-        foregroundBrush.Color(static_cast<til::color>(newAppearance.DefaultForeground()));
+        if (_core.Settings().UseBackgroundImageForWindow())
+        {
+            foregroundBrush.Color(Windows::UI::Colors::Transparent());
+        }
+        else
+        {
+            foregroundBrush.Color(static_cast<til::color>(newAppearance.DefaultForeground()));
+        }
         TSFInputControl().Foreground(foregroundBrush);
 
         _core.ApplyAppearance(_focused);
@@ -394,7 +401,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
     // - <none>
     void TermControl::_SetBackgroundImage(const IControlAppearance& newAppearance)
     {
-        if (newAppearance.BackgroundImage().empty())
+        if (newAppearance.BackgroundImage().empty() || _core.Settings().UseBackgroundImageForWindow())
         {
             BackgroundImage().Source(nullptr);
             return;
@@ -452,10 +459,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
     {
         auto settings{ _core.Settings() };
         auto bgColor = til::color{ _core.FocusedAppearance().DefaultBackground() };
+
+        auto transparentBg = settings.UseBackgroundImageForWindow();
+        if (transparentBg)
+        {
+            bgColor = Windows::UI::Colors::Transparent();
+        }
         // GH#11743: Make sure to use the Core's current UseAcrylic value, not
         // the one from the settings. The Core's runtime UseAcrylic may have
         // changed from what was in the original settings.
-        if (_core.UseAcrylic())
+        if (_core.UseAcrylic() && !transparentBg)
         {
             // See if we've already got an acrylic background brush
             // to avoid the flicker when setting up a new one
@@ -518,8 +531,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
     // - 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)
+    void TermControl::_changeBackgroundColor(til::color bg)
     {
+        auto transparent_bg = _core.Settings().UseBackgroundImageForWindow();
+        if (transparent_bg)
+        {
+            bg = Windows::UI::Colors::Transparent();
+        }
+
         if (auto acrylic = RootGrid().Background().try_as<Media::AcrylicBrush>())
         {
             acrylic.FallbackColor(bg);
@@ -529,7 +548,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         {
             const auto originalOpacity = solidColor.Opacity();
             solidColor.Color(bg);
-            solidColor.Opacity(originalOpacity);
         }
     }
 
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index 95aa82cdb92..b8cb02d883a 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -205,7 +205,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
         void _InitializeBackgroundBrush();
         winrt::fire_and_forget _coreBackgroundColorChanged(const IInspectable& sender, const IInspectable& args);
-        void _changeBackgroundColor(const til::color bg);
+        void _changeBackgroundColor(til::color bg);
         void _changeBackgroundOpacity();
 
         bool _InitializeTerminal();
diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
index 90e11994320..51386e6be34 100644
--- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
+++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl
@@ -72,6 +72,7 @@ namespace Microsoft.Terminal.Settings.Model
         INHERITABLE_SETTING(Boolean, SnapToGridOnResize);
         INHERITABLE_SETTING(Boolean, ForceFullRepaintRendering);
         INHERITABLE_SETTING(Boolean, SoftwareRendering);
+        INHERITABLE_SETTING(Boolean, UseBackgroundImageForWindow);
         INHERITABLE_SETTING(Boolean, ForceVTInput);
         INHERITABLE_SETTING(Boolean, DebugFeaturesEnabled);
         INHERITABLE_SETTING(Boolean, StartOnUserLogin);
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index 279237f9fba..5c90f6e5fb3 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -26,6 +26,7 @@ Author(s):
     X(bool, FocusFollowMouse, "focusFollowMouse", false)                                                                                                   \
     X(bool, ForceFullRepaintRendering, "experimental.rendering.forceFullRepaint", false)                                                                   \
     X(bool, SoftwareRendering, "experimental.rendering.software", false)                                                                                   \
+    X(bool, UseBackgroundImageForWindow, "experimental.useBackgroundImageForWindow", false)                                                                \
     X(bool, ForceVTInput, "experimental.input.forceVT", false)                                                                                             \
     X(bool, TrimBlockSelection, "trimBlockSelection", true)                                                                                                \
     X(bool, DetectURLs, "experimental.detectURLs", true)                                                                                                   \
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
index 0b56266b708..e17abb8a588 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
@@ -284,6 +284,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         _FocusFollowMouse = globalSettings.FocusFollowMouse();
         _ForceFullRepaintRendering = globalSettings.ForceFullRepaintRendering();
         _SoftwareRendering = globalSettings.SoftwareRendering();
+        _UseBackgroundImageForWindow = globalSettings.UseBackgroundImageForWindow();
         _ForceVTInput = globalSettings.ForceVTInput();
         _TrimBlockSelection = globalSettings.TrimBlockSelection();
         _DetectURLs = globalSettings.DetectURLs();
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h
index 40301422a8a..2bdb7acd68f 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h
@@ -149,6 +149,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         INHERITABLE_SETTING(Model::TerminalSettings, bool, RetroTerminalEffect, false);
         INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceFullRepaintRendering, false);
         INHERITABLE_SETTING(Model::TerminalSettings, bool, SoftwareRendering, false);
+        INHERITABLE_SETTING(Model::TerminalSettings, bool, UseBackgroundImageForWindow, false);
         INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false);
 
         INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderPath);
diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h
index 08f114eb389..f9c880e83db 100644
--- a/src/cascadia/inc/ControlProperties.h
+++ b/src/cascadia/inc/ControlProperties.h
@@ -68,4 +68,5 @@
     X(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \
     X(bool, ForceFullRepaintRendering, false)                                                                                                            \
     X(bool, SoftwareRendering, false)                                                                                                                    \
+    X(bool, UseBackgroundImageForWindow, false)                                                                                                          \
     X(bool, UseAtlasEngine, false)


Test patch: