# Terminal Component Interaction Flow in Windows Terminal: The WPF-Native Bridge Architecture

> Explore the Terminal component interaction flow in Windows Terminal and discover how the WPF native bridge architecture handles input events and renders output for a seamless experience.

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

---

**The Terminal component interaction flow in Windows Terminal operates through a bidirectional bridge where a thin WPF managed UI forwards input events to a native C++ core via a C API, while the core processes VT100/ANSI sequences and renders output directly to an HWND hosted within the WPF control.**

The Terminal component interaction flow in Windows Terminal represents a sophisticated architectural pattern that separates presentation from processing. In the `microsoft/terminal` repository, this flow connects the managed WPF frontend with the native terminal engine through a well-defined C API boundary, enabling high-performance text rendering while maintaining flexible UI capabilities.

## How the Terminal Component Interaction Flow Works

The architecture divides responsibilities into three distinct stages: input capture, core processing, and rendering output. Each stage crosses the managed-native boundary through specific exported functions defined in [`src/cascadia/TerminalControl/HwndTerminal.hpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalControl/HwndTerminal.hpp).

### Stage 1: Input Capture from WPF to Native

User interaction begins in the managed WPF layer within [`src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs). The `TerminalControl` hosts a `TerminalContainer` (an `HwndHost` subclass) defined in [`src/cascadia/WpfTerminalControl/TerminalContainer.cs`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WpfTerminalControl/TerminalContainer.cs).

The `TerminalContainer` installs a message hook (`TerminalContainer_MessageHook`) that intercepts Windows messages destined for the native HWND. Relevant messages are immediately forwarded across the C API boundary:

- `WM_KEYDOWN` / `WM_SYSKEYDOWN` → `TerminalSendKeyEvent` (with vkey, scan code, flags, **keyDown = true**)
- `WM_KEYUP` / `WM_SYSKEYUP` → `TerminalSendKeyEvent` (with **keyDown = false**)
- `WM_CHAR` → `TerminalSendCharEvent`
- `WM_MOUSEWHEEL` → `TerminalUserScroll`
- `WM_SETFOCUS` / `WM_KILLFOCUS` → `TerminalSetFocused`
- `WM_WINDOWPOSCHANGED` (size change) → `TerminalTriggerResize` or `TerminalCalculateResize` depending on `AutoResize`

### Stage 2: Core Processing in the Terminal Engine

Once input crosses the boundary, [`src/cascadia/TerminalControl/HwndTerminal.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalControl/HwndTerminal.cpp) unwraps the terminal instance pointer and forwards calls to the core `Microsoft::Terminal::Core::Terminal` class defined in [`src/cascadia/TerminalCore/Terminal.hpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalCore/Terminal.hpp).

The core implements the **VT100/ANSI state machine** ([`src/terminal/parser/StateMachine.hpp`](https://github.com/microsoft/terminal/blob/main/src/terminal/parser/StateMachine.hpp)) and the `ITerminalApi` interface ([`src/terminal/adapter/ITerminalApi.hpp`](https://github.com/microsoft/terminal/blob/main/src/terminal/adapter/ITerminalApi.hpp)). Input methods such as `SendKeyEvent`, `SendCharEvent`, `UserResize`, and `UserScroll` perform the following:

- Update the **text buffer** and **scrollback**
- Modify **selection** state
- Toggle **terminal modes** (e.g., application cursor keys)
- Generate **CSI responses** (e.g., cursor position reports)

After processing, the core may request a render update or produce output data destined for the PTY.

### Stage 3: Rendering and Output Communication

The terminal core communicates back to the WPF UI through two registered callbacks established during initialization in `TerminalContainer.BuildWindowCore`:

1. **Write Callback** (`TerminalRegisterWriteCallback`) – handles data flowing *from* the core *to* the PTY
2. **Scroll Callback** (`TerminalRegisterScrollCallback`) – notifies the UI of viewport changes

When the core generates output (e.g., echoing user input or sending control sequence responses), it invokes the write callback (`OnWrite` in `TerminalContainer`), which forwards the data to the attached `ITerminalConnection` via `Connection.WriteInput`.

For rendering, the core holds a **Renderer** instance ([`src/renderer/base/renderer.cpp`](https://github.com/microsoft/terminal/blob/main/src/renderer/base/renderer.cpp)) that uses back-ends such as `AtlasEngine` or `UiaEngine`. The renderer draws directly onto the native HWND created by `CreateTerminal`. The WPF `TerminalContainer` simply hosts this HWND; it does not participate in the paint loop.

When the viewport scrolls, the core calls the scroll callback (`OnScroll`), which raises the `TerminalScrolled` event. `TerminalControl` handles this to update the WPF scrollbar position.

## Key Source Files in the Terminal Architecture

Understanding the Terminal component interaction flow requires familiarity with these specific files in the `microsoft/terminal` repository:

- [`src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WpfTerminalControl/TerminalControl.xaml.cs) – WPF control that forwards UI events to the native host
- [`src/cascadia/WpfTerminalControl/TerminalContainer.cs`](https://github.com/microsoft/terminal/blob/main/src/cascadia/WpfTerminalControl/TerminalContainer.cs) – `HwndHost` implementation that creates the native HWND and registers callbacks
- [`src/cascadia/TerminalControl/HwndTerminal.hpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalControl/HwndTerminal.hpp) – Exported C API (`CreateTerminal`, `TerminalSendKeyEvent`, etc.) used by the managed host
- [`src/cascadia/TerminalControl/HwndTerminal.cpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalControl/HwndTerminal.cpp) – Implements the C API and forwards calls to the core `Terminal` object
- [`src/cascadia/TerminalCore/Terminal.hpp`](https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalCore/Terminal.hpp) – Core VT100/ANSI parser, text buffer, and selection logic
- [`src/renderer/base/renderer.cpp`](https://github.com/microsoft/terminal/blob/main/src/renderer/base/renderer.cpp) – Rendering infrastructure that paints to the native HWND
- [`src/terminal/adapter/ITerminalApi.hpp`](https://github.com/microsoft/terminal/blob/main/src/terminal/adapter/ITerminalApi.hpp) – Interface implemented by the core for UI integration
- [`src/terminal/input/terminalInput.hpp`](https://github.com/microsoft/terminal/blob/main/src/terminal/input/terminalInput.hpp) – Key and character event handling inside the core
- [`src/terminal/parser/StateMachine.hpp`](https://github.com/microsoft/terminal/blob/main/src/terminal/parser/StateMachine.hpp) – VT100 state machine that interprets escape sequences

## Implementation Examples

### Hosting the Terminal in XAML

```xml
<!-- MainWindow.xaml -->
<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:Microsoft.Terminal.Wpf"
        Title="My Terminal">
    <local:TerminalControl x:Name="TermCtrl" />
</Window>

```

### Wiring a PTY Connection in C#

```csharp
using Microsoft.Terminal.Wpf;
using Microsoft.Terminal.Settings.Models;

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();

        // Create a ConPTY connection
        ITerminalConnection conn = new ConPtyConnection(@"cmd.exe");

        // Assign it to the control
        TermCtrl.Connection = conn;

        // Configure theme and font
        var theme = new TerminalTheme 
        { 
            DefaultBackground = 0x000000, 
            DefaultForeground = 0xFFFFFF 
        };
        TermCtrl.SetTheme(theme, "Cascadia Mono", 10);
    }
}

```

### Handling Scroll Events

```csharp
TermCtrl.TerminalScrolled += (s, e) =>
{
    // e.viewTop, e.viewHeight, e.bufferSize
    Console.WriteLine($"Viewport scrolled to line: {e.viewTop}");
};

```

### Programmatic Resizing

```csharp
await TermCtrl.ResizeAsync(rows: 40, columns: 120, CancellationToken.None);

```

## Summary

- The **Terminal component interaction flow** follows a strict managed-to-native bridge pattern, separating the WPF UI from the high-performance C++ core.
- Input travels from `TerminalControl` → `TerminalContainer` → `HwndTerminal` C API → `Terminal` core, where the VT100 state machine processes sequences.
- Output flows back through registered callbacks (`TerminalRegisterWriteCallback`, `TerminalRegisterScrollCallback`) to update the PTY connection and WPF scrollbar.
- Rendering bypasses WPF's visual tree; the native `Renderer` draws directly onto the HWND created by `CreateTerminal`, ensuring optimal performance for text-intensive workloads.

## Frequently Asked Questions

### How does keyboard input cross from the WPF layer to the native terminal core?

Keyboard input is captured by the `TerminalContainer_MessageHook` in [`TerminalContainer.cs`](https://github.com/microsoft/terminal/blob/main/TerminalContainer.cs), which intercepts `WM_KEYDOWN`, `WM_KEYUP`, and `WM_CHAR` messages. These are forwarded across the C API boundary via `TerminalSendKeyEvent` and `TerminalSendCharEvent` defined in [`HwndTerminal.hpp`](https://github.com/microsoft/terminal/blob/main/HwndTerminal.hpp). The C++ implementation in [`HwndTerminal.cpp`](https://github.com/microsoft/terminal/blob/main/HwndTerminal.cpp) unwraps the terminal pointer and calls `SendKeyEvent` or `SendCharEvent` on the core `Microsoft::Terminal::Core::Terminal` instance.

### Why does Windows Terminal use an HWND hosted inside WPF rather than pure WPF rendering?

The terminal requires high-performance text rendering capable of handling rapid VT100/ANSI sequence processing, large scrollback buffers, and complex glyph shaping without the overhead of WPF's retained-mode graphics system. By hosting a native HWND via `HwndHost` in [`TerminalContainer.cs`](https://github.com/microsoft/terminal/blob/main/TerminalContainer.cs), the application leverages DirectWrite and Atlas rendering engines ([`src/renderer/base/renderer.cpp`](https://github.com/microsoft/terminal/blob/main/src/renderer/base/renderer.cpp)) that draw directly to the HWND surface, achieving the necessary throughput for terminal workloads while the WPF layer handles chrome, theming, and window management.

### How does the terminal core notify the WPF UI of scroll position changes?

When the viewport changes due to new output or user scrolling, the core invokes the scroll callback registered via `TerminalRegisterScrollCallback` during initialization in `BuildWindowCore`. This callback maps to `OnScroll` in [`TerminalContainer.cs`](https://github.com/microsoft/terminal/blob/main/TerminalContainer.cs), which raises the `TerminalScrolled` event. The `TerminalControl` subscribes to this event and updates the WPF scrollbar's position and extent based on the `viewTop`, `viewHeight`, and `bufferSize` parameters passed through the event args.

### What happens when the terminal window is resized?

Resize events originate in WPF's `OnRenderSizeChanged` handler in [`TerminalControl.xaml.cs`](https://github.com/microsoft/terminal/blob/main/TerminalControl.xaml.cs), which calls `termContainer.Resize`. This invokes `TerminalTriggerResize` or `TerminalCalculateResize` in [`HwndTerminal.cpp`](https://github.com/microsoft/terminal/blob/main/HwndTerminal.cpp), depending on the `AutoResize` setting. The native method calls `UserResize` on the core `Terminal` instance, which recalculates visible rows and columns, updates the text buffer dimensions, and triggers a redraw via the `Renderer`. The new dimensions are then propagated back to the PTY connection to ensure the shell receives a `SIGWINCH` equivalent notification.