Repository: microsoft/terminal (language: cpp)

Issue:
Consider using a "system message" after a session restore, instead of ^[2J to clear the screen 
v1.20.11072.0 (bug bash on 4/16)

Consideration: we could use conpty inherit cursor mode 


Discussion / hints:

Fix patch:
diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt
index 36e345cfbbd..775694924d3 100644
--- a/.github/actions/spelling/expect/expect.txt
+++ b/.github/actions/spelling/expect/expect.txt
@@ -1718,6 +1718,7 @@ sysparams
 sysparamsext
 SYSTEMHAND
 SYSTEMMENU
+SYSTEMTIME
 tabview
 TAdd
 taef
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index cfba0a1d46a..f2ecc0ccaa2 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -1796,6 +1796,38 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             return;
         }
 
+        FILETIME lastWriteTime;
+        SYSTEMTIME lastWriteSystemTime;
+        if (!GetFileTime(file.get(), nullptr, nullptr, &lastWriteTime) ||
+            !FileTimeToSystemTime(&lastWriteTime, &lastWriteSystemTime))
+        {
+            return;
+        }
+
+        wchar_t dateBuf[256];
+        const auto dateLen = GetDateFormatEx(nullptr, 0, &lastWriteSystemTime, nullptr, &dateBuf[0], ARRAYSIZE(dateBuf), nullptr);
+        wchar_t timeBuf[256];
+        const auto timeLen = GetTimeFormatEx(nullptr, 0, &lastWriteSystemTime, nullptr, &timeBuf[0], ARRAYSIZE(timeBuf));
+
+        std::wstring message;
+        if (dateLen > 0 && timeLen > 0)
+        {
+            const auto msg = RS_(L"SessionRestoreMessage");
+            const std::wstring_view date{ &dateBuf[0], gsl::narrow_cast<size_t>(dateLen) };
+            const std::wstring_view time{ &timeBuf[0], gsl::narrow_cast<size_t>(timeLen) };
+            // This escape sequence string
+            // * sets the color to white on a bright black background ("\x1b[100;37m")
+            // * prints "  [Restored <date> <time>]  <spaces until end of line>  "
+            // * resets the color ("\x1b[m")
+            // * newlines
+            // * clears the screen ("\x1b[2J")
+            // The last step is necessary because we launch ConPTY without PSEUDOCONSOLE_INHERIT_CURSOR by default.
+            // This will cause ConPTY to emit a \x1b[2J sequence on startup to ensure it and the terminal are in-sync.
+            // If we didn't do a \x1b[2J ourselves as well, the user would briefly see the last state of the terminal,
+            // before it's quickly scrolled away once ConPTY has finished starting up, which looks weird.
+            message = fmt::format(FMT_COMPILE(L"\x1b[100;37m  [{} {} {}]\x1b[K\x1b[m\r\n\x1b[2J"), msg, date, time);
+        }
+
         wchar_t buffer[32 * 1024];
         DWORD read = 0;
 
@@ -1821,9 +1853,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             }
         }
 
-        // This pushes the restored contents up into the scrollback.
-        const auto lock = _terminal->LockForWriting();
-        _terminal->Write(L"\x1b[2J");
+        {
+            const auto lock = _terminal->LockForWriting();
+
+            // Normally the cursor should already be at the start of the line, but let's be absolutely sure it is.
+            if (_terminal->GetCursorPosition().x != 0)
+            {
+                _terminal->Write(L"\r\n");
+            }
+
+            _terminal->Write(message);
+        }
     }
 
     void ControlCore::_rendererWarning(const HRESULT hr, wil::zwstring_view parameter)
diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw
index 02245a01577..328aa4a03cd 100644
--- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw
@@ -296,4 +296,8 @@ Please either install the missing font or choose another one.</value>
     <value>Select output</value>
     <comment>The tooltip for a button for selecting all of a command's output</comment>
   </data>
+  <data name="SessionRestoreMessage" xml:space="preserve">
+    <value>Restored</value>
+    <comment>"Restored" as in "This content was restored"</comment>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 81934f10f65..e50523cbd28 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -1181,11 +1181,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
             if (!_restorePath.empty())
             {
-                winrt::get_self<ControlCore>(_core)->RestoreFromPath(_restorePath.c_str());
-                _restorePath = {};
+                _restoreInBackground();
+            }
+            else
+            {
+                _core.Connection().Start();
             }
-
-            _core.Connection().Start();
         }
         else
         {
@@ -1272,6 +1273,44 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         return true;
     }
 
+    winrt::fire_and_forget TermControl::_restoreInBackground()
+    {
+        const auto path = std::exchange(_restorePath, {});
+        const auto weakSelf = get_weak();
+        winrt::apartment_context uiThread;
+
+        try
+        {
+            co_await winrt::resume_background();
+
+            const auto self = weakSelf.get();
+            if (!self)
+            {
+                co_return;
+            }
+
+            winrt::get_self<ControlCore>(_core)->RestoreFromPath(path.c_str());
+        }
+        CATCH_LOG();
+
+        try
+        {
+            co_await uiThread;
+
+            const auto self = weakSelf.get();
+            if (!self)
+            {
+                co_return;
+            }
+
+            if (const auto connection = _core.Connection())
+            {
+                connection.Start();
+            }
+        }
+        CATCH_LOG();
+    }
+
     void TermControl::_CharacterHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
                                         const Input::CharacterReceivedRoutedEventArgs& e)
     {
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index 8568cb49b0a..a92a37ca564 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -319,6 +319,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             Reattach
         };
         bool _InitializeTerminal(const InitializeReason reason);
+        winrt::fire_and_forget _restoreInBackground();
         void _SetFontSize(int fontSize);
         void _TappedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
         void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);


Test patch: