Skip to content

Fixes #3985. Remove unix driver; augment ansi driver to support configurable size detection#4821

Merged
tig merged 15 commits intov2_developfrom
copilot/remove-unix-driver
Mar 10, 2026
Merged

Fixes #3985. Remove unix driver; augment ansi driver to support configurable size detection#4821
tig merged 15 commits intov2_developfrom
copilot/remove-unix-driver

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 10, 2026

The unix and ansi drivers were functionally near-identical on Unix, with the ansi driver being a strict superset. This removes the unix driver entirely and consolidates on ansi as the sole ANSI-based driver for all platforms, while adding configurable size detection to preserve the one advantage the unix driver had.

Deleted (unix-driver-specific)

  • Drivers/UnixDriver/UnixComponentFactory.cs, UnixInput.cs, UnixInputProcessor.cs, UnixOutput.cs, IUnixInput.cs
  • Corresponding tests in Tests/UnitTestsParallelizable/Drivers/Unix/ and UnixDriver/

Moved (shared infrastructure, still used by ansi driver)

  • UnixIOHelper.cs, UnixRawModeHelper.cs, UnixTerminalHelper.cs, SuspendHelper.cs, UnixClipboard.csDrivers/ root

New: SizeDetectionMode config property

Controls how the ansi driver detects terminal size via Driver.SizeDetection ([ConfigurationProperty]):

// In tui-app.json or programmatically:
Driver.SizeDetection = SizeDetectionMode.AnsiQuery; // default — CSI 18t query, ~500ms throttle
Driver.SizeDetection = SizeDetectionMode.Polling;   // ioctl/Console API — synchronous, useful over SSH tunnels
  • AnsiQuery (default) uses AnsiSizeMonitor — ANSI CSI 18t escape-sequence queries, matching previous ansi-driver behavior.
  • Polling uses SizeMonitorImpl with AnsiOutput.NativeSizeQuery — synchronous ioctl(TIOCGWINSZ) on Unix / Console.WindowWidth/Height on Windows.
  • Driver.SizeDetection follows the Driver.Force16Colors [ConfigurationProperty] pattern; SizeDetectionMode is registered in SourceGenerationContext for AOT.

Size detection correctness fixes

  • AnsiOutput gains a NativeSizeQuery delegate property; GetSize() calls it when set, enabling SizeMonitorImpl to obtain real OS terminal dimensions in Polling mode.
  • SizeMonitorImpl now initialises _lastSize from consoleOut.GetSize() in its constructor (previously Size.Empty), so the first Poll() is a no-op when the terminal size has not changed — eliminating a spurious SizeChanged event that was causing test failures.

Registry changes

  • DriverRegistry.Names.UNIX constant removed.
  • UNIX driver registration removed; GetDefaultDriver() returns ansi on Unix/macOS.
  • ApplicationImpl.Driver.cs and all test references to Names.UNIX / UnixComponentFactory updated.

Tests

  • SizeMonitorTests.cs rewritten to reflect corrected first-poll semantics.
  • New AnsiComponentFactorySizeMonitorTests.cs verifies: (1) injected monitor is returned directly, (2) AnsiQuery returns AnsiSizeMonitor, (3) Polling returns SizeMonitorImpl with NativeSizeQuery wired up, (4) end-to-end resize detection via NativeSizeQuery.

Documentation

  • docfx/docs/drivers.md updated: unix driver removed from comparison table, ansi shown as the Unix/macOS default, auto-selection section corrected, new Size Detection (ANSI Driver) section added documenting the Driver.SizeDetection configuration property.

Pull Request checklist:

  • I've named my PR in the form of "Fixes #issue. Terse description."
  • My code follows the style guidelines of Terminal.Gui - if you use Visual Studio, hit CTRL-K-D to automatically reformat your files before committing.
  • My code follows the Terminal.Gui library design guidelines
  • I ran dotnet test before commit
  • I have made corresponding changes to the API documentation (using /// style comments)
  • My changes generate no new warnings
  • I have checked my code and corrected any poor grammar or misspellings
  • I conducted basic QA to assure all features are working
Original prompt

This section details on the original issue you should resolve

<issue_title>Remove unix driver; augment ansi to optionally support polling size detection on Unix</issue_title>
<issue_description>## Summary

Remove the unix driver entirely and consolidate on the ansi driver as the sole ANSI-based driver for all platforms. Augment the ansi driver to optionally support synchronous (ioctl-based) size detection on Unix, controlled via a new ConfigurationProperty.

Rationale

A detailed comparison of the unix and ansi drivers reveals they are functionally near-identical on Unix, with the ansi driver being a strict superset:

What's Identical

  • InputProcessor: UnixInputProcessor and AnsiInputProcessor are functionally duplicate — same base class (InputProcessorImpl<char>), same AnsiKeyConverter, same Process() body, same InjectKeyDownEvent/InjectMouseEvent implementations.
  • ANSI parsing pipeline: Both use AnsiResponseParser<char>AnsiKeyboardParserAnsiMouseParser.
  • Raw mode: Both use UnixRawModeHelper (termios) on Unix.
  • I/O primitives: Both use UnixIOHelper (poll/read/write) on Unix.
  • Output rendering: Both inherit from OutputBase (dirty-cell iteration, ANSI sequence generation).
  • Suspend: Both delegate to UnixTerminalHelper.Suspend()SuspendHelper (SIGTSTP).
  • Mouse interpretation: Same shared MouseInterpreterMouseButtonClickTracker.

Where the Ansi Driver Is Better

Aspect Unix Driver Ansi Driver
Responsibility separation UnixInput owns terminal state (alt screen, cursor, mouse) — architectural smell AnsiOutput owns terminal state — cleaner separation
Cross-platform Unix/macOS only Unix, macOS, and Windows (via VT mode helpers)
Testability No DI — UnixComponentFactory has no constructor params Full DI — AnsiComponentFactory accepts mock AnsiInput, IOutput, ISizeMonitor
Buffer capture No AnsiOutput.GetLastBuffer() for test verification
Cursor positioning Creates/disposes a dup()'d fd + FileStream + StreamWriter per call Simply writes CSI_SetCursorPosition through normal Write() path
Kitty keyboard Not supported EnableKittyKeyboard()/DisableKittyKeyboard()
Degraded mode Silent bail Explicit AnsiPlatform enum with well-defined no-op paths
Input flush on shutdown Unbounded drain + tcflush Bounded (max 10 attempts, 5ms timeout), platform-aware
Error handling in Read Minimal Explicit EOF and errno checking

The One Advantage of the Unix Driver: Synchronous Size Detection

The only functional advantage the Unix driver has is its SizeMonitorImpl using ioctl(TIOCGWINSZ) — a synchronous native syscall that gives immediate, reliable size information. The Ansi driver's AnsiSizeMonitor uses async ANSI queries (ESC[18tESC[8;h;wt), which:

  • Are throttled to 500ms between queries
  • Depend on the terminal correctly responding
  • Add latency to initial size detection
  • May not work on all terminal emulators

This is the concern this issue addresses — making the ansi driver support both strategies.

Proposed Solution

1. Remove the Unix Driver

Delete the Drivers/UnixDriver/ directory entirely:

  • UnixComponentFactory.cs
  • IUnixInput.cs, UnixInput.cs
  • UnixInputProcessor.cs
  • UnixOutput.cs
  • UnixIOHelper.cskeep this (shared by Ansi driver too)
  • UnixRawModeHelper.cskeep this (shared by Ansi driver too)
  • UnixTerminalHelper.cskeep this (used by AnsiOutput.Suspend())
  • SuspendHelper.cskeep this
  • UnixClipboard.cskeep this (used by DriverImpl.CreateClipboard())

Remove DriverRegistry.Names.UNIX registration. Update GetDefaultDriver() to return "ansi" on Unix/macOS instead of "unix".

2. Add SizeDetectionMode ConfigurationProperty

Add a new configuration property to control which size detection strategy the Ansi driver uses:

// Proposed enum
public enum SizeDetectionMode
{
    /// <summary>
    /// Uses ioctl(TIOCGWINSZ) on Unix, Console API on Windows.
    /// Synchronous, immediate, and reliable. Default on Unix.
    /// </summary>
    Polling,

    /// <summary>
    /// Sends CSI 18t ANSI query, parses ESC[8;h;wt response.
    /// Works over SSH/serial/any ANSI terminal. Async with 500ms throttle.
    /// </summary>
    AnsiQuery
}

Add the config property on the Driver class (which already holds global driver settings like Force16Colors):

[ConfigurationProperty (Scope = typeof (SettingsScope))]
public static SizeDetectionMode SizeDetection { get; set; } = SizeDetectionMode.Polling;

The JSON config would be:

{ "Driver.SizeDetection": "AnsiQuery" }

3. Update AnsiComponentFactory.CreateSizeMonitor()

Change the current type-based decision to a config-based one:...


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

tig and others added 2 commits March 10, 2026 10:54
…pport polling size detection

- Delete UnixComponentFactory, IUnixInput, UnixInput, UnixInputProcessor, UnixOutput
- Move shared Unix infrastructure (UnixIOHelper, UnixRawModeHelper, UnixTerminalHelper, SuspendHelper, UnixClipboard) to Drivers/ root
- Remove Names.UNIX and UNIX driver registration from DriverRegistry
- Update GetDefaultDriver() to return 'ansi' on Unix/macOS instead of 'unix'
- Add SizeDetectionMode enum (Polling/AnsiQuery)
- Add Driver.SizeDetection ConfigurationProperty defaulting to Polling
- Update AnsiComponentFactory.CreateSizeMonitor() to use config-based selection
- Register SizeDetectionMode in SourceGenerationContext for AOT
- Remove Unix driver tests for deleted classes
- Update all references to Names.UNIX and UnixComponentFactory

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Copilot AI changed the title [WIP] Remove unix driver and enhance ansi for polling size detection Fixes #3985. Remove unix driver; augment ansi driver to support polling size detection on Unix Mar 10, 2026
Copilot finished work on behalf of tig March 10, 2026 17:14
@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 10, 2026

@copilot you need to update drivers.md and the other deep dives that refer to the unix driver.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 10, 2026

@tig The model claude-sonnet-4.6 is not available for your account. This can happen if the model was disabled by your organization's policy or if your Copilot plan doesn't include access to it.

You can try again without specifying a model (just @copilot) to use the default, or choose a different model from the model picker.

If you want to contact GitHub about this error, please mention the following identifier so they can better serve you: 051c2231-38db-4614-ac68-3523c4fd4e11

@tig tig marked this pull request as ready for review March 10, 2026 17:16
@tig tig self-requested a review as a code owner March 10, 2026 17:16
@tig tig requested review from BDisp and Copilot March 10, 2026 17:16

This comment was marked as outdated.

@tig
Copy link
Copy Markdown
Collaborator

tig commented Mar 10, 2026

@copilot non-parallelt unit tests are failing on all runners. you must fix. also "New behavior selects the size monitor based on the new Driver.SizeDetection configuration (and returns AnsiSizeMonitor vs SizeMonitorImpl). There are existing tests for AnsiComponentFactory creation, but none cover this selection logic. Please add unit tests that verify: (1) injected sizeMonitor is returned, (2) AnsiQuery returns AnsiSizeMonitor (when output is AnsiOutput), and (3) Polling returns SizeMonitorImpl."

Copy link
Copy Markdown
Collaborator

@BDisp BDisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for the flaw in the unit tests, everything seems to be fine to me.

Copilot AI and others added 2 commits March 10, 2026 18:03
…ing uses NativeSizeQuery, update docs and tests

Co-authored-by: tig <585482+tig@users.noreply.github.com>
…lock with trace logging

Co-authored-by: tig <585482+tig@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 10, 2026

@copilot you need to update drivers.md and the other deep dives that refer to the unix driver.

Updated in commit 30ae317. drivers.md now shows 3 drivers (removed unix), the comparison table reflects ansi as the Unix/macOS default, auto-selection and code examples reference DriverRegistry.Names.ANSI, and a new Size Detection (ANSI Driver) section documents the Driver.SizeDetection config property.

@copilot non-parallelt unit tests are failing on all runners. you must fix. also "New behavior selects the size monitor based on the new Driver.SizeDetection configuration (and returns AnsiSizeMonitor...

Fixed in commit 30ae317. Root cause: CreateSizeMonitor() was defaulting to SizeMonitorImpl, but AnsiOutput.GetSize() returns a cached 80×25 that is only updated by ANSI query responses — so without AnsiSizeMonitor sending those queries, the size was permanently stuck.

Fixes:

  1. AnsiQuery is now the defaultAnsiSizeMonitor is used by default, restoring the original working behavior.
  2. Polling mode wires up AnsiOutput.NativeSizeQuery — ioctl on Unix, Console.WindowWidth/Height on Windows — so SizeMonitorImpl.Poll() actually sees the real terminal size.
  3. SizeMonitorImpl._lastSize is now initialised from consoleOut.GetSize() in the constructor (not Size.Empty) — this removes the spurious first-poll SizeChanged event that was causing the non-parallel test failures.

New tests in AnsiComponentFactorySizeMonitorTests.cs verify all three paths.

Copilot AI changed the title Fixes #3985. Remove unix driver; augment ansi driver to support polling size detection on Unix Fixes #3985. Remove unix driver; augment ansi driver to support configurable size detection Mar 10, 2026
Copilot finished work on behalf of tig March 10, 2026 18:06
tig and others added 4 commits March 10, 2026 12:32
Previously, the ANSI driver was stuck at 80×25 and did not respond to terminal resize events due to using SizeMonitorImpl, which only saw a cached size. This commit ensures AnsiSizeMonitor is always used for AnsiOutput, regardless of SizeDetectionMode, enabling correct resize detection via CSI 18t queries. Added Trace.Lifecycle instrumentation for event/trace observability. Updated and added comprehensive tests for monitor behavior and trace output. Also restored and cleaned up clipboard, IO, and Windows VT helper files. Documentation details the bug, fix, and verification steps. The ANSI driver now reliably tracks terminal size changes on all supported platforms.
SuspendHelper contains only Unix libc P/Invoke (killpg, uname) for SIGTSTP.
It belongs in UnixHelpers alongside the other Unix-specific helpers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move tcdrain/fsync to UnixIOHelper.FlushStdout() and
FlushFileBuffers to WindowsVTOutputHelper.FlushStdout().
Add isatty to UnixIOHelper for Phase 3.
AnsiTerminalHelper is now a pure dispatcher with zero DllImport.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create WindowsConsoleHelper with GetStdHandle/GetConsoleMode.
Driver.IsAttachedToTerminal dispatches to WindowsConsoleHelper
and UnixIOHelper.IsTerminal; contains zero DllImport itself.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
tig and others added 4 commits March 10, 2026 12:57
NetWinVTConsole uses different console mode flags than
WindowsVTInputHelper (more conservative), so it cannot be
deleted as a straight replacement. Move it to WindowsHelpers
to consolidate all Win32 P/Invoke in the correct directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reformat and reorganize code for readability

Refactored several files to improve code readability and maintainability by reformatting constructor initializations, Bash command invocations, and P/Invoke declarations. No functional changes were made; all updates are organizational and stylistic.
Restore terminal to cooked mode (stty sane) before sending SIGTSTP so the
shell can function while the process is stopped. Add diagnostic logging to
the suspend/resume lifecycle and fix the killpg P/Invoke declaration
(SetLastError, parameter name).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Termios P/Invoke struct used uint (4 bytes) for tcflag_t/speed_t and
NCCS=32, which matches Linux (56 bytes total) but is wrong for macOS where
tcflag_t/speed_t are unsigned long (8 bytes) and NCCS=20 (72 bytes total).
This caused a buffer overflow in tcgetattr and corrupted terminal state on
restore.

Replace the struct with a raw IntPtr buffer (128 bytes) since we only need
to save and restore the blob opaquely — no individual fields are read.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tig tig requested a review from BDisp March 10, 2026 22:33
Copy link
Copy Markdown
Collaborator

@BDisp BDisp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work.

@tig tig merged commit 1d88c87 into v2_develop Mar 10, 2026
11 checks passed
@tig tig deleted the copilot/remove-unix-driver branch March 10, 2026 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove unix driver; augment ansi to optionally support polling size detection on Unix

4 participants