Class: WindowAdapter

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

Overview

#Design change:

Terminal writes to the buffer. Buffer batches up updates to an output adapter (the window, but could be a terminal...)

But for now, the now stupidly misnamed TrackChanges class just bifurcates buffer changes and passes them to both the buffer and the screen, and "nothing" should talk to the window directly. Once all window updates are moved to TrackChanges/WindowAdapter We want to make it smarter.

Instance Method Summary collapse

Constructor Details

#initialize(window, term) ⇒ WindowAdapter

Returns a new instance of WindowAdapter.



14
15
16
17
# File 'lib/windowadapter.rb', line 14

def initialize window, term
  @window = window
  @term = term
end

Instance Method Details

#brighten(col, bg) ⇒ Object



53
54
55
56
# File 'lib/windowadapter.rb', line 53

def brighten(col, bg)
  # FIXME. Should bring it towards bg
  [col].pack("l").each_byte.map{|b| (b.ord+128).clamp(0,255) }.pack("C*").unpack("l")[0]
end

#char_hObject



20
# File 'lib/windowadapter.rb', line 20

def char_h = @window.char_h

#char_wObject



19
# File 'lib/windowadapter.rb', line 19

def char_w = @window.char_w

#clearObject



21
# File 'lib/windowadapter.rb', line 21

def clear  = @window.clear(0,0,@window.width,@window.height)

#clear_area(x, y, w, h) ⇒ Object



58
# File 'lib/windowadapter.rb', line 58

def clear_area(x,y,w,h)  = @window.clear(x*char_w,y*char_h,w,h)

#clear_cells(x, y, w, h) ⇒ Object



59
# File 'lib/windowadapter.rb', line 59

def clear_cells(x,y,w,h) = clear_area(x,y, w * char_w, h * char_h)

#clear_line(y, from_x, to_x = nil) ⇒ Object



61
62
63
64
65
66
67
68
69
70
# File 'lib/windowadapter.rb', line 61

def clear_line y, from_x, to_x = nil
  if to_x
    # to_x is an INCLUSIVE end column, matching TermBuffer#clear_line
    # (EL mode 1 clears [0..cursor] inclusive). Clearing to_x-from_x
    # cells left the cursor column itself stale on screen.
    clear_cells(from_x, y, to_x - from_x + 1, 1)
  else
    clear_cells(from_x, y, @term.term_width - from_x, 1)
  end
end

#delete_lines(y, num, maxy) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/windowadapter.rb', line 88

def delete_lines(y, num, maxy)
  # Deleting more lines than the region holds clears [y..maxy] entirely.
  # Without this clamp a large num gave Window#scroll_up a negative height
  # and a negative clear origin that clamped to row 0, wiping lines above
  # the scroll region.
  num = [num, maxy - y + 1].min
  return if num <= 0
  # Move the rows below the deleted block - [y+num .. maxy] - up by num
  # rows, into [y .. maxy-num]; Window#scroll_up clears the vacated rows
  # at the bottom of the region.
  @window.scroll_up((y + num) * char_h, @term.term_width * char_w,
    (maxy - (y + num) + 1) * char_h, num * char_h)
end

#dim(col) ⇒ Object

FIXME



49
50
51
# File 'lib/windowadapter.rb', line 49

def dim(col) #FIXME
  [col].pack("l").each_byte.map{|b| b.ord*0.4 }.pack("C*").unpack("l")[0]
end

#draw(x, y, c, fg, bg, flags, lineattrs) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/windowadapter.rb', line 123

def draw(x,y,c,fg,bg,flags,lineattrs)
  inverse = flags.allbits?(INVERSE)
  if inverse
    fg,bg=bg,fg
  end

  if flags.allbits?(FAINT)
    fg = dim(fg)
  end

  if flags.anybits?(BLINK) && @term.blink_state
    fg = inverse ? brighten(fg,bg) : dim(fg)
  elsif flags.anybits?(RAPID_BLINK) && @term.rblink_state
    fg = inverse ? brighten(fg,bg) : dim(fg)
  end

  if x.nil?
    # @BUG
    STDERR.puts "\e[35m@BUG\[0m: x.nil? @windowadapter#draw"
    return
  end
  
  @window.draw(x*char_w, y*char_h, c, fg, bg, lineattrs)
  # FIXME: Take into account lineattrs
  draw_flag_lines(flags, x, y, c.length, fg)
end

#draw_flag_lines(flags, x, y, len, fg) ⇒ Object

Migrating draw_flush



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/windowadapter.rb', line 105

def draw_flag_lines(flags, x,y, len, fg)
  x *= char_w
  y *= char_h
  w = len * char_w
  if flags.allbits?(OVERLINE)
    @window.draw_line(x,y,w,fg)
  end
  if flags.allbits?(CROSSED_OUT)
    @window.draw_line(x,y+char_h/2+2, w, fg)
  end
  if flags.anybits?(UNDERLINE | DBL_UNDERLINE)
    @window.draw_line(x,y+char_h-3, w, fg)
    if flags.allbits?(DBL_UNDERLINE)
      @window.draw_line(x,y+char_h-1, w, fg)
    end
  end
end

#insert_lines(y, num, maxy) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/windowadapter.rb', line 72

def insert_lines(y, num, maxy)
  # Inserting more lines than fit between y and the region bottom just
  # blanks the whole [y..maxy] span; clamp so the geometry below never
  # goes negative (which would clear rows above the region - see DL).
  num = [num, maxy - y + 1].min
  return if num <= 0
  # Move the rows from the insertion point down to the region bottom -
  # [y .. maxy-num] - down by num rows, into [y+num .. maxy];
  # Window#scroll_down clears the vacated rows at the top (the inserted
  # blanks). The old code scrolled from screen row 0 and ignored y, so an
  # IL anywhere below the top dragged every line above it (and outside the
  # scroll region) down, blanking row 0.
  @window.scroll_down(y * char_h, @term.term_width * char_w,
    (maxy - y - num + 1) * char_h, num * char_h)
end

#redraw_allObject

Force a complete redraw of the window contents



151
152
153
154
155
156
157
158
# File 'lib/windowadapter.rb', line 151

def redraw_all
  # First clear the entire window
  @window.clear(0, 0, @window.width, @window.height)
  
  # Force the window to flush and update its display
  @window.dirty!
  @window.flush
end

#scroll_selection(start, bottom) ⇒ Object

A line of the scroll region [start..bottom] has moved into history. Forward to the orchestrator so it can keep a live text selection pinned to the content it covers. The headless harness/bench hosts have no selection state and do not implement this, so it no-ops there.



37
38
39
# File 'lib/windowadapter.rb', line 37

def scroll_selection(start, bottom)
  @term.shift_selection_for_scroll(start, bottom) if @term.respond_to?(:shift_selection_for_scroll)
end

#scroll_up(scroll_start, scroll_end) ⇒ Object



161
162
163
164
165
166
167
168
# File 'lib/windowadapter.rb', line 161

def scroll_up(scroll_start, scroll_end)
  @window.scroll_up(
    char_h*((scroll_start||0)+1),
    @window.width,
    (scroll_end-scroll_start)*char_h,
    char_h
  )
end

#scrollback_anchorObject

A line has entered history while scrolled back: keep the same absolute lines in view (and the selection mapping consistent) by deepening the offset instead of letting the viewport drift.



31
# File 'lib/windowadapter.rb', line 31

def scrollback_anchor = @window.scrollback_anchor

#scrollback_modeObject

True while the view is scrolled back into history. Live (pty-driven) screen writes are suppressed in this state so output doesn't paint over the scrolled-back display; the buffer is still updated underneath.



26
# File 'lib/windowadapter.rb', line 26

def scrollback_mode = @window.scrollback_mode

#set_columns(cols) ⇒ Object

DECCOLM: the terminal asked to switch to cols columns (80/132). The orchestrator (RubyTerm) owns the window pixels, font scale, config and the pty size report, so delegate to it. The headless harness "term" does not implement this (the virtual grid is fixed), so it no-ops.



45
46
47
# File 'lib/windowadapter.rb', line 45

def set_columns(cols)
  @term.set_columns(cols) if @term.respond_to?(:set_columns)
end