Cursor Output

Cursor output is the shared streaming API used by Terminal and CursorBuffer.

CursorWriter combines cursor movement, active color state, active character attributes, plain text output, and paragraph printing into a single, consistent interface. MoveMode distinguishes between absolute and relative cursor movement.

Use this API when you want to print mixed text and color arguments, write terminal-style output into a CursorBuffer, or precisely control cursor positioning.

This page documents the shared output model only. Screen lifecycle, full-screen rendering, refresh settings, and backend ownership remain on Terminal.

Usage

Printing Mixed Arguments

CursorWriter is designed for fluent, terminal-style output.

The print() and printLine() functions accept a mix of colors, character styles, character attributes, terminal strings, and plain text in a single call. As arguments are processed, the active color and attribute state is updated automatically.

terminal.printLine(
    fg::BrightGreen,
    "Service started at ",
    fg::BrightWhite,
    "09:42");

terminal.print(
    bg::BrightBlack,
    fg::BrightYellow,
    "[Q] ",
    fg::BrightWhite,
    "quit");

auto emphasis = CharAttributes{};
emphasis.setBold(true);
emphasis.setUnderline(true);
const auto headingStyle = CharStyle{Color{fg::BrightWhite, bg::Inherited}, emphasis};

terminal.printLine(
    headingStyle,
    "Important",
    CharAttributes::reset(),
    fg::BrightBlack,
    " uses both bold and underline.");

Because the active color and attribute state is part of the writer, subsequent output continues using the most recently selected values until you change them again or call CursorWriter::setDefaultColor(). Use CharAttributes::reset() when you want to explicitly turn all attributes back off. Use CursorWriter::style() and CursorWriter::setStyle() when you want to read or update the combined style in one step.

Moving the Cursor Explicitly

Use MoveMode when you need to control whether a movement is relative to the current cursor position or absolute within the writable area.

cursorBuffer.moveCursor(Position{10, 3}, MoveMode::Absolute);
cursorBuffer.print("Header");

cursorBuffer.moveCursor(Position{0, 2}, MoveMode::Relative);
cursorBuffer.printLine("First entry");

All convenience functions—such as moveLeft(), moveRight(), moveUp(), moveDown(), moveTo(), and moveHome()—are built on top of this same distinction.

VT100-Compatible Line Wrapping

The cursor writer follows a wrapping model compatible with traditional VT100-style terminals. This behavior is slightly different from a naïve “wrap immediately at the edge” approach.

When writing characters near the right edge of the buffer:

  • A character written into the last column does not immediately move the cursor to the next line.

  • Instead, a pending wrap is recorded internally.

  • The actual line break only happens when the next character is written.

This means:

  • The last column can be filled without triggering an immediate line break.

  • The next character causes the cursor to move to the beginning of the next line (if auto-wrap is enabled).

Wide characters (display width 2) are handled carefully:

  • If a wide character would not fully fit at the end of the line:

    • With auto-wrap enabled, a line break occurs first.

    • With auto-wrap disabled, the character is ignored.

Additional details to keep in mind:

  • If auto-wrap is disabled, writing at the right edge keeps the cursor pinned to the last column.

  • Explicit cursor movement (e.g. moveCursor()) clears any pending wrap.

  • Line breaks triggered by wrapping use the configured overflow mode (wrap, shift, or expand).

This behavior ensures compatibility with terminal output expectations and avoids subtle off-by-one rendering issues when mixing manual positioning and streaming text.

Printing Wrapped Paragraphs

Both Terminal and CursorBuffer implement the same paragraph-printing workflow via CursorWriter.

This allows you to reuse one paragraph configuration for direct terminal output and for buffered, scrollable content.

auto options = ParagraphOptions::defaultOptions();
options.setMaximumLineWraps(2);
options.setParagraphSpacing(ParagraphSpacing::DoubleLine);

logBuffer.printParagraph(
    "The same paragraph API works for live terminal output and for buffered history views.",
    options);

When writing to a Terminal, output is sent immediately to the active backend.

When writing to a CursorBuffer, the output becomes part of the buffer content and can later be rendered using Terminal::updateScreen() or displayed through a buffer view.

Choosing Between Terminal and CursorBuffer

Use Terminal when output should be written directly to the real console.

Use CursorBuffer when you want to keep the same streaming API, but retain the output for scrolling, clipping, copying, or re-rendering.

In practice, CursorWriter acts as the bridge between immediate output and retained, in-memory rendering.

Interface

class CursorWriter

The shared interface for buffers/terminals that support cursor-based output.

Subclassed by erbsland::cterm::CursorBuffer, erbsland::cterm::Terminal

Public Functions

virtual Color color() const noexcept = 0

Get the current color.

Returns:

The currently tracked terminal color state.

virtual CharAttributes charAttributes() const noexcept = 0

Get the current character attributes.

Returns:

The currently tracked character attribute state.

inline CharStyle style() const noexcept

Get the current combined text style.

Returns:

The currently tracked terminal style.

virtual void setColor(Color color) noexcept = 0

Set foreground and background color.

Note

Inherited colors are converted to Default colors.

Parameters:

color – The new combined color.

virtual void setCharAttributes(CharAttributes attributes) noexcept = 0

Set all character attributes.

Unspecified attributes are treated as disabled.

Parameters:

attributes – The new character attributes.

inline void setStyle(const CharStyle style) noexcept

Set the full terminal style.

Inherited colors are converted to defaults and unspecified attributes are treated as disabled.

Parameters:

style – The new combined style.

inline void setColor(const Foreground foregroundColor, const Background backgroundColor) noexcept

Set foreground and background color.

Note

Inherited colors are converted to Default colors.

Parameters:
  • foregroundColor – The new foreground color.

  • backgroundColor – The new background color.

virtual void setForeground(Foreground color) noexcept = 0

Set the foreground color.

Note

Inherited colors are converted to Default colors.

Parameters:

color – The new foreground color.

virtual void setBackground(Background color) noexcept = 0

Set the background color.

Note

Inherited colors are converted to Default colors.

Parameters:

color – The new background color.

inline void setDefaultColor() noexcept

Set the terminal default foreground and background colors.

virtual CharAttributes supportedCharAttributes() const noexcept = 0

Get the character attributes supported by this writer.

Returns:

The supported character attributes.

inline void setBold(const bool enabled) noexcept

Enable or disable the bold attribute.

Parameters:

enabledtrue to enable bold, false to disable it.

inline void setDim(const bool enabled) noexcept

Enable or disable the dim attribute.

Parameters:

enabledtrue to enable dim, false to disable it.

inline void setItalic(const bool enabled) noexcept

Enable or disable the italic attribute.

Parameters:

enabledtrue to enable italic, false to disable it.

inline void setUnderline(const bool enabled) noexcept

Enable or disable the underline attribute.

Parameters:

enabledtrue to enable underline, false to disable it.

inline void setBlink(const bool enabled) noexcept

Enable or disable the blink attribute.

Parameters:

enabledtrue to enable blink, false to disable it.

inline void setReverse(const bool enabled) noexcept

Enable or disable the reverse attribute.

Parameters:

enabledtrue to enable reverse, false to disable it.

inline void setHidden(const bool enabled) noexcept

Enable or disable the hidden attribute.

Parameters:

enabledtrue to enable hidden, false to disable it.

inline void setStrikethrough(const bool enabled) noexcept

Enable or disable the strikethrough attribute.

Parameters:

enabledtrue to enable strikethrough, false to disable it.

inline virtual void moveLeft(const Coordinate count) noexcept

Move the cursor to the left.

If the resulting position is out of bounds, the result is undefined.

Parameters:

count – The number of terminal cells to move.

inline virtual void moveRight(const Coordinate count) noexcept

Move the cursor to the right.

If the resulting position is out of bounds, the result is undefined.

Parameters:

count – The number of terminal cells to move.

inline virtual void moveUp(const Coordinate count) noexcept

Move the cursor up.

If the resulting position is out of bounds, the result is undefined.

Parameters:

count – The number of terminal cells to move.

inline virtual void moveDown(const Coordinate count) noexcept

Move the cursor down.

If the resulting position is out of bounds, the result is undefined.

Parameters:

count – The number of terminal cells to move.

inline virtual void moveTo(const Position pos) noexcept

Move the cursor to the given position.

If the resulting position is out of bounds, the result is undefined.

Parameters:

pos – The position to move the cursor to.

inline virtual void moveHome() noexcept

Moves the cursor to the home position.

virtual void moveCursor(Position posOrDelta, MoveMode mode) noexcept = 0

Move the cursor absolute or relative.

If the resulting position is out of bounds, the result is undefined.

Parameters:
  • posOrDelta – The absolute position or delta for the move.

  • mode – The move mode, either absolute or relative.

inline virtual std::optional<Position> cursorPosition() noexcept

Try to get the current cursor position.

Not all implementations support retrieving the cursor position.

Returns:

The current cursor position, or std::nullopt if it cannot be determined.

virtual void setAutoWrap(bool enabled) noexcept = 0

Enabled/disable auto-wrap.

Auto wrap controls if the cursor automatically wraps to the next line when reaching the right margin. This is a feature that can be enabled or disabled. Do not confuse this with line wrapping, which is a different feature.

Parameters:

enabled – Whether to enable or disable auto-wrap.

inline virtual void setCursorVisible(bool visible) noexcept

Make the cursor visible/invisible.

Not all implementations support changing the cursor visibility.

Parameters:

visible – Whether to make the cursor visible or invisible.

virtual Size size() const noexcept = 0

Get the size of the screen/writing area.

virtual void clearScreen() noexcept = 0

Clears the screen/writing area.

virtual void write(const Char &character) noexcept = 0

Write a character at the current cursor position.

Inherited color components resolve against the currently active color. Overwrites the character under the cursor.

Parameters:

character – The character to write.

virtual void write(const String &str) noexcept = 0

Write a string at the current cursor position.

Inherited color components in each character resolve against the currently active color. Overwrites the characters under the cursor.

Parameters:

str – The string to write.

inline void write(std::string_view text) noexcept

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

virtual void write(const ReadableBuffer &buffer) noexcept = 0

Write a buffer at the current cursor position.

This will not perform any additional formatting, clipping, or processing. Each line of the buffer will be written, and a line-break added after each line.

Parameters:

buffer – The buffer to write.

virtual void writeLineBreak() noexcept = 0

Write a line-break.

This will move the cursor to the beginning of the next line.

template<PrintableArg... Args>
inline void print(Args... args) noexcept

Print elements at the cursor position.

Parameters:

args – The arguments to print.

template<PrintableArg... Args>
inline void printLine(Args... args) noexcept

Print elements at the cursor position and add a line break.

Parameters:

args – The arguments to print.

inline auto printParagraph(const String &paragraph, const ParagraphOptions &options = ParagraphOptions::defaultOptions()) noexcept -> int

Print a word-wrapped paragraph at the cursor position.

Parameters:
  • paragraph – The paragraph text to write. Can use line breaks and tabs, see documentation.

  • options – The paragraph options to use.

Returns:

The number of lines written (including empty lines).

inline auto printParagraph(const std::string &paragraph, const ParagraphOptions &options = ParagraphOptions::defaultOptions()) noexcept -> int

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

enum class erbsland::cterm::MoveMode

The cursor move mode.

Values:

enumerator Absolute

Absolute move.

enumerator Relative

Relative move.