class

Termisu::Terminal

Inherits Termisu::Renderer / Reference / Object

High-level terminal interface combining I/O backend, Terminfo, and cell buffer.

Provides a complete terminal UI API including:

  • Cell-based rendering with double buffering
  • Cursor movement and visibility
  • Colors and text attributes
  • Alternate screen mode

Example:

terminal = Termisu::Terminal.new
terminal.enable_raw_mode
terminal.enter_alternate_screen

terminal.set_cell(10, 5, 'H', fg: Color.red)
terminal.set_cell(11, 5, 'i', fg: Color.green)
terminal.set_cursor(12, 5)
terminal.render

terminal.close

Constants

BRACKETED_PASTE_DISABLE = "\e[?2004l"
BRACKETED_PASTE_ENABLE = "\e[?2004h"

Bracketed paste escape sequences (DEC private mode 2004).

While enabled the terminal wraps pasted text in \e[200~ ... \e[201~ and hands the bytes between them over verbatim, without the CR/LF translation it applies to typed input. Terminals that don't implement it ignore the sequences and keep sending pastes as plain input.

BSU = "\e[?2026h"

Synchronized update escape sequences. Prevents screen tearing by buffering output between BSU and ESU. Supported by: Windows Terminal, Kitty, iTerm2, Wezterm, Alacritty 0.13+, foot, mintty, Ghostty. Unsupported terminals simply ignore these sequences.

ESU = "\e[?2026l"
KITTY_KEYBOARD_DISABLE = "\e[<u"
KITTY_KEYBOARD_ENABLE = "\e[>17u"

Enhanced keyboard protocol escape sequences. These protocols disambiguate keys that normally send the same bytes (e.g., Tab vs Ctrl+I, Enter vs Ctrl+M).

Kitty keyboard protocol (most comprehensive): https://sw.kovidgoyal.net/kitty/keyboard-protocol/ Flags: 1=disambiguate, 2=report_event_types, 4=report_alternate_keys 8=report_all_keys, 16=report_text

Log = Termisu::Logs::Terminal
MODIFY_OTHER_KEYS_DISABLE = "\e[>4;0m"
MODIFY_OTHER_KEYS_ENABLE = "\e[>4;2m"

modifyOtherKeys (xterm, widely supported): Mode 2 reports modified keys as CSI 27 ; modifier ; keycode ~

MOUSE_DISABLE_NORMAL = "\e[?1000l"
MOUSE_DISABLE_SGR = "\e[?1006l"
MOUSE_ENABLE_NORMAL = "\e[?1000h"

Mouse protocol escape sequences. Using CSI ? sequences for xterm-compatible mouse tracking.

MOUSE_ENABLE_SGR = "\e[?1006h"

Constructors

new(backend : Terminal::Backend = Terminal::Backend.new, terminfo : Terminfo = Terminfo.new, *, sync_updates : Bool = true)

Creates a new terminal.

Parameters:

  • backend - Terminal::Backend instance for I/O operations (default: Terminal::Backend.new)
  • terminfo - Terminfo instance for capability strings (default: Terminfo.new)
  • sync_updates - Enable DEC mode 2026 synchronized updates (default: true)
Source

Instance methods

alternate_screen?

Returns whether alternate screen mode is active.

Source
apply_sgr(fg : Color, bg : Color, attr : Attribute, old_fg : Color | Nil, old_bg : Color | Nil, old_attr : Attribute) : Nil

Emits a full style transition as one combined SGR sequence (\e[p1;p2;...m) instead of one write per granular change.

The transition is computed against the terminal's own cached style, not the caller's old_ view: the cache also tracks direct API calls (foreground=, enable_bold, ...), so it is at least as current — mirroring how the granular setters have always consulted it.

Attribute removal uses the ECMA-48 selective off codes (22/23/24/25/27/28/29) so colors survive attribute drops without the sgr0-plus-recolor round trip. SGR 22 clears both bold and dim, so whichever of the two the target style retains is re-emitted after it.

Source
background=(color : Color)

Sets the background color with full ANSI-8, ANSI-256, and RGB support.

Caches the color to avoid redundant escape sequences when called repeatedly with the same color.

Source
bracketed_paste?

Returns whether bracketed paste mode is currently enabled.

Source
clear_cells

Clears the cell buffer (fills with default cells).

Call render() to display changes on screen.

Source
clear_screen

Clears the screen.

Writes the clear screen escape sequence immediately and flushes. Also resets cached render state since screen content is cleared.

Source
close

Closes the terminal and underlying backend.

Source
current_mode

Returns the current terminal mode, or nil if not yet set.

Delegates to underlying Backend instance.

Source
cursor
Source
cursor_shape=(shape : Cursor::Shape)
Source
disable_bracketed_paste

Disables bracketed paste mode.

Pasted text goes back to arriving as plain input with no boundary markers.

Source
disable_enhanced_keyboard

Disables enhanced keyboard protocol.

Returns to legacy keyboard mode where Tab/Ctrl+I, Enter/Ctrl+M, etc. are indistinguishable.

Source
disable_mouse

Disables mouse input tracking.

Disables both SGR and normal mouse protocols.

Source
disable_raw_mode

Disables raw mode on the terminal.

Source
enable_bold

Enables bold text.

Caches attribute state to avoid redundant escape sequences.

Source
enable_bracketed_paste

Enables bracketed paste mode.

The terminal then wraps pasted text in \e[200~ ... \e[201~, which the input parser surfaces as Input::Key::PasteStart / Input::Key::PasteEnd, and stops translating line endings inside the paste.

Without it a paste is indistinguishable from typing: a pasted CRLF arrives as the same bytes Enter produces, and some terminals map the LF to a second CR so one pasted line break looks exactly like two deliberate Enters. No amount of content inspection can separate those, which is why the boundary markers are the only correct fix.

The bytes between the markers are still reported exactly as they arrive (a pasted CR is Key::Enter with char == '\r'): the markers say where the paste is, they do not normalize what is inside it.

Example:

terminal.enable_bracketed_paste
# Pastes are now delimited by Key::PasteStart / Key::PasteEnd
terminal.disable_bracketed_paste # When done
Source
enable_cursive

Enables italic/cursive text.

Caches attribute state to avoid redundant escape sequences.

Source
enable_dim

Enables dim/faint text.

Caches attribute state to avoid redundant escape sequences.

Source
enable_enhanced_keyboard

Enables enhanced keyboard protocol for disambiguated key reporting.

This enables the Kitty keyboard protocol (if supported) and falls back to modifyOtherKeys. Enhanced mode allows distinguishing between keys that normally send the same bytes:

  • Tab vs Ctrl+I
  • Enter vs Ctrl+M
  • Backspace vs Ctrl+H

Not all terminals support these protocols. Unsupported terminals will simply ignore the escape sequences and continue with legacy behavior.

Example:

terminal.enable_enhanced_keyboard
# Now Ctrl+I and Tab are distinguishable
terminal.disable_enhanced_keyboard # When done
Source
enable_hidden

Enables hidden/invisible text.

Caches attribute state to avoid redundant escape sequences.

Source
enable_mouse

Enables mouse input tracking.

Enables SGR extended mouse protocol (mode 1006) for better coordinate support and unambiguous button detection. Falls back to normal mode (1000) on older terminals that don't support SGR.

Example:

terminal.enable_mouse
# Now mouse events will be reported via poll_event
terminal.disable_mouse # When done
Source
enable_raw_mode

Enables raw mode on the terminal.

Source
enable_reverse

Enables reverse video.

Caches attribute state to avoid redundant escape sequences.

Source
enable_strikethrough

Enables strikethrough text.

Caches attribute state to avoid redundant escape sequences.

Source
enable_underline

Enables underline.

Caches attribute state to avoid redundant escape sequences.

Source
enhanced_keyboard?

Returns whether enhanced keyboard protocol is enabled.

Source
enter_alternate_screen

Enters alternate screen mode.

Switches to alternate screen buffer, clears the screen, enters keypad mode, and hides cursor. Also resets cached render state since we're entering a fresh screen.

Source
exit_alternate_screen

Exits alternate screen mode.

Shows cursor, exits keypad mode, and returns to main screen buffer. Also resets cached render state since we're returning to the main screen which may have different state.

Source
flush

Delegates flush to backend.

Source
foreground=(color : Color)

Sets the foreground color with full ANSI-8, ANSI-256, and RGB support.

Caches the color to avoid redundant escape sequences when called repeatedly with the same color.

Source
get_cell(x : Int32, y : Int32) : Cell | Nil

Gets a cell at the specified position from the buffer.

Returns nil if coordinates are out of bounds.

Source
hide_cursor

Writes hide cursor escape sequence.

Source
infd

Returns the input file descriptor for Reader.

Source
invalidate_buffer

Invalidates the buffer, forcing a full re-render on next render().

Call this after the terminal screen has been cleared externally. Unlike sync(), this doesn't render immediately - it marks the buffer so the next render() call will redraw everything.

Source
mouse_enabled?

Returns whether mouse tracking is currently enabled.

Source
move_cursor(x : Int32 = @cursor.x, y : Int32 = @cursor.y)

Moves cursor to the specified position (writes escape sequence).

Source
outfd

Returns the output file descriptor.

Source
query_size

Queries the terminal size directly from the backend.

Always performs the live TIOCGWINSZ ioctl, bypassing the cache. Intended for resize detection, which must observe external size changes.

Source
raw_mode?

Returns whether raw mode is currently enabled.

Source
render

Renders cell buffer changes to the screen.

Only cells that have changed since the last render are redrawn (diff-based). This is more efficient than full redraws for partial updates.

When sync_updates is enabled, wraps the render in DEC mode 2026 sequences (BSU/ESU) to prevent screen tearing during rapid updates.

Source
reset_attributes

Resets all attributes to default.

Also clears cached color/attribute state since reset affects all styling.

Source
reset_render_state

Resets the cached render state.

Call this when the terminal state becomes unknown (e.g., after external programs have modified the terminal, or after errors). This forces the next color/attribute calls to emit escape sequences even if the cached values match.

The following operations automatically reset render state:

  • enter_alternate_screen
  • exit_alternate_screen
  • clear_screen
  • reset_attributes
Source
resize(width : Int32, height : Int32)

Resizes the buffer to new dimensions.

Preserves existing content where possible. Also refreshes the cached size so subsequent size calls reflect the new dimensions.

Source
set_cell(*args, **options)

Sets a cell at the specified position in the buffer.

Parameters:

  • x: Column position (0-based)
  • y: Row position (0-based)
  • grapheme: Character to display
  • fg: Foreground color (default: white)
  • bg: Background color (default: default terminal color)
  • attr: Text attributes (default: None)

Returns false if coordinates are out of bounds. Call render() to display changes on screen.

Source
set_cell(*args, **options, &)

Sets a cell at the specified position in the buffer.

Parameters:

  • x: Column position (0-based)
  • y: Row position (0-based)
  • grapheme: Character to display
  • fg: Foreground color (default: white)
  • bg: Background color (default: default terminal color)
  • attr: Text attributes (default: None)

Returns false if coordinates are out of bounds. Call render() to display changes on screen.

Source
set_mode(mode : Terminal::Mode)

Sets terminal to specific mode using Terminal::Mode flags.

Updates raw_mode_enabled tracking based on whether mode is raw. Does not handle screen or cursor transitions - use with_mode for that.

Parameters:

  • mode: Terminal::Mode flags specifying desired behavior

Example:

terminal.set_mode(Terminal::Mode.cooked)
terminal.set_mode(Terminal::Mode.raw)

ameba:disable Naming/AccessorMethodName

Source
show_cursor

Writes show cursor escape sequence.

Source
size

Returns the terminal size as {width, height}.

The value is cached to avoid an ioctl syscall on every call (write and move_cursor query size on hot rendering paths). The cache is refreshed by resize() and invalidated after mode switches. Use query_size to force a live query.

Source
sync

Forces a full redraw of all cells.

Useful after terminal resize or screen corruption.

When sync_updates is enabled, wraps the sync in DEC mode 2026 sequences (BSU/ESU) to prevent screen tearing during the full redraw.

Source
sync_updates=(sync_updates : Bool)

Sets whether synchronized updates are enabled.

Can be toggled at runtime. Set to false for debugging or compatibility with terminals that misbehave with sync sequences.

Source
sync_updates?

Returns whether synchronized updates are enabled.

When enabled, render operations are wrapped in BSU/ESU sequences to prevent screen tearing. Enabled by default.

Source
title
Source
title=(title : String)
Source
with_cbreak_mode(preserve_screen : Bool = true, &)

Executes a block with cbreak mode.

Cbreak mode provides character-by-character input with echo and signal handling. Useful for interactive prompts where you want immediate response but still show typed characters.

By default, preserves alternate screen since cbreak is typically used within a TUI context.

Example:

terminal.with_cbreak_mode do
  print &quot;Press any key: &quot;
  key = STDIN.read_char
end
Source
with_cooked_mode(preserve_screen : Bool = false, &)

Executes a block with cooked (shell-like) mode.

Cooked mode enables canonical input, echo, and signal handling - ideal for shell-out operations where the subprocess needs full terminal control.

By default, exits alternate screen to show the normal terminal, then re-enters alternate screen after the block.

Example:

terminal.with_cooked_mode do
  system(&quot;vim file.txt&quot;)
end
Source
with_mode(mode : Terminal::Mode, preserve_screen : Bool = false, &)

Executes a block with specific terminal mode, restoring previous mode after.

This is the recommended way to temporarily switch modes for operations like shell-out or password input. Handles:

  • Mode switching via Backend
  • Alternate screen exit/entry based on preserve_screen parameter
  • Cursor visibility (shown for user-interactive modes)

Parameters:

  • mode: Terminal::Mode to use within the block
  • preserve_screen: If false (default) and mode is canonical, exits alternate screen during block. If true, stays in alternate screen.

Example:

terminal.with_mode(Terminal::Mode.cooked) do
  system(&quot;vim file.txt&quot;)
end
# Previous mode and screen state restored
Source
with_password_mode(preserve_screen : Bool = true, &)

Executes a block with password input mode.

Password mode enables canonical (line-buffered) input with signal handling but disables echo. Perfect for secure password entry.

By default, preserves alternate screen since password prompts often appear within a TUI context.

Example:

terminal.with_password_mode do
  print &quot;Password: &quot;
  password = gets
end
Source
with_raw_mode

Executes a block with raw mode enabled, ensuring cleanup.

Source
write(data : String, columns_advanced = 0)

Write data to the terminal. Use columns_advanced to specify how much this will move the cursor to the right. If this would move the cursor beyond the terminal's width, it will wrap into the next line

Source
write(data : Bytes, columns_advanced = 0)

Byte overload of write for pre-composed escape sequences and batch content; streams to the backend without materializing a String.

Source

Nested types