This page covers how a single Windows Terminal window is created, initialized, and torn down. It describes the AppHost class, the IslandWindow / NonClientIslandWindow class hierarchy, how XAML Islands are embedded in the Win32 window, and how AppLogic acts as the factory for per-window logic objects.
For multi-window coordination, single-instance management, and wWinMain, see 6.2. For the XAML page content (TerminalPage) that runs inside the window, see 4.1. For settings loading, see 5.1.
Every visible terminal window is represented by three collaborating objects:
| Object | Type | Responsibility |
|---|---|---|
AppHost | C++ class | Lifecycle coordinator; owns the window and the window logic |
IslandWindow / NonClientIslandWindow | C++ class | Win32 HWND, XAML Island, message loop |
TerminalWindow (_windowLogic) | WinRT runtime class | Per-window UI state (tabs, panes, title, theme) |
AppLogic | WinRT runtime class | Application-level factory; shared settings, ContentManager |
AppLogic is a singleton owned by WindowEmperor. Each window gets its own AppHost, IslandWindow, and TerminalWindow.
Window type selection diagram
Sources: src/cascadia/WindowsTerminal/AppHost.cpp47-85 src/cascadia/WindowsTerminal/AppHost.h38-50
Window class inheritance
Sources: src/cascadia/WindowsTerminal/BaseWindow.h1-217 src/cascadia/WindowsTerminal/IslandWindow.h1-166 src/cascadia/WindowsTerminal/NonClientIslandWindow.h1-104
AppHost is constructed by WindowEmperor when a new window is requested. Its constructor runs through a fixed sequence before any XAML work is done.
AppHost construction sequence
Sources: src/cascadia/WindowsTerminal/AppHost.cpp47-85 src/cascadia/TerminalApp/AppLogic.cpp497-517
After construction, AppHost::Initialize() must be called separately. This is because XAML Island initialization requires WindowsXamlManager::InitializeForCurrentThread() to have already been called, which happens on the window thread.
AppHost::Initialize() src/cascadia/WindowsTerminal/AppHost.cpp175-307 is the main initialization entry point. It performs:
_window->Initialize() creates the DesktopWindowXamlSource and attaches it to the HWND._windowLogic so it can initialize WinRT components that require a window handle._useNonClientArea, registers _UpdateTitleBarContent before Create() is called, so the titlebar content is available immediately._appLogic.Create() then _windowLogic.Create()._revokers and _windowCallbacks structs._window->SetContent(_windowLogic.GetRoot()) and _window->OnAppInitialized().Sources: src/cascadia/WindowsTerminal/AppHost.cpp175-307
IslandWindow::Initialize() src/cascadia/WindowsTerminal/IslandWindow.cpp373-405 creates a DesktopWindowXamlSource (_source) and attaches it to the Win32 HWND via IDesktopWindowXamlSourceNative::AttachToWindow. The interop child HWND (_interopWindowHandle) is stored for later resizing.
The XAML tree root is a Controls::Grid assigned to _source.Content(). AppHost::Initialize() later calls SetContent() to put the TerminalPage XAML tree into this grid.
Sources: src/cascadia/WindowsTerminal/IslandWindow.cpp373-405 src/cascadia/WindowsTerminal/IslandWindow.h97-100
When tabs-in-titlebar is enabled, NonClientIslandWindow adds a second XAML subtree in the window's non-client area. It uses DwmExtendFrameIntoClientArea to make the OS titlebar invisible, then renders its own using a TitlebarControl.
To handle mouse input over the custom titlebar, NonClientIslandWindow creates a drag bar child HWND (_dragBarWindow) using a separate WNDCLASSEX named DRAG_BAR_WINDOW_CLASS. This transparent layered window sits on top of the XAML island in the titlebar region and intercepts mouse messages before they reach the XAML island.
| Region | Win32 hit-test result | Action |
|---|---|---|
| Close button | HTCLOSE | forwarded to titlebar |
| Maximize button | HTMAXBUTTON | forwarded; enables snap layouts (GH#9443) |
| Minimize button | HTMINBUTTON | forwarded to titlebar |
| Top border | HTTOP | resize from top |
| Anywhere else | HTCAPTION | drag to move window |
The drag bar's message handler is _InputSinkMessageHandler src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp164-307 and its hit-test logic is _dragBarNcHitTest src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp107-147
Sources: src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp65-105 src/cascadia/WindowsTerminal/NonClientIslandWindow.h52-104
AppLogic is the application-wide object (one per process) that loads settings and creates per-window objects.
| Method | Purpose |
|---|---|
AppLogic::ReloadSettings() | Loads/reloads CascadiaSettings from disk |
AppLogic::CreateNewWindow() | Constructs a TerminalWindow for a new window, passing the current settings snapshot and ContentManager |
AppLogic::ContentManager | Returns the shared ContentManager that manages ControlInteractivity lifetimes across windows |
AppLogic::GlobalHotkeys() | Returns global hotkeys from the action map |
CreateNewWindow() src/cascadia/TerminalApp/AppLogic.cpp497-517 instantiates TerminalWindow with a SettingsLoadEventArgs snapshot, so each window starts with a consistent settings view.
Settings file changes are detected via a wil::unique_folder_change_reader_nothrow watcher and debounced through _reloadSettings (a ThrottledFunc<> with 100ms debounce) before ReloadSettings() is invoked.
Sources: src/cascadia/TerminalApp/AppLogic.cpp122-161 src/cascadia/TerminalApp/AppLogic.cpp303-337 src/cascadia/TerminalApp/AppLogic.cpp497-517 src/cascadia/TerminalApp/AppLogic.h27-96
Window initial placement is computed in AppHost::_initialResizeAndRepositionWindow() src/cascadia/WindowsTerminal/AppHost.cpp474-600 called from the WM_CREATE callback registered via SetCreateCallback.
The algorithm:
_windowLogic.GetInitialPosition() for the saved or default position.MonitorFromRect() to find the nearest monitor.GetDpiForMonitor() for the effective DPI.EnumDisplayMonitors; if not, snap to the nearest work area._windowLogic.GetLaunchDimensions(dpix) for the desired client area size in DIPs._window->GetTotalNonClientExclusiveSize(dpix).SetWindowPos.BaseWindow::HandleDpiChange() src/cascadia/WindowsTerminal/BaseWindow.h91-109 responds to WM_DPICHANGED by repositioning to the system-suggested rect and updating _currentDpi. NonClientIslandWindow::OnSize() also calls RefreshCurrentDPI() when resizing to keep DPI state consistent across monitor moves src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp495-508
Sources: src/cascadia/WindowsTerminal/AppHost.cpp452-600 src/cascadia/WindowsTerminal/BaseWindow.h91-109
When the user drags a window border, IslandWindow::_OnSizing() src/cascadia/WindowsTerminal/IslandWindow.cpp245-340 intercepts WM_SIZING and calls _pfnSnapDimensionCallback (set by AppHost to _windowLogic.CalcSnappedDimension) to snap the client width/height to a multiple of the terminal's character cell size. WM_GETMINMAXINFO enforces minimum dimensions similarly.
Sources: src/cascadia/WindowsTerminal/IslandWindow.cpp245-340 src/cascadia/WindowsTerminal/IslandWindow.cpp443-475
AppHost maintains two sets of event subscriptions:
_revokers – winrt::auto_revoke tokens for events on TerminalWindow (a WinRT type). All revoked at once by reassigning the Revokers struct to {}._windowCallbacks – plain winrt::event_token values for events on IslandWindow (a non-WinRT type). Manually revoked in _revokeWindowCallbacks().This distinction exists because IslandWindow is a plain C++ class and cannot produce auto-revokers.
Selected event wiring
| Event source | Event | Handler in AppHost |
|---|---|---|
IslandWindow | WindowCloseButtonClicked | → _windowLogic.CloseWindow() |
IslandWindow | DragRegionClicked | → _windowLogic.TitlebarClicked() |
IslandWindow | MouseScrolled | → _WindowMouseWheeled() (manual plumbing) |
IslandWindow | WindowActivated | → _WindowActivated() (updates _lastActivatedTime) |
IslandWindow | MaximizeChanged | → _windowLogic.Maximized() |
TerminalWindow | TitleChanged | → _window->UpdateTitle() |
TerminalWindow | FullscreenChanged | → _window->FullscreenChanged() |
TerminalWindow | SetTaskbarProgress | → SetTaskbarProgress() |
TerminalWindow | RequestedThemeChanged | → _updateTheme() |
TerminalWindow | IsQuakeWindowChanged | → _IsQuakeWindowChanged() |
TerminalWindow | ShowWindowChanged | → throttled _window->ShowWindowChanged() |
TerminalWindow | RequestMoveContent | → _handleMoveContent() |
Sources: src/cascadia/WindowsTerminal/AppHost.cpp175-307 src/cascadia/WindowsTerminal/AppHost.h134-181
IslandWindow::Initialize() creates an ITaskbarList3 via CoCreateInstance(CLSID_TaskbarList) src/cascadia/WindowsTerminal/IslandWindow.cpp391-398
When TerminalWindow raises SetTaskbarProgress, AppHost::SetTaskbarProgress() src/cascadia/WindowsTerminal/AppHost.cpp103-112 queries _windowLogic.TaskbarState() for the current state and progress value, then calls _window->SetTaskbarProgress(state, progress), which forwards to ITaskbarList3::SetProgressState and SetProgressValue.
Sources: src/cascadia/WindowsTerminal/AppHost.cpp103-112 src/cascadia/WindowsTerminal/IslandWindow.cpp391-398
AppHost::GetVirtualDesktopId() src/cascadia/WindowsTerminal/AppHost.cpp334-374 lazily fetches the virtual desktop GUID for the window's HWND. Because IVirtualDesktopManager::GetWindowDesktopId() is a cross-process COM call into explorer.exe and is O(n) over all HWNDs, it is always called on a background thread via co_await winrt::resume_background().
AppHost::HandleSummon() src/cascadia/WindowsTerminal/AppHost.cpp822-864 supports moving a window to the currently active virtual desktop when summoned via a global hotkey. It uses IVirtualDesktopManager::MoveWindowToDesktop() and reads the current desktop ID from VirtualDesktopUtils::GetCurrentVirtualDesktopId(). The IVirtualDesktopManager instance is lazily created and cached in a til::shared_mutex-protected com_ptr via the free function getDesktopManager().
Sources: src/cascadia/WindowsTerminal/AppHost.cpp30-45 src/cascadia/WindowsTerminal/AppHost.cpp334-374 src/cascadia/WindowsTerminal/AppHost.cpp822-864
Quake mode causes the window to occupy the top half of the nearest monitor and behave like a drop-down terminal. It is detected via _windowLogic.IsQuakeWindow().
Effects:
_initialResizeAndRepositionWindow forces origin to the monitor's top-left and dimensions to full width, half height src/cascadia/WindowsTerminal/AppHost.cpp559-573IslandWindow::_OnSizing() blocks all resize edges except the bottom when IsQuakeWindow() is true src/cascadia/WindowsTerminal/IslandWindow.cpp256-264IslandWindow::_OnMoving() blocks movement via Alt+Space → Move src/cascadia/WindowsTerminal/IslandWindow.cpp352-366WM_WINDOWPOSCHANGING re-evaluates the quake size when the window moves to a different monitor src/cascadia/WindowsTerminal/IslandWindow.cpp643-696SW_HIDE) instead of minimizing to the taskbar.Sources: src/cascadia/WindowsTerminal/IslandWindow.cpp245-366 src/cascadia/WindowsTerminal/IslandWindow.cpp643-696 src/cascadia/WindowsTerminal/AppHost.cpp559-574
PTY processes can request window show/hide via ConEmu-style escape sequences. These requests come in rapidly, so AppHost::Initialize() creates a ThrottledFunc<bool> (_showHideWindowThrottler) with a 200ms trailing delay src/cascadia/WindowsTerminal/AppHost.cpp291-299 When ShowWindowChanged fires, the request is enqueued; the last value in any 200ms window takes effect. This prevents the Terminal and PTY from becoming out-of-sync when multiple show/hide requests arrive in quick succession.
Sources: src/cascadia/WindowsTerminal/AppHost.cpp291-299
AppHost::Close() src/cascadia/WindowsTerminal/AppHost.cpp309-326 performs teardown in a specific order to avoid AV bugs with XAML callbacks arriving after destruction:
_revokers = {} → revokes all TerminalWindow event subscriptions immediately._frameTimer and _showHideWindowThrottler._revokeWindowCallbacks() → manually remove IslandWindow event tokens._window->Close()._windowLogic.DismissDialog() and null out _windowLogic.IslandWindow::Close() src/cascadia/WindowsTerminal/IslandWindow.cpp87-110 first sets GWLP_USERDATA to 0 to prevent any queued window messages from being dispatched to the (being-destroyed) C++ object, then nulls out _source.Content() before closing the DesktopWindowXamlSource. The content null-out is required because WinUI holds a strong reference to the first DesktopWindowXamlSource ever created; without it, closing the first window leaks its content tree permanently.
Sources: src/cascadia/WindowsTerminal/AppHost.cpp309-326 src/cascadia/WindowsTerminal/IslandWindow.cpp87-110
Sources: src/cascadia/WindowsTerminal/AppHost.cpp47-326 src/cascadia/WindowsTerminal/IslandWindow.cpp82-110
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.