class

TermBuf::Buffer

Inherits Reference < Object

Stability: stable — changes only in a major release.

The in-memory terminal screen.

The buffer holds one grid: back, what the application has drawn. What a terminal is believed to be showing lives in a Sink, one per output, along with the painter and encoder that put it there. Attaching a second sink is what lets the same buffer drive two displays that paint at different times and know different things about what they can do.

A single dirty-flag scheme would be smaller, but it leaves nothing to diff against after a forced repaint or a resize, and nothing for the scroll detector to verify a shift against.

Not fibre-safe, and deliberately so: one fibre owns the buffer and every mutation reaches it as a command.

Constructors

new(width : Int32, height : Int32)

A blank buffer width cells across and height rows down, with no sink attached and no device anywhere in sight.

Source

Instance methods

attach(sink : Sink) : Nil

Starts painting this buffer to sink. Called by Sink itself, so an application builds a sink rather than attaching one.

Source
back

What the application has drawn.

Source
blit(source : Buffer, x : Int32, y : Int32, from : Rect | Nil = nil) : Nil

Copies cells out of source, its top left landing at (x, y), taking from of it or all of it. Whatever falls outside this buffer is cut.

For compositing an off-screen panel: draw into a Buffer of its own through a BufferSurface, then blit it into place. Styles and clusters are interned per buffer, so the ids a source cell carries mean nothing here and are translated on the way in. Stored widths are copied rather than remeasured, so a panel keeps the layout it was drawn with even if the two buffers measure clusters differently.

A cluster only partly inside the copied rectangle arrives as a blank, since part of one cannot be drawn.

Source
bounds

The rectangle covering every cell.

Source
clear(style : Style = Style::DEFAULT, blend : Blend | Nil = nil) : Nil

Blanks every cell.

Source
clusters

The interned multi code point clusters, for cells a Char cannot hold.

Source
damage

What no attached sink has painted yet. Each sink keeps its own record as well, since they paint at different moments; this one is cleared once every one of them has caught up.

Source
detach(sink : Sink) : Nil

Stops painting this buffer to sink.

Source
dirty?

Whether anything is left for a sink to paint.

Source
fill(rect : Rect, char : Char = ' ', style : Style = Style::DEFAULT, blend : Blend | Nil = nil) : Nil

Sets every cell of rect to char.

With a blend, each cell gets the style the blend answers for it rather than style, which is what a wash over what is already painted wants: a faded rectangle, or a tint that leaves the colours under it showing. The rectangle is read before any of it is written, so a blend sees the cells as they were rather than as this fill is leaving them.

Source
height

Rows down.

Source
hit(x : Int32, y : Int32) : Hit | Nil

What sits at (x, y), or nil when that is off the grid.

A continuation resolves to the cluster it belongs to, so the hit's x can be to the left of the column asked about and Hit#lead says whether it moved. Read from the back grid: what the application has drawn, which is what it wants to reason about, rather than what the terminal is believed to be showing.

hit = buffer.hit column, row
puts hit.text if hit

Coordinates are 0-based buffer cells. An SGR mouse report is 1-based and carries its own columns; converting one is the mouse decoder's job, not this method's. Turning a hit into an index within a line editor's text belongs to whoever owns that text, which is the widget shard.

Source
invalidate

Marks the whole screen dirty and has every sink forget what its terminal was showing, so the next paint rewrites every cell.

Source
policy

How clusters are measured. Set by the driver from what the terminal said when it was asked, because how many cells an emoji takes is a question about the terminal rather than about Unicode. See Unicode::WidthPolicy.

Changing it does not remeasure what is already written: cells carry the width they were placed with. Invalidate and redraw after changing it.

Source
policy=(policy : Unicode::WidthPolicy)

How clusters are measured. Set by the driver from what the terminal said when it was asked, because how many cells an emoji takes is a question about the terminal rather than about Unicode. See Unicode::WidthPolicy.

Changing it does not remeasure what is already written: cells carry the width they were placed with. Invalidate and redraw after changing it.

Source
region(x : Int32, y : Int32, width : Int32, height : Int32, scrollback : Int32 = 0) : Region

Registers a region. Regions are for scrolling and scrollback; they do not clip writes, and the buffer does not stop them overlapping.

Source
region(bounds : Rect, scrollback : Int32 = 0) : Region

Declares a region over bounds, keeping up to scrollback rows of what scrolls off the top.

Source
regions

Regions declared with #region, in the order they were made.

Source
resize(width : Int32, height : Int32, style : Style = Style::DEFAULT) : Nil

Resizes the back grid and every attached sink, keeping whatever content still fits anchored at the top left. Leaves everything dirty and drops any scroll hints, since the next paint has to redraw the screen outright.

Source
scroll(rect : Rect, lines : Int32, style : Style = Style::DEFAULT) : Nil

Scrolls rect by lines rows, positive moving content up. Records a hint for the painter.

Source
scroll_hints

Scrolls no attached sink has consumed yet, oldest first.

Source
scroll_hints_since(serial : Int64) : Array(ScrollHint)

The hints recorded after serial, oldest first.

Source
scroll_region(region : Region, lines : Int32, style : Style = Style::DEFAULT) : Nil

Scrolls a region, keeping the rows that leave the top if the region has scrollback capacity.

Source
scroll_serial

The serial given to the last scroll recorded. A sink that has read up to this has nothing left to catch up on.

Source
sinks

The outputs painting this buffer.

Source
styles

The interned styles both grids refer to by id.

Source
to_text

The back grid as text, one line per row, for specs and debugging.

Source
width

Columns across.

Source
write(x : Int32, y : Int32, text : String, style : Style = Style::DEFAULT, blend : Blend | Nil = nil) : Int32

Writes text starting at (x, y), one grapheme cluster per cell, stopping at the right edge of the row. Returns the columns consumed.

Zero width clusters are skipped: a combining mark with no base character in front of it has nothing to attach to, and a control character is never stored in the buffer.

With a blend, each cell gets the style the blend answers for it from what is already there and style — text over something already painted, a label across a progress bar, without the caller working out where the bar's colours change. Style::KEEP_BACKGROUND is that blend. A cluster covering several cells takes the style its first one lands on.

Source
write_char(x : Int32, y : Int32, char : Char, style : Style = Style::DEFAULT, blend : Blend | Nil = nil) : Int32

Writes a single character at (x, y). Returns the columns it consumed: zero if it is zero width, if it is a control character, or if it takes more columns than are left before the right edge.

With a blend, the style placed is what it answers for the cell rather than style itself. See #write.

Source

Nested types