# How Windows Terminal Architecture Works: Multi-Process UI and Monarch Coordination

> Explore the Windows Terminal architecture. Discover how its multi-process UI, content terminal, and Monarch coordination enable powerful features and efficient management.

- Repository: [Microsoft/terminal](https://github.com/microsoft/terminal)
- Tags: architecture
- Published: 2026-02-26

---

**Windows Terminal uses a multi-process architecture that separates the UI window process from the content terminal process, coordinated by a Monarch process that manages global state and enables features like single-instance mode and tab tear-off.**

Windows Terminal, the open-source terminal emulator from Microsoft available at `microsoft/terminal`, implements a sophisticated multi-process architecture designed to isolate UI responsibilities from heavy terminal I/O and rendering tasks. This design, documented in the [Process Model 2.0 specification](https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0/%235000%20-%20Process%20Model%202.0.md), enables advanced features like tab tear-off, single-instance mode, and global hotkeys while maintaining stability through process isolation.

## Architectural Layers and Responsibilities

Windows Terminal is built as a **multi-process, component-based UI** that separates the *window* (the visible XAML/WinUI frame) from the *content* (the terminal buffer, PTY connection, renderer, etc.). The architecture consists of five primary layers:

| Layer | Responsibility | Main Types |
|-------|----------------|------------|
| **Window (UI) process** | Hosts the XAML island, draws the title bar, receives user input, and forwards it to the content. Only one UI thread exists per window. | `WindowEmperor`, `AppHost`, `IslandWindow` / `NonClientIslandWindow` |
| **Content (terminal) process** | Owns the terminal core (`Terminal`), PTY connection, and the DirectX renderer (`DxEngine`). Runs the heavy I/O and rendering loop. | `TerminalApp::AppLogic`, `TerminalApp::TerminalWindow`, `TermControl` |
| **Monarch/Peasant coordination** | A single *Monarch* process tracks all windows (peasants) and assigns them IDs, enabling features like *single-instance mode*, *tab tear-off*, *glom-onto-most-recent-window*, and *global hotkeys*. | `WindowEmperor` (Monarch) and each `AppHost` (Peasant) |
| **Pane model** | Tabs contain a binary-tree of panes; each leaf pane hosts a `TermControl` (content process). Splits are vertical or horizontal. | `Pane`, `PaneNode` (logic lives in the settings model) – see the spec for the tree layout. |
| **Persistence & session management** | Settings, window geometry and tab/pane state are serialized to JSON and restored on launch. | `WindowEmperor::_persistState`, `WindowEmperor::_finalizeSessionPersistence` |

## Process Model: Window vs. Content Separation

The fundamental separation between **Window** and **Content** processes is the cornerstone of Windows Terminal's stability. When a new window is required, `WindowEmperor::CreateNewWindow` constructs an `AppHost`, which creates a *window* (`IslandWindow` or `NonClientIslandWindow`) and, via `AppHost::_HandleCommandlineArgs`, asks the **AppLogic** to create a **content process**.

The content process registers a unique GUID with COM/WinRT (`CoRegisterClassObject`) so the window process can later retrieve the same instance via `create_instance` — this is the "content GUID". The window creates a **SwapChain** (`CreateSwapChainForCompositionSurfaceHandle`) that is **shared** with the content process. The content draws directly into that swap chain; the window only presents it. Communication (e.g., `SummonWindowRequested`, `CommandlineArgs`) goes through **WinRT projections**, which automatically marshal calls across process boundaries.

## Monarch and Peasant Coordination

All `AppHost` instances register with the **Monarch** (the first `WindowEmperor` that starts). The monarch keeps a list of peasants (`_windows`) and assigns each a **window ID**. When another `wt.exe` instance runs, it contacts the monarch to:

- **Glom** the command line onto the most-recent window (`Monarch::GetMostRecentPeasant`)
- **Tear-off** a tab by moving its associated content GUIDs to a new window process (`WindowEmperor::_summonWindow`)
- **Single-instance mode** where the `Monarch` receives the new args and forwards them

Global hotkeys and the notification-area icon are managed by `WindowEmperor::_setupGlobalHotkeys` and `_notificationAreaMenuRequested`, while session persistence uses `WindowEmperor::_persistState` and `WindowEmperor::_finalizeSessionPersistence` to serialize settings and geometry to JSON.

## Window Creation and Lifecycle

When the Monarch creates a new window, it instantiates an `AppHost` and initializes the XAML island. The following simplified code from [`src/cascadia/WindowsTerminal/WindowEmperor.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/WindowEmperor.cpp) illustrates this flow:

```cpp
// Inside WindowEmperor::CreateNewWindow
auto args = winrt::TerminalApp::WindowRequestedArgs{ /*…populate from command line…*/ };
auto appHost = std::make_shared<AppHost>(this, _appLogic, args);
_appLogic = winrt::TerminalApp::App{ nullptr }; // (Monarch creates the AppLogic once)
_windows.emplace_back(appHost);
appHost->Initialize();               // sets up XAML island, registers callbacks

```

*Source:* **[src/cascadia/WindowsTerminal/WindowEmperor.cpp#L124-L138]**

`AppHost::Initialize` defers window creation until after XAML layout completes, registering auto-revoked WinRT event handlers for `WindowActivated`, `FullscreenChanged`, and `SummonWindowRequested`.

## Command-Line Dispatch and Single-Instance Mode

In single-instance mode, new command-line arguments are dispatched to existing windows rather than spawning new processes. The Monarch routes these via `_dispatchCommandline`:

```cpp
void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs args)
{
    // Forward to the most-recent peasant window
    auto* target = _mostRecentWindow();
    if (target)
    {
        target->DispatchCommandline(std::move(args));
    }
}

```

*Source:* **[src/cascadia/WindowsTerminal/WindowEmperor.cpp#L46-L54]**

The target `AppHost` (peasant) handles the dispatch by ensuring window visibility and executing the command:

```cpp
void AppHost::DispatchCommandline(winrt::TerminalApp::CommandlineArgs args)
{
    winrt::TerminalApp::SummonWindowBehavior summon{};
    summon.MoveToCurrentDesktop(false);
    summon.ToggleVisibility(false);    // just make sure the window is visible
    HandleSummon(std::move(summon));   // bring the window forward
    _windowLogic.ExecuteCommandline(std::move(args));
}

```

*Source:* **[src/cascadia/WindowsTerminal/AppHost.cpp#L98-L108]**

## Pane and Tab Management

Tabs in Windows Terminal contain a **binary tree of panes**, where each leaf node hosts a `TermControl` running in the content process. This design supports complex layouts with vertical and horizontal splits. The tree structure is serialized to JSON for persistence and tab tear-off operations.

The following pseudo-code illustrates creating a vertical split from a focused leaf pane, as described in the [#532 – Panes and Split Windows](https://github.com/microsoft/terminal/blob/main/doc/specs/%23532%20-%20Panes%20and%20Split%20Windows.md) specification:

```cpp
// Pseudo-code: creating a vertical split from the focused leaf pane
auto leaf = tab.FocusedPane();                // leaf = Terminal pane
auto parent = leaf.Split(Orientation::Vertical); // creates a parent node
auto newLeaf = parent.CreateChild();          // second leaf pane

```

## Rendering Pipeline

The rendering path leverages DirectX to offload heavy graphics work from the UI thread:

1. The **content process** creates a **DirectX swap chain** bound to a surface handle via `CreateSwapChainForCompositionSurfaceHandle`.
2. `TermControl` writes the terminal bitmap into the swap chain.
3. The **window process** attaches that swap chain to a `SwapChainPanel` using `ISwapChainPanelNative2::SetSwapChainHandle`.
4. The UI thread simply composes the panel inside the XAML island.

This separation allows the heavy rendering work to run off-UI-thread and even off-process, improving stability—a crash in the content process no longer kills the whole window.

## Key Source Files

The implementation of this architecture spans several critical files in the `src/cascadia/WindowsTerminal` directory:

| File | Role | Link |
|------|------|------|
| [`src/cascadia/WindowsTerminal/WindowEmperor.h`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/WindowEmperor.h) | Core manager (Monarch) that tracks windows, hotkeys, session persistence. | [WindowEmperor.h](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/WindowEmperor.h) |
| [`src/cascadia/WindowsTerminal/WindowEmperor.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/WindowEmperor.cpp) | Implementation of the monarch/peasant logic, command-line dispatch, hot-key handling. | [WindowEmperor.cpp](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/WindowEmperor.cpp) |
| [`src/cascadia/WindowsTerminal/AppHost.h`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/AppHost.h) | Represents a *peasant* – a single window + its content process. | [AppHost.h](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/AppHost.h) |
| [`src/cascadia/WindowsTerminal/AppHost.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/AppHost.cpp) | Sets up the XAML island, creates the content process, wires events, handles summon/commandline. | [AppHost.cpp](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/AppHost.cpp) |
| [`src/cascadia/WindowsTerminal/IslandWindow.h`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/IslandWindow.h) / `.cpp` | Thin UI host that holds the `SwapChainPanel` and forwards input/events. | [IslandWindow.h](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/IslandWindow.h) |
| [`src/cascadia/WindowsTerminal/NonClientIslandWindow.h`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.h) / `.cpp` | Variant of `IslandWindow` with custom title-bar handling. | [NonClientIslandWindow.h](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/NonClientIslandWindow.h) |
| `doc/specs/#5000 - Process Model 2.0.md` | Design spec describing the separation of Window/Content processes and Monarch/Peasant model. | [Process Model 2.0 spec](https://github.com/microsoft/terminal/blob/main/doc/specs/%235000%20-%20Process%20Model%202.0/%235000%20-%20Process%20Model%202.0.md) |
| `doc/specs/#532 - Panes and Split Windows.md` | Specification of the pane binary-tree layout and split behaviour. | [Pane spec](https://github.com/microsoft/terminal/blob/main/doc/specs/%23532%20-%20Panes%20and%20Split%20Windows.md) |

## Summary

- **Windows Terminal architecture** separates the UI window process from the terminal content process to improve stability and performance.
- The **Monarch/Peasant** pattern enables single-instance mode, global hotkeys, and tab tear-off by coordinating multiple window processes through a central `WindowEmperor`.
- **Inter-process communication** relies on WinRT projections and COM registration (`CoRegisterClassObject`), allowing the window process to retrieve content instances via unique GUIDs.
- **Rendering** is offloaded to the content process using a shared DirectX swap chain presented through `ISwapChainPanelNative2::SetSwapChainHandle`.
- **Pane management** uses a binary tree structure within tabs to support complex split layouts, serialized to JSON for persistence.

## Frequently Asked Questions

### How does Windows Terminal handle single-instance mode?

When Windows Terminal runs in single-instance mode, new command-line arguments are forwarded to the existing **Monarch** process rather than spawning a new window process. The Monarch routes these arguments to the most recent **Peasant** window via `WindowEmperor::_dispatchCommandline`, which calls `AppHost::DispatchCommandline` to execute the command in the existing terminal window.

### What is the difference between the Monarch and Peasant processes?

The **Monarch** is the first `WindowEmperor` instance that starts and maintains global state, tracking all active windows (Peasants) and assigning them unique window IDs. **Peasants** are individual `AppHost` instances representing separate terminal windows; they register with the Monarch to enable coordination features like tab tear-off and global hotkey management.

### How does Windows Terminal achieve process isolation for rendering?

The terminal separates rendering into the **content process**, which owns the `Terminal` core, PTY connection, and `DxEngine`. This process creates a DirectX swap chain bound to a surface handle via `CreateSwapChainForCompositionSurfaceHandle`. The **window process** attaches this shared swap chain to a `SwapChainPanel` using `ISwapChainPanelNative2::SetSwapChainHandle`, allowing the UI thread to simply compose the panel without performing heavy rendering work.

### Where is the pane split logic implemented in the source code?

The pane model is implemented as a binary tree where tabs contain `Pane` nodes and leaf panes host `TermControl` instances. The logic for creating vertical and horizontal splits resides primarily in [`src/cascadia/WindowsTerminal/AppHost.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WindowsTerminal/AppHost.cpp) and the settings model, while the detailed specification for the tree layout and UI behavior is documented in **[doc/specs/#532 - Panes and Split Windows.md](https://github.com/microsoft/terminal/blob/main/doc/specs/%23532%20-%20Panes%20and%20Split%20Windows.md)**.