Problem
Currently, inline mode has ad-hoc deferral logic that waits for specific ANSI query responses (terminal size via CSI 18t and cursor position via CSI ?6n) before allowing the first render. This logic is scattered across ApplicationMainLoop.IterationImpl and AnsiSizeMonitor with hardcoded timeout values and per-query boolean flags.
Other ANSI queries that run at startup also need deterministic completion before the app can safely render:
- Terminal size (CSI 18t) — needed for correct layout
- Cursor position (CSI ?6n) — needed for inline mode positioning
- Kitty keyboard protocol detection — determines input handling mode
- Terminal color capabilities — affects scheme/theming decisions
- Device attributes (DA1) — identifies terminal type
Each of these currently has its own handling path with no unified "all startup queries complete" signal.
Proposed Solution
Add a generic ANSI Startup Readiness Gate — a mechanism that:
- Registers startup queries — any component (size monitor, input processor, color detector) can register an ANSI query that must complete before the app is "ready"
- Tracks completion — each registered query transitions from pending → complete (via response) or pending → timed out
- Provides a single readiness signal —
bool IsReady (or Task WaitForReadyAsync(TimeSpan timeout)) that the main loop checks before the first render
- Supports per-query timeouts — individual queries can have different timeout values
- Emits trace diagnostics — uses
Trace.Lifecycle to log which queries are pending, completed, or timed out
API sketch
public interface IAnsiStartupGate
{
/// Register a startup query that must complete before readiness.
IDisposable RegisterQuery (string name, TimeSpan timeout);
/// Signal that a registered query has completed.
void MarkComplete (string name);
/// True when all registered queries are complete or timed out.
bool IsReady { get; }
}
Integration points
AnsiSizeMonitor registers size + cursor position queries
KittyKeyboardProtocolDetector registers keyboard capability query
TerminalColorDetector registers color capability query
ApplicationMainLoop.IterationImpl checks IsReady instead of the current per-query boolean flags
ApplicationImpl.Begin() can use IsReady to decide whether to defer the initial LayoutAndDraw
Current Workaround
The inline mode deferral in IterationImpl uses:
ISizeMonitor.InitialSizeReceived (combines size + cursor position for inline mode)
- A 500ms hardcoded timeout
- Per-iteration polling with
_inlineSizeConfirmed / _inlineWaitStarted fields
This works but doesn't generalize to other startup queries and requires each new query to add its own deferral plumbing.
Context
This emerged from PR #4933 (inline rendering mode) where we needed to defer rendering until both terminal size and cursor position were known. The pattern will become more important as Terminal.Gui relies more on ANSI capability detection at startup.
Problem
Currently, inline mode has ad-hoc deferral logic that waits for specific ANSI query responses (terminal size via CSI 18t and cursor position via CSI ?6n) before allowing the first render. This logic is scattered across
ApplicationMainLoop.IterationImplandAnsiSizeMonitorwith hardcoded timeout values and per-query boolean flags.Other ANSI queries that run at startup also need deterministic completion before the app can safely render:
Each of these currently has its own handling path with no unified "all startup queries complete" signal.
Proposed Solution
Add a generic ANSI Startup Readiness Gate — a mechanism that:
bool IsReady(orTask WaitForReadyAsync(TimeSpan timeout)) that the main loop checks before the first renderTrace.Lifecycleto log which queries are pending, completed, or timed outAPI sketch
Integration points
AnsiSizeMonitorregisters size + cursor position queriesKittyKeyboardProtocolDetectorregisters keyboard capability queryTerminalColorDetectorregisters color capability queryApplicationMainLoop.IterationImplchecksIsReadyinstead of the current per-query boolean flagsApplicationImpl.Begin()can useIsReadyto decide whether to defer the initialLayoutAndDrawCurrent Workaround
The inline mode deferral in
IterationImpluses:ISizeMonitor.InitialSizeReceived(combines size + cursor position for inline mode)_inlineSizeConfirmed/_inlineWaitStartedfieldsThis works but doesn't generalize to other startup queries and requires each new query to add its own deferral plumbing.
Context
This emerged from PR #4933 (inline rendering mode) where we needed to defer rendering until both terminal size and cursor position were known. The pattern will become more important as Terminal.Gui relies more on ANSI capability detection at startup.