libghostty-dotnet

March 31, 2026 · View on GitHub

.NET examples and interop bindings for libghostty windows soft fork.

This repo also serves as a visual testing ground: an automated test suite launches each example, sends input, resizes windows, runs commands, and verifies correct rendering across DPI modes using screenshot comparison.

Prerequisites

  • Zig (0.15+): builds libghostty from source
  • .NET SDK 9.0: builds and runs examples
  • ClangSharp: regenerates bindings when the C API changes

Install on Windows

# winget
winget install zig.zig
winget install Microsoft.DotNet.SDK.9

# or choco
choco install zig dotnet-sdk

# ClangSharp (dotnet global tool)
dotnet tool install --global ClangSharpPInvokeGenerator

Setup

./setup.ps1

This clones and builds libghostty from source, then restores .NET packages. After setup, open any example .slnx in Visual Studio or run with dotnet run.

Examples

ExampleDescriptionStatus
Win32Raw Win32 P/Invoke, direct port of the C exampleDone
WinFormsPanel-based embedding with WinForms eventsDone
WPF-SimpleHwndHost embedding with GhosttyApp wrapperDone
WPF-DirectHwndHost embedding with raw NativeMethodsDone
WinUI 3SwapChainPanel composition surface (#3)Done
UnityIn-game terminal via render texturePlanned
AvaloniaCross-platform NativeControlHostPlanned

Visual Testing

The test suite uses FlaUI for UI automation and ImageSharp for screenshot comparison.

Test coverage

FeatureTestedNotes
App launchWindow appears within timeout
Window titleNot empty
Window sizeValid dimensions, reasonable bounds
Terminal rendersScreenshot is not blank
Clean shutdownWM_CLOSE, exit code 0, no crash dialog
Keyboard input (typing)Visible output after keystrokes
Enter executes commandScreen changes after Enter
BackspaceVisible change after deleting characters
Window resizeTerminal updates, two sizes compared
Minimum window sizeShrink to 320x240, no crash
Focus/cursorCursor visible when focused
Command executionecho output, prompt returns
ScrollbackShift+PageUp after generating output
Clipboard copy/pasteSelect, copy, type, paste cycle
Long-running commandsOutput updates over time (ping)
DPI: Unaware modeLaunches and renders
DPI: SystemAware modeLaunches and renders
DPI: PerMonitorV2 modeLaunches and renders
DPI: mode affects renderingScreenshots differ across modes (high-DPI displays)
DPI: window reports valueGetDpiForWindow >= 96
Unicode/emoji input❌🔨
Mouse click❌🔨SendMouseButton API exists
Mouse selection (drag)❌🔨
Mouse scroll (wheel)❌🔨SendMouseScroll API exists
Ctrl+C (interrupt)❌🔨
Tab completion❌🔨
ANSI colors/formatting❌🔨Verify colored output renders differently
Cursor styles❌🔨Block, beam, underline
Window maximize/restore❌🔨
Window minimize/restore❌🔨
Multi-monitor (move between)❌🔨DPI change on move
Fullscreen toggle❌🔨
Content scale changes❌🔨SetContentScale API exists
Occlusion handling❌🔨SetOcclusion API exists
Modifier keys (Ctrl, Alt, Shift)❌🔨Key combos beyond clipboard
Rapid input (stress)❌🔨Fast typing, no dropped keys
Rapid resize (stress)❌🔨Continuous resize, no crash
Resource cleanup❌🔨No handle/memory leaks after close
Selection clipboard❌🔨supports_selection_clipboard in config
Surface close callback❌🔨Terminal-initiated close
Multiple surfaces❌🔨More than one terminal per app
Config loading❌🔨ghostty_config_load_default_files
# Run all visual tests
just test-visual

# Smoke tests only (fast)
just ci-test-smoke

# Full CI pipeline (build + all tests)
just ci

# Update screenshot baselines after intentional visual changes
just update-baselines

Tests are parameterized across all examples. Adding a new example to TestConfiguration.AllExamples automatically includes it in every test.

On workstations, tests use aggressive focus management to handle other windows competing for foreground. In CI (CI env var set), focus handling is lightweight since no contention exists.

Updating libghostty

# Use custom repo/branch/commit
./setup.ps1 -Repo https://github.com/deblasis/ghostty.git -Branch windows -Commit abc123

# Save the override to libghostty.json
./setup.ps1 -Repo https://github.com/deblasis/ghostty.git -Branch windows -Commit abc123 -Save

Regenerating bindings

When the C API changes:

./generate-bindings.ps1

Requires ClangSharp: dotnet tool install --global ClangSharpPInvokeGenerator