class TermBuf::Painter

Overview

Works out the operations that bring the terminal from what it is showing to what the application has drawn.

Two passes. The first looks for whole bands of rows that merely moved, and asks the terminal to scroll them rather than sending them again. The second walks the rows that are still different and emits the changed runs.

The painter mutates the buffer's front grid as it extracts scrolls, so that the second pass diffs against the post-scroll state. That means the bytes it returns have to actually reach the terminal; a caller that throws them away must call Buffer#invalidate before painting again.

Defined in:

termbuf/core/painter.cr

Constant Summary

ERASE_THRESHOLD = 4

A trailing run of blanks longer than this is cheaper to erase than to write spaces over.

MOVE_COST = 4

What moving the cursor a short distance within a row costs. A gap of unchanged cells cheaper than this to reprint is not worth skipping.

ROW_COST = 12

Redrawing a row costs at least this much, so a scroll starts paying for itself once it saves this many rows.

SCROLL_OVERHEAD = 24

Roughly what a scroll costs in bytes: margins, the scroll itself, and releasing the margins again.

STYLE_COST = 6

What changing style costs, near enough.

Constructors

Instance Method Summary

Constructor Detail

def self.new(capabilities : Capabilities) #

[View source]

Instance Method Detail

def capabilities : Capabilities #

What the terminal can do, which decides whether a scroll or an erase is available at all.


[View source]
def capabilities=(capabilities : Capabilities) : Capabilities #

Changes what the painter may use. The next paint should be forced, since what is on the terminal was drawn under the old mask.


[View source]
def hardware_cursor : Tuple(Int32, Int32) | Nil #

Where to leave the terminal's own cursor when the frame ends, or nil to leave it hidden.

A frame that changes no cells is still worth sending when this has moved: the cursor is what tells someone where they are typing.


[View source]
def hardware_cursor=(hardware_cursor : Tuple(Int32, Int32) | Nil) #

Where to leave the terminal's own cursor when the frame ends, or nil to leave it hidden.

A frame that changes no cells is still worth sending when this has moved: the cursor is what tells someone where they are typing.


[View source]
def paint(buffer : Buffer) : Array(Op) #

The operations that bring the terminal up to date. Empty when there is nothing to do. The caller writes them out and then calls Buffer#commit_paint.


[View source]
def reset_state : Nil #

Forgets what the terminal was last told about its own cursor, so the next frame says it again. For a forced repaint, where nothing on the screen can be taken on trust.


[View source]