VirtualTerminal 1.1.0
dotnet add package VirtualTerminal --version 1.1.0
NuGet\Install-Package VirtualTerminal -Version 1.1.0
<PackageReference Include="VirtualTerminal" Version="1.1.0" />
<PackageVersion Include="VirtualTerminal" Version="1.1.0" />
<PackageReference Include="VirtualTerminal" />
paket add VirtualTerminal --version 1.1.0
#r "nuget: VirtualTerminal, 1.1.0"
#:package VirtualTerminal@1.1.0
#addin nuget:?package=VirtualTerminal&version=1.1.0
#tool nuget:?package=VirtualTerminal&version=1.1.0
VirtualTerminal
VirtualTerminal is a small WPF control and infrastructure for hosting an ANSI / VT-compatible terminal inside your .NET applications.
It renders a Windows console screen buffer into a WPF UI, and lets you plug in different backends (local command line via ConPTY, SSH, custom processes, etc.) through a simple session abstraction.
This repository contains three main projects:
- VirtualTerminal – core WPF control, buffer interop and session abstractions.
- VirtualTerminal.CommandLine – addon that hosts a local command line (ConPTY).
- VirtualTerminal.SecureShell – addon that connects to a remote host over SSH (SSH.NET).
Known Issues
Before continue, i want you to know that library currently is not in perfect shape and has some flaws!
Here I listed issues, that i'm aware of and working on patching them!
- Backspace removing whole line, instead of one character
- Cursor in console maybe mismathing his position in certain scenarios
- No auto scrolling to prompt
Please share your feedback if you encountered any problems! Also, im planning migration to new ANSI escape sequence encoder engine!
Getting started
Install / reference projects
- Add the
VirtualTerminalproject to your solution (or reference the compiledVirtualTerminal.dll). - (Optional) Add
VirtualTerminal.CommandLineand/orVirtualTerminal.SecureShellif you want those backends and reference them from your app. - Target
net10.0-windowsand enable WPF (as in the sampleVirtualTerminal.TestApp).
Basic XAML setup
In your WPF project, declare the namespace and drop the control onto a window:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vt="clr-namespace:VirtualTerminal;assembly=VirtualTerminal">
<Grid>
<vt:VirtualTerminalView x:Name="Terminal"
AllowDirectInput="True"
ScrollDownVisible="True"
ScreenBackground="Black"
ScreenForeground="White" />
</Grid>
</Window>
Basic code-behind: attach a session
Create a session instance and assign it to the control’s Session property.
For example, using the local command line backend from VirtualTerminal.CommandLine:
using System.Windows;
using VirtualTerminal;
public partial class MainWindow : Window
{
private CommandLineSession? _session;
public MainWindow()
{
InitializeComponent();
_session = new CommandLineSession(); // defaults to cmd.exe
Terminal.Session = _session;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
_session?.Dispose();
}
}
Alternatively, for testing UI without a real backend, you can use DummyTerminalSession:
Terminal.Session = new DummyTerminalSession();
Terminal.Session.AppendLine("Hello from DummyTerminalSession");
Important:
VirtualTerminalViewdoes not own / dispose the session. You are responsible for disposing yourTerminalSessionwhen your window/control is closed.
Core concepts and public API (VirtualTerminal project)
VirtualTerminalView – WPF terminal control
Located in VirtualTerminalView.xaml / VirtualTerminalView.xaml.cs.
Purpose
- Displays the content of a
VirtualTerminalBufferin a WPF UI usingVirtualTerminalScreen. - Bridges user input (keyboard) to an attached
TerminalSession.
Key properties
TerminalSession? Session(DependencyProperty)- The active session. When changed,
VirtualTerminalViewsubscribes toSession.BufferUpdatedto repaint the terminal.
- The active session. When changed,
bool AllowDirectInput(DependencyProperty, defaulttrue)- If
true, keyboard input is sent directly toSessionusing VT key sequences.
- If
bool CursorBlinking(DependencyProperty, defaulttrue)- Reserved for cursor blinking logic (currently not fully wired).
bool ScrollDownVisible(DependencyProperty, defaulttrue)- Controls visibility of the floating “scroll to bottom” button.
Color ScreenBackground(DependencyProperty, defaultColors.Black)- Logical background color of the terminal. Validated via
ColorHelper.IsValidConsoleColor.
- Logical background color of the terminal. Validated via
Color ScreenForeground(DependencyProperty, defaultColors.White)- Logical foreground color (text).
string OutputText(DependencyProperty, read-only API)- Reserved for exposing buffer content as text (not heavily used internally).
bool AutoScrolling(DependencyProperty, defaulttrue)- Indicates whether scroll is pinned to bottom (auto-scroll on new data).
static Encoding Encoding- Exposes
VirtualTerminalBuffer.Encoding– the encoding used for the screen buffer (UTF‑16 / Unicode).
- Exposes
Key behavior
- On
Session.BufferUpdatedit:- Reads
CONSOLE_SCREEN_BUFFER_INFOandCHAR_INFO[]viaSession.Buffer. - Calls
PART_Output.UpdateBuffer(info, buffer)to re-render.
- Reads
- Handles
PreviewKeyDownwhenAllowDirectInput == true:- Converts
KeyEventArgsto VT sequences viaKeyHelper.Convert. - Calls
Session.Append(...)to send the input into the session.
- Converts
- Mouse wheel with
Ctrlpressed zooms the terminal font (changesPART_Output.FontSize). - A floating “scroll to bottom” button appears when not scrolled to the bottom.
VirtualTerminalScreen – low-level rendering element
Located in VirtualTerminalScreen.cs.
Purpose
- A
FrameworkElementthat rendersCHAR_INFO[]rows as text using WPFDrawingVisuals. - Manages its own collection of visuals per row for efficient re-rendering.
Key API
void UpdateBuffer(CONSOLE_SCREEN_BUFFER_INFO newInfo, CHAR_INFO[] buffer)- Updates the internal state and schedules drawing of the new buffer.
Size GetCellSize()- Measures the size of one text cell (based on current font).
Dependency properties
FontFamily FontFamily– default"Consolas".double FontSize– default14.0.Color Foreground– defaultLightGray.Color Background– defaultBlack.
These affect how characters are rendered; ColorHelper is used to translate console attributes into WPF colors.
PromptBox – command input control (optional)
Located in PromptBox.xaml / PromptBox.xaml.cs.
Purpose
- A lightweight command-entry UI: shows a prompt label (e.g.
"> "), a text box, and a submit button. - On Enter (or clicking the submit button) it:
- Raises the routed event
CommandSubmittedwith the submitted text. - If
Sessionis set, writes the command into the terminal viaSession.AppendLine(command).
- Raises the routed event
Key properties
TerminalSession? Session- Optional target session. If set, submitted commands are appended to the terminal.
string Prompt(default"> ")- Prompt text displayed before the input box.
string InputText- Two-way bound text of the input
TextBox.
- Two-way bound text of the input
bool IsInputEnabled(defaulttrue)- Enables/disables the input
TextBox.
- Enables/disables the input
Events
CommandSubmitted(routed/bubbling)- Fired when the user submits a command (Enter / button click).
KeyPressed(routed/bubbling)- Declared for key-level input scenarios. (Note: current implementation focuses on Enter + VT200 special keys forwarding.)
Notes
PromptBoxignores modifier keys and Backspace in itsPreviewKeyDownhandler.- VT200 special keys (arrows, function keys, etc.) are detected via
KeyHelper.GetVT200Codeand forwarded toSession.Append(...)whenSessionis set.
ITerminalSession – session abstraction
Located in Session/ITerminalSession.cs.
Purpose
- Minimal interface for anything that can supply data to a
VirtualTerminalBuffer. - The UI (
VirtualTerminalView) is written againstTerminalSessionbut you can build other UI againstITerminalSessiondirectly.
Members
event EventHandler? BufferUpdated- Must be raised whenever the underlying buffer content changes and the UI should re-render.
VirtualTerminalBuffer Buffer { get; }- Screen buffer containing the current console image.
Encoding InputEncoding { get; }- Encoding expected by
WriteInput.
- Encoding expected by
string Title { get; }- Logical title of the session (can be used for window/tab captions).
void Resize(int columns, int rows)- Called when the UI is resized.
void WriteInput(ReadOnlySpan<byte> data)- Writes input bytes (usually VT sequences) to the backend.
TerminalSession – base class for sessions
Located in Session/TerminalSession.cs.
Purpose
- Base class implementing
ITerminalSessionand common logic:- Manages
VirtualTerminalBuffer. - Implements default
ResizeandWriteInputthat write directly into the buffer. - Implements
IDisposablepattern.
- Manages
Key members
VirtualTerminalBuffer Buffer { get; }– created in constructor.virtual Encoding InputEncoding { get; set; }– defaults toVirtualTerminalBuffer.Encoding.virtual string Title { get; }– uses current process name and PID.virtual void Resize(int columns, int rows)- Calls
Buffer.ResizeBuffer(columns, rows).
- Calls
virtual void WriteInput(ReadOnlySpan<byte> data)- Writes directly to
Buffer.Write(data). Derived sessions often override this to send data to a process or network.
- Writes directly to
protected void NotifyBufferUpdated()- Raises
BufferUpdatedevent; must be called when new output is written toBuffer.
- Raises
void Dispose()/protected abstract void Dispose(bool disposing)- Derived classes must override
Dispose(bool)to free their own resources.
- Derived classes must override
DummyTerminalSession
Located in Session/DummyTerminalSession.cs.
- Minimal concrete implementation of
TerminalSessionthat does nothing special. Titleis"Dummy :P".Dispose(bool)is empty (no extra resources).- Useful for design-time / testing scenarios.
TerminalSessionExtensions – helpers and small features
Located in Session/TerminalSessionExtensions.cs.
Key extension methods on ITerminalSession:
void Append(string text)- Encodes
textusingsession.InputEncodingand callsWriteInput.
- Encodes
void AppendLine()/void AppendLine(string text)- Appends a platform VT newline (
Key.Entermapping) optionally after writing text.
- Appends a platform VT newline (
void Clear()- Sends VT sequence
ESC[2J ESC[Hto clear the screen and move cursor home.
- Sends VT sequence
TextWriter CreateBufferWriter()- Returns a
TextWriterthat writes intosession.Buffer(BufferStreamWriter).
- Returns a
void RedirectConsole()- Sets
Console.Outto aTextWriterthat writes into the session’s buffer.
- Sets
RedirectConsole feature
Callsession.RedirectConsole();once, and then anyConsole.Write*in your process will be routed into the terminal buffer (and thus into the UI).
VirtualTerminalBuffer – console buffer wrapper
Located in Interop/VirtualTerminalBuffer.cs.
Purpose
- Wraps a Windows console screen buffer handle, with VT processing enabled.
- Provides read/write, resize and info APIs used by sessions and the UI.
Key members
IntPtr InputHandle/IntPtr OutputHandle- Raw handles to console input/output.
int Rows/int Cols- Current logical size of the buffer.
bool IsDisposed- Indicates if the buffer has been disposed.
static Encoding Encoding- Encoding used by the console buffer (
Unicode).
- Encoding used by the console buffer (
void Write(ReadOnlySpan<byte> data)- Writes bytes to the console via
WriteConsole.
- Writes bytes to the console via
CHAR_INFO[] ReadBuffer(CONSOLE_SCREEN_BUFFER_INFO info)- Reads the visible region into a
CHAR_INFO[]array.
- Reads the visible region into a
CONSOLE_SCREEN_BUFFER_INFO GetBufferInfo()- Returns current buffer info from the OS.
void ResizeBuffer(int cols, int rows)- Changes console screen buffer size.
Internally, VirtualTerminalBuffer uses ConsoleHelper to allocate a hidden console and enable Unicode + VT processing.
VirtualTerminalBufferExtensions
Located in Interop/VirtualTerminalBufferExtensions.cs.
Key extension methods
EnableInputFlags(this VirtualTerminalBuffer buffer, ConsoleInputFlags flags)/DisableInputFlags(...)- Enables/disables specific input flags on the console input handle.
EnableOutputFlags(this VirtualTerminalBuffer buffer, ConsoleOutputFlags flags)/DisableOutputFlags(...)- Enables/disables output flags on the output handle.
void BindActive(this VirtualTerminalBuffer buffer)- Calls
SetConsoleActiveScreenBufferto make this buffer the active console buffer.
- Calls
bool IsCursorVisible(this VirtualTerminalBuffer buffer)- Returns current cursor visibility by querying
GetConsoleCursorInfo.
- Returns current cursor visibility by querying
These are mostly for advanced scenarios where you need more control over the underlying console.
ConsoleHelper
Located in Helpers/ConsoleHelper.cs.
void Allocate()- Allocates a console if one doesn’t exist, and hides its window.
void SetEncoding()- Sets console input and output code pages to UTF‑8.
KeyHelper
Located in Helpers/KeyHelper.cs.
Purpose
- Converts WPF
Key/KeyEventArgsinto VT200-compatible key sequences or printable characters.
Key members
string? Convert(KeyEventArgs e)/string? Convert(Key key)- Returns VT escape sequences for arrows, function keys, etc., or printable text via
ToUnicode.
- Returns VT escape sequences for arrows, function keys, etc., or printable text via
string? GetVT200Code(Key key)- Maps special keys to CSI / ESC sequences.
string? GetCharFromKey(Key key)- Uses Windows APIs to get the Unicode character represented by a key.
bool IsModifier(Key key)- Returns
truefor Shift/Ctrl/Alt keys.
- Returns
ColorHelper
Located in Helpers/ColorHelper.cs.
Key members
Color ConvertToColor(ConsoleCharacterAttributes attributes, bool isBackground)- Converts console attribute flags into WPF
Color.
- Converts console attribute flags into WPF
bool IsValidConsoleColor(Color color)- Returns whether a color is in the standard console palette (0,128,255 per channel).
ConsoleCharacterAttributes ConvertToAttributes(Color color, bool isBackground)- Inverse of
ConvertToColor.
- Inverse of
Creating your own TerminalSession
You can implement custom backends (e.g., your own process, REPL, game, container shell) by inheriting from TerminalSession (recommended) or implementing ITerminalSession from scratch.
Recommended: inherit from TerminalSession
- Subclass
TerminalSession:
using System.Text;
using VirtualTerminal.Session;
using VirtualTerminal.Interop;
public sealed class MyCustomSession : TerminalSession
{
private readonly SomeClient _client;
public override string Title => "My Custom Backend";
public MyCustomSession(SomeClient client, Encoding? encoding = null)
: base(encoding ?? Encoding.UTF8)
{
_client = client;
// Subscribe to your backend’s output
_client.OutputReceived += OnOutputReceived;
}
private void OnOutputReceived(byte[] data)
{
// Convert from your backend encoding into buffer encoding
byte[] conv = Encoding.Convert(InputEncoding, VirtualTerminalBuffer.Encoding, data);
Buffer.Write(conv);
NotifyBufferUpdated();
}
public override void WriteInput(ReadOnlySpan<byte> data)
{
// Forward user input into backend
_client.Send(data.ToArray());
}
public override void Resize(int columns, int rows)
{
base.Resize(columns, rows);
_client.Resize(columns, rows);
}
protected override void Dispose(bool disposing)
{
if (!disposing)
return;
_client.Dispose();
}
}
- Attach your session to the control:
var client = new SomeClient(...);
var session = new MyCustomSession(client);
Terminal.Session = session;
- Wire output events and call
NotifyBufferUpdated()
- When your backend receives data, convert it into
VirtualTerminalBuffer.Encodingand call:Buffer.Write(...)NotifyBufferUpdated();
This is what both CommandLineSession and SecureShellSession do internally.
Implementing ITerminalSession manually
If you need maximum control, you can implement ITerminalSession directly:
- Maintain your own
VirtualTerminalBufferinstance. - Implement the
BufferUpdatedevent. - Implement
Resize/WriteInputto talk to your backend. - Use
VirtualTerminalBufferExtensionsfor advanced console features if you choose to use a Windows console internally.
Small features & UX details
- RedirectConsole (
ITerminalSession.RedirectConsole()):- Redirects
Console.Outto the terminal buffer viaBufferStreamWriter.
- Redirects
- Screen clear (
ITerminalSession.Clear()):- Sends ANSI sequence to clear the screen.
- Font zoom:
Ctrl + Mouse WheelchangesVirtualTerminalScreen.FontSize.
- Scroll to bottom button:
- A circular button appears when you are scrolled up; clicking it scrolls to the latest output.
- Auto-scrolling:
- When
AutoScrollingistrueand new output arrives, the view stays pinned to the bottom.
- When
Addons
The following projects live in this repository and extend the core VirtualTerminal functionality:
- VirtualTerminal.CommandLine – exposes
CommandLineSession(ConPTY-backed local shell). - VirtualTerminal.SecureShell – exposes
SecureShellSession(SSH.NET-backed remote shell).
Each addon has its own README with setup and usage instructions:
VirtualTerminal.CommandLine/README.mdVirtualTerminal.SecureShell/README.md
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-windows7.0 is compatible. |
-
net10.0-windows7.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on VirtualTerminal:
| Package | Downloads |
|---|---|
|
VirtualTerminal.CommandLine
Package Description |
|
|
VirtualTerminal.SecureShell
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.