* Initial plan * Replace direct Driver access with ViewBase helper methods Co-authored-by: tig <585482+tig@users.noreply.github.com> * Update documentation to reference View methods instead of Driver Co-authored-by: tig <585482+tig@users.noreply.github.com> * Fix documentation wording to avoid driver reference * Fix missed Driver accesses in Slider.cs Co-authored-by: tig <585482+tig@users.noreply.github.com> * Use Application.Screen instead of ScreenRows/ScreenCols, keep MoveToScreen/FillRectScreen internal for ViewBase only Co-authored-by: tig <585482+tig@users.noreply.github.com> * Remove MoveToScreen and FillRectScreen helper methods, use Driver directly in ViewBase classes Co-authored-by: tig <585482+tig@users.noreply.github.com> * Improve documentation clarity and grammar per CONTRIBUTING.md guidelines Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add MoveToScreenPosition helper to eliminate Driver.Move calls in View subclasses Co-authored-by: tig <585482+tig@users.noreply.github.com> * Remove MoveToScreenPosition helper method, Menu.cs accesses Driver directly Co-authored-by: tig <585482+tig@users.noreply.github.com> * Replace Driver.Move with View.Move in Menu.cs Co-authored-by: tig <585482+tig@users.noreply.github.com> * Update documentation to reflect Driver encapsulation changes Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add PR warnings policy to CONTRIBUTING.md Co-authored-by: tig <585482+tig@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Tig <tig@users.noreply.github.com> Co-authored-by: tig <585482+tig@users.noreply.github.com>
12 KiB
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 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"
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:
IConsoleInput<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.
IConsoleOutput
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.
ConsoleDriverFacade<T>
A unified facade that implements IConsoleDriver 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:
┌─────────────────────────────────────────────┐
│ 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, runsIConsoleInput.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
IConsoleOutput
- 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():
- ApplicationImpl.Init() is invoked
- Creates a
MainLoopCoordinator<T>with the appropriateComponentFactory<T> - MainLoopCoordinator.StartAsync() begins:
- Starts the input thread which creates
IConsoleInput<T> - Initializes the main UI loop which creates
IConsoleOutput - Creates
ConsoleDriverFacade<T>and assigns toApplication.Driver - Waits for both threads to be ready
- Starts the input thread which creates
- Returns control to the application
Shutdown Flow
When Application.Shutdown() is called:
- Cancellation token is triggered
- Input thread exits its read loop
IConsoleOutputis disposed- Main thread waits for input thread to complete
- All resources are cleaned up
Component Interfaces
IConsoleDriver
The main driver interface that the framework uses internally. Provides:
- Screen Management:
Screen,Cols,Rows,Contents - Drawing Operations:
AddRune(),AddStr(),Move(),FillRect() - Cursor Management:
SetCursorVisibility(),UpdateCursor() - Attribute Management:
CurrentAttribute,SetAttribute(),MakeColor() - Clipping:
Clipproperty - Events:
KeyDown,KeyUp,MouseEvent,SizeChanged - Platform Features:
SupportsTrueColor,Force16Colors,Clipboard
Note: The driver is internal to Terminal.Gui. View classes should not access Driver directly. Instead:
- Use @Terminal.Gui.App.Application.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
IConsoleDriverFacade
Extended interface for v2 drivers that exposes the internal components:
IInputProcessor InputProcessorIOutputBuffer OutputBufferIWindowSizeMonitor WindowSizeMonitor
This interface allows advanced scenarios and testing.
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
Application._forceFakeConsoleis true
Example: Checking Driver Capabilities
Application.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()andView.AddStr()for drawingApplication.Screenfor screen dimensions
Custom Drivers
To 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();
Legacy Drivers
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.).
See Also
- @Terminal.Gui.Drivers - API Reference
- @Terminal.Gui.App.Application - Application class
- @Terminal.Gui.App.ApplicationImpl - Application implementation
- @Terminal.Gui.App.MainLoopCoordinator`1 - Main loop coordination