Cross-Platform Driver Model
Overview
The driver model is the mechanism by which Terminal.Gui supports multiple platforms. Windows, Mac, Linux, and unit test environments are all supported through a modular, component-based architecture.
Terminal.Gui v2 uses a sophisticated driver architecture that separates concerns and enables platform-specific optimizations while maintaining a consistent API. The architecture is based on the Component Factory pattern and uses multi-threading to ensure responsive input handling.
Available Drivers
Terminal.Gui provides console driver implementations optimized for different platforms:
- DotNetDriver (
dotnet) - A cross-platform driver that uses the .NETSystem.ConsoleAPI. Works on all platforms (Windows, macOS, Linux). Best for maximum compatibility. - WindowsDriver (
windows) - A Windows-optimized driver that uses native Windows Console APIs for enhanced performance and platform-specific features. - UnixDriver (
unix) - A Unix/Linux/macOS-optimized driver that uses platform-specific APIs for better integration and performance. - FakeDriver (
fake) - A mock driver designed for unit testing. Simulates console behavior without requiring a real terminal.
Automatic Driver Selection
The appropriate driver is automatically selected based on the platform when you call Application.Init():
- Windows (Win32NT, Win32S, Win32Windows) →
WindowsDriver - Unix/Linux/macOS →
UnixDriver
Explicit Driver Selection
You can explicitly specify a driver in several ways:
Method 1: Set ForceDriver using Configuration Manager
{
"ForceDriver": "fake"
}
Method 2: Pass driver name to Init
Application.Init(driverName: "unix");
Method 3: Set ForceDriver on instance
using (IApplication app = Application.Create())
{
app.ForceDriver = "fake";
app.Init();
}
Valid driver names: "dotnet", "windows", "unix", "fake"
ForceDriver as Configuration Property
The ForceDriver property is a configuration property marked with [ConfigurationProperty], which means:
- It can be set through the configuration system (e.g.,
config.json) - Changes raise the
ForceDriverChangedevent - It persists across application instances when using the static
Applicationclass
// Subscribe to driver changes
Application.ForceDriverChanged += (sender, e) =>
{
Console.WriteLine($"Driver changed: {e.OldValue} → {e.NewValue}");
};
// Change driver
Application.ForceDriver = "fake";
Discovering Available Drivers
Use GetDriverTypes() to discover which drivers are available at runtime:
(List<Type?> driverTypes, List<string?> driverNames) = Application.GetDriverTypes();
Console.WriteLine("Available drivers:");
foreach (string? name in driverNames)
{
Console.WriteLine($" - {name}");
}
// Output:
// Available drivers:
// - dotnet
// - windows
// - unix
// - fake
Note: GetDriverTypes() uses reflection to discover driver implementations and is marked with [RequiresUnreferencedCode("AOT")] and [Obsolete] as part of the legacy static API.
Architecture
Component Factory Pattern
The v2 driver architecture uses the Component Factory pattern to create platform-specific components. Each driver has a corresponding factory:
NetComponentFactory- Creates components for DotNetDriverWindowsComponentFactory- Creates components for WindowsDriverUnixComponentFactory- Creates components for UnixDriverFakeComponentFactory- Creates components for FakeDriver
Core Components
Each driver is composed of specialized components, each with a single responsibility:
IInput<T>
Reads raw console input events from the terminal. The generic type T represents the platform-specific input type:
ConsoleKeyInfofor DotNetDriver and FakeDriverWindowsConsole.InputRecordfor WindowsDrivercharfor UnixDriver
Runs on a dedicated input thread to avoid blocking the UI.
IOutput
Renders the output buffer to the terminal. Handles:
- Writing text and ANSI escape sequences
- Setting cursor position
- Managing cursor visibility
- Detecting terminal window size
IInputProcessor
Translates raw console input into Terminal.Gui events:
- Converts raw input to
Keyevents (handles keyboard input) - Parses ANSI escape sequences (mouse events, special keys)
- Generates
MouseEventArgsfor mouse input - Handles platform-specific key mappings
IOutputBuffer
Manages the screen buffer and drawing operations:
- Maintains the
Contentsarray (what should be displayed) - Provides methods like
AddRune(),AddStr(),Move(),FillRect() - Handles clipping regions
- Tracks dirty regions for efficient rendering
IWindowSizeMonitor
Detects terminal size changes and raises SizeChanged events when the terminal is resized.
DriverFacade<T>
A unified facade that implements IDriver and coordinates all the components. This is what gets assigned to Application.Driver.
Threading Model
The driver architecture employs a multi-threaded design for optimal responsiveness:
┌─────────────────────────────────────────────┐
│ IApplication.Init() │
│ Creates MainLoopCoordinator<T> with │
│ ComponentFactory<T> │
└────────────────┬────────────────────────────┘
│
├──────────────────┬───────────────────┐
│ │ │
┌────────▼────────┐ ┌──────▼─────────┐ ┌──────▼──────────┐
│ Input Thread │ │ Main UI Thread│ │ Driver │
│ │ │ │ │ Facade │
│ IInput │ │ ApplicationMain│ │ │
│ reads console │ │ Loop processes │ │ Coordinates all │
│ input async │ │ events, layout,│ │ components │
│ into queue │ │ and rendering │ │ │
└─────────────────┘ └────────────────┘ └─────────────────┘
Input Thread: Started by
MainLoopCoordinator, runsIInput.Run()which continuously reads console input and queues it into a thread-safeConcurrentQueue<T>.Main UI Thread: Runs
ApplicationMainLoop.Iteration()which:- Processes input from the queue via
IInputProcessor - Executes timeout callbacks
- Checks for UI changes (layout/drawing)
- Renders updates via
IOutput
- Processes input from the queue via
This separation ensures that input is never lost and the UI remains responsive during intensive operations.
Initialization Flow
When you call Application.Init():
- IApplication.Init() is invoked
- Creates a
MainLoopCoordinator<T>with the appropriateComponentFactory<T> - MainLoopCoordinator.StartAsync() begins:
- Starts the input thread which creates
IInput<T> - Initializes the main UI loop which creates
IOutput - Creates
DriverFacade<T>and assigns toIApplication.Driver - Waits for both threads to be ready
- Starts the input thread which creates
- Returns control to the application
Shutdown Flow
When IApplication.Shutdown() is called:
- Cancellation token is triggered
- Input thread exits its read loop
IOutputis disposed- Main thread waits for input thread to complete
- All resources are cleaned up
Component Interfaces
IDriver
The main driver interface that the framework uses internally. IDriver is organized into logical regions:
Driver Lifecycle
Init(),Refresh(),End()- Core lifecycle methodsGetName(),GetVersionInfo()- Driver identificationSuspend()- Platform-specific suspend support
Driver Components
InputProcessor- Processes input into Terminal.Gui eventsOutputBuffer- Manages screen buffer stateSizeMonitor- Detects terminal size changesClipboard- OS clipboard integration
Screen and Display
Screen,Cols,Rows,Left,Top- Screen dimensionsSetScreenSize(),SizeChanged- Size management
Color Support
SupportsTrueColor- 24-bit color capabilityForce16Colors- Force 16-color mode
Content Buffer
Contents- Screen buffer arrayClip- Clipping regionClearContents(),ClearedContents- Buffer management
Drawing and Rendering
Col,Row,CurrentAttribute- Drawing stateMove(),AddRune(),AddStr(),FillRect()- Drawing operationsSetAttribute(),GetAttribute()- Attribute managementWriteRaw(),GetSixels()- Raw output and graphicsRefresh(),ToString(),ToAnsi()- Output rendering
Cursor
UpdateCursor()- Position cursorGetCursorVisibility(),SetCursorVisibility()- Visibility management
Input Events
KeyDown,KeyUp,MouseEvent- Input eventsEnqueueKeyEvent()- Test support
ANSI Escape Sequences
QueueAnsiRequest()- ANSI request handling
Note: The driver is internal to Terminal.Gui. View classes should not access Driver directly. Instead:
- Use Screen to get screen dimensions
- Use @Terminal.Gui.ViewBase.View.Move for positioning (with viewport-relative coordinates)
- Use @Terminal.Gui.ViewBase.View.AddRune and @Terminal.Gui.ViewBase.View.AddStr for drawing
- ViewBase infrastructure classes (in
Terminal.Gui/ViewBase/) can access Driver when needed for framework implementation
Driver Creation and Selection
The driver selection logic in ApplicationImpl.Driver.cs prioritizes component factory type over the driver name parameter:
- Component Factory Type: If an
IComponentFactoryis already set, it determines the driver - Driver Name Parameter: The
driverNameparameter toInit()is checked next - ForceDriver Property: The
ForceDriverconfiguration property is evaluated - Platform Detection: If none of the above specify a driver, the platform is detected:
- Windows (Win32NT, Win32S, Win32Windows) →
WindowsDriver - Unix/Linux/macOS →
UnixDriver - Other platforms →
DotNetDriver(fallback)
- Windows (Win32NT, Win32S, Win32Windows) →
This prioritization ensures flexibility while maintaining deterministic behavior.
Platform-Specific Details
DotNetDriver (NetComponentFactory)
- Uses
System.Consolefor all I/O operations - Input: Reads
ConsoleKeyInfoviaConsole.ReadKey() - Output: Uses
Console.Write()and ANSI escape sequences - Works on all platforms but may have limited features
- Best for maximum compatibility and simple applications
WindowsDriver (WindowsComponentFactory)
- Uses Windows Console API via P/Invoke
- Input: Reads
InputRecordstructs viaReadConsoleInput - Output: Uses Windows Console API for optimal performance
- Supports Windows-specific features and better performance
- Automatically selected on Windows platforms
Visual Studio Debug Console Support
When running in Visual Studio's debug console (VSDebugConsole.exe), WindowsDriver detects the VSAPPIDNAME environment variable and automatically adjusts its behavior:
- Disables the alternative screen buffer (which is not supported in VS debug console)
- Preserves the original console colors on startup
- Restores the original colors and clears the screen on shutdown
This ensures Terminal.Gui applications can be debugged directly in Visual Studio without rendering issues.
UnixDriver (UnixComponentFactory)
- Uses Unix/Linux terminal APIs
- Input: Reads raw
chardata from terminal - Output: Uses ANSI escape sequences
- Supports Unix-specific features
- Automatically selected on Unix/Linux/macOS platforms
FakeDriver (FakeComponentFactory)
- Simulates console behavior for unit testing
- Uses
FakeConsolefor all operations - Allows injection of predefined input
- Captures output for verification
- Always used when
IApplication.ForceDriverisfake
Important: View subclasses should not access Application.Driver. Use the View APIs instead:
View.Move(col, row)for positioningView.AddRune()andView.AddStr()for drawingView.App.Screenfor screen dimensions
See Also
- Terminal.Gui.Drivers - API Reference
- IApplication - Application interface
- @Terminal.Gui.App.MainLoopCoordinator`1 - Main loop coordination