Skip to content

[ API reference ]

Frame

koto::Frame maintains the front and back cell buffers (m_front_buffer and m_back_buffer), the hit buffer (m_hit_buffer), harvested id bounding boxes (m_tag_bounds), and the differential SGR output stream (m_output). Header: koto/frame.hpp (included by koto/koto.hpp).

MemberSignatureDescriptionDefault
Frame(Terminal& t)Frame(Terminal& t)Constructs a frame sized to t.get_rows() by t.get_cols() attached to t.Front buffer Cell{U'\xFF'}, hit buffer TAG_NONE
Frame(int rows, int cols)Frame(int rows, int cols)Constructs a headless frame with m_term = nullptr (negative dimensions clamped to 0).Front buffer Cell{U'\xFF'}, hit buffer TAG_NONE
handle_resize()void handle_resize(int rows, int cols)Reallocates buffers to rows * cols, fills front buffer with U'\xFF', and clears tags and cursor cache.None
set_cell()void set_cell(int row, int col, Cell c, Id tag_id = Id::none())Writes c to back buffer with wide-glyph continuation management and stamps tag_id if not Id::none().tag_id = Id::none()
get_cell()Cell get_cell(int row, int col) constReturns the back-buffer cell at (row, col), or Cell{} if out of bounds.None
operator[]Cell& operator[](int row, int col) / const Cell& operator[](int row, int col) constC++23 2D subscript into m_back_buffer (returns dummy/empty Cell if out of bounds).None
fill(area, cell, tag_id)void fill(Area area, Cell c, Id tag_id = Id::none())Calls set_cell for every coordinate in area and unions area into m_tag_bounds.tag_id = Id::none()
fill(area, ch, fg, bg, tag_id)void fill(Area area, char32_t ch, uint32_t fg = COLOR_DEFAULT, uint32_t bg = COLOR_DEFAULT, Id tag_id = Id::none())Fills area with Cell{ch, fg, bg, 1, false} and optional tag_id.fg = COLOR_DEFAULT, bg = COLOR_DEFAULT, tag_id = Id::none()
clear_back_buffer()void clear_back_buffer()Fills m_back_buffer with Cell{}, m_hit_buffer with TAG_NONE, clears m_tag_bounds, and hides cursor.None
render()void render(bool flush = true)Diffs m_back_buffer against m_front_buffer, populates m_output wrapped in [?2026h..[?2026l, and flushes to STDOUT_FILENO if flush && m_term && m_term->is_tty().flush = true
set_cursor()void set_cursor(int row, int col)Marks the hardware cursor visible at (row, col) for the current frame.Cursor hidden
area()Area area() constReturns Area{0, 0, m_cols, m_rows}.None
rows() / height()int rows() const noexcept / int height() const noexceptReturns m_rows.None
cols() / width()int cols() const noexcept / int width() const noexceptReturns m_cols.None
tag_cell()void tag_cell(int row, int col, Id tag_id)Stamps tag_id at (row, col) in m_hit_buffer and unions Area{row, col, 1, 1} into m_tag_bounds.None
tag_area()void tag_area(Area area, Id tag_id)Stamps tag_id across area (clipped to frame) and unions area into m_tag_bounds.None
hit_at()Id hit_at(int row, int col) constReturns the Id at (row, col) in m_hit_buffer, or Id::none() if out of bounds.Id::none()
clear_hit_buffer()void clear_hit_buffer(Id default_tag = Id::none())Fills m_hit_buffer with default_tag.value() and clears m_tag_bounds.default_tag = Id::none()
bounds_of()std::optional<Area> bounds_of(Id tag_id) constReturns the harvested bounding Area for tag_id, or std::nullopt if not drawn/tagged.std::nullopt
all_tag_bounds()const std::unordered_map<uint32_t, Area>& all_tag_bounds() constReturns the full map of harvested Id values to bounding Areas.Empty map
to_plain_text()std::string to_plain_text() constConcatenates to_plain_text_row(r) for all rows separated by \n.None
to_plain_text_row()std::string to_plain_text_row(int r) constSerializes row r to UTF-8, skipping continuation cells and stripping trailing ASCII spaces."" if out of bounds
raw_output()std::string_view raw_output() const noexceptReturns the ANSI byte sequence generated by the most recent render() call.""
cursor_row() / cursor_col() / cursor_visible()int cursor_row() const / int cursor_col() const / bool cursor_visible() constInspects the cursor state set by set_cursor().-1, -1, false
#include <koto/koto.hpp>
#include <cassert>
using namespace koto;
int main()
{
Frame frame(4, 16);
frame.fill(Area(0, 0, 16, 4), U'.', Color::DarkGray, Color::Default, Id("canvas"));
frame[1, 2] = Cell{U'X', Color::Cyan, Color::Default};
assert(frame.hit_at(0, 0) == Id("canvas"));
assert(frame.bounds_of(Id("canvas")) == Area(0, 0, 16, 4));
assert((frame[1, 2].ch == U'X'));
frame.render(false);
assert(!frame.raw_output().empty());
return 0;
}
  • Move-only semantics: Frame deletes its copy constructor and copy assignment operator (Frame(const Frame&) = delete) to prevent accidental full-grid buffer copies. Pass Frame& by reference.
  • Direct operator[r, c] vs. set_cell(): Mutating frame[r, c] directly modifies m_back_buffer in place without running the wide-character continuation cleanup or hit-buffer tagging performed by set_cell(). Use set_cell() whenever writing width-2 glyphs or id tags.
  • Zero-output clean frames: If no non-continuation cell differs between m_front_buffer and m_back_buffer and the cursor state has not changed, render() returns immediately without emitting [?2026h or calling write_all().
  • Write failure recovery: If write_all(STDOUT_FILENO, m_output) fails during render(true), Frame fills m_front_buffer with Cell{U'ÿ'} so the next render pass forces a full screen repaint.