Skip to content

Rendering and painting

TerminalRenderer draws a session’s screen into a RenderTexture sized exactly columns * cellWidth by rows * cellHeight. Every quad lands on integer pixels and the glyph atlas is sampled texel for texel, which is what keeps the text free of subpixel shimmer. The texture is at session.Renderer.Target; use it anywhere a texture goes.

The renderer redraws only when something changed: the emulator’s content, the cursor or text blink, a theme change, or an overlay edit. It exposes ScrollOffset for scrollback, Selection for the selected region, and AfterRender and TargetChanged events.

Three sizes stay separate:

  • Grid is columns and rows: how much text fits. Set it on the session or with session.SetSize(cols, rows).
  • Render size is the cell pixel size, and so the texture resolution. It comes from the theme’s font size; session.SetFontSize(pixels) changes it and rebuilds the target. The grid is unaffected.
  • Display size is the surface the texture is shown on, scaled however that surface is scaled.

What to use when lists which control to set for each goal.

A Terminal Screen (3D) measures its surface and keeps the grid’s shape on it, centered in a border of the terminal background; turn off Preserve Aspect to stretch it instead. Its Resolution, in pixels across the screen, sets the session’s font size for you: the largest at which the columns and rows fit. With Fit Rows on, the rows follow the columns instead, so the grid fills the screen; change the column count with session.SetSize. Give one screen per session a resolution. A player measures only meshes imported with Read/Write on.

A uGUI screen has a scale mode for the flat case:

Scale mode The picture Use it for
Native Pixels One texture pixel per display pixel, centered The sharpest text, in a panel larger than the grid
Fit In Parent Scaled to the parent, the grid’s shape kept A fixed grid in a panel of any size
Stretch To Fill Stretched to the parent A screen driven by a viewport fitter
Manual Fills the RectTransform as your layout sets it A layout group or an animated panel

SnapToNativeSize() sizes the RectTransform to the texture once, for a Manual screen.

For a terminal that fills a window the way a real one does, add a Terminal Viewport Fitter: it derives the cell size from a point size scaled by the display DPI, then reflows the grid to fit the viewport at that size, so the text stays crisp and constant in physical size across displays. Cell Point Size is the cell height at 96 DPI, DPI Override stands in for the display’s DPI when it is above zero, and the grid limits bound the columns and rows the fitter picks.

Beyond text, you can composite textures into the terminal, for inline images or a full bitmap display:

var overlay = session.Renderer.AddOverlay(myTexture, new RectInt(x, y, wCells, hCells));
overlay.Tint = Color.white;
// ...
session.Renderer.RemoveOverlay(overlay);

An overlay draws over a cell rectangle after the text pass, and setting any of its properties redraws the grid.

The texture Set
Covers the cells Nothing more
Sits under text that labels it BehindText
Changes every frame: a video, a camera’s RenderTexture Live
Changes now and then Call session.Renderer.SetDirty() after each change
Hides for a while Visible = false

Overlays stay with the renderer through a resize or a font change.

Inside an interface, a TextureView places a texture by layout; see Textures. For freeform drawing, subscribe to AfterRender, which hands you the RenderTexture after each redraw with its bound state restored.

Bold and italic draw in the font’s own styles. Faint dims the color, underline and strikethrough draw as lines, inverse swaps the colors, blink blinks, and invisible text holds its cells blank. The cursor draws as a block, an underline, or a bar, whichever the host asked for, and hides while the view is scrolled into history.

The block element runes (U+2580 through U+259F, the halves, eighths, and quadrants) render as exact rectangles from a geometry table rather than font glyphs, so bars, meters, and fills tile edge to edge at any cell size, and adjoining runes meet without seams. The shade characters stay glyphs, since they are textures rather than shapes.

The emulator is a VT100, with the VT102 and xterm additions programs rely on. Every VT100 screen of vttest passes; Tools/vttest/run.sh in the development project replays them and checks each one, and Vt100Tests in the Core tests covers the same ground sequence by sequence. A program you write for it can rely on these:

Group Supported
Colors and attributes (SGR) Bold, faint, italic, underline, blink, inverse, invisible, strikethrough; 16 colors, 256 colors, and truecolor
Cursor movement CUU, CUD, CUF, CUB, CNL, CPL, CHA, HPA, VPA, CUP, HVP, save and restore
Cursor display Show and hide (DECTCEM), shape (DECSCUSR)
Erase In line, in display, scrollback, and characters (ECH)
Insert and delete Lines (IL, DL) and characters (ICH, DCH), insert mode (IRM), repeat (REP)
Scrolling Scroll regions (DECSTBM), SU, SD, index, reverse index, next line
Tabs Set, clear, forward, and backward
Line size Double width (DECDWL), double height (DECDHL), single (DECSWL)
Screens The alternate screen (47, 1047, 1049), reverse screen (DECSCNM), alignment pattern (DECALN), full reset (RIS), soft reset (DECSTR)
ANSI modes Keyboard lock (KAM), insert (IRM), local echo (SRM), new line (LNM)
DEC modes Cursor keys (DECCKM), VT52 (DECANM), 132 columns (DECCOLM), origin (DECOM), autowrap (DECAWM), keypad (DECKPAM, DECKPNM), bracketed paste; smooth scroll, auto repeat, interlace, and the printer modes are accepted
Keyboard Keypad application codes, LEDs (DECLL), answerback (ENQ)
VT52 mode Cursor steps and addressing, home, reverse line feed, erases, the graphics set, identify, keypad modes, and the return to ANSI
Mouse Click, button drag, and any motion tracking (1000, 1002, 1003), X10 and SGR (1006) encodings
Character sets ASCII, United Kingdom, and DEC line drawing through G0 and G1
Reports Device attributes (DA, DECID), device status and cursor position (DSR), terminal parameters (DECREQTPARM)
OSC The window title (0 and 2)

UTF-8 and wide East Asian characters are handled. A combining mark takes no cell: it joins its base letter where Unicode has one character for the pair, as e and an acute accent become é, and is dropped otherwise. Sixel and ReGIS graphics are parsed and dropped; the overlay API above covers the bitmap-display need.

DECCOLM clears the screen and homes the cursor. It also switches the emulator between 80 and 132 columns when AllowColumnChange is on, or when the host sets xterm’s mode 40; otherwise the width stays with the screen. ColumnsRequested reports the request either way. Answerback is what ENQ is answered with, and KeyboardLedsChanged reports the keyboard lights.

TerminalEmulator.Bell and TitleChanged report the bell and the title, for a sound or a label of your own.

Scripting is the API guide.