Repository: microsoft/terminal (language: cpp)

Issue:
[DxD] `closeOnExit` should be sensible when launched via defterm
### Windows Terminal version

Windows Terminal Version: 1.13.11432.0

### Windows build number

Microsoft Windows [Version 10.0.22000.739]

### Other Software

_No response_

### Steps to reproduce

1. Turn PC on
2. Wait approximately 30min
3. Windows Terminal opens with error "ERROR: The specified directory does not exist."

### Expected Behavior

Windows Terminal shouldn't opens every time I turn PC on. 

### Actual Behavior

Every time I turn PC on and approximately after 30min Windows Terminal automatically opens new windows terminal with error "ERROR: The specified directory does not exist." WT startup is turned off. I don't use additional script to open WT.
Screenshot of the error https://prnt.sc/OTSf0FKHT9Dt
Full tab path C:\WINDOWS\System32\forfiles.exe


Discussion / hints:
> Full tab path C:\WINDOWS\System32\forfiles.exe

Sounds like you've got the Windows Terminal set up as the default terminal emulator, and something you have installed is running a `forfiles` command. That `forfiles` is automatically launching in a Terminal window, instead of a console window. However, the Terminal doesn't automatically close when a commandline application exits with an error, unlike the console. 

Now, trick here is that `forfiles` here isn't actually telling you what the missing directory is, just that it couldn't find it. I honestly don't know how you'd figure out who's launching that.

You might be able to mitigate this by setting [`closeOnExit: always`](https://docs.microsoft.com/en-us/windows/terminal/customize-settings/profile-advanced#profile-termination-behavior) in the `profiles.defaults` part of the settings. 

I think we had previously discussed defterm invocations defaulting to that value - lemme bring that up with the team again. 
🌶️ take: for a defterm session, if the profile doesn't have a `closeOnExit` set, we treat that as `always`, instead of `graceful`. We'd need to change that setting to an optional, but because it gets exposed from `Profile` as a winrt type, it'd need to be an `IReference<CloseOnExitMode>`, which is a little gross. 
discusssion: 
* is "close on exit" for _things started by the Terminal_? Can we just reframe `graceful` to "always" for defterm?
* Meh, lets do the optional thing above?
* Meh lets just just do always as the default? 
* Maybe we should have an setting between always and graceful "always so long as we actually got some bytes out of the connection"
* but uhg, that's not actually helpful for defterm
* you can't actually _set_ graceful explicitly in the SUI - the profile will already say you're set to "graceful"
* **Maybe there needs to be "gracefulIfLaunchedByTerminal", which is the new default, which is "always" for defterm. It's annoying but okay.** 



Fix patch:
diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json
index 1543b7b3986..d740324b310 100644
--- a/doc/cascadia/profiles.schema.json
+++ b/doc/cascadia/profiles.schema.json
@@ -2106,14 +2106,15 @@
           "$ref": "#/$defs/BellSound"
         },
         "closeOnExit": {
-          "default": "graceful",
-          "description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
+          "default": "automatic",
+          "description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"automatic\" (behave as \"graceful\" only for processes launched by terminal, behave as \"always\" otherwise)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
           "oneOf": [
             {
               "enum": [
                 "never",
                 "graceful",
-                "always"
+                "always",
+                "automatic"
               ],
               "type": "string"
             },
diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp
index b8c1d079ed2..19b09d5e9ce 100644
--- a/src/cascadia/TerminalApp/Pane.cpp
+++ b/src/cascadia/TerminalApp/Pane.cpp
@@ -1019,9 +1019,17 @@ void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundatio
 
     if (_profile)
     {
+        if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic)
+        {
+            // For 'automatic', we only care about the connection state if we were launched by Terminal
+            // Since we were launched via defterm, ignore the connection state (i.e. we treat the
+            // close on exit mode as 'always', see GH #13325 for discussion)
+            Close();
+        }
+
         const auto mode = _profile.CloseOnExit();
         if ((mode == CloseOnExitMode::Always) ||
-            (mode == CloseOnExitMode::Graceful && newConnectionState == ConnectionState::Closed))
+            ((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed))
         {
             Close();
         }
@@ -3108,6 +3116,15 @@ int Pane::GetLeafPaneCount() const noexcept
     return _IsLeaf() ? 1 : (_firstChild->GetLeafPaneCount() + _secondChild->GetLeafPaneCount());
 }
 
+// Method Description:
+// - Should be called when this pane is created via a default terminal handoff
+// - Finalizes our configuration given the information that we have been
+//   created via default handoff
+void Pane::FinalizeConfigurationGivenDefault()
+{
+    _isDefTermSession = true;
+}
+
 // Method Description:
 // - Returns true if the pane or one of its descendants is read-only
 bool Pane::ContainsReadOnly() const
diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h
index 2c1fec243d6..7094f3b3643 100644
--- a/src/cascadia/TerminalApp/Pane.h
+++ b/src/cascadia/TerminalApp/Pane.h
@@ -132,6 +132,8 @@ class Pane : public std::enable_shared_from_this<Pane>
     bool FocusPane(const std::shared_ptr<Pane> pane);
     std::shared_ptr<Pane> FindPane(const uint32_t id);
 
+    void FinalizeConfigurationGivenDefault();
+
     bool ContainsReadOnly() const;
 
     // Method Description:
@@ -241,6 +243,8 @@ class Pane : public std::enable_shared_from_this<Pane>
 
     bool _zoomed{ false };
 
+    bool _isDefTermSession{ false };
+
     winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
     winrt::Windows::Media::Playback::MediaPlayer::MediaEnded_revoker _mediaEndedRevoker;
 
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 769c40d1ae5..eb8f02f06b8 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -3264,7 +3264,11 @@ namespace winrt::TerminalApp::implementation
             // elevated version of the Terminal with that profile... that's a
             // recipe for disaster. We won't ever open up a tab in this window.
             newTerminalArgs.Elevate(false);
-            _CreateNewTabFromPane(_MakePane(newTerminalArgs, false, connection));
+            const auto newPane = _MakePane(newTerminalArgs, false, connection);
+            newPane->WalkTree([](auto pane) {
+                pane->FinalizeConfigurationGivenDefault();
+            });
+            _CreateNewTabFromPane(newPane);
 
             // Request a summon of this window to the foreground
             _SummonWindowRequestedHandlers(*this, nullptr);
diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
index f7a229ee0de..37f90414406 100644
--- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
@@ -714,6 +714,10 @@
     <value>Never close automatically</value>
     <comment>An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal never closes, even if the process exits in a controlled or uncontrolled scenario. The user would have to manually close the terminal.</comment>
   </data>
+  <data name="Profile_CloseOnExitAutomatic.Content" xml:space="preserve">
+    <value>Automatic</value>
+    <comment>An option to choose from for the "profile termination behavior" (or "close on exit") setting. When selected, the terminal closes if the process exits in a controlled scenario successfully and the process was launched by Windows Terminal.</comment>
+  </data>
   <data name="Profile_ColorScheme.Header" xml:space="preserve">
     <value>Color scheme</value>
     <comment>Header for a control to select the scheme (or set) of colors used in the session. This is selected from a list of options managed by the user.</comment>
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index aeb3872b399..d95dbdfe43e 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -74,7 +74,7 @@ Author(s):
     X(bool, SuppressApplicationTitle, "suppressApplicationTitle", false)                                                                                       \
     X(guid, ConnectionType, "connectionType")                                                                                                                  \
     X(hstring, Icon, "icon", L"\uE756")                                                                                                                        \
-    X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Graceful)                                                                                  \
+    X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic)                                                                                 \
     X(hstring, TabTitle, "tabTitle")                                                                                                                           \
     X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible)                                                                                            \
     X(bool, UseAtlasEngine, "experimental.useAtlasEngine", false)                                                                                              \
diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl
index 76357b65898..608dfc5f914 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.idl
+++ b/src/cascadia/TerminalSettingsModel/Profile.idl
@@ -26,7 +26,8 @@ namespace Microsoft.Terminal.Settings.Model
     {
         Never = 0,
         Graceful,
-        Always
+        Always,
+        Automatic
     };
 
     [flags]
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
index de205f332ff..c3f3f1e1f5e 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
@@ -136,10 +136,11 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::TextAntialiasingMode)
 // - Helper for converting a user-specified closeOnExit value to its corresponding enum
 JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode)
 {
-    JSON_MAPPINGS(3) = {
+    JSON_MAPPINGS(4) = {
         pair_type{ "always", ValueType::Always },
         pair_type{ "graceful", ValueType::Graceful },
         pair_type{ "never", ValueType::Never },
+        pair_type{ "automatic", ValueType::Automatic },
     };
 
     // Override mapping parser to add boolean parsing
diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json
index 7a672723c33..76d5f4b3bd1 100644
--- a/src/cascadia/TerminalSettingsModel/defaults.json
+++ b/src/cascadia/TerminalSettingsModel/defaults.json
@@ -43,7 +43,7 @@
             "icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
             "colorScheme": "Campbell",
             "antialiasingMode": "grayscale",
-            "closeOnExit": "graceful",
+            "closeOnExit": "automatic",
             "cursorShape": "bar",
             "fontFace": "Cascadia Mono",
             "fontSize": 12,
@@ -62,7 +62,7 @@
             "icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
             "colorScheme": "Campbell",
             "antialiasingMode": "grayscale",
-            "closeOnExit": "graceful",
+            "closeOnExit": "automatic",
             "cursorShape": "bar",
             "fontFace": "Cascadia Mono",
             "fontSize": 12,


Test patch: