Rendering
Rendering architecture
Follow immutable snapshots through backend-neutral display lists, retained Skia rows, and native platform presentation.
Rendering architecture
The renderer is optional and keeps the headless core independent from UI and graphics libraries.
Terminal snapshots and revisioned events
|
v
XtermSharp.Rendering.Controllers
TerminalRenderController
XtermSharp.Rendering.Themes / Selection / Display
theme and selection resolution
immutable per-row display lists
|
v
XtermSharp.Rendering.Skia.Backends
font metrics and fallback
HarfBuzz shaping
retained SKPicture rows
shared rendering telemetry overlay
|
v
Platform adapters
XtermSharp.Avalonia.Controls.TerminalView
dispatcher and GPU-aware presentation
XtermSharp.Maui.Controls.TerminalView
SKGLView with SKCanvasView fallback, touch and soft keyboard
XtermSharp.WinForms.Controls.TerminalView
dispatcher and optional OpenTK GPU surface with software fallback
XtermSharp.Wpf.Controls.TerminalView
dependency properties and OpenTK/WPF GPU surface with bitmap fallback
XtermSharp.WinUI.Controls.TerminalView
dependency properties, CoreText and SKSwapChainPanel with bitmap fallback
shared responsibilities
frame scheduling, DPI-aware resize, keyboard, mouse, clipboard and IME
Threading and frames
Terminal events are dispatched by its ordered processor task. The rendering controller’s event
handlers only merge dirty rows and raise Invalidated; they do not request snapshots or draw.
The platform adapter schedules one PrepareFrameAsync operation at render priority, moves frame
preparation to the worker pool, obtains an immutable viewport snapshot and atomically publishes a
TerminalRenderFrame. Later revisions are coalesced, unchanged TerminalLineSnapshot objects
reuse cached display rows, and frames with empty pixel damage do not invalidate the platform
visual. The Avalonia adapter raises direct-property changes only when values change; the Windows
Forms adapter publishes one ViewportChanged event for scroll, extent or grid-size changes; the
WPF adapter updates read-only dependency properties; and the WinUI adapter publishes get-only
dependency-property wrappers for those viewport values.
GPU acceleration
TerminalView presents its retained Skia pictures directly through Avalonia’s current Skia API
lease. When Avalonia supplies a GRContext, picture replay, text, fills and decorations execute on
the GPU-backed surface. If the host selected a software renderer, or the graphics device is
temporarily unavailable, the same display list is rendered by Skia in software without changing
terminal behavior or requiring a second graphics context.
GPU selection belongs to the Avalonia application host. Desktop applications using
UsePlatformDetect() use Avalonia’s platform renderer selection and normally prefer Metal,
Direct3D, Vulkan or OpenGL where supported. TerminalView.ActiveRenderMode reports Unknown,
Software or Gpu for the most recently presented frame, and TerminalView.IsGpuAccelerated
provides a bindable boolean convenience property. Both reset when the control detaches or changes
terminal sessions. Setting RequestedRenderMode to Software rasterizes the retained display list
into a CPU-backed Skia surface before compositing it through the current Avalonia lease. Gpu
requests direct replay but falls back to software when the host does not supply a GRContext.
The MAUI adapter presents through SKGLView and replays the retained pictures on the platform GPU
surface. It keeps the existing SKCanvasView as a software fallback and switches to it when the
GPU handler or frame callback reports an error. Runtime mode changes swap the visible surface while
keeping the previous software frame visible until the first requested GPU frame succeeds.
The Windows Forms adapter can present through a modern OpenTK GLControl when
RequestedRenderMode is Gpu or Auto. The default remains Software, and the compatibility
property EnableGpuRendering maps true to Gpu and false to Software. This opt-in avoids
blocking hosts that cannot create a GLFW context during control construction. The normal path wraps the current framebuffer in an Skia
GRBackendRenderTarget; a 32-bit premultiplied WinForms bitmap remains the fallback.
The WPF adapter uses OpenTK.GLWpfControl as a child visual and wraps its current framebuffer in
the same Skia GPU surface. Its per-monitor-DPI WriteableBitmap remains available when WPF cannot
create or retain the OpenGL context. WPF device-independent pointer coordinates and padding remain
aligned with the controller’s logical viewport.
The WinUI adapter uses SkiaSharp’s SKSwapChainPanel (ANGLE-backed) and keeps the existing
DPI-scaled premultiplied BGRA WriteableBitmap path as a fallback. XamlRoot.RasterizationScale
keeps logical pointer coordinates, terminal padding and physical pixels aligned across monitor
changes. All five adapters report the mode of the most recently presented frame through
ActiveRenderMode and IsGpuAccelerated.
All five adapters expose the same SkiaRenderModePreference through RequestedRenderMode.
Auto and Gpu attempt the platform GPU surface and preserve software fallback, while Software
immediately disables or hides the GPU surface and redraws the current retained frame on the CPU.
Switching back to Auto or Gpu clears a previous GPU-failure latch and retries without replacing
the assigned Terminal or rebuilding its parser/buffer state.
Every platform TerminalView.ShowRenderingDebugOverlay enables a top-right Skia overlay with the
active GPU/software renderer, rolling presentation FPS and average, maximum and minimum frame
intervals. Sampling and drawing live in XtermSharp.Rendering.Skia, while each adapter only maps
the switch into its native property system and requests a repaint. Sampling resets after a long
idle gap so terminal inactivity is not reported as a single slow frame. The overlay is disabled by
default and can be enabled from XAML, for example in Avalonia:
<Window xmlns:xterm="clr-namespace:XtermSharp.Avalonia.Controls;assembly=XtermSharp.Avalonia">
<xterm:TerminalView ShowRenderingDebugOverlay="True" />
</Window>
Synchronized output mode holds an already published frame for at most one second. Leaving the mode flushes immediately; the timeout prevents a malformed stream from freezing the visible terminal.
Backend contract
ITerminalRenderBackend<TSurface> is experimental. It measures the configured font and executes a
backend-neutral TerminalDisplayList on its native surface. Compatible fixed-width ASCII cells and
adjacent background cells are batched into runs before reaching a backend. Display commands contain
rectangles, text clusters, colors and line styles but no Skia or Avalonia types. Future GDI,
Direct2D or other backends can therefore reuse terminal color, attribute, selection and cursor
semantics.
The Skia backend uses the direct Skia text path for safe printable ASCII runs and HarfBuzz for text
that requires shaping. It selects fallback typefaces per cluster, clips glyphs to their allocated
cells and records each changed display row as an SKPicture during worker-side frame preparation.
The platform paint path normally only replays retained pictures. Font metrics, fonts, fill paints
and current row pictures are cached with bounded stale-picture eviction. Theme, font or DPI changes
invalidate the relevant display rows; different presenters are expected to preserve terminal
semantics, not pixel-identical rasterization. Its optional debug overlay is drawn after retained
terminal rows and therefore remains outside terminal snapshots, selection and damage state. The
WinUI adapter performs a full bitmap presentation while telemetry is enabled so every updated
overlay pixel reaches its WriteableBitmap despite row-based terminal damage tracking.
The MAUI adapter uses the same SkiaTerminalRenderBackend and retained row pictures as Avalonia.
It presents them through SKGLView first and SKCanvasView on fallback, scales logical terminal
coordinates to the platform surface and maps Skia touch coordinates back to MAUI logical units.
Applications call UseXtermSharpMaui() while building their MauiApp to register the SkiaSharp
view handler and supported platform input shims. TerminalView owns the shared committed-text,
touch-scroll, selection, clipboard, key and wheel semantics; target-specific shims only translate native events
into those APIs. Windows hardware keyboard and mouse-wheel routing therefore stays in the MAUI
component instead of requiring an application-side adapter, while touch and committed-input fixes
remain shared by Android, iOS, Mac Catalyst and Windows.
Platform ownership
TerminalView.Terminal is externally assigned in all platform packages. Controls subscribe,
render and forward input but never dispose the terminal. Applications remain responsible for
PTY/session wiring. When a control detaches or receives another terminal, it cancels pending frame
work and link queries, releases Skia resources and unsubscribes from the old instance.