Sharpie is a modern .NET terminal manipulation library that provides object-oriented bindings
for NCurses, PDCurses, and PDCursesMod. It targets .NET 9.0+ and provides a comprehensive API
for building terminal-based applications with features like windows, pads, colors, mouse support,
and event handling.
- Target Framework: .NET 9.0
- Language Version: C# 10
- Architecture: Object-oriented wrapper around native Curses libraries
- Cross-platform: Linux, macOS, Windows (x64 and ARM64)
- Testing:
MSTestwithMoqandShouldly - Build System: Makefile + dotnet CLI
Sharpie/
├── Sharpie/ # Main library
│ ├── Abstractions/ # Interface definitions
│ ├── Backend/ # Curses backend implementations
│ ├── Font/ # Font handling (Figlet)
│ └── *.cs # Core classes (Terminal, Screen, etc.)
├── Sharpie.Tests/ # Test suite
├── Demos/ # Example applications
├── NativeLibraries/ # Native library projects
└── lib/ # Pre-built native libraries
- Terminal - Main entry point, manages terminal lifecycle
- Screen - Main screen window, manages child windows/pads
- Window/Pad - Drawing surfaces with different capabilities
- EventPump - Event handling system
- ColorManager - Color and styling management
- ICursesBackend - Abstraction over native Curses libraries
- Backend Pattern:
ICursesBackendabstracts native library differences - Surface Hierarchy:
Surface→TerminalSurface→Screen/Window/Pad - Event-Driven: Uses event pump for input handling
- Resource Management: IDisposable pattern throughout
- Thread Safety: SynchronizationContext-based execution model
- .NET 9.0 SDK
- Make (for build automation)
- Native Curses libraries (NCurses, PDCurses, PDCursesMod)
make check-tools # Ensure all tools are installed
make build # Build the project
make lint # Check formatting and code style
make format # Apply formatting
make test # Run tests with coverage
make test-report # Generate test coverage report
make docs # Generate documentation- Nito.AsyncEx.Context - Async context management
- Moq - Mocking framework (tests)
- Shouldly - Assertion library (tests)
- MSTest - Testing framework
- Classes: PascalCase (e.g.,
Terminal,ColorManager) - Methods: PascalCase (e.g.,
WriteText,Refresh) - Properties: PascalCase (e.g.,
Size,Handle) - Fields: camelCase with underscore prefix (e.g.,
_batchUpdateLocks) - Interfaces: PascalCase with 'I' prefix (e.g.,
ICursesBackend)
- NO COMMENTS in code (explicitly forbidden by user rules)
- Use
[PublicAPI]attribute for public APIs - Use
[SuppressMessage]for intentional violations - Prefer
Debug.Assert()for internal validation - Use
Should.Throw<>()for exception testing
- Use custom exception types:
CursesOperationException,CursesSynchronizationException - Check return values from
Curseslibraries with.Check()extension method - Use
AssertAlive()andAssertSynchronized()for state validation
[TestClass]
public class ClassNameTests
{
private Mock<ICursesBackend> _cursesMock = null!;
private ClassUnderTest _instance = null!;
[TestInitialize]
public void TestInitialize()
{
_cursesMock = new();
// Setup mocks
}
[TestMethod]
public void MethodName_Scenario_ExpectedResult()
{
// Arrange
_cursesMock.Setup(s => s.SomeMethod()).Returns(expectedValue);
// Act
var result = _instance.SomeMethod();
// Assert
result.ShouldBe(expectedValue);
_cursesMock.Verify(v => v.SomeMethod(), Times.Once);
}
}MethodName_WhenCondition_ExpectedResultMethodName_Throws_IfConditionMethodName_Returns_IfCondition
- Use
Mock<T>for all dependencies - Setup return values with
.Setup().Returns() - Verify calls with
.Verify() - Use
It.IsAny<T>()for parameter matching - Use
Times.Once,Times.Neverfor call verification
TestHelpers.cscontains extension methods for common test operationsMockResolve<T>()for native symbol resolver mockingMockArea()for surface area mocking
public sealed class Terminal : ITerminal, IDisposable
{
public void Dispose()
{
if (Disposed) return;
// Cleanup resources
_screen?.Dispose();
_terminalInstanceActive = false;
Disposed = true;
}
}terminal.Run((t, e) =>
{
switch (e)
{
case KeyEvent { Key: Key.Character, Char.Value: 'q' }:
return Task.FromResult(false); // Exit
case TerminalResizeEvent:
// Handle resize
return Task.CompletedTask;
default:
return Task.CompletedTask;
}
});using (terminal.AtomicRefresh())
{
// Multiple operations that should be batched
screen.WriteText("Hello");
screen.DrawBorder();
// Refresh happens automatically on dispose
}- Defines all Curses function signatures
- Platform-specific implementations in
Backend/folder - Uses native symbol resolution for dynamic loading
public abstract class BaseCursesBackend : ICursesBackend
{
protected internal INativeSymbolResolver CursesSymbolResolver { get; }
public int SomeMethod() =>
CursesSymbolResolver.Resolve<FunctionMap.SomeMethod>()();
}- All operations must run on the correct
SynchronizationContext - Use
AssertSynchronized()to validate thread context - Only one
Terminalinstance can be active at a time
- Always dispose
Terminalinstances - Use
usingstatements for automatic disposal - Be careful with native handle management
- Different Curses backends have different capabilities
- Use feature detection rather than platform detection
- Handle missing features gracefully
- Events are processed in the main thread
- Use
Delegate()for cross-thread operations - Handle
TerminalResizeEventfor responsive layouts
- Follow existing patterns and naming conventions
- Add comprehensive tests for new functionality
- Use appropriate mocking for dependencies
- Ensure thread safety considerations
- Run
make formatto apply consistent formatting - Run
make lintto check for lint warnings - Run
make testto ensure no regressions - Run
make test-reportto check coverage - Update documentation if needed
The project includes pre-built native libraries in lib/:
- NCurses - Linux/macOS
- PDCurses - Windows
- PDCursesMod - Enhanced Windows support
Native library projects in NativeLibraries/ handle packaging these libraries for NuGet distribution.
- Uses DocFX for API documentation
- Run
make docsto generate documentation - Documentation is published to GitHub Pages
The project uses GitHub Actions for:
- Multi-platform builds (Linux, macOS, Windows)
- Native library compilation
- Test execution and coverage reporting
- Documentation publishing
- NuGet package publishing