class Termisu
- Termisu
- Reference
- Object
Overview
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
Defined in:
termisu.crtermisu/log.cr
termisu/version.cr
Constant Summary
-
Log =
::Log.for("termisu") -
Main log instance for Termisu library
-
VERSION =
{{ (`shards version`).chomp.stringify }} -
Full version string from shard.yml
-
VERSION_MAJOR =
0 -
VERSION_MINOR =
1 -
VERSION_PATCH =
0 -
VERSION_STATE =
nil
Constructors
-
.new
Initializes Termisu with all required components.
Instance Method Summary
-
#add_event_source(source : Event::Source) : self
Adds a custom event source to the event loop.
-
#alternate_screen?(*args, **options)
Returns true if alternate screen mode is active.
-
#alternate_screen?(*args, **options, &)
Returns true if alternate screen mode is active.
-
#clear
Clears the cell buffer (fills with spaces).
-
#close
Closes Termisu and cleans up all resources.
-
#disable_enhanced_keyboard(*args, **options)
Enables enhanced keyboard protocol for disambiguated key reporting.
-
#disable_enhanced_keyboard(*args, **options, &)
Enables enhanced keyboard protocol for disambiguated key reporting.
-
#disable_mouse(*args, **options)
Enables mouse input tracking.
-
#disable_mouse(*args, **options, &)
Enables mouse input tracking.
-
#disable_timer : self
Disables the timer source.
-
#each_event(timeout : Time::Span, &)
Yields each event with timeout between events.
-
#each_event(timeout_ms : Int32, &)
Yields each event with timeout in milliseconds.
-
#each_event(&)
Yields each event as it becomes available.
-
#enable_enhanced_keyboard(*args, **options)
Enables enhanced keyboard protocol for disambiguated key reporting.
-
#enable_enhanced_keyboard(*args, **options, &)
Enables enhanced keyboard protocol for disambiguated key reporting.
-
#enable_mouse(*args, **options)
Enables mouse input tracking.
-
#enable_mouse(*args, **options, &)
Enables mouse input tracking.
-
#enable_timer(interval : Time::Span = 16.milliseconds) : self
Enables the timer source for animation and game loops.
-
#enhanced_keyboard?(*args, **options)
Enables enhanced keyboard protocol for disambiguated key reporting.
-
#enhanced_keyboard?(*args, **options, &)
Enables enhanced keyboard protocol for disambiguated key reporting.
-
#hide_cursor(*args, **options)
Sets cursor position and makes it visible.
-
#hide_cursor(*args, **options, &)
Sets cursor position and makes it visible.
-
#input_available? : Bool
Checks if input data is available.
-
#mouse_enabled?(*args, **options)
Enables mouse input tracking.
-
#mouse_enabled?(*args, **options, &)
Enables mouse input tracking.
- #peek_byte(*args, **options)
- #peek_byte(*args, **options, &)
-
#poll_event(timeout : Time::Span) : Event::Any | Nil
Polls for an event with timeout.
-
#poll_event(timeout_ms : Int32) : Event::Any | Nil
Polls for an event with timeout in milliseconds.
-
#poll_event : Event::Any
Polls for the next event, blocking until one is available.
-
#raw_mode?(*args, **options)
Returns true if raw mode is enabled.
-
#raw_mode?(*args, **options, &)
Returns true if raw mode is enabled.
- #read_byte(*args, **options)
- #read_byte(*args, **options, &)
- #read_bytes(*args, **options)
- #read_bytes(*args, **options, &)
-
#remove_event_source(source : Event::Source) : self
Removes a custom event source from the event loop.
-
#render(*args, **options)
Renders cell buffer changes to the screen.
-
#render(*args, **options, &)
Renders cell buffer changes to the screen.
-
#set_cell(*args, **options)
Sets a cell at the specified position.
-
#set_cell(*args, **options, &)
Sets a cell at the specified position.
-
#set_cursor(*args, **options)
Sets cursor position and makes it visible.
-
#set_cursor(*args, **options, &)
Sets cursor position and makes it visible.
-
#show_cursor(*args, **options)
Sets cursor position and makes it visible.
-
#show_cursor(*args, **options, &)
Sets cursor position and makes it visible.
-
#size(*args, **options)
Returns terminal size as {width, height}.
-
#size(*args, **options, &)
Returns terminal size as {width, height}.
-
#sync(*args, **options)
Forces a full redraw of all cells.
-
#sync(*args, **options, &)
Forces a full redraw of all cells.
-
#timer_enabled? : Bool
Returns true if the timer is currently enabled.
-
#timer_interval : Time::Span | Nil
Returns the current timer interval, or nil if timer is disabled.
-
#timer_interval=(interval : Time::Span) : Time::Span
Sets the timer interval.
-
#try_poll_event : Event::Any | Nil
Tries to poll for an event without blocking.
-
#wait_event : Event::Any
Waits for and returns the next event (blocking).
-
#wait_for_input(timeout_ms : Int32) : Bool
Waits for input data with a timeout in milliseconds.
Constructor Detail
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.
Instance Method Detail
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)
Clears the cell buffer (fills with spaces).
Note: This clears the buffer, not the screen. Call render() to apply.
Closes Termisu and cleans up all resources.
Performs graceful shutdown in the correct order:
- Stop event loop (stops all sources, closes channel, waits for fibers)
- Exit alternate screen
- Disable raw mode
- 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.
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
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
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
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
Disables the timer source.
Stops Tick events from being emitted. Safe to call when timer is already disabled.
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
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
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
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
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
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
Enables the timer source for animation and game loops.
When enabled, Tick events are emitted at the specified interval. Default interval is 16ms (~60 FPS).
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
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
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
Sets cursor position and makes it visible. Hides the cursor (rendered on next render()). Shows the cursor (rendered on next render()).
Sets cursor position and makes it visible. Hides the cursor (rendered on next render()). Shows the cursor (rendered on next render()).
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
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
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
Polls for an event with timeout in milliseconds.
Parameters:
- timeout_ms: Timeout in milliseconds (0 for non-blocking)
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
termisu.sync # Redraw after resize
when Termisu::Event::Tick
# Animation frame
end
termisu.render
end
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.
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.
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.
Sets a cell at the specified position.
Parameters:
- x: Column position (0-based)
- y: Row position (0-based)
- ch: 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
Sets a cell at the specified position.
Parameters:
- x: Column position (0-based)
- y: Row position (0-based)
- ch: 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
Sets cursor position and makes it visible. Hides the cursor (rendered on next render()). Shows the cursor (rendered on next render()).
Sets cursor position and makes it visible. Hides the cursor (rendered on next render()). Shows the cursor (rendered on next render()).
Sets cursor position and makes it visible. Hides the cursor (rendered on next render()). Shows the cursor (rendered on next render()).
Sets cursor position and makes it visible. Hides the cursor (rendered on next render()). Shows the cursor (rendered on next render()).
Forces a full redraw of all cells.
Useful after terminal resize or screen corruption.
Forces a full redraw of all cells.
Useful after terminal resize or screen corruption.
Returns the current timer interval, or nil if timer is disabled.
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
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
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}"
Waits for input data with a timeout in milliseconds.