.. Copyright (c) 2026 Tobias Erbsland - Erbsland DEV. https://erbsland.dev SPDX-License-Identifier: Apache-2.0 ************* Cursor Output ************* Cursor output is the shared streaming API used by :cpp:any:`Terminal ` and :cpp:any:`CursorBuffer `. :cpp:any:`CursorWriter ` combines cursor movement, active color state, active character attributes, plain text output, and paragraph printing into a single, consistent interface. :cpp:any:`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 :cpp:any:`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 :doc:`terminal`. Usage ===== Printing Mixed Arguments ------------------------ :cpp:any:`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. .. code-block:: cpp 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 :cpp:any:`CursorWriter::setDefaultColor() `. Use :cpp:any:`CharAttributes::reset() ` when you want to explicitly turn all attributes back off. Use :cpp:any:`CursorWriter::style() ` and :cpp:any:`CursorWriter::setStyle() ` when you want to read or update the combined style in one step. Moving the Cursor Explicitly ---------------------------- Use :cpp:any:`MoveMode ` when you need to control whether a movement is relative to the current cursor position or absolute within the writable area. .. code-block:: cpp 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 :cpp:any:`Terminal ` and :cpp:any:`CursorBuffer ` implement the same paragraph-printing workflow via :cpp:any:`CursorWriter `. This allows you to reuse one paragraph configuration for direct terminal output and for buffered, scrollable content. .. code-block:: cpp 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 :cpp:any:`Terminal `, output is sent immediately to the active backend. When writing to a :cpp:any:`CursorBuffer `, the output becomes part of the buffer content and can later be rendered using :cpp:any:`Terminal::updateScreen() ` or displayed through a buffer view. Choosing Between Terminal and CursorBuffer ------------------------------------------ Use :cpp:any:`Terminal ` when output should be written directly to the real console. Use :cpp:any:`CursorBuffer ` when you want to keep the same streaming API, but retain the output for scrolling, clipping, copying, or re-rendering. In practice, :cpp:any:`CursorWriter ` acts as the bridge between immediate output and retained, in-memory rendering. Interface ========= .. doxygenclass:: erbsland::cterm::CursorWriter :members: .. doxygenenum:: erbsland::cterm::MoveMode