Skip to content

Commit f769597

Browse files
Persist window layout every few minutes (#18898)
If `persistedWindowLayout` is enabled, we now persist the window layout every few minutes (excluding the text buffer). This was done by adding a `SafeDispatcherTimer` to the `WindowEmperor` that calls `PersistState()` every 5 minutes. For `BuildStartupKind`, I split up `Persist` into `PersistAll` and `PersistLayout`. This way, we go through all the same code flow that `Persist` had except for specifically serializing the buffer. ## Validation Steps Performed ✅ (with the timer set to 3 seconds) create a window layout and ensure the layout is restored after forcefully stopping Terminal (aka simulating a "crash") Closes #18838
1 parent 9e0ca3a commit f769597

File tree

9 files changed

+39
-13
lines changed

9 files changed

+39
-13
lines changed

src/cascadia/TerminalApp/IPaneContent.idl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ namespace TerminalApp
88
None,
99
Content,
1010
MovePane,
11-
Persist,
11+
PersistLayout,
12+
PersistAll
1213
};
1314

1415
runtimeclass BellEventArgs

src/cascadia/TerminalApp/TerminalPage.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,7 +1953,7 @@ namespace winrt::TerminalApp::implementation
19531953
}
19541954
}
19551955

1956-
void TerminalPage::PersistState()
1956+
void TerminalPage::PersistState(bool serializeBuffer)
19571957
{
19581958
// This method may be called for a window even if it hasn't had a tab yet or lost all of them.
19591959
// We shouldn't persist such windows.
@@ -1968,7 +1968,7 @@ namespace winrt::TerminalApp::implementation
19681968
for (auto tab : _tabs)
19691969
{
19701970
auto t = winrt::get_self<implementation::TabBase>(tab);
1971-
auto tabActions = t->BuildStartupActions(BuildStartupKind::Persist);
1971+
auto tabActions = t->BuildStartupActions(serializeBuffer ? BuildStartupKind::PersistAll : BuildStartupKind::PersistLayout);
19721972
actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end()));
19731973
}
19741974

src/cascadia/TerminalApp/TerminalPage.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ namespace winrt::TerminalApp::implementation
113113

114114
safe_void_coroutine RequestQuit();
115115
safe_void_coroutine CloseWindow();
116-
void PersistState();
116+
void PersistState(bool serializeBuffer);
117117

118118
void ToggleFocusMode();
119119
void ToggleFullscreen();

src/cascadia/TerminalApp/TerminalPaneContent.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ namespace winrt::TerminalApp::implementation
141141
// "attach existing" rather than a "create"
142142
args.ContentId(_control.ContentId());
143143
break;
144-
case BuildStartupKind::Persist:
144+
case BuildStartupKind::PersistAll:
145145
{
146146
const auto connection = _control.Connection();
147147
const auto id = connection ? connection.SessionId() : winrt::guid{};
@@ -156,6 +156,7 @@ namespace winrt::TerminalApp::implementation
156156
}
157157
break;
158158
}
159+
case BuildStartupKind::PersistLayout:
159160
default:
160161
break;
161162
}

src/cascadia/TerminalApp/TerminalWindow.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,11 @@ namespace winrt::TerminalApp::implementation
266266
AppLogic::Current()->NotifyRootInitialized();
267267
}
268268

269-
void TerminalWindow::PersistState()
269+
void TerminalWindow::PersistState(bool serializeBuffer)
270270
{
271271
if (_root)
272272
{
273-
_root->PersistState();
273+
_root->PersistState(serializeBuffer);
274274
}
275275
}
276276

src/cascadia/TerminalApp/TerminalWindow.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ namespace winrt::TerminalApp::implementation
7171

7272
void Create();
7373

74-
void PersistState();
74+
void PersistState(bool serializeBuffer);
7575

7676
void UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args);
7777

src/cascadia/TerminalApp/TerminalWindow.idl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ namespace TerminalApp
5959
Boolean ShouldImmediatelyHandoffToElevated();
6060
void HandoffToElevated();
6161

62-
void PersistState();
62+
void PersistState(Boolean serializeBuffer);
6363

6464
Windows.UI.Xaml.UIElement GetRoot();
6565

src/cascadia/WindowsTerminal/WindowEmperor.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
314314
_createMessageWindow(windowClassName.c_str());
315315
_setupGlobalHotkeys();
316316
_checkWindowsForNotificationIcon();
317+
_setupSessionPersistence(_app.Logic().Settings().GlobalSettings().ShouldUsePersistedLayout());
317318

318319
// When the settings change, we'll want to update our global hotkeys
319320
// and our notification icon based on the new settings.
@@ -323,6 +324,7 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow)
323324
_assertIsMainThread();
324325
_setupGlobalHotkeys();
325326
_checkWindowsForNotificationIcon();
327+
_setupSessionPersistence(args.NewSettings().GlobalSettings().ShouldUsePersistedLayout());
326328
}
327329
});
328330

@@ -921,10 +923,22 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c
921923
return DefWindowProcW(window, message, wParam, lParam);
922924
}
923925

924-
void WindowEmperor::_finalizeSessionPersistence() const
926+
void WindowEmperor::_setupSessionPersistence(bool enabled)
925927
{
926-
const auto state = ApplicationState::SharedInstance();
928+
if (!enabled)
929+
{
930+
_persistStateTimer.Stop();
931+
return;
932+
}
933+
_persistStateTimer.Interval(std::chrono::minutes(5));
934+
_persistStateTimer.Tick([&](auto&&, auto&&) {
935+
_persistState(ApplicationState::SharedInstance(), false);
936+
});
937+
_persistStateTimer.Start();
938+
}
927939

940+
void WindowEmperor::_persistState(const ApplicationState& state, bool serializeBuffer) const
941+
{
928942
// Calling an `ApplicationState` setter triggers a write to state.json.
929943
// With this if condition we avoid an unnecessary write when persistence is disabled.
930944
if (state.PersistedWindowLayouts())
@@ -936,12 +950,19 @@ void WindowEmperor::_finalizeSessionPersistence() const
936950
{
937951
for (const auto& w : _windows)
938952
{
939-
w->Logic().PersistState();
953+
w->Logic().PersistState(serializeBuffer);
940954
}
941955
}
942956

943-
// Ensure to write the state.json before we TerminateProcess()
957+
// Ensure to write the state.json
944958
state.Flush();
959+
}
960+
961+
void WindowEmperor::_finalizeSessionPersistence() const
962+
{
963+
const auto state = ApplicationState::SharedInstance();
964+
965+
_persistState(state, true);
945966

946967
if (_needsPersistenceCleanup)
947968
{

src/cascadia/WindowsTerminal/WindowEmperor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class WindowEmperor
6464
void _registerHotKey(int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept;
6565
void _unregisterHotKey(int index) noexcept;
6666
void _setupGlobalHotkeys();
67+
void _setupSessionPersistence(bool enabled);
68+
void _persistState(const winrt::Microsoft::Terminal::Settings::Model::ApplicationState& state, bool serializeBuffer) const;
6769
void _finalizeSessionPersistence() const;
6870
void _checkWindowsForNotificationIcon();
6971

@@ -77,6 +79,7 @@ class WindowEmperor
7779
bool _notificationIconShown = false;
7880
bool _forcePersistence = false;
7981
bool _needsPersistenceCleanup = false;
82+
SafeDispatcherTimer _persistStateTimer;
8083
std::optional<bool> _currentSystemThemeIsDark;
8184
int32_t _windowCount = 0;
8285
int32_t _messageBoxCount = 0;

0 commit comments

Comments
 (0)