Pictures

Brief

Programs that draw pictures in a terminal show them in a Terminal Delight pane. Run kitten icat photo.png, chafa, or an agent that renders a chart with the Kitty graphics protocol, and the picture lands in the cells the program asked for, scrolls with the text around it, and bends with the glass like everything else in the tube.

Pictures stay while you are looking at their pane. When you switch to another tab, the pane forgets them, and coming back shows the text alone. They are not saved with the session and do not come back when a window reattaches.

Nothing to switch on. Sixel and iTerm2 pictures are not drawn yet.

The Technical version lists what is supported, what a program is told when it asks, and how a picture reaches a window attached to a session host.

Technical

Pictures

What draws

The terminal core, rio-vt, receives and stores Kitty graphics; the pane draws what the core holds over the grid.1

Part of the protocolIn a pane
Formats: PNG (f=100), RGB (f=24), RGBA (f=32)Drawn
Sending: inline data (t=d), a file (t=f), a temporary file (t=t), shared memory (t=s)All four
A file, temporary file or shared memory that also says more chunks follow (m=1), as mpv's shared-memory mode sends every frameNot drawn yet: the picture waits for a chunk that never comes (issue 833)
One picture command, as a single escape sequenceUp to 4 MiB. The protocol's chunks are 4 KB each, so only a sender that skips chunking meets the limit
Placement in cells (c=, r=), cropping (x= y= w= h=), offsets within a cell (X=, Y=)Drawn
Stacking (z=)Negative draws under the text, zero and above over it
Deleting (a=d)Honoured
Unicode placeholders (U=1, U+10EEEE cells)Not drawn yet: the cells show as text
Sixel, iTerm2 inline imagesNot drawn yet

A picture is not drawn while the rows are read upside down (the anchor-to-top read) or in text crawl, where it has no single cell to sit in. It is still held, and it returns with the plain read.

A temporary file (t=t) is deleted by kitty's rule: only when its name contains tty-graphics-protocol and it sits in /tmp, /dev/shm or $TMPDIR. Terminal Delight deletes it itself after the core has read it, because the core's own check looks for that marker anywhere in the path. Shared memory (t=s) reaches the core only when it names a regular file in /dev/shm.

When a picture goes

program sends kitten icat, chafa core keeps it placed in cells pane draws it while on screen tab hidden both forgotten switching tabs is the only thing that takes a picture away
Figure 1. A picture lasts as long as your attention on its pane. When the pane's tab is hidden, the pane drops the texture and the core deletes the image, so the pixels are gone from memory as well as from the screen. A program that wants the picture back draws it again.

No snapshot carries a picture, so a window that attaches to a pane shows its text and none of the pictures drawn before it arrived. TD_PICTUREDEBUG=1 makes a window print one line to its log each time a hidden pane lets pictures go, for checking this by script.

What a program is told

Picture programs ask the terminal what it is before they draw. Terminal Delight answers as itself.2

QuestionAnswer
Kitty graphics query (a=q)OK, from the core
Primary device attributes (CSI c)?62;22c: a VT220 with ANSI colour, and no claim to sixel or clipboard access
XTVERSION (CSI > 0 q)terminal-delight 0.3.0
Cell size in pixels (CSI 16 t)The cell the pane told the pseudoterminal, in device pixels
Kitty keyboard protocol (CSI ? u)No answer: keys are sent in the legacy encodings

kitten icat decides from the first two answers together. Before the core swap it waited ten seconds in every pane and then gave up, because the old device-attributes answer was too short for its detector.3

Panes on a session host

A pane on a session host is parsed twice: by the host, which answers the program, and by your window, which draws. A picture sent as a temporary file or as shared memory is deleted by the first terminal that reads it, and the host reads first, so for those two methods the host rewrites the picture into its copy of the stream as inline data before the original is deleted. Inline data and plain files reach the window as the program sent them.4

A window attached to a session host started before the core swap still draws pictures sent as inline data or a file, because the bytes pass through the old host to the window's own core. That host answers the questions in the table above the old way, so kitten icat still waits there until the host is replaced.

Try it

printf '\033_Ga=T,f=24,s=2,v=1,c=8,r=2;/wAAAP8A\033\\\n'

Two pixels, red and green, stretched over eight cells by two. The line after it starts below the picture.

What would prove this page wrong

A picture that stays on screen after its tab has been hidden and shown again; a picture drawn over the wrong cells after the pane scrolls; a temporary-file picture that shows in a window-owned pane and not in a hosted one; or kitten icat pausing before it draws in a pane whose host was started from this build.

Sources

  1. The core boundary and the picture verbs: app/src/vt/mod.rs and app/src/vt/rio.rs; drawing: picture_elements in app/src/pane.rs; forgetting: Workspace::forget_pictures_out_of_sight in app/src/main.rs.
  2. The answers and their tests: app/src/vt/rio.rs, as_terminal_delight and the tests beside it.
  3. The ten-second wait, measured at 10,076 ms and 10,044 ms in a hidden window: issue 750; the detector's length check is in kitty's kittens/icat/detect.go.
  4. The rewrite: app/src/picturewire.rs; the test that fails without it: a_picture_sent_as_a_temporary_file_reaches_the_attached_window in app/src/host.rs.