class

Termisu

Inherits Reference / Object

Main Termisu class - Terminal User Interface library.

Provides a clean, minimal API for terminal manipulation by delegating all logic to specialized components: Terminal and Reader.

The async event system uses Event::Loop to multiplex multiple event sources:

  • Input events (keyboard, mouse)
  • Resize events (terminal size changes)
  • Timer events (optional, for animation/game loops)

Example:

termisu = Termisu.new

# Set cells with colors and attributes
termisu.set_cell(10, 5, 'H', fg: Color.red, bg: Color.black, attr: Attribute::Bold)
termisu.set_cell(11, 5, 'i', fg: Color.green)
termisu.set_cell(12, 5, '!', fg: Color.blue)

# Render applies changes (diff-based rendering)
termisu.render

termisu.close

Constants

Log = ::Log.for("termisu")

Main log instance for Termisu library

VERSION = "0.6.2"
VERSION_MAJOR = 0
VERSION_MINOR = 6
VERSION_PATCH = 2
VERSION_STATE = nil

Constructors

new(*, sync_updates : Bool = true)

Initializes Termisu with all required components.

Sets up terminal I/O, rendering, input reader, and async event system. Automatically enables raw mode and enters alternate screen.

The Event::Loop is started with Input and Resize sources by default. Timer source is optional and can be enabled with enable_timer.

Parameters:

  • sync_updates - Enable DEC mode 2026 synchronized updates (default: true). When enabled, render operations are wrapped in BSU/ESU sequences to prevent screen tearing. Unsupported terminals ignore these sequences.
Source

Instance methods

add_event_source(source : Event::Source) : self

Adds a custom event source to the event loop.

Custom sources must extend Event::Source and implement the abstract interface: #start(channel), #stop, #running?, and #name.

If the event loop is already running, the source is started immediately. Events from the source will appear in poll_event alongside built-in events.

Parameters:

  • source: An Event::Source implementation

Returns self for method chaining.

Example:

class NetworkSource < Termisu::Event::Source
  def start(output)
    # Start listening for network events
  end

  def stop
    # Stop listening
  end

  def running? : Bool
    @running
  end

  def name : String
    "network"
  end
end

termisu.add_event_source(NetworkSource.new)
Source
alternate_screen?(*args, **options)

Returns true if alternate screen mode is active.

Source
alternate_screen?(*args, **options, &)

Returns true if alternate screen mode is active.

Source
bracketed_paste?(*args, **options)

Enables bracketed paste mode.

Pastes are then delimited by Key::PasteStart and Key::PasteEnd events, and the terminal delivers the bytes between them verbatim instead of translating line endings the way it does for typed input.

This is the only reliable way to tell a paste from typing. A pasted CRLF otherwise arrives as the very bytes Enter produces, and some terminals turn the LF into a second CR — one pasted line break then looks identical to two deliberate Enters, so no inspection of the content can separate them.

The events between the markers are unchanged: a pasted CR is still Key::Enter with char == '\r', an LF still char == '\n'. Bracketing tells you where the paste is; interpreting its bytes is up to you.

Note: terminals that don't implement mode 2004 ignore the request, and no markers ever arrive — the same behavior as leaving it disabled.

Example:

termisu.enable_bracketed_paste
pasting = false
termisu.each_event do |event|
  case event
  when Termisu::Event::Key
    case event.key
    when .paste_start? then pasting = true
    when .paste_end?   then pasting = false
    else                    handle(event, pasted: pasting)
    end
  end
end
Source
bracketed_paste?(*args, **options, &)

Enables bracketed paste mode.

Pastes are then delimited by Key::PasteStart and Key::PasteEnd events, and the terminal delivers the bytes between them verbatim instead of translating line endings the way it does for typed input.

This is the only reliable way to tell a paste from typing. A pasted CRLF otherwise arrives as the very bytes Enter produces, and some terminals turn the LF into a second CR — one pasted line break then looks identical to two deliberate Enters, so no inspection of the content can separate them.

The events between the markers are unchanged: a pasted CR is still Key::Enter with char == '\r', an LF still char == '\n'. Bracketing tells you where the paste is; interpreting its bytes is up to you.

Note: terminals that don't implement mode 2004 ignore the request, and no markers ever arrive — the same behavior as leaving it disabled.

Example:

termisu.enable_bracketed_paste
pasting = false
termisu.each_event do |event|
  case event
  when Termisu::Event::Key
    case event.key
    when .paste_start? then pasting = true
    when .paste_end?   then pasting = false
    else                    handle(event, pasted: pasting)
    end
  end
end
Source
clear

Clears the cell buffer (fills with spaces).

Note: This clears the buffer, not the screen. Call render() to apply.

Source
close

Closes Termisu and cleans up all resources.

Performs graceful shutdown in the correct order:

  1. Stop event loop (stops all sources, closes channel, waits for fibers)
  2. Exit alternate screen
  3. Disable raw mode
  4. Close reader and terminal

The event loop is stopped first to ensure fibers that might be using the reader are terminated before the reader is closed.

Source
current_mode(*args, **options)

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

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

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

Source
disable_bracketed_paste(*args, **options)

Enables bracketed paste mode.

Pastes are then delimited by Key::PasteStart and Key::PasteEnd events, and the terminal delivers the bytes between them verbatim instead of translating line endings the way it does for typed input.

This is the only reliable way to tell a paste from typing. A pasted CRLF otherwise arrives as the very bytes Enter produces, and some terminals turn the LF into a second CR — one pasted line break then looks identical to two deliberate Enters, so no inspection of the content can separate them.

The events between the markers are unchanged: a pasted CR is still Key::Enter with char == '\r', an LF still char == '\n'. Bracketing tells you where the paste is; interpreting its bytes is up to you.

Note: terminals that don't implement mode 2004 ignore the request, and no markers ever arrive — the same behavior as leaving it disabled.

Example:

termisu.enable_bracketed_paste
pasting = false
termisu.each_event do |event|
  case event
  when Termisu::Event::Key
    case event.key
    when .paste_start? then pasting = true
    when .paste_end?   then pasting = false
    else                    handle(event, pasted: pasting)
    end
  end
end
Source
disable_bracketed_paste(*args, **options, &)

Enables bracketed paste mode.

Pastes are then delimited by Key::PasteStart and Key::PasteEnd events, and the terminal delivers the bytes between them verbatim instead of translating line endings the way it does for typed input.

This is the only reliable way to tell a paste from typing. A pasted CRLF otherwise arrives as the very bytes Enter produces, and some terminals turn the LF into a second CR — one pasted line break then looks identical to two deliberate Enters, so no inspection of the content can separate them.

The events between the markers are unchanged: a pasted CR is still Key::Enter with char == '\r', an LF still char == '\n'. Bracketing tells you where the paste is; interpreting its bytes is up to you.

Note: terminals that don't implement mode 2004 ignore the request, and no markers ever arrive — the same behavior as leaving it disabled.

Example:

termisu.enable_bracketed_paste
pasting = false
termisu.each_event do |event|
  case event
  when Termisu::Event::Key
    case event.key
    when .paste_start? then pasting = true
    when .paste_end?   then pasting = false
    else                    handle(event, pasted: pasting)
    end
  end
end
Source
disable_enhanced_keyboard(*args, **options)

Enables enhanced keyboard protocol for disambiguated key reporting.

In standard terminal mode, certain keys are indistinguishable:

  • Tab sends the same byte as Ctrl+I (0x09)
  • Enter sends the same byte as Ctrl+M (0x0D)
  • Backspace may send the same byte as Ctrl+H (0x08)

Enhanced mode enables the Kitty keyboard protocol and/or modifyOtherKeys, which report keys in a way that preserves the distinction.

Note: Not all terminals support these protocols. Unsupported terminals will silently ignore the escape sequences and continue with legacy mode. Supported terminals include: Kitty, WezTerm, foot, Ghostty, recent xterm.

Example:

termisu.enable_enhanced_keyboard
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Key
      # Now Ctrl+I and Tab are distinguishable!
      if event.ctrl? && event.key.lower_i?
        puts "Ctrl+I pressed"
      elsif event.key.tab?
        puts "Tab pressed"
      end
    end
  end
end
termisu.disable_enhanced_keyboard
Source
disable_enhanced_keyboard(*args, **options, &)

Enables enhanced keyboard protocol for disambiguated key reporting.

In standard terminal mode, certain keys are indistinguishable:

  • Tab sends the same byte as Ctrl+I (0x09)
  • Enter sends the same byte as Ctrl+M (0x0D)
  • Backspace may send the same byte as Ctrl+H (0x08)

Enhanced mode enables the Kitty keyboard protocol and/or modifyOtherKeys, which report keys in a way that preserves the distinction.

Note: Not all terminals support these protocols. Unsupported terminals will silently ignore the escape sequences and continue with legacy mode. Supported terminals include: Kitty, WezTerm, foot, Ghostty, recent xterm.

Example:

termisu.enable_enhanced_keyboard
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Key
      # Now Ctrl+I and Tab are distinguishable!
      if event.ctrl? && event.key.lower_i?
        puts "Ctrl+I pressed"
      elsif event.key.tab?
        puts "Tab pressed"
      end
    end
  end
end
termisu.disable_enhanced_keyboard
Source
disable_mouse(*args, **options)

Enables mouse input tracking.

Once enabled, mouse events will be reported via poll_event. Supports SGR extended protocol (mode 1006) for large terminals and falls back to normal mode (1000) for compatibility.

Example:

termisu.enable_mouse
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Mouse
      puts "Click at #{event.x},#{event.y}"
    end
  end
end
termisu.disable_mouse
Source
disable_mouse(*args, **options, &)

Enables mouse input tracking.

Once enabled, mouse events will be reported via poll_event. Supports SGR extended protocol (mode 1006) for large terminals and falls back to normal mode (1000) for compatibility.

Example:

termisu.enable_mouse
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Mouse
      puts "Click at #{event.x},#{event.y}"
    end
  end
end
termisu.disable_mouse
Source
disable_timer

Disables the timer source.

Stops Tick events from being emitted. Safe to call when timer is already disabled.

Source
each_event(timeout : Time::Span, &)

Yields each event with timeout between events.

If no event arrives within timeout, yields nothing and continues. Useful when you need to do periodic work between events.

Parameters:

  • timeout: Maximum time to wait for each event

Example:

termisu.each_event(100.milliseconds) do |event|
  # Process event
end
# Can do other work between events when timeout expires
Source
each_event(timeout_ms : Int32, &)

Yields each event with timeout in milliseconds.

Source
each_event

Yields each event as it becomes available.

Blocks waiting for each event. Use this for simple event loops.

Example:

termisu.each_event do |event|
  case event
  when Termisu::Event::Key
    break if event.key.escape?
  when Termisu::Event::Tick
    # Animation frame
  end
  termisu.render
end
Source
enable_bracketed_paste(*args, **options)

Enables bracketed paste mode.

Pastes are then delimited by Key::PasteStart and Key::PasteEnd events, and the terminal delivers the bytes between them verbatim instead of translating line endings the way it does for typed input.

This is the only reliable way to tell a paste from typing. A pasted CRLF otherwise arrives as the very bytes Enter produces, and some terminals turn the LF into a second CR — one pasted line break then looks identical to two deliberate Enters, so no inspection of the content can separate them.

The events between the markers are unchanged: a pasted CR is still Key::Enter with char == '\r', an LF still char == '\n'. Bracketing tells you where the paste is; interpreting its bytes is up to you.

Note: terminals that don't implement mode 2004 ignore the request, and no markers ever arrive — the same behavior as leaving it disabled.

Example:

termisu.enable_bracketed_paste
pasting = false
termisu.each_event do |event|
  case event
  when Termisu::Event::Key
    case event.key
    when .paste_start? then pasting = true
    when .paste_end?   then pasting = false
    else                    handle(event, pasted: pasting)
    end
  end
end
Source
enable_bracketed_paste(*args, **options, &)

Enables bracketed paste mode.

Pastes are then delimited by Key::PasteStart and Key::PasteEnd events, and the terminal delivers the bytes between them verbatim instead of translating line endings the way it does for typed input.

This is the only reliable way to tell a paste from typing. A pasted CRLF otherwise arrives as the very bytes Enter produces, and some terminals turn the LF into a second CR — one pasted line break then looks identical to two deliberate Enters, so no inspection of the content can separate them.

The events between the markers are unchanged: a pasted CR is still Key::Enter with char == '\r', an LF still char == '\n'. Bracketing tells you where the paste is; interpreting its bytes is up to you.

Note: terminals that don't implement mode 2004 ignore the request, and no markers ever arrive — the same behavior as leaving it disabled.

Example:

termisu.enable_bracketed_paste
pasting = false
termisu.each_event do |event|
  case event
  when Termisu::Event::Key
    case event.key
    when .paste_start? then pasting = true
    when .paste_end?   then pasting = false
    else                    handle(event, pasted: pasting)
    end
  end
end
Source
enable_enhanced_keyboard(*args, **options)

Enables enhanced keyboard protocol for disambiguated key reporting.

In standard terminal mode, certain keys are indistinguishable:

  • Tab sends the same byte as Ctrl+I (0x09)
  • Enter sends the same byte as Ctrl+M (0x0D)
  • Backspace may send the same byte as Ctrl+H (0x08)

Enhanced mode enables the Kitty keyboard protocol and/or modifyOtherKeys, which report keys in a way that preserves the distinction.

Note: Not all terminals support these protocols. Unsupported terminals will silently ignore the escape sequences and continue with legacy mode. Supported terminals include: Kitty, WezTerm, foot, Ghostty, recent xterm.

Example:

termisu.enable_enhanced_keyboard
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Key
      # Now Ctrl+I and Tab are distinguishable!
      if event.ctrl? && event.key.lower_i?
        puts "Ctrl+I pressed"
      elsif event.key.tab?
        puts "Tab pressed"
      end
    end
  end
end
termisu.disable_enhanced_keyboard
Source
enable_enhanced_keyboard(*args, **options, &)

Enables enhanced keyboard protocol for disambiguated key reporting.

In standard terminal mode, certain keys are indistinguishable:

  • Tab sends the same byte as Ctrl+I (0x09)
  • Enter sends the same byte as Ctrl+M (0x0D)
  • Backspace may send the same byte as Ctrl+H (0x08)

Enhanced mode enables the Kitty keyboard protocol and/or modifyOtherKeys, which report keys in a way that preserves the distinction.

Note: Not all terminals support these protocols. Unsupported terminals will silently ignore the escape sequences and continue with legacy mode. Supported terminals include: Kitty, WezTerm, foot, Ghostty, recent xterm.

Example:

termisu.enable_enhanced_keyboard
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Key
      # Now Ctrl+I and Tab are distinguishable!
      if event.ctrl? && event.key.lower_i?
        puts "Ctrl+I pressed"
      elsif event.key.tab?
        puts "Tab pressed"
      end
    end
  end
end
termisu.disable_enhanced_keyboard
Source
enable_mouse(*args, **options)

Enables mouse input tracking.

Once enabled, mouse events will be reported via poll_event. Supports SGR extended protocol (mode 1006) for large terminals and falls back to normal mode (1000) for compatibility.

Example:

termisu.enable_mouse
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Mouse
      puts "Click at #{event.x},#{event.y}"
    end
  end
end
termisu.disable_mouse
Source
enable_mouse(*args, **options, &)

Enables mouse input tracking.

Once enabled, mouse events will be reported via poll_event. Supports SGR extended protocol (mode 1006) for large terminals and falls back to normal mode (1000) for compatibility.

Example:

termisu.enable_mouse
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Mouse
      puts "Click at #{event.x},#{event.y}"
    end
  end
end
termisu.disable_mouse
Source
enable_system_timer(interval : Time::Span = 16.milliseconds) : self

Enables the kernel-level system timer for precise animation timing.

Uses platform-specific timers for high-precision tick events:

  • Linux: timerfd with epoll (<1ms precision)
  • macOS/BSD: kqueue EVFILT_TIMER
  • Fallback: monotonic clock with poll

The SystemTimer compensates for processing time, maintaining consistent frame rates. It also provides missed_ticks count for detecting dropped frames when processing takes longer than the interval.

Parameters:

  • interval: Time between tick events (default: 16ms for 60 FPS)

Example:

termisu.enable_system_timer(16.milliseconds) # 60 FPS with kernel timing

termisu.each_event do |event|
  case event
  when Termisu::Event::Tick
    if event.missed_ticks > 0
      puts "Dropped #{event.missed_ticks} frame(s)"
    end
    termisu.render
  when Termisu::Event::Key
    break if event.key.escape?
  end
end

termisu.disable_timer
Source
enable_timer(interval : Time::Span = 16.milliseconds) : self

Enables the sleep-based timer source for animation and game loops.

When enabled, Tick events are emitted at the specified interval. Default interval is 16ms (~60 FPS).

Note: The sleep-based timer has ~5ms overhead per frame due to processing time not being compensated. For more precise timing, use enable_system_timer which uses kernel-level timers.

Parameters:

  • interval: Time between tick events (default: 16ms for 60 FPS)

Example:

termisu.enable_timer(16.milliseconds) # 60 FPS

termisu.each_event do |event|
  case event
  when Termisu::Event::Tick
    # Update animation state
    termisu.render
  when Termisu::Event::Key
    break if event.key.escape?
  end
end

termisu.disable_timer
Source
enhanced_keyboard?(*args, **options)

Enables enhanced keyboard protocol for disambiguated key reporting.

In standard terminal mode, certain keys are indistinguishable:

  • Tab sends the same byte as Ctrl+I (0x09)
  • Enter sends the same byte as Ctrl+M (0x0D)
  • Backspace may send the same byte as Ctrl+H (0x08)

Enhanced mode enables the Kitty keyboard protocol and/or modifyOtherKeys, which report keys in a way that preserves the distinction.

Note: Not all terminals support these protocols. Unsupported terminals will silently ignore the escape sequences and continue with legacy mode. Supported terminals include: Kitty, WezTerm, foot, Ghostty, recent xterm.

Example:

termisu.enable_enhanced_keyboard
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Key
      # Now Ctrl+I and Tab are distinguishable!
      if event.ctrl? && event.key.lower_i?
        puts "Ctrl+I pressed"
      elsif event.key.tab?
        puts "Tab pressed"
      end
    end
  end
end
termisu.disable_enhanced_keyboard
Source
enhanced_keyboard?(*args, **options, &)

Enables enhanced keyboard protocol for disambiguated key reporting.

In standard terminal mode, certain keys are indistinguishable:

  • Tab sends the same byte as Ctrl+I (0x09)
  • Enter sends the same byte as Ctrl+M (0x0D)
  • Backspace may send the same byte as Ctrl+H (0x08)

Enhanced mode enables the Kitty keyboard protocol and/or modifyOtherKeys, which report keys in a way that preserves the distinction.

Note: Not all terminals support these protocols. Unsupported terminals will silently ignore the escape sequences and continue with legacy mode. Supported terminals include: Kitty, WezTerm, foot, Ghostty, recent xterm.

Example:

termisu.enable_enhanced_keyboard
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Key
      # Now Ctrl+I and Tab are distinguishable!
      if event.ctrl? && event.key.lower_i?
        puts "Ctrl+I pressed"
      elsif event.key.tab?
        puts "Tab pressed"
      end
    end
  end
end
termisu.disable_enhanced_keyboard
Source
hide_cursor(*args, **options)

Hides the cursor.

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

Hides the cursor.

Source
input_available?

Checks if input data is available.

Source
mouse_enabled?(*args, **options)

Enables mouse input tracking.

Once enabled, mouse events will be reported via poll_event. Supports SGR extended protocol (mode 1006) for large terminals and falls back to normal mode (1000) for compatibility.

Example:

termisu.enable_mouse
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Mouse
      puts "Click at #{event.x},#{event.y}"
    end
  end
end
termisu.disable_mouse
Source
mouse_enabled?(*args, **options, &)

Enables mouse input tracking.

Once enabled, mouse events will be reported via poll_event. Supports SGR extended protocol (mode 1006) for large terminals and falls back to normal mode (1000) for compatibility.

Example:

termisu.enable_mouse
loop do
  if event = termisu.poll_event(100)
    case event
    when Termisu::Event::Mouse
      puts "Click at #{event.x},#{event.y}"
    end
  end
end
termisu.disable_mouse
Source
peek_byte(*args, **options)
Source
peek_byte(*args, **options, &)
Source
poll_event(timeout : Time::Span) : Event::Any | Nil

Polls for an event with timeout.

Returns an Event or nil if timeout expires.

Parameters:

  • timeout: Maximum time to wait for an event

Example:

if event = termisu.poll_event(100.milliseconds)
  # Handle event
else
  # No event within timeout - do other work
end
Source
poll_event(timeout_ms : Int32) : Event::Any | Nil

Polls for an event with timeout in milliseconds.

Parameters:

  • timeout_ms: Timeout in milliseconds (0 for non-blocking)
Source
poll_event

Polls for the next event, blocking until one is available.

This is the recommended way to handle events. Returns structured Event objects (Event::Key, Event::Mouse, Event::Resize, Event::Tick) from the unified Event::Loop channel.

Blocks indefinitely until an event arrives.

Example:

loop do
  event = termisu.poll_event
  case event
  when Termisu::Event::Key
    break if event.ctrl_c? || event.key.escape?
  when Termisu::Event::Resize
    # Buffer size is already updated to match the new terminal size.
    termisu.sync # Redraw after resize
  when Termisu::Event::Tick
    # Animation frame
  end
  termisu.render
end
Source
raw_mode?(*args, **options)

Returns true if raw mode is enabled.

Source
raw_mode?(*args, **options, &)

Returns true if raw mode is enabled.

Source
read_byte(*args, **options)
Source
read_byte(*args, **options, &)
Source
read_bytes(*args, **options)
Source
read_bytes(*args, **options, &)
Source
remove_event_source(source : Event::Source) : self

Removes a custom event source from the event loop.

If the source is running, it will be stopped before removal. Removing a source that isn't registered is a no-op.

Parameters:

  • source: The Event::Source to remove

Returns self for method chaining.

Source
render(*args, **options)

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 clear_screen + write for partial updates.

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

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 clear_screen + write for partial updates.

Source
set_cell(*args, **options)

Sets a cell at the specified position.

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.

Example:

termisu.set_cell(10, 5, 'A', fg: Color.red, attr: Attribute::Bold)
termisu.render # Apply changes
Source
set_cell(*args, **options, &)

Sets a cell at the specified position.

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.

Example:

termisu.set_cell(10, 5, 'A', fg: Color.red, attr: Attribute::Bold)
termisu.render # Apply changes
Source
show_cursor(*args, **options)

Shows the cursor.

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

Shows the cursor.

Source
size(*args, **options)

Returns terminal size as {width, height}.

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

Returns terminal size as {width, height}.

Source
suspend(preserve_screen : Bool, &)

Suspends TUI mode with option to preserve alternate screen.

Parameters:

  • preserve_screen: If true, stays in alternate screen during suspension. Useful for brief prompts that don't need scrollback access.

Example:

termisu.suspend(preserve_screen: true) do
  print "Continue? [y/n]: "
  answer = gets
end
Source
suspend

Suspends TUI mode for shell-out or external program execution.

This is an alias for with_cooked_mode that makes the intent clearer when temporarily handing control to a shell or external program.

Handles:

  • Exits alternate screen (shows normal terminal with scrollback)
  • Switches to cooked mode (line buffering, echo, signals)
  • Pauses event loop input processing
  • Restores everything on block exit

Example:

termisu.suspend do
  system("vim file.txt")
end
# TUI fully restored
Source
sync(*args, **options)

Forces a full redraw of all cells.

Useful after terminal resize or screen corruption.

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

Forces a full redraw of all cells.

Useful after terminal resize or screen corruption.

Source
sync_updates=(value : 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?(*args, **options)

Returns true if synchronized updates (DEC mode 2026) are enabled.

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

Source
sync_updates?(*args, **options, &)

Returns true if synchronized updates (DEC mode 2026) are enabled.

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

Source
timer_enabled?

Returns true if the timer is currently enabled.

Source
timer_interval

Returns the current timer interval, or nil if timer is disabled.

Source
timer_interval=(interval : Time::Span) : Time::Span

Sets the timer interval.

Can be called while timer is running to change the interval dynamically. Raises if timer is not enabled.

Parameters:

  • interval: New interval between tick events

Example:

termisu.enable_timer
termisu.timer_interval = 8.milliseconds # 120 FPS
Source
title(*args, **options)

The title assigned to the Terminal's window

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

The title assigned to the Terminal's window

Source
title=(arg)

The title assigned to the Terminal's window

Source
try_poll_event

Tries to poll for an event without blocking.

Returns an event if one is immediately available, or nil otherwise. This uses Crystal's select/else for true non-blocking behavior, making it ideal for game loops or fiber-based architectures.

Example:

# Game loop pattern
loop do
  while event = termisu.try_poll_event
    case event
    when Termisu::Event::Key
      break if event.key.escape?
    end
  end

  # Update game state
  update_game()
  termisu.render
  sleep 16.milliseconds
end
Source
wait_event

Waits for and returns the next event (blocking).

Alias for poll_event without timeout. Blocks until an event is available from any source.

Example:

event = termisu.wait_event
puts "Got event: #{event}"
Source
wait_for_input(timeout_ms : Int32) : Bool

Waits for input data with a timeout in milliseconds.

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 within a TUI.

Event loop input processing is paused during the block.

Example:

termisu.with_cbreak_mode do
  print "Press any key: "
  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.

Event loop input processing is paused during the block to avoid conflicts with the shell or subprocess.

Example:

termisu.with_cooked_mode do
  system("vim file.txt")
end
# Back to TUI mode, event loop active
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:

  • Event loop coordination (pauses input for user-interactive modes)
  • Mode switching via Terminal (which handles termios, screen, cursor)
  • Automatic restoration on block exit or exception

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:

termisu.with_mode(Terminal::Mode.cooked) do
  system("vim file.txt")
end
# Terminal state fully 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.

Example:

password = termisu.with_password_mode do
  print "Password: "
  gets
end
Source

Nested types