class BBS::FrameBuffer
An off-screen grid of character cells, each holding one character and the SGR escape sequence that styles it. Widgets and windows draw into a frame buffer with 1-based coordinates; diff then turns the changes since the previous frame into the ANSI bytes to send to the terminal.
Its class methods are rubbs’ one set of tools for ANSI text: ::plain, ::visible_width, ::clip_ansi and ::wrap_ansi (the one word-wrap).
buf = BBS::FrameBuffer.new(80, 24) buf.box(x: 1, y: 1, width: 20, height: 5, title: 'Hello') session.write(buf.diff(BBS::FrameBuffer.new(80, 24)))
Anything drawn outside the grid is clipped.
Constants
- ANSI_TOKEN
-
One token of ANSI text: a CSI sequence (first group), a CSI sequence cut off by the end of the text (second group) or one character (third group).
- BLANK
-
The empty cell a cleared buffer is filled with: a space, no style.
- BOX_CHARS
-
Box-drawing characters per frame style (
:single,:double): the corners:tl,:tr,:bl,:brand the edges:h,:v. - CONTROL_CHAR
-
A character a cell never holds: a C0 control, DEL, a C1 control or a bidirectional control, any of which would move or restyle the caller’s screen behind the frame buffer’s back.
- Cell
-
One screen cell:
charis the character shown,sgrthe SGR escape sequence that styles it (nilfor the terminal default). - RESETS
-
The two SGR resets, the long form and the short one.
- SHORT_RESET
-
SGR reset with no parameter, the short form of
Ansi::RESET.
Attributes
The width (cols) and height (rows) of the grid in cells.
The width (cols) and height (rows) of the grid in cells.
Public Class Methods
Source
# File lib/bbs/frame_buffer.rb, line 47 def initialize(cols, rows) @cols = cols @rows = rows clear end
Creates a blank buffer of cols by rows cells with the cursor at the top left.
Public Instance Methods
Source
# File lib/bbs/frame_buffer.rb, line 54 def clear @cells = Array.new(@rows) { Array.new(@cols) { BLANK } } @cx = 0 @cy = 0 end
Fills every cell with BLANK and moves the cursor to the top left.
Source
# File lib/bbs/frame_buffer.rb, line 101 def diff(from) out = +'' state = { sgr: :none, at: nil } changes(from).each { |r, c, cell| out << diff_cell(r, c, cell, state) } out << Ansi::RESET if state[:sgr] != :none && state[:sgr] out end
Returns the ANSI byte String that turns a screen showing the frame buffer from into this one: only changed cells are written, with a cursor move only where the next changed cell is not adjacent. Each style change starts from a reset; the stream ends with a reset when any styled cell was written. Both buffers must have the same size.
Source
# File lib/bbs/frame_buffer.rb, line 63 def move(col, row) @cx = col - 1 @cy = row - 1 end
Moves the cursor to column col, row row (1-based). Off-screen positions are kept as they are: whatever is written there is clipped, not piled up in the last column or row.
Source
# File lib/bbs/frame_buffer.rb, line 72 def write(text, sgr: nil) self.class.text(text).each_char do |ch| break if @cy >= @rows put(ch, sgr) unless ch.match?(CONTROL_CHAR) end end
Writes text character by character from the cursor with style sgr, advancing the cursor. Control characters (CONTROL_CHAR, escape sequences among them) are dropped and take no cell; characters off the grid are dropped.
# File lib/bbs/frame_buffer.rb, line 85 def write_ansi(text, base_sgr: nil) sgr = base_sgr self.class.text(text).scan(ANSI_TOKEN) do |seq, _cut, char| if seq sgr = csi(seq, sgr, base_sgr) elsif char && !char.match?(CONTROL_CHAR) put(char, sgr) end end end
Writes text from the cursor, interpreting its SGR sequences: they add to base_sgr, the starting style, and a reset (\e[m, \e[0m) returns to it. \e[nC writes n styled spaces. Other escape sequences and control characters (CONTROL_CHAR) are skipped.
Protected Instance Methods
Source
# File lib/bbs/frame_buffer.rb, line 115 def cell_at(row, col) @cells[row][col] end
Returns the Cell at the 0-based row and col.
Source
# File lib/bbs/frame_buffer.rb, line 112 def visible? = @cy >= 0 && @cy < @rows && @cx >= 0 && @cx < @cols
Returns whether the cursor is inside the grid.