@@ -33,7 +33,8 @@ constexpr const auto FrameUpdateInterval = std::chrono::milliseconds(16);
33
33
AppHost::AppHost (const winrt::TerminalApp::AppLogic& logic,
34
34
winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args,
35
35
const Remoting::WindowManager& manager,
36
- const Remoting::Peasant& peasant) noexcept :
36
+ const Remoting::Peasant& peasant,
37
+ std::unique_ptr<IslandWindow> window) noexcept :
37
38
_appLogic{ logic },
38
39
_windowLogic{ nullptr }, // don't make one, we're going to take a ref on app's
39
40
_windowManager{ manager },
@@ -48,13 +49,22 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic,
48
49
49
50
// _HandleCommandlineArgs will create a _windowLogic
50
51
_useNonClientArea = _windowLogic.GetShowTabsInTitlebar ();
51
- if (_useNonClientArea)
52
+
53
+ const bool isWarmStart = window != nullptr ;
54
+ if (isWarmStart)
52
55
{
53
- _window = std::make_unique<NonClientIslandWindow>(_windowLogic. GetRequestedTheme () );
56
+ _window = std::move (window );
54
57
}
55
58
else
56
59
{
57
- _window = std::make_unique<IslandWindow>();
60
+ if (_useNonClientArea)
61
+ {
62
+ _window = std::make_unique<NonClientIslandWindow>(_windowLogic.GetRequestedTheme ());
63
+ }
64
+ else
65
+ {
66
+ _window = std::make_unique<IslandWindow>();
67
+ }
58
68
}
59
69
60
70
// Update our own internal state tracking if we're in quake mode or not.
@@ -69,14 +79,10 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic,
69
79
std::placeholders::_2);
70
80
_window->SetCreateCallback (pfn);
71
81
72
- _window->MouseScrolled ({ this , &AppHost::_WindowMouseWheeled });
73
- _window->WindowActivated ({ this , &AppHost::_WindowActivated });
74
- _window->WindowMoved ({ this , &AppHost::_WindowMoved });
75
-
76
- _window->ShouldExitFullscreen ({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen });
77
-
78
- _window->SetAlwaysOnTop (_windowLogic.GetInitialAlwaysOnTop ());
79
- _window->SetAutoHideWindow (_windowLogic.AutoHideWindow ());
82
+ _windowCallbacks.MouseScrolled = _window->MouseScrolled ({ this , &AppHost::_WindowMouseWheeled });
83
+ _windowCallbacks.WindowActivated = _window->WindowActivated ({ this , &AppHost::_WindowActivated });
84
+ _windowCallbacks.WindowMoved = _window->WindowMoved ({ this , &AppHost::_WindowMoved });
85
+ _windowCallbacks.ShouldExitFullscreen = _window->ShouldExitFullscreen ({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen });
80
86
81
87
_window->MakeWindow ();
82
88
}
@@ -286,6 +292,14 @@ void AppHost::Initialize()
286
292
_windowLogic.SetTitleBarContent ({ this , &AppHost::_UpdateTitleBarContent });
287
293
}
288
294
295
+ // These call APIs that are reentrant on the window message loop. If
296
+ // you call them in the ctor, we might deadlock. The ctor for AppHost isn't
297
+ // always called on the window thread - for reheated windows, it could be
298
+ // called on a random COM thread.
299
+
300
+ _window->SetAlwaysOnTop (_windowLogic.GetInitialAlwaysOnTop ());
301
+ _window->SetAutoHideWindow (_windowLogic.AutoHideWindow ());
302
+
289
303
// MORE EVENT HANDLERS HERE!
290
304
// MAKE SURE THEY ARE ALL:
291
305
// * winrt::auto_revoke
@@ -295,13 +309,14 @@ void AppHost::Initialize()
295
309
// tearing down, after we've nulled out the window, during the dtor. That
296
310
// can cause unexpected AV's everywhere.
297
311
//
298
- // _window callbacks don't need to be treated this way, because:
299
- // * IslandWindow isn't a WinRT type (so it doesn't have neat revokers like this)
300
- // * This particular bug scenario applies when we've already freed the window.
312
+ // _window callbacks are a little special:
313
+ // * IslandWindow isn't a WinRT type (so it doesn't have neat revokers like
314
+ // this), so instead they go in their own special helper struct.
315
+ // * they all need to be manually revoked in _revokeWindowCallbacks.
301
316
302
317
// Register the 'X' button of the window for a warning experience of multiple
303
318
// tabs opened, this is consistent with Alt+F4 closing
304
- _window->WindowCloseButtonClicked ([this ]() {
319
+ _windowCallbacks. WindowCloseButtonClicked = _window->WindowCloseButtonClicked ([this ]() {
305
320
_CloseRequested (nullptr , nullptr );
306
321
});
307
322
// If the user requests a close in another way handle the same as if the 'X'
@@ -310,11 +325,11 @@ void AppHost::Initialize()
310
325
311
326
// Add an event handler to plumb clicks in the titlebar area down to the
312
327
// application layer.
313
- _window->DragRegionClicked ([this ]() { _windowLogic.TitlebarClicked (); });
328
+ _windowCallbacks. DragRegionClicked = _window->DragRegionClicked ([this ]() { _windowLogic.TitlebarClicked (); });
314
329
315
- _window->WindowVisibilityChanged ([this ](bool showOrHide) { _windowLogic.WindowVisibilityChanged (showOrHide); });
330
+ _windowCallbacks. WindowVisibilityChanged = _window->WindowVisibilityChanged ([this ](bool showOrHide) { _windowLogic.WindowVisibilityChanged (showOrHide); });
316
331
317
- _window->UpdateSettingsRequested ({ this , &AppHost::_requestUpdateSettings });
332
+ _windowCallbacks. UpdateSettingsRequested = _window->UpdateSettingsRequested ({ this , &AppHost::_requestUpdateSettings });
318
333
319
334
_revokers.Initialized = _windowLogic.Initialized (winrt::auto_revoke, { this , &AppHost::_WindowInitializedHandler });
320
335
_revokers.RequestedThemeChanged = _windowLogic.RequestedThemeChanged (winrt::auto_revoke, { this , &AppHost::_UpdateTheme });
@@ -325,14 +340,14 @@ void AppHost::Initialize()
325
340
_revokers.SystemMenuChangeRequested = _windowLogic.SystemMenuChangeRequested (winrt::auto_revoke, { this , &AppHost::_SystemMenuChangeRequested });
326
341
_revokers.ChangeMaximizeRequested = _windowLogic.ChangeMaximizeRequested (winrt::auto_revoke, { this , &AppHost::_ChangeMaximizeRequested });
327
342
328
- _window->MaximizeChanged ([this ](bool newMaximize) {
343
+ _windowCallbacks. MaximizeChanged = _window->MaximizeChanged ([this ](bool newMaximize) {
329
344
if (_windowLogic)
330
345
{
331
346
_windowLogic.Maximized (newMaximize);
332
347
}
333
348
});
334
349
335
- _window->AutomaticShutdownRequested ([this ]() {
350
+ _windowCallbacks. AutomaticShutdownRequested = _window->AutomaticShutdownRequested ([this ]() {
336
351
// Raised when the OS is beginning an update of the app. We will quit,
337
352
// to save our state, before the OS manually kills us.
338
353
Remoting::WindowManager::RequestQuitAll (_peasant);
@@ -428,6 +443,9 @@ void AppHost::Close()
428
443
_frameTimer.Tick (_frameTimerToken);
429
444
}
430
445
_showHideWindowThrottler.reset ();
446
+
447
+ _revokeWindowCallbacks ();
448
+
431
449
_window->Close ();
432
450
433
451
if (_windowLogic)
@@ -437,6 +455,50 @@ void AppHost::Close()
437
455
}
438
456
}
439
457
458
+ void AppHost::_revokeWindowCallbacks ()
459
+ {
460
+ // You'll recall, IslandWindow isn't a WinRT type so it can't have auto-revokers.
461
+ //
462
+ // Instead, we need to manually remove our callbacks we registered on the window object.
463
+ _window->MouseScrolled (_windowCallbacks.MouseScrolled );
464
+ _window->WindowActivated (_windowCallbacks.WindowActivated );
465
+ _window->WindowMoved (_windowCallbacks.WindowMoved );
466
+ _window->ShouldExitFullscreen (_windowCallbacks.ShouldExitFullscreen );
467
+ _window->WindowCloseButtonClicked (_windowCallbacks.WindowCloseButtonClicked );
468
+ _window->DragRegionClicked (_windowCallbacks.DragRegionClicked );
469
+ _window->WindowVisibilityChanged (_windowCallbacks.WindowVisibilityChanged );
470
+ _window->UpdateSettingsRequested (_windowCallbacks.UpdateSettingsRequested );
471
+ _window->MaximizeChanged (_windowCallbacks.MaximizeChanged );
472
+ _window->AutomaticShutdownRequested (_windowCallbacks.AutomaticShutdownRequested );
473
+ }
474
+
475
+ // revoke our callbacks, discard our XAML content (TerminalWindow &
476
+ // TerminalPage), and hand back our IslandWindow. This does _not_ close the XAML
477
+ // island for this thread. We should not be re-used after this, and our caller
478
+ // can destruct us like they normally would during a close. The returned
479
+ // IslandWindow will retain ownership of the DesktopWindowXamlSource, for later
480
+ // reuse.
481
+ [[nodiscard]] std::unique_ptr<IslandWindow> AppHost::Refrigerate ()
482
+ {
483
+ // After calling _window->Close() we should avoid creating more WinUI related actions.
484
+ // I suspect WinUI wouldn't like that very much. As such unregister all event handlers first.
485
+ _revokers = {};
486
+ _showHideWindowThrottler.reset ();
487
+
488
+ _revokeWindowCallbacks ();
489
+
490
+ // DO NOT CLOSE THE WINDOW
491
+ _window->Refrigerate ();
492
+
493
+ if (_windowLogic)
494
+ {
495
+ _windowLogic.DismissDialog ();
496
+ _windowLogic = nullptr ;
497
+ }
498
+
499
+ return std::move (_window);
500
+ }
501
+
440
502
// Method Description:
441
503
// - Called every time when the active tab's title changes. We'll also fire off
442
504
// a window message so we can update the window's title on the main thread,
@@ -1049,22 +1111,7 @@ static bool _isActuallyDarkTheme(const auto requestedTheme)
1049
1111
// Windows 10, so that we don't even get that spew
1050
1112
void _frameColorHelper (const HWND h, const COLORREF color)
1051
1113
{
1052
- static const bool isWindows11 = []() {
1053
- OSVERSIONINFOEXW osver{};
1054
- osver.dwOSVersionInfoSize = sizeof (osver);
1055
- osver.dwBuildNumber = 22000 ;
1056
-
1057
- DWORDLONG dwlConditionMask = 0 ;
1058
- VER_SET_CONDITION (dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
1059
-
1060
- if (VerifyVersionInfoW (&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE )
1061
- {
1062
- return true ;
1063
- }
1064
- return false ;
1065
- }();
1066
-
1067
- if (isWindows11)
1114
+ if (Utils::IsWindows11 ())
1068
1115
{
1069
1116
LOG_IF_FAILED (DwmSetWindowAttribute (h, DWMWA_BORDER_COLOR, &color, sizeof (color)));
1070
1117
}
0 commit comments