Skip to content

Add generic ANSI startup readiness gate for terminal capability queries #4934

@tig

Description

@tig

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:

  1. Registers startup queries — any component (size monitor, input processor, color detector) can register an ANSI query that must complete before the app is "ready"
  2. Tracks completion — each registered query transitions from pending → complete (via response) or pending → timed out
  3. Provides a single readiness signalbool IsReady (or Task WaitForReadyAsync(TimeSpan timeout)) that the main loop checks before the first render
  4. Supports per-query timeouts — individual queries can have different timeout values
  5. 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.

Metadata

Metadata

Assignees

Projects

Status

✅ Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions