docfx/docs/ansihandling.md
Terminal.Gui's ANSI handling subsystem (Terminal.Gui.Drivers.AnsiHandling) provides comprehensive support for parsing and generating ANSI escape sequences. This includes keyboard input, mouse events, terminal queries/responses, and text formatting.
When running in a terminal, input arrives as a stream of characters. Beyond regular characters ('a', 'b', 'c'), terminals communicate special input (arrow keys, function keys, mouse events, terminal responses) through escape sequences - character sequences beginning with ESC (\x1B).
The ANSI handling subsystem has two main responsibilities:
┌─────────────────────────────────────────────────────────────────────┐
│ Input Stream (chars) │
└────────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ AnsiResponseParser │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ State Machine: Normal → ExpectingEscapeSequence → InResponse │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────┼───────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────────┐ ┌──────────────────┐ ┌─────────────────┐ │
│ │AnsiMouseParser│ │AnsiKeyboardParser│ │Expected Response│ │
│ └───────────────┘ └──────────────────┘ │ Matching │ │
│ │ │ └─────────────────┘ │
│ ▼ ▼ │ │
│ Mouse Event Key Event Callback │
└─────────────────────────────────────────────────────────────────────┘
The central class that filters input streams, distinguishing regular keypresses from escape sequences.
Two Variants:
AnsiResponseParser - Simple string-based processingAnsiResponseParser<TInputRecord> - Preserves metadata alongside characters (e.g., ConsoleKeyInfo)Parser States:
| State | Description |
|---|---|
Normal | Processing regular characters, passing them through |
ExpectingEscapeSequence | Encountered ESC, waiting to see if a sequence follows |
InResponse | Building a complete escape sequence |
State Transitions:
Normal ──[ESC]──► ExpectingEscapeSequence ──[valid char]──► InResponse
▲ │ │
│ │[ESC] │[terminator]
│ ▼ │
│ Release + restart │
└─────────────────────────────────────────────────────────────┘
A critical challenge: when the parser sees ESC, is it:
ESC[A (cursor up)?ESC O P (F1 in SS3 mode)?The parser accumulates characters, and the caller (e.g., InputProcessor) decides when enough time has elapsed to call Release(), which resolves any pending state.
// Parser holds ESC waiting for more input
// After timeout, caller invokes:
string? released = parser.Release(); // Forces resolution of held content
Abstracts the storage of accumulated characters:
StringHeld - Simple string accumulation for AnsiResponseParserGenericHeld<T> - Preserves metadata tuples for AnsiResponseParser<T>Matches input against registered patterns and converts to xref:Terminal.Gui.Input.Key objects.
Pattern Priority:
Ss3Pattern - SS3 sequences (e.g., ESC O P → F1)CsiKeyPattern - CSI tilde sequences (e.g., ESC[3~ → Delete)CsiCursorPattern - CSI cursor/function sequences (e.g., ESC[A → CursorUp)EscAsAltPattern - Escape-as-Alt (e.g., ESC g → Alt+G) [last-minute only]Ss3Pattern)Legacy sequences for F1-F4 and navigation keys:
| Sequence | Key |
|---|---|
ESC O P | F1 |
ESC O Q | F2 |
ESC O R | F3 |
ESC O S | F4 |
ESC O D | CursorLeft |
ESC O C | CursorRight |
ESC O A | CursorUp |
ESC O B | CursorDown |
ESC O H | Home |
ESC O F | End |
CsiKeyPattern)Function keys and editing keys with optional modifiers:
Format: ESC [ <keycode> [; <modifier>] ~
| Keycode | Key | Example |
|---|---|---|
| 1 | Home | ESC[1~ |
| 2 | Insert | ESC[2~ |
| 3 | Delete | ESC[3~ or ESC[3;5~ (Ctrl+Delete) |
| 4 | End | ESC[4~ |
| 5 | PageUp | ESC[5~ |
| 6 | PageDown | ESC[6~ |
| 11-15 | F1-F5 | ESC[15~ (F5) |
| 17-21 | F6-F10 | ESC[17~ (F6) |
| 23-24 | F11-F12 | ESC[23~ (F11) |
Modifier Codes:
| Code | Modifier |
|---|---|
| 2 | Shift |
| 3 | Alt |
| 4 | Shift+Alt |
| 5 | Ctrl |
| 6 | Ctrl+Shift |
| 7 | Ctrl+Alt |
| 8 | Ctrl+Shift+Alt |
CsiCursorPattern)Arrow keys and navigation with optional modifiers:
Format: ESC [ [1; <modifier>] <letter>
| Letter | Key |
|---|---|
| A | CursorUp |
| B | CursorDown |
| C | CursorRight |
| D | CursorLeft |
| H | Home |
| F | End |
| P-S | F1-F4 |
| Z | Shift+Tab |
Example: ESC[1;5A = Ctrl+CursorUp
EscAsAltPattern)Interprets ESC followed by a character as Alt+character.
Format: ESC <char>
ESC a → Alt+AESC G → Alt+Shift+GESC ^A (Ctrl+A) → Ctrl+Alt+AImportant: This pattern is marked IsLastMinute = true because it conflicts with longer sequences. It's only applied during Release() when no other pattern matches.
Parses SGR (1006) extended mouse format: ESC[<button;x;y{M|m}
M = button pressm = button releaseButton Codes:
| Code | Button | Notes |
|---|---|---|
| 0, 32 | Left | 32+ = motion with button |
| 1, 33 | Middle | |
| 2, 34 | Right | |
| 35 | None | Motion without button |
| 64 | WheelUp | |
| 65 | WheelDown | |
| 68 | WheelLeft | |
| 69 | WheelRight |
Modifier Offsets:
Click: Press(M) ─────────────────────────► Release(m)
Drag: Press(M) ──► Motion(M,32+) ──► ... ──► Release(m)
Move: Motion(M,35) ──► Motion(M,35) ──► ...
Scroll: WheelUp(64) or WheelDown(65) [single event, no M/m]
The parser supports waiting for specific terminal responses (e.g., device attributes, cursor position).
var request = new AnsiEscapeSequenceRequest
{
Request = "\x1B[6t", // Request cursor position
Terminator = "t", // Response ends with 't'
Value = "6", // Optional: disambiguate from other 't' responses
ResponseReceived = response => HandleResponse(response),
Abandoned = () => HandleTimeout()
};
Value-Based Disambiguation:
The Value property enables distinguishing between multiple requests that share the same terminator. For example:
\x1B[6t (cursor position report) and \x1B[8t (screen size report) both end with tValue = "6" ensures the expectation only matches responses containing [6;...tValue is null/empty, any response ending with the terminator matchesThe matching logic:
TerminatorValue is specified, extracts the first numeric token after [ (e.g., [8;24;80t → "8")ValueStopExpecting() was calledThread Safety: All expectation operations (Expect(), StopExpecting()) are protected by internal locks to ensure safe concurrent access.
Manages request throttling and queuing:
var scheduler = new AnsiRequestScheduler(parser);
scheduler.SendOrSchedule(driver, request); // Sends or queues
scheduler.RunSchedule(driver); // Processes queued requests
Collision Prevention:
The scheduler tracks outstanding requests by (Terminator, Value) tuple:
\x1B[6t if a prior [6t request is still pending\x1B[8t concurrently (different value, same terminator)(Terminator, Value) pair clears or times outConverts xref:Terminal.Gui.Input.Key objects to escape sequences for input injection:
AnsiKeyboardEncoder.Encode(Key.CursorUp); // Returns "ESC[A"
AnsiKeyboardEncoder.Encode(Key.CursorUp.WithCtrl); // Returns "ESC[1;5A"
AnsiKeyboardEncoder.Encode(Key.A.WithAlt); // Returns "ESC a"
Converts xref:Terminal.Gui.Input.Mouse events to SGR format:
AnsiMouseEncoder.Encode(new Mouse
{
Flags = MouseFlags.LeftButtonPressed,
ScreenPosition = new Point(5, 10)
});
// Returns "ESC[<0;6;11M" (1-based coordinates)
Static utility class providing ANSI sequence constants and helpers.
// Enable comprehensive mouse tracking
EscSeqUtils.CSI_EnableMouseEvents
// = CSI_EnableAnyEventMouse (1003) + CSI_EnableUrxvtExtModeMouse (1015) + CSI_EnableSgrExtModeMouse (1006)
// Disable all mouse tracking
EscSeqUtils.CSI_DisableMouseEvents
| Mode | Description |
|---|---|
| 1003 | Any-event tracking (motion with/without buttons) |
| 1006 | SGR format (decimal, unlimited coordinates) |
| 1015 | URXVT format (UTF-8 coordinates, legacy fallback) |
EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll // ESC[?1049h
EscSeqUtils.CSI_RestoreCursorAndRestoreAltBufferWithBackscroll // ESC[?1049l
EscSeqUtils.CSI_ClearScreen(ClearScreenOptions.EntireScreen) // ESC[2J
xref:Terminal.Gui.Drivers.EscSeqUtils.KnownTerminators contains valid ANSI response terminators for CSI sequences, used by the parser to detect sequence completion.
Wraps URLs in text with OSC 8 hyperlink sequences:
ESC]8;;https://example.com\x07https://example.com ESC]8;;\x07
This enables clickable links in terminals that support OSC 8 (Windows Terminal, iTerm2, etc.).
Extensive trace logging is built into the implementation. Enable via the logging system to capture: