Icod.DCurses 2.3.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Icod.DCurses --version 2.3.0
                    
NuGet\Install-Package Icod.DCurses -Version 2.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Icod.DCurses" Version="2.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Icod.DCurses" Version="2.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Icod.DCurses" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Icod.DCurses --version 2.3.0
                    
#r "nuget: Icod.DCurses, 2.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Icod.DCurses@2.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Icod.DCurses&version=2.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Icod.DCurses&version=2.3.0
                    
Install as a Cake Tool

Icod.DCurses

Icod TUI Toolchain

PR Staging build Main Release validation

Icod.DCurses is a managed, cross-platform curses-style terminal UI library for .NET. It provides retained logical screens, windows, pads, panels, Unicode-aware text, semantic metadata, retained mixed-media presentation, geometry/layout, damage-aware refresh, and deterministic interaction routing without taking ownership of the application's event loop or application policy.

Status

This source tree defines the stable Icod.DCurses 2.3.0 release. Check NuGet for published availability and version history.

Version 2.3 adds CursesRasterAtlas, a bounded cell-aligned raster presentation owner for tile-oriented applications. Applications keep durable source pixels and gameplay state; DCurses retains placeholder cells, viewport coordinates and transaction serialization; stable Icod.Terminal 1.28.0 owns exact cell-pixel geometry, live raster identities, acknowledgement and lifecycle certainty. Presentations copy the known front frame to a back frame, apply bounded RGB24/RGBA32 damage, then select the completed frame. Complete-atlas workloads are coalesced into one region update, and recoverable presentation timeouts select the sample's text fallback instead of leaving the application blocked.

The raster-atlas sample demonstrates ATLAS → FRAME → TEXT selection, supplied 16×16 terrain/player artwork, movement, collision, camera scrolling, retained help/status presentation, explicit resize recreation, and clean Q/Escape exits. Live acceptance is specific to the tested environments: protocol completion is ordered, not a promise of universally atomic or gapless physical presentation. See release readiness and known limitations.

Version 2.2 adds immutable discovery of the effective single-key and multi-key command bindings in current routing precedence, plus bounded command sequences with explicit pending, completed, mismatch, fallback, and cancellation results. It keeps command execution, labels, localization, timeouts, and the event loop application-owned. The editor and roguelike samples exercise these facilities through public APIs; the 2.2 API baseline records the additive contract over 2.1.

Version 2.1.0 adds immutable Unicode text layout, source-position and selection geometry, retained layout projection, prepared bulk cell writes, large-content viewport coordinates, stateless track layout, and bounded opt-in refresh diagnostics. The roguelike and editor samples demonstrate the public APIs with application-owned state. The 2.1 API baseline and stable-source qualification describe the source contract and Staging evidence. The main push workflow validates Release configuration before publication.

Version 2.0.0 completes the direct dependency cutover: production DCurses depends only on Icod.Terminal 1.18.0, which may restore Icod.TermInfo transitively. Version 2.0 changes the public profile and dimensions types and the assembly identity. See the 2.0 migration guide and 2.0 roadmap.

Version 1.6 adds retained mixed-media presentation over the published Icod.Terminal 1.15.0 persistent-raster / Unicode-placeholder ownership model. Raster placeholder cells participate in ordinary DCurses windows, pads, viewports, panels, clipping, scrolling, composition, damage, and refresh while Terminal remains the sole owner of live raster protocol identity, acknowledgement, encoding, and lifecycle certainty.

The frozen 1.6 public contract is:

75 exported types
559 canonical declared contract lines
sha256 266e23e6f3b4d5be98c81b5d5774f1de47d488d9ede7025877e46388cae6d458

Version 1.6 is the final published 1.x line. It is additive over the published 1.5 contract and keeps AssemblyVersion at 1.0.0.0.

Support the Project

Icod.DCurses and its ecosystem packages (Icod.Terminal and Icod.TermInfo) are built and maintained by a solo developer. If these packages save you or your team time, please consider supporting their continued development and maintenance.

GitHub Sponsors Ko-fi PayPal

Architecture

Icod.DCurses is the retained presentation and interaction layer of the Icod terminal stack:

higher-level terminal applications / future widgets
                         |
                    Icod.DCurses
 retained text + raster presentation
 windows / pads / viewports / panels / cells / metadata
 geometry / layout / composition / clipping / damage / refresh
 interaction regions / scopes / focus / capture / gestures / commands
                         |
                    Icod.Terminal
 live session / input / lifecycle / semantic protocols
 physical terminal state / persistent raster ownership / serialized output
                         |
                    Icod.TermInfo
 immutable terminal capability data and planning
                         |
                  terminal / tty
  • Icod.TermInfo owns immutable terminal capability data and reusable inspection/planning facilities.
  • Icod.Terminal owns the live terminal conversation: modes, dimensions, lifecycle, input, semantic protocols, persistent raster ownership, reversible terminal state, and serialized output.
  • Icod.DCurses owns retained terminal-cell presentation and deterministic interaction mechanisms.
  • Applications own their event loop, command execution, source-image durability, widget/application semantics, navigation, and high-level layout policy.

The direct 2.x runtime dependency is:

Icod.Terminal 1.28.0

Icod.TermInfo is not a direct dependency of DCurses 2.x; NuGet may restore it transitively through Terminal. The 2.0 major-version boundary requires a consumer rebuild from 1.6; follow the 2.0 migration guide. The previous 1.6 package retains its historical direct dependencies on Icod.Terminal 1.15.0 and Icod.TermInfo 1.14.0.

Install

Install the stable 2.3 package from NuGet:

dotnet add package Icod.DCurses --version 2.3.0

For the previous stable release:

dotnet add package Icod.DCurses --version 2.2.0

The package targets:

net8.0
net9.0
net10.0

Quick Start

using Icod.DCurses;

await using CursesSession session = await CursesSession.OpenAsync();
CursesWindow screen = session.StandardScreen;

screen.Clear();
screen.Move( 0, 0 );
screen.Write(
	"Hello from Icod.DCurses",
	new CursesStyle(
		CursesColor.Default,
		CursesColor.Default,
		CursesTextAttributes.Bold
	)
);

await session.RefreshAsync();
CursesEvent terminalEvent = await session.ReadEventAsync();

2.2 Interaction Convenience Example

Applications can register semantic command sequences, inspect the currently effective bindings for help text, and route one normalized input event at a time. A pending prefix has no hidden timer; the application decides when to call CancelPendingCommandSequence().

CursesKeyGesture g = CursesKeyGesture.ForCharacter( new Rune( 'g' ) );
CursesCommand goTop = new( "document.go-top" );
router.BindGlobalGestureSequence( [ g, g ], goTop );

IReadOnlyList<CursesCommandSequenceBinding> helpBindings =
	router.GetEffectiveGestureSequenceBindings();

CursesCommandSequenceResult result = router.ProcessCommandSequence( input );
if ( result.Kind is CursesCommandSequenceResultKind.Completed ) {
	ExecuteApplicationCommand( result.Command! );
} else if ( result.Fallback is not null ) {
	HandleOrdinaryRoutingResult( result.Fallback );
}

Focused-region, active-scope, and router-global precedence matches ordinary routing. Sequence length and registration counts have public limits, and focus/scope/binding lifecycle changes invalidate pending state deterministically.

2.1 Text Layout Example

With 2.1, the session and screen from the quick start above can present an immutable layout. Source positions use UTF-16 offsets at legal text-element boundaries; visual positions use rows and terminal columns. DCurses computes the layout and projects the selected lines into retained cells, while the application decides what text to show and when to refresh.

CursesTextLayout layout = CursesTextLayout.Create(
	"Hello 👩‍💻 world",
	new CursesTextLayoutOptions( 20 ) {
		WrapMode = CursesTextWrapMode.TextElement
	}
);
CursesTextPosition afterEmoji = layout.GetNextPosition( new CursesTextPosition( 6 ) );
CursesTextVisualPosition caret = layout.GetVisualPosition( afterEmoji );

screen.PresentTextLayout( layout, 0, layout.Lines.Count, 0, 0 );
if ( caret.Line < screen.Rows && caret.Column < screen.Columns ) {
	screen.Move( caret.Line, caret.Column );
}
await session.RefreshAsync();

The application chooses the cursor position and can call HitTest and GetSelectionRectangles for editing and selection. See the editor sample for a complete event loop and the roguelike sample for viewport geometry, track layout, and sparse updates. These APIs first appear in 2.1.0; the 2.0 package does not contain them.

Retained Raster Presentation

Applications supply a backend-neutral TerminalRasterImage, create the live resource/placeholder through CursesSession, and retain opaque placeholder cells in ordinary DCurses surfaces:

using Icod.DCurses;
using Icod.Terminal;

TerminalRasterImage image = TerminalRasterImage.CreateRgb24(
	1,
	1,
	[ 0x20, 0x40, 0x80 ]
);

TerminalControlResult<CursesRasterResource> resourceResult =
	await session.CreateRasterResourceAsync( image );

if ( resourceResult.IsAvailable ) {
	await using CursesRasterResource resource = resourceResult.GetRequiredValue();
	TerminalControlResult<CursesRasterPlaceholder> placeholderResult =
		await resource.CreatePlaceholderAsync( 1, 1 );

	if ( placeholderResult.IsAvailable ) {
		await using CursesRasterPlaceholder placeholder = placeholderResult.GetRequiredValue();
		screen.Move( 2, 4 );
		screen.WriteRasterCell( placeholder.GetCell( 0, 0 ) );
		await session.RefreshAsync();
	}
}

The public raster ownership shape is:

CursesSession
    -> CursesRasterResource
        -> CursesRasterPlaceholder
            -> CursesRasterCell

CursesRasterOwnershipState reports Current, Stale, Released, or Disposed. DCurses never exposes Terminal-private raster ids, constructs Kitty/Sixel command bytes, retains a hidden source-image cache for replay, or silently switches graphics backends.

2.3 Cell-Aligned Raster Atlas

After the application verifies Terminal's persistent-raster capability, it can query exact current cell-pixel geometry and create one two-frame atlas. The initial image must divide exactly into the requested 1..256 rows and columns. Each presentation accepts at most 4,096 unique RGB24/RGBA32 images that exactly match one tile:

CursesRasterAtlasGeometry geometry =
	await session.QueryRasterAtlasGeometryAsync(
		rows: 12,
		columns: 20,
		timeout: TimeSpan.FromSeconds( 2 )
	);

TerminalRasterImage initialImage = BuildApplicationOwnedImage( geometry );
TerminalControlResult<CursesRasterAtlas> creation =
	await session.CreateRasterAtlasAsync( initialImage, geometry.Rows, geometry.Columns );

if ( creation.IsAvailable ) {
	await using CursesRasterAtlas atlas = creation.GetRequiredValue();
	screen.WriteRasterAtlas(
		0,
		0,
		atlas,
		new CursesRectangle( 0, 0, atlas.Rows, atlas.Columns )
	);
	await session.RefreshAsync();

	CursesRasterAtlasPresentationResult result = await atlas.PresentAsync(
		[ new CursesRasterAtlasTileUpdate( row, column, replacementTile ) ]
	);
}

Creation consumes one Terminal raster resource, one Unicode placeholder placement and two animation frames. Disposal releases the placeholder before the resource. A controlled presentation failure leaves the known front frame selected and reports how many tile replacements were acknowledged. An exception after output may have committed; it sets RequiresRecreation, rejects later cell/presentation use, and the application must dispose and recreate from its own pixels. DCurses never retains the initial image, automatically retries, or chooses a hidden fallback.

Resize is also application-owned: dispose the old atlas, clear or replace its retained cells, query fresh geometry, create a new atlas and project its new cells. Applications should provide an ordinary text path when capability verification, exact geometry, creation or presentation is unavailable. See the complete sample and terminal notes and measurement report.

For terminals with verified ordinary raster output but no persistent image identities, applications can explicitly present a complete viewport using await session.RefreshRasterAsync(image, row, column, geometry). Query exact geometry as above, keep the final screen row outside the image, and verify Terminal's ordinary RasterGraphics capability before use. Terminal 1.28.0 chooses Kitty or Sixel and encodes the image in the same transaction as the text refresh. Each call clears and repaints uncovered text, omitting the logical fallback cells beneath the image; DCurses stores no source image and performs no automatic replay. The sample requests synchronized output to reduce intermediate redraws on hosts that honor it. Visible panels must not overlap this immediate image, and retained raster cells cannot coexist with it. The next ordinary refresh clears previous frame damage and restores text. The sample uses a text view while help is open and exposes --raster for direct testing. Physical placement, clearing, and resize still require live terminal acceptance.

Raster selection and fallback

Terminal's ordinary raster capability policy prefers verified Kitty, then verified Sixel. When neither is usable, the application can choose text. This is capability selection, not automatic recovery from a failed Kitty write: Terminal sends through one selected backend and does not replay the image through Sixel after an error.

The sample owns a separate presentation ladder:

Mode Selection
ATLAS Verified persistent identities and exact geometry; uses Kitty animation facilities
FRAME Atlas setup unavailable or recoverably fails, with verified ordinary raster and usable geometry; ordinary Kitty is preferred over Sixel
TEXT Raster capability/geometry unavailable, or recoverable frame/presentation failure; also forced with --text

--raster selects ordinary FRAME directly and bypasses persistent-atlas negotiation. A Kitty atlas setup failure can therefore fall back to Kitty FRAME. Persistent atlas identities are not emulated through Sixel, and FRAME does not identify the selected graphics protocol. Atlas presentation uncertainty abandons the atlas for text; no hidden cache or backend replay occurs.

Feature Inventory

  • Logical screens and windows — retained curses-style cell surfaces, cursor movement, editing, scrolling, styles, and explicit refresh.
  • Unicode-aware text — terminal text-element measurement, slicing, width-two coherence, and configurable ambiguous-width policy.
  • Pads and viewports — off-screen retained surfaces with independently clipped projections onto the visible screen.
  • Semantic metadata — retained metadata such as hyperlinks, emitted through Terminal-owned semantic operations.
  • Retained mixed media — lazy row-sparse raster state participating in editing, scrolling, copy/overlay, pads/viewports, panels, clipping, resize, and damage refresh.
  • 2.3 raster atlas — exact cell-pixel geometry, bounded double-buffered tile replacement, retained rectangular projection, conservative lifecycle certainty, and caller-driven fallback/recreation.
  • Retained panels — deterministic z-order, movement, visibility, resizing, clipping, transparency, composition, and disposal. Blank+raster coordinates remain visually present under blank-cell transparency.
  • Geometry and layout — immutable rectangles/insets plus stateless split, dock, and clip helpers.
  • Lifecycle and refresh — physical-state invalidation after terminal uncertainty, sparse/full redraw, synchronized output, and stale raster rejection before output.
  • Interaction routing — bounded regions/scopes, logical and spatial focus, explicit pointer capture, clock-free gestures, semantic commands, and pointer-shape preferences.
  • 2.2 interaction conveniences — detached effective-binding discovery and bounded, explicitly cancellable multi-key command composition without application callback or event-loop ownership.
  • 2.1 text layout — immutable styled Unicode layout, legal source positions, caret/hit/selection geometry, and retained window projection.
  • 2.1 large-content presentation — application-owned viewport coordinates, fixed/weighted tracks, and prepared bulk cell writes for visible slices.
  • 2.1 refresh diagnostics — bounded opt-in snapshots of refresh outcomes and prepared work; observation is disabled by default.

Design Boundaries

  • No second terminal capability database or raw-input reader.
  • No private OSC/CSI/DCS/APC graphics framing for Terminal-owned facilities.
  • No public Terminal-private persistent-raster ids.
  • No hidden raster source cache/re-upload or Sixel emulation of retained raster identities.
  • No atlas batching, Indexed8 partial replacement, map-sized storage, automatic replay, or inferred raster backend.
  • No widget/control framework, callback dispatcher, retained capture/bubble event tree, automatic focus-on-click, PTY/process hosting, terminal emulation, or application framework.
  • Application policy remains above DCurses.

Samples and Documentation

Icod.DCurses.RasterAtlas.Sample is the 2.3 tile-application acceptance sample; --text forces text; --raster bypasses the persistent atlas and exercises complete-frame Kitty/Sixel output. Icod.DCurses.MixedMedia.Sample demonstrates all three retained presentation axes together—ordinary text, CursesHyperlink semantic metadata, and raster placeholder cells—inside a pannable pad, with panel overlays, clipping, interaction geometry, serialized refresh, and graceful continuation when raster ownership is unavailable.

Recommended documentation entry points:

Compatibility and Versioning

Version 2.0 changes the public profile/dimensions signatures and advances AssemblyVersion to 2.0.0.0; applications upgrading from 1.6 must rebuild. Stable 1.0.0 remains the historical compatibility floor for the 1.x line.

The frozen 2.0 public contract is:

75 exported types
559 canonical declared contract lines
sha256 1d33658358af26049d858e084a80d9f3b80abab974c1c4d3bfb36c2c2b477c65

Authors

Inspired by original work from Bill Joy, author of the original termcap; Mary Ann (born Mark) Horton, author of terminfo; Pavel Curtis, author of pcurses; and Zeyd Ben-Halim, Eric S. Raymond, and Thomas Dickey, whose work developed and maintained libtinfo and ncurses.

Managed .NET implementation by Timothy J. Bruce uniblab@hotmail.com.

Copyright (c) 2026 Timothy J. Bruce

License

Icod.DCurses is licensed under the GNU Lesser General Public License, version 3 or later. Sample applications are licensed under the GNU General Public License, version 3 or later, as stated in their source headers.

See LICENSE and the per-project/source declarations for the applicable terms.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Icod.DCurses:

Package Downloads
Icod.Widgets

Reusable terminal UI widgets for .NET, including color themes, Unicode text editing, forms, menus, dialogs, lists, filesystem pickers, and game controls above Icod.DCurses.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.5.0 391 10/12/2026
2.4.0 135 10/11/2026
2.3.1 271 10/10/2026
2.3.0 55 10/9/2026
2.2.0 453 9/25/2026
2.1.0 100 9/24/2026
2.0.0 104 9/23/2026
1.6.0 392 9/17/2026
1.5.0 106 9/15/2026
1.4.0 104 9/14/2026
1.3.0 111 9/12/2026
1.2.0 154 9/10/2026
1.1.0 119 9/10/2026
1.0.0 108 9/9/2026
0.9.0 113 9/9/2026
0.8.0 110 9/9/2026
0.7.0 115 9/9/2026
0.6.0 113 9/9/2026
0.5.0 116 9/8/2026
0.4.0 112 9/8/2026
Loading failed

2.3.0 adds CursesRasterAtlas: exact cell-pixel geometry, bounded double-buffered RGB24/RGBA32 tile updates, retained rectangular projection, conservative lifecycle ownership, explicit recreation/fallback guidance, and complete-frame refresh through verified ordinary Kitty/Sixel output. The 2.0 assembly version remains stable and the sole direct runtime dependency is Icod.Terminal 1.28.0.