Class: RubyTUI::Terminal

Inherits:
Object
  • Object
show all
Defined in:
lib/rubytui/terminal.rb

Overview

Orchestrates the rendering pipeline with double buffering.

Terminal manages two buffers and diffs them each frame to send only changed cells to the backend. This is the core of the immediate-mode rendering system.

Examples:

Drawing a frame

terminal.draw do |frame|
  frame.buffer.set_string(0, 0, "Hello!")
  frame.render_widget(my_widget, frame.area)
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(backend:) ⇒ Terminal

Create a terminal that renders through backend.

Allocates both buffers at the backend's current Backend#size and, where supported, installs a SIGWINCH trap (replacing any existing handler) so the buffers are reallocated and the screen cleared on the next #draw after a resize.

Parameters:

  • backend (Backend) —

    the rendering backend



27
28
29
30
31
32
33
# File 'lib/rubytui/terminal.rb', line 27

def initialize(backend:)
  @backend = backend
  @needs_resize = false
  @current = 0
  allocate_buffers
  setup_resize_handler
end

Instance Attribute Details

#backend ⇒ Backend (readonly)

Returns the rendering backend (AnsiBackend or TestBackend).

Returns:

  • (Backend) —

    the rendering backend (AnsiBackend or TestBackend)



17
18
19
# File 'lib/rubytui/terminal.rb', line 17

def backend
  @backend
end

Instance Method Details

#cell_pixel_size ⇒ Array(Integer, Integer)

Get cell pixel dimensions for aspect-ratio calculations.

Queries the terminal via CSI 16 t if not already cached. Use this with Widgets::Image.fit_area for proper image sizing.

Returns:

  • (Array(Integer, Integer)) —

    cell dimensions [width_px, height_px]



111
112
113
# File 'lib/rubytui/terminal.rb', line 111

def cell_pixel_size
  @backend.query_cell_pixel_size
end

#clear_all_images! ⇒ void

This method returns an undefined value.

Clear all Kitty graphics protocol images.

Useful during transitions or when precise tracking isn't possible.



120
121
122
# File 'lib/rubytui/terminal.rb', line 120

def clear_all_images!
  @backend.clear_all_images!
end

#draw {|frame| ... } ⇒ Frame

Main draw method — the heart of the rendering pipeline.

Resets the current buffer, yields a Frame for the caller to render widgets into, diffs against the previous buffer, sends changes to the backend, and swaps buffers.

The cursor is hidden while drawing and shown again only if the frame requested a position via Frame#set_cursor_position. Image placement changes are sent to the backend, and the backend is flushed before returning.

Yields:

  • (frame) —

    a Frame to render widgets into

Yield Parameters:

Returns:

  • (Frame) —

    the rendered frame



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/rubytui/terminal.rb', line 49

def draw
  check_resize

  # A pending full repaint (image placements changed last frame, or
  # force_redraw!): clear the screen and forget the previous buffer so
  # every cell and image is re-emitted. Image pixels live on a layer
  # separate from text cells, so placement changes can reveal stale
  # terminal content that the cell diff alone cannot detect.
  if @full_repaint
    @full_repaint = false
    @force_images = true
    # Cells only — image placements must survive so deletions and
    # changes still diff correctly against the previous frame.
    previous_buffer.reset_cells!
    @backend.clear
  end

  # Reset the current (next) buffer
  current_buffer.reset!

  # Create frame and yield to caller
  frame = Frame.new(current_buffer, current_buffer.area)
  yield frame

  # Diff against the previous buffer
  changes = current_buffer.diff(previous_buffer)

  # Send changes to backend
  @backend.hide_cursor
  @backend.draw(changes)

  # Handle image placements (Kitty graphics protocol)
  draw_images(current_buffer, previous_buffer)

  # Handle cursor position
  if (pos = frame.cursor_position)
    @backend.show_cursor
    @backend.move_cursor(pos[0], pos[1])
  end

  @backend.flush

  # Swap buffers
  @current = 1 - @current

  frame
end

#enter_alternate_screen ⇒ void

This method returns an undefined value.

Delegate alternate screen management to backend.



126
127
128
# File 'lib/rubytui/terminal.rb', line 126

def enter_alternate_screen
  @backend.enter_alternate_screen
end

#force_redraw! ⇒ void

This method returns an undefined value.

Force a full redraw on the next #draw call.

The next frame clears the screen, re-sends every cell, and re-transmits current image placements.



143
144
145
# File 'lib/rubytui/terminal.rb', line 143

def force_redraw!
  @full_repaint = true
end

#leave_alternate_screen ⇒ void

This method returns an undefined value.

Leave the alternate screen buffer via the backend.



133
134
135
# File 'lib/rubytui/terminal.rb', line 133

def leave_alternate_screen
  @backend.leave_alternate_screen
end

#size ⇒ Rect

Get current terminal size as a Rect.

Returns:

  • (Rect) —

    a Rect at origin with terminal dimensions



100
101
102
103
# File 'lib/rubytui/terminal.rb', line 100

def size
  w, h = @backend.size
  Rect.new(x: 0, y: 0, width: w, height: h)
end