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.
Terminal.Gui provides console driver implementations optimized for different platforms:
dotnet) - A cross-platform driver that uses the .NET System.Console API. Works on all platforms (Windows, macOS, Linux). Best for maximum compatibility.windows) - A Windows-optimized driver that uses native Windows Console APIs for enhanced performance and platform-specific features.unix) - A Unix/Linux/macOS-optimized driver that uses platform-specific APIs for better integration and performance.fake) - A mock driver designed for unit testing. Simulates console behavior without requiring a real terminal.The appropriate driver is automatically selected based on the platform when you call Application.Init():
WindowsDriverUnixDriverYou can explicitly specify a driver in three ways:
// Method 1: Set ForceDriver property before Init
Application.ForceDriver = "dotnet";
Application.Init();
// Method 2: Pass driver name to Init
Application.Init(driverName: "unix");
// Method 3: Pass a custom IConsoleDriver instance
var customDriver = new MyCustomDriver();
Application.Init(driver: customDriver);
Valid driver names: "dotnet", "windows", "unix", "fake"
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 FakeDriverEach driver is composed of specialized components, each with a single responsibility:
Reads raw console input events from the terminal. The generic type T represents the platform-specific input type:
ConsoleKeyInfo for DotNetDriver and FakeDriverWindowsConsole.InputRecord for WindowsDriverchar for UnixDriverRuns on a dedicated input thread to avoid blocking the UI.
Renders the output buffer to the terminal. Handles:
Translates raw console input into Terminal.Gui events:
Key events (handles keyboard input)MouseEventArgs for mouse inputManages the screen buffer and drawing operations:
Contents array (what should be displayed)AddRune(), AddStr(), Move(), FillRect()Detects terminal size changes and raises SizeChanged events when the terminal is resized.
A unified facade that implements IConsoleDriver and coordinates all the components. This is what gets assigned to Application.Driver.
The driver architecture employs a multi-threaded design for optimal responsiveness:
┌─────────────────────────────────────────────┐
│ ApplicationImpl.Init() │
│ Creates MainLoopCoordinator<T> with │
│ ComponentFactory<T> │
└────────────────┬────────────────────────────┘
│
├──────────────────┬───────────────────┐
│ │ │
┌────────▼────────┐ ┌──────▼─────────┐ ┌──────▼──────────┐
│ Input Thread │ │ Main UI Thread│ │ ConsoleDriver │
│ │ │ │ │ Facade │
│ IConsoleInput │ │ ApplicationMain│ │ │
│ reads console │ │ Loop processes │ │ Coordinates all │
│ input async │ │ events, layout,│ │ components │
│ into queue │ │ and rendering │ │ │
└─────────────────┘ └────────────────┘ └─────────────────┘
Input Thread: Started by MainLoopCoordinator, runs IConsoleInput.Run() which continuously reads console input and queues it into a thread-safe ConcurrentQueue<T>.
Main UI Thread: Runs ApplicationMainLoop.Iteration() which:
IInputProcessorIConsoleOutputThis separation ensures that input is never lost and the UI remains responsive during intensive operations.
When you call Application.Init():
MainLoopCoordinator<T> with the appropriate ComponentFactory<T>IConsoleInput<T>IConsoleOutputConsoleDriverFacade<T> and assigns to Application.DriverWhen Application.Shutdown() is called:
IConsoleOutput is disposedThe main driver interface that the framework uses internally. Provides:
Screen, Cols, Rows, ContentsAddRune(), AddStr(), Move(), FillRect()SetCursorVisibility(), UpdateCursor()CurrentAttribute, SetAttribute(), MakeColor()Clip propertyKeyDown, KeyUp, MouseEvent, SizeChangedSupportsTrueColor, Force16Colors, ClipboardNote: The driver is internal to Terminal.Gui. View classes should not access Driver directly. Instead:
Terminal.Gui/ViewBase/) can access Driver when needed for framework implementationExtended interface for v2 drivers that exposes the internal components:
IInputProcessor InputProcessorIOutputBuffer OutputBufferIWindowSizeMonitor WindowSizeMonitorThis interface allows advanced scenarios and testing.
System.Console for all I/O operationsConsoleKeyInfo via Console.ReadKey()Console.Write() and ANSI escape sequencesInputRecord structs via ReadConsoleInputWhen running in Visual Studio's debug console (VSDebugConsole.exe), WindowsDriver detects the VSAPPIDNAME environment variable and automatically adjusts its behavior:
This ensures Terminal.Gui applications can be debugged directly in Visual Studio without rendering issues.
char data from terminalFakeConsole for all operationsApplication._forceFakeConsole is trueApplication.Init();
// The driver is internal - access through Application properties
// Check screen dimensions
var screenWidth = Application.Screen.Width;
var screenHeight = Application.Screen.Height;
// Check if 24-bit color is supported
bool supportsTrueColor = Application.Driver?.SupportsTrueColor ?? false;
// Access advanced components (for framework/infrastructure code only)
if (Application.Driver is IConsoleDriverFacade facade)
{
// Access individual components for advanced scenarios
IInputProcessor inputProcessor = facade.InputProcessor;
IOutputBuffer outputBuffer = facade.OutputBuffer;
IWindowSizeMonitor sizeMonitor = facade.WindowSizeMonitor;
// Use components for advanced scenarios
sizeMonitor.SizeChanging += (s, e) =>
{
Console.WriteLine($"Terminal resized to {e.Size}");
};
}
Important: View subclasses should not access Application.Driver. Use the View APIs instead:
View.Move(col, row) for positioningView.AddRune() and View.AddStr() for drawingApplication.Screen for screen dimensionsTo create a custom driver, implement IComponentFactory<T>:
public class MyComponentFactory : ComponentFactory<MyInputType>
{
public override IConsoleInput<MyInputType> CreateInput()
{
return new MyConsoleInput();
}
public override IConsoleOutput CreateOutput()
{
return new MyConsoleOutput();
}
public override IInputProcessor CreateInputProcessor(
ConcurrentQueue<MyInputType> inputBuffer)
{
return new MyInputProcessor(inputBuffer);
}
}
Then use it:
ApplicationImpl.ChangeComponentFactory(new MyComponentFactory());
Application.Init();
Terminal.Gui v1 drivers that implement IConsoleDriver but not IConsoleDriverFacade are still supported through a legacy compatibility layer. However, they do not benefit from the v2 architecture improvements (multi-threading, component separation, etc.).
Note: The legacy MainLoop infrastructure (including the MainLoop class, IMainLoopDriver interface, and FakeMainLoop) has been removed in favor of the modern architecture. All drivers now use the MainLoopCoordinator and ApplicationMainLoop system exclusively.