Terminal

The terminal classes provide the runtime connection between your rendered content and the real console. Terminal manages the application lifecycle, refresh strategy, cursor control, direct text output, and the platform backend that ultimately emits text and control sequences.

This section focuses on application-level terminal control. For the shared streaming output API used by both Terminal and CursorBuffer, see Cursor Output. For custom backend implementations and capability fallback behavior, see Backend.

Usage

Preparing and Rendering a Screen

Terminal is the high-level entry point for full-screen terminal applications. It owns the platform backend, manages the visible screen state, and renders ReadableBuffer objects with clipping and optional crop marks.

auto terminal = Terminal{Size{90, 28}};
terminal.initializeScreen();
terminal.setRefreshMode(Terminal::RefreshMode::Overwrite);

auto buffer = Buffer{terminal.size()};
buffer.fill(Char{" ", Color{fg::Default, bg::Black}});
buffer.drawText(
    "Frame and buffer rendering",
    Rectangle{2, 2, buffer.size().width() - 4, 5},
    Alignment::Center,
    Color{fg::BrightWhite, bg::Blue});

auto settings = UpdateSettings{};
settings.setSwitchToAlternateBuffer(false);

terminal.updateScreen(buffer, settings);
terminal.flush();
terminal.restoreScreen();

Terminal::size() returns the drawable size used by updateScreen(). By default, this includes a one-column and one-row compatibility margin so full-screen buffers work out of the box.

Disable that margin with setSafeMarginEnabled(false) if you want to use the full detected terminal size and rely purely on cursor-based updates.

For short-lived tools that should leave their output in the normal terminal history, disable alternate-screen switching via UpdateSettings as shown above.

Render Loop for Interactive Applications

The following example shows a minimal render loop for an interactive terminal application.

struct MyApp {
    void renderFrame() {
        _terminal.testScreenSize();
        _buffer.resize(_terminal.size());
        _buffer.fill(Char{" ", bg::Black});

        // Draw the current frame into the buffer.
        _terminal.updateScreen(_buffer, _updateSettings);
    }

    void handleKey(Key key) {
        if (key == Key{Key::Character, U'q'}) {
            _quitRequested = true;
        }
    }

    void run() {
        _terminal.initializeScreen();
        _terminal.input().setMode(Input::Mode::Key);

        _updateSettings.setMinimumSize(Size{40, 20});
        _updateSettings.setMinimumSizeMessage(String("Too Small!"));

        while (!_quitRequested) {
            const auto key = _terminal.input().read(std::chrono::milliseconds{90});
            if (key.valid()) {
                handleKey(key);
            }
            renderFrame();
        }

        _terminal.restoreScreen();
    }

    Terminal _terminal;
    Buffer _buffer;
    UpdateSettings _updateSettings;
    bool _quitRequested;
};

Call Terminal::initializeScreen() once when your application starts, and Terminal::restoreScreen() once just before it exits.

After initialization, use Terminal::isInteractive() to detect whether a real terminal is attached. This allows you to switch to a plain-text fallback when output is redirected.

If your application is interrupted (for example via Ctrl+C), the library restores the terminal state automatically before returning control to the shell.

Create exactly one Terminal instance for the lifetime of your application. While multiple instances are possible, they share the same underlying backend, so a single instance is simpler and safer.

For interactive programs, switch the input mode to Input::Mode::Key. This hides the cursor and lets your application react to individual key presses via input().read(). The timeout also serves as a natural frame rate control.

Keep a persistent Buffer instance, resize it to the current terminal size, and reuse it for each frame. After drawing into the buffer, call updateScreen() to present the new frame.

By default, rendering is optimized to update only the parts of the screen that actually changed. This makes frequent refresh rates practical, as only modified regions are written to the terminal.

Direct Writes, Line Buffering, and Refresh Modes

For simpler tools and status output, you can use Terminal without a full-screen buffer. print(), printLine(), and write() send text directly to the terminal while still applying color handling.

These direct-write functions are part of the shared CursorWriter interface. This page focuses on how they behave on a real terminal; the reusable streaming API itself is documented on Cursor Output.

terminal.setOutputMode(Terminal::OutputMode::FullControl);
terminal.printLine(fg::BrightGreen, "Service started");
terminal.print("Current mode: ", fg::BrightYellow, "interactive");
terminal.writeLineBreak();
terminal.flush();

Line buffering keeps partial lines in memory until a newline is written or flush() is called. This keeps incremental output clean and avoids fragmented lines.

For full-screen applications:

  • RefreshMode::Clear clears the entire screen before each frame.

  • RefreshMode::Overwrite moves to the home position and works with the back buffer for efficient updates.

Controlling Screen Update Settings

UpdateSettings controls how Terminal::updateScreen() behaves when content does not fit the terminal or the terminal is too small.

auto settings = UpdateSettings{};
settings.setMinimumSize(Size{60, 18});
settings.setMinimumSizeBackground(Char{" ", Color{fg::Inherited, bg::Red}});
settings.setMinimumSizeMessage(String{"Please enlarge the terminal"});
settings.setShowCropMarks(true);

terminal.updateScreen(buffer, settings);

Use this to define minimum sizes, customize fallback backgrounds, and control crop indicators when the layout cannot be rendered as intended.

Output Modes

Terminal supports two output modes:

  • Terminal::OutputMode::FullControl (default) enables colored output, cursor control, refresh handling, back-buffer updates, and terminal-size detection.

  • Terminal::OutputMode::Text keeps the terminal in plain-text mode. In this mode, write() and print() still work, but cursor control, refresh handling, screen clearing, size detection, and the back buffer are disabled.

The terminal always owns a backend internally, but most applications can treat it as an implementation detail and rely on the high-level API.

If you need to inject a custom backend or understand capability fallback behavior, continue with Backend.

Interface

class UpdateSettings

Settings controlling how Terminal::updateScreen() renders a buffer.

Public Functions

UpdateSettings() = default

Create default screen update settings.

Size minimumSize() const noexcept

Get the minimum terminal size required for rendering the buffer.

Returns:

The minimum supported terminal size.

void setMinimumSize(Size minimumSize) noexcept

Set the minimum terminal size required for rendering the buffer.

Parameters:

minimumSize – The minimum supported terminal size.

const Char &minimumSizeBackground() const noexcept

Get the background character used if the terminal is too small.

void setMinimumSizeBackground(Char character) noexcept

Set the background fill character when the terminal is too small.

Parameters:

character – The background character

const String &minimumSizeMessage() const noexcept

Get the message displayed if the terminal size is too small.

void setMinimumSizeMessage(String message) noexcept

Set the message displayed if the terminal is too small.

Parameters:

message – The displayed message.

bool showCropMarks() const noexcept

Check if crop marks are enabled.

void setShowCropMarks(bool showCropMarks) noexcept

Enable or disable crop marks.

Parameters:

showCropMarkstrue to render crop marks for truncated content.

const Char &cropMarkRight() const noexcept

Get the mark rendered when content is cropped on the right.

void setCropMarkRight(Char cropMarkRight) noexcept

Set the mark rendered if the content is cropped on the right.

Parameters:

cropMarkRight – The right crop mark.

const Char &cropMarkBottomRight() const noexcept

Get the mark rendered in the bottom right corner when content is cropped.

void setCropMarkBottomRight(Char cropMarkBottomRight) noexcept

Set the mark in the bottom right corner if content is cropped on the bottom and right.

Parameters:

cropMarkBottomRight – The bottom-right crop mark.

const Char &cropMarkBottom() const noexcept

Get the mark rendered when content is cropped at the bottom.

void setCropMarkBottom(Char cropMarkBottom) noexcept

Set the mark rendered if the content is cropped at the bottom.

Parameters:

cropMarkBottom – The bottom crop mark.

bool switchToAlternateBuffer() const noexcept

Test if the update shall switch to the alternate screen buffer.

void setSwitchToAlternateBuffer(bool switchToAlternateBuffer) noexcept

Enable or disable switching to the alternate screen buffer.

Parameters:

switchToAlternateBuffertrue to switch to the alternate screen buffer.

void applyTo(BufferViewBase &view) const noexcept

Apply these settings to a BufferView.

void setMinimumSizeMark(Char minimumSizeMark) noexcept

Set the minimum-size background character through the legacy name.

Deprecated:

Use setMinimumSizeBackground() instead.

Parameters:

minimumSizeMark – The background character shown when the terminal is too small.

const Char &minimumSizeMark() const noexcept

Get the minimum-size background character through the legacy name.

Deprecated:

Use minimumSizeBackground() instead.

Returns:

The background character shown when the terminal is too small.

UpdateSettings(Size minimumSize, Char minimumSizeBackground, bool showCropMarks, Char cropMarkRight, Char cropMarkBottom) noexcept

Construct update settings using the deprecated aggregate-style compatibility constructor.

Deprecated:

Construct UpdateSettings{} and configure it with setters instead.

Parameters:
  • minimumSize – The minimum terminal size required for normal rendering.

  • minimumSizeBackground – The fill character for the size-too-small background.

  • showCropMarkstrue to show crop marks for truncated content.

  • cropMarkRight – The crop mark to draw at the right edge.

  • cropMarkBottom – The crop mark to draw at the bottom edge.

Public Static Functions

static const UpdateSettings &defaultSettings() noexcept

Shared default value.

class Terminal : public erbsland::cterm::CursorWriter

High-level terminal interface for screen control, color output, and key input.

Public Types

enum class RefreshMode : uint8_t

Screen clearing strategy used between rendered frames.

Values:

enumerator Keep

Do not emit cursor or clear-screen control sequences automatically.

enumerator Clear

Clear the full screen before rendering the next frame.

enumerator Overwrite

Move the cursor to the top-left corner before rendering the next frame.

enum class OutputMode : uint8_t

The output mode for the terminal.

Values:

enumerator Text

Only output plain text with no colors and no cursor control.

enumerator FullControl

Full control over the terminal, including colors and cursor control.

using ScreenSizeChangedCallback = std::function<void(Size)>

Callback invoked when the drawable terminal size changes or is initialized.

Public Functions

explicit Terminal()

Create a new terminal instance with default values.

explicit Terminal(TerminalFlags flags = {})

Create a new terminal instance.

Parameters:

flags – The terminal flags to use.

explicit Terminal(Size size = {80, 25}, TerminalFlags flags = {})

Create a new terminal instance.

The size is automatically bounded to the minimum and maximum supported sizes.

Parameters:
  • size – The fallback terminal size used when automatic detection is unavailable.

  • flags – The terminal flags to use.

explicit Terminal(BackendPtr backend, Size size = {80, 25})

Create a new terminal instance with a custom backend.

The size is automatically bounded to the minimum and maximum supported sizes.

Parameters:
  • backend – The backend to use for the terminal.

  • size – The fallback terminal size used when automatic detection is unavailable.

inline virtual Size size() const noexcept override

Get the size of the screen/writing area.

virtual Color color() const noexcept override

Get the current color.

Returns:

The currently tracked terminal color state.

virtual CharAttributes charAttributes() const noexcept override

Get the current character attributes.

Returns:

The currently tracked character attribute state.

virtual void setColor(Color color) noexcept override

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 override

Set all character attributes.

Unspecified attributes are treated as disabled.

Parameters:

attributes – The new character attributes.

virtual void setForeground(Foreground color) noexcept override

Set the foreground color.

Note

Inherited colors are converted to Default colors.

Parameters:

color – The new foreground color.

virtual void setBackground(Background color) noexcept override

Set the background color.

Note

Inherited colors are converted to Default colors.

Parameters:

color – The new background color.

virtual CharAttributes supportedCharAttributes() const noexcept override

Get the character attributes supported by this writer.

Returns:

The supported character attributes.

virtual void moveLeft(Coordinate count) noexcept override

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.

virtual void moveRight(Coordinate count) noexcept override

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.

virtual void moveUp(Coordinate count) noexcept override

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.

virtual void moveDown(Coordinate count) noexcept override

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.

virtual void moveTo(Position pos) noexcept override

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.

virtual void moveHome() noexcept override

Moves the cursor to the home position.

virtual void moveCursor(Position posOrDelta, MoveMode mode) noexcept override

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.

virtual void setAutoWrap(bool enabled) noexcept override

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.

virtual void setCursorVisible(bool visible) noexcept override

Make the cursor visible/invisible.

Not all implementations support changing the cursor visibility.

Parameters:

visible – Whether to make the cursor visible or invisible.

virtual void write(const Char &character) noexcept override

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 override

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.

virtual void write(const ReadableBuffer &buffer) noexcept override

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 override

Write a line-break.

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

void setSize(Size size) noexcept

Modify the size of the terminal.

The size is automatically bounded to the minimum and maximum supported sizes. If size detection is enabled, the terminal size will be automatically detected and updated.

Parameters:

size – The new terminal size.

inline RefreshMode refreshMode() const noexcept

Get the refresh mode.

inline void setRefreshMode(const RefreshMode mode) noexcept

Set the refresh mode.

Parameters:

mode – The screen refresh strategy to use.

inline OutputMode outputMode() const noexcept

Get the current output mode for the terminal.

void setOutputMode(OutputMode outputMode) noexcept

Set the output mode.

Switching to OutputMode::Text disables size detection, refresh modes, and back-buffer updates.

Parameters:

outputMode – The output mode to set.

bool sizeDetectionEnabled() const noexcept

Check whether dynamic terminal size detection is enabled.

void setSizeDetectionEnabled(bool enabled) noexcept

Set if dynamic terminal size detection is enabled.

Can only be enabled while the output mode is OutputMode::FullControl.

Parameters:

enabledtrue to enable automatic size detection.

bool lineBufferEnabled() const noexcept

Check whether line buffering is enabled for incremental writes.

Returns:

true if text output is collected until a newline or flush().

void setLineBufferEnabled(bool enabled) noexcept

Enable or disable line buffering for incremental writes.

When enabled, output is accumulated until a newline or flush() is reached. Line buffering can only be enabled if the backend supports both color and cursor ANSI codes.

Parameters:

enabledtrue to enable buffered writes.

bool safeMarginEnabled() const noexcept

Check whether the compatibility safe margin is enabled.

Returns:

true if one column and one row are reserved from the detected terminal size.

void setSafeMarginEnabled(bool enabled) noexcept

Enable or disable the compatibility safe margin.

When enabled, the reported drawable size is reduced by one column and one row. Disable this only when the terminal should use its full detected size and newline-free screen updates.

Parameters:

enabledtrue to reserve one column and one row from the terminal size.

bool backBufferEnabled() const noexcept

Check whether the optional back buffer is enabled for smart overwrite updates.

Returns:

true if updateScreen() keeps the previous rendered frame for diff-based updates.

void setBackBufferEnabled(bool enabled) noexcept

Enable or disable the optional back buffer used by smart overwrite updates.

Enabling the back buffer forces the next updateScreen() call to redraw the full frame once. Can only be enabled while the output mode is OutputMode::FullControl.

Parameters:

enabledtrue to enable the back buffer.

void setBackend(BackendPtr backend) noexcept

Set a custom backend for the terminal.

Parameters:

backend – The backend to use for the terminal. If nullptr is passed, the default backend is restored.

Input &input() noexcept

Access the input interface.

Returns:

The platform-specific input backend owned by this terminal.

void initializeScreen() noexcept

Initialize the console once before the application starts.

Applies platform-specific setup, optionally clears the screen, and tests for the initial screen size. Also hides the cursor by default, as it is usually only made visible when the user makes input. Call this at the start of your application.

bool isInteractive() const noexcept

Check whether an interactive terminal is attached to the process.

Call this after initializeScreen() to see if screen-size detection and terminal control features are active.

Returns:

true if the backend detected an interactive terminal.

void testScreenSize() noexcept

Detect terminal resize changes.

After calling this method, size() returns a safe size for the terminal.

void restoreScreen() noexcept

Restore terminal settings when the application is quit.

Call this at the end of your application. This should restore the terminal to its original state, including cursor visibility and any other settings that were modified during initialization.

virtual void clearScreen() noexcept override

Clears the screen.

In OutputMode::Text, this method has no effect. If you need the screen cleared immediately, call flush() after this method.

bool isAlternateScreenActive() const noexcept

Test if the alternate screen is active.

This is no terminal detection, it just returns the internal state.

void setAlternateScreen(bool enabled) noexcept

Activate or deactivate the alternate screen.

If activated or deactivated, the buffer is immediately flushed to the terminal.

void updateScreen(const ReadableBuffer &buffer, const UpdateSettings &settings = {}) noexcept

Render a buffer onto the terminal.

The buffer is clipped to the drawable area reported by size() and optionally annotated with crop marks. If the terminal is smaller than the configured minimum size, only the minimum-size marker is rendered. When switchToAlternateBuffer is true and the alternate screen is not active, this call first switches to the alternate screen and then renders the buffer.

Parameters:
  • buffer – The buffer to render.

  • settings – Additional rendering settings for crop marks and minimum terminal size handling.

void flush() noexcept

Flush the all buffer immediately to the terminal.

inline void lineBreak() noexcept

Write a terminal line break.

Deprecated:

Use writeLineBreak() instead.

inline bool colorEnabled() const noexcept

Test if non-text output mode is active.

Deprecated:

Use outputMode() instead.

Returns:

true if the terminal is not in OutputMode::Text.

void setColorEnabled(bool enabled) noexcept

Enable or disable text-only output mode through the legacy boolean API.

Deprecated:

Use setOutputMode() instead.

Parameters:

enabledtrue to allow color/control output, false for plain text mode.

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.

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 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.

using erbsland::cterm::TerminalPtr = std::shared_ptr<Terminal>

Shared pointer to a terminal instance.

enum class erbsland::cterm::TerminalFlag : uint8_t

A terminal flag.

Values:

enumerator NoSignalHandling

Disables signal handling to restore the screen when the application is terminated.

If this flag is set, you must ensure that the restoreScreen() method is called when the application is terminated by a signal. Otherwise, the terminal will not be restored properly.

class TerminalFlags

A set of terminal flags.

Terminal flags control the behavior of the built-in terminal backend. These flags can only be set at construction time and cannot be modified after that.

Public Types

using Mask = uint8_t

Unsigned storage type used for the combined flag bits.

using Enum = TerminalFlag

The enum type combined by this flag set.

Public Functions

template<typename ...tFlags>
inline constexpr TerminalFlags(tFlags... flags)

Create a combined set of flags.

inline TerminalFlags operator|(const TerminalFlag flag) const

Combine this flag set with one additional flag.

Parameters:

flag – The flag to add.

Returns:

The combined flag set.

inline bool has(const TerminalFlag flag) const noexcept

Test if a flag is set.

inline void set(const TerminalFlag flag, const bool enabled = true)

Set a flag.

inline void clear(const TerminalFlag flag)

Clear a flag.

Friends

inline friend TerminalFlags operator|(const TerminalFlag flag, const TerminalFlags flags)

Combine one flag with an existing flag set.

Parameters:
  • flag – The flag to add.

  • flags – The existing flag set.

Returns:

The combined flag set.

inline friend TerminalFlags operator|(const TerminalFlags flags1, const TerminalFlags flags2)

Combine two flag sets.

Parameters:
  • flags1 – The first flag set.

  • flags2 – The second flag set.

Returns:

The combined flag set.