Skip to content

Commit 11126f9

Browse files
committed
Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023
2 parents e31202b + 7010626 commit 11126f9

13 files changed

+492
-124
lines changed

src/cascadia/WindowsTerminal/AppHost.cpp

+84-37
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ constexpr const auto FrameUpdateInterval = std::chrono::milliseconds(16);
3333
AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic,
3434
winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args,
3535
const Remoting::WindowManager& manager,
36-
const Remoting::Peasant& peasant) noexcept :
36+
const Remoting::Peasant& peasant,
37+
std::unique_ptr<IslandWindow> window) noexcept :
3738
_appLogic{ logic },
3839
_windowLogic{ nullptr }, // don't make one, we're going to take a ref on app's
3940
_windowManager{ manager },
@@ -48,13 +49,22 @@ AppHost::AppHost(const winrt::TerminalApp::AppLogic& logic,
4849

4950
// _HandleCommandlineArgs will create a _windowLogic
5051
_useNonClientArea = _windowLogic.GetShowTabsInTitlebar();
51-
if (_useNonClientArea)
52+
53+
const bool isWarmStart = window != nullptr;
54+
if (isWarmStart)
5255
{
53-
_window = std::make_unique<NonClientIslandWindow>(_windowLogic.GetRequestedTheme());
56+
_window = std::move(window);
5457
}
5558
else
5659
{
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+
}
5868
}
5969

6070
// 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,
6979
std::placeholders::_2);
7080
_window->SetCreateCallback(pfn);
7181

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 });
8086

8187
_window->MakeWindow();
8288
}
@@ -286,6 +292,14 @@ void AppHost::Initialize()
286292
_windowLogic.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent });
287293
}
288294

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+
289303
// MORE EVENT HANDLERS HERE!
290304
// MAKE SURE THEY ARE ALL:
291305
// * winrt::auto_revoke
@@ -295,13 +309,14 @@ void AppHost::Initialize()
295309
// tearing down, after we've nulled out the window, during the dtor. That
296310
// can cause unexpected AV's everywhere.
297311
//
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.
301316

302317
// Register the 'X' button of the window for a warning experience of multiple
303318
// tabs opened, this is consistent with Alt+F4 closing
304-
_window->WindowCloseButtonClicked([this]() {
319+
_windowCallbacks.WindowCloseButtonClicked = _window->WindowCloseButtonClicked([this]() {
305320
_CloseRequested(nullptr, nullptr);
306321
});
307322
// If the user requests a close in another way handle the same as if the 'X'
@@ -310,11 +325,11 @@ void AppHost::Initialize()
310325

311326
// Add an event handler to plumb clicks in the titlebar area down to the
312327
// application layer.
313-
_window->DragRegionClicked([this]() { _windowLogic.TitlebarClicked(); });
328+
_windowCallbacks.DragRegionClicked = _window->DragRegionClicked([this]() { _windowLogic.TitlebarClicked(); });
314329

315-
_window->WindowVisibilityChanged([this](bool showOrHide) { _windowLogic.WindowVisibilityChanged(showOrHide); });
330+
_windowCallbacks.WindowVisibilityChanged = _window->WindowVisibilityChanged([this](bool showOrHide) { _windowLogic.WindowVisibilityChanged(showOrHide); });
316331

317-
_window->UpdateSettingsRequested({ this, &AppHost::_requestUpdateSettings });
332+
_windowCallbacks.UpdateSettingsRequested = _window->UpdateSettingsRequested({ this, &AppHost::_requestUpdateSettings });
318333

319334
_revokers.Initialized = _windowLogic.Initialized(winrt::auto_revoke, { this, &AppHost::_WindowInitializedHandler });
320335
_revokers.RequestedThemeChanged = _windowLogic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme });
@@ -325,14 +340,14 @@ void AppHost::Initialize()
325340
_revokers.SystemMenuChangeRequested = _windowLogic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested });
326341
_revokers.ChangeMaximizeRequested = _windowLogic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested });
327342

328-
_window->MaximizeChanged([this](bool newMaximize) {
343+
_windowCallbacks.MaximizeChanged = _window->MaximizeChanged([this](bool newMaximize) {
329344
if (_windowLogic)
330345
{
331346
_windowLogic.Maximized(newMaximize);
332347
}
333348
});
334349

335-
_window->AutomaticShutdownRequested([this]() {
350+
_windowCallbacks.AutomaticShutdownRequested = _window->AutomaticShutdownRequested([this]() {
336351
// Raised when the OS is beginning an update of the app. We will quit,
337352
// to save our state, before the OS manually kills us.
338353
Remoting::WindowManager::RequestQuitAll(_peasant);
@@ -428,6 +443,9 @@ void AppHost::Close()
428443
_frameTimer.Tick(_frameTimerToken);
429444
}
430445
_showHideWindowThrottler.reset();
446+
447+
_revokeWindowCallbacks();
448+
431449
_window->Close();
432450

433451
if (_windowLogic)
@@ -437,6 +455,50 @@ void AppHost::Close()
437455
}
438456
}
439457

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+
440502
// Method Description:
441503
// - Called every time when the active tab's title changes. We'll also fire off
442504
// 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)
10491111
// Windows 10, so that we don't even get that spew
10501112
void _frameColorHelper(const HWND h, const COLORREF color)
10511113
{
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())
10681115
{
10691116
LOG_IF_FAILED(DwmSetWindowAttribute(h, DWMWA_BORDER_COLOR, &color, sizeof(color)));
10701117
}

src/cascadia/WindowsTerminal/AppHost.h

+27-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ class AppHost
1212
AppHost(const winrt::TerminalApp::AppLogic& logic,
1313
winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs args,
1414
const winrt::Microsoft::Terminal::Remoting::WindowManager& manager,
15-
const winrt::Microsoft::Terminal::Remoting::Peasant& peasant) noexcept;
15+
const winrt::Microsoft::Terminal::Remoting::Peasant& peasant,
16+
std::unique_ptr<IslandWindow> window = nullptr) noexcept;
1617

1718
void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
1819
void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
1920
void Initialize();
2021
void Close();
22+
23+
[[nodiscard]] std::unique_ptr<IslandWindow> Refrigerate();
24+
2125
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
2226
void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
2327

@@ -58,6 +62,8 @@ class AppHost
5862

5963
void _preInit();
6064

65+
void _revokeWindowCallbacks();
66+
6167
void _HandleCommandlineArgs(const winrt::Microsoft::Terminal::Remoting::WindowRequestedArgs& args);
6268
void _HandleSessionRestore(const bool startedForContent);
6369

@@ -202,4 +208,24 @@ class AppHost
202208
winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested;
203209
winrt::Microsoft::Terminal::Remoting::Peasant::SendContentRequested_revoker SendContentRequested;
204210
} _revokers{};
211+
212+
// our IslandWindow is not a WinRT type. It can't make auto_revokers like
213+
// the above. We also need to make sure to unregister ourself from the
214+
// window when we refrigerate the window thread so that the window can later
215+
// be re-used.
216+
struct WindowRevokers
217+
{
218+
winrt::event_token MouseScrolled;
219+
winrt::event_token WindowActivated;
220+
winrt::event_token WindowMoved;
221+
winrt::event_token ShouldExitFullscreen;
222+
winrt::event_token WindowCloseButtonClicked;
223+
winrt::event_token DragRegionClicked;
224+
winrt::event_token WindowVisibilityChanged;
225+
winrt::event_token UpdateSettingsRequested;
226+
winrt::event_token MaximizeChanged;
227+
winrt::event_token AutomaticShutdownRequested;
228+
// LOAD BEARING!!
229+
//If you add events here, make sure they're revoked in AppHost::_revokeWindowCallbacks
230+
} _windowCallbacks{};
205231
};

src/cascadia/WindowsTerminal/BaseWindow.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,17 @@ class BaseWindow
210210

211211
bool _minimized = false;
212212

213+
void _setupUserData()
214+
{
215+
SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
216+
}
213217
// Method Description:
214218
// - This method is called when the window receives the WM_NCCREATE message.
215219
// Return Value:
216220
// - The value returned from the window proc.
217221
[[nodiscard]] virtual LRESULT OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept
218222
{
219-
SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
223+
_setupUserData();
220224

221225
EnableNonClientDpiScaling(_window.get());
222226
_currentDpi = GetDpiForWindow(_window.get());

0 commit comments

Comments
 (0)