Module: RETerm

Defined in:
lib/reterm.rb,
lib/reterm/init.rb,
lib/reterm/util.rb,
lib/reterm/panel.rb,
lib/reterm/config.rb,
lib/reterm/layout.rb,
lib/reterm/loader.rb,
lib/reterm/resize.rb,
lib/reterm/window.rb,
lib/reterm/layouts.rb,
lib/reterm/version.rb,
lib/reterm/terminal.rb,
lib/reterm/component.rb,
lib/reterm/color_pair.rb,
lib/reterm/components.rb,
lib/reterm/layouts/grid.rb,
lib/reterm/components/dial.rb,
lib/reterm/components/entry.rb,
lib/reterm/components/image.rb,
lib/reterm/components/label.rb,
lib/reterm/components/radio.rb,
lib/reterm/layouts/vertical.rb,
lib/reterm/mixins/nav_input.rb,
lib/reterm/components/button.rb,
lib/reterm/components/dialog.rb,
lib/reterm/components/matrix.rb,
lib/reterm/components/rocker.rb,
lib/reterm/components/splash.rb,
lib/reterm/components/hslider.rb,
lib/reterm/components/vslider.rb,
lib/reterm/components/youtube.rb,
lib/reterm/layouts/horizontal.rb,
lib/reterm/mixins/common_keys.rb,
lib/reterm/mixins/log_helpers.rb,
lib/reterm/mixins/mouse_input.rb,
lib/reterm/components/template.rb,
lib/reterm/mixins/item_helpers.rb,
lib/reterm/mixins/key_bindings.rb,
lib/reterm/mixins/nav_controls.rb,
lib/reterm/components/alphalist.rb,
lib/reterm/components/histogram.rb,
lib/reterm/components/isometric.rb,
lib/reterm/mixins/cdk_component.rb,
lib/reterm/components/ascii_text.rb,
lib/reterm/components/button_box.rb,
lib/reterm/components/cmd_output.rb,
lib/reterm/mixins/button_helpers.rb,
lib/reterm/components/scroll_list.rb,
lib/reterm/components/select_list.rb,
lib/reterm/mixins/common_controls.rb,
lib/reterm/mixins/component_input.rb,
lib/reterm/components/close_button.rb,
lib/reterm/mixins/event_dispatcher.rb,
lib/reterm/components/drop_down_menu.rb,
lib/reterm/components/password_entry.rb,
lib/reterm/components/scrolling_area.rb,
lib/reterm/components/revealing_label.rb,
lib/reterm/components/multi_line_entry.rb

Overview

The Ruby Enhanced Terminal interactive framework facilitating dynmaic/feature rich terminal applications.

Defined Under Namespace

Modules: ButtonHelpers, CDKComponent, CommonControls, CommonKeys, ComponentInput, Components, EventDispatcher, ItemHelpers, KeyBindings, Layouts, LogHelpers, MouseInput, NavControls, NavInput Classes: ColorPair, Component, Config, Layout, Loader, Panel, Terminal, Window

Constant Summary collapse

SYNC_TIMEOUT =

in milliseconds if enabled

150
NC =

XXX Ncurses is exposed so that users may employ any constants if desired. This should not be needed though and is discouraged to maintain portability

Ncurses
ANSI_COLORS =
{
       1 => :bold,
      30 => :black,
      31 => :red,
      32 => :green,
      33 => :yellow,
      34 => :blue,
      35 => :magenta,
      36 => :cyan,
      37 => :white,

      # XXX techincally the 'bright' variations
      90 => :black,
      91 => :red,
      92 => :green,
      93 => :yellow,
      94 => :blue,
      95 => :magenta,
      96 => :cyan,
      97 => :white,
}
RESIZE_TIME =

Seconds between terminal size syncs

0.2
VERSION =
"0.6.3"

Instance Method Summary collapse

Instance Method Details

#activate_sync!Object

Enables the input timeout and component syncronization.

Used internally by components that need to periodically be updated outside of user input.



186
187
188
189
190
191
# File 'lib/reterm/init.rb', line 186

def activate_sync!
  @@sync_activated = true
  Window.all.each { |w|
    w.win.timeout(SYNC_TIMEOUT)
  }
end

#cdk_enabled?Boolean

Return boolean indicating if the CDK subsystem is enabled. CDK is a library used by various components providing many various ready to use ncurses widgets.

Returns:

  • (Boolean)


176
177
178
# File 'lib/reterm/init.rb', line 176

def cdk_enabled?
  !!@cdk_enabled
end

#cursor?Boolean

Cursor

Returns:

  • (Boolean)


118
119
120
# File 'lib/reterm/init.rb', line 118

def cursor?
  !(@@cursor_disabled ||= false)
end

#disable_cursor!Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/reterm/init.rb', line 122

def disable_cursor!
  # store cursor state
  o = cursor?

  # set cursor state
  @@cursor_disabled = true
  Ncurses::curs_set(0)

  # invoke block if given
  return unless block_given?
  yield

  # restore cursor state after block
  enable_cursor! if o
end

#enable_cdk!Object

Enable the CDK subsystem. Used by CDKbased components

See Also:



160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/reterm/init.rb', line 160

def enable_cdk!
  return if @cdk_enabled

  @cdk_enabled = true
  require 'cdk'

  # XXX defines standard color pairs, but we use our own
  # for standarization across components, but we need
  # to set cdk components to the correct color scheme
  # manually
  # CDK::Draw.initCDKColor
end

#enable_cursor!Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/reterm/init.rb', line 138

def enable_cursor!
  # store cursor state
  o = cursor?

  # set cursor state
  @@cursor_disabled = false
  Ncurses::curs_set(1)

  # invoke block if given
  return unless block_given?
  yield

  # restore cursor state after block
  disable_cursor! unless o
end

#file_append(f, t) ⇒ Object

Helper appending string to debug file



113
114
115
# File 'lib/reterm/util.rb', line 113

def file_append(f, t)
  File.open(f, "a") { |f| f.write t }
end

#flush_inputObject

Flushes all input queues



118
119
120
# File 'lib/reterm/util.rb', line 118

def flush_input
  Ncurses.flushinp
end

#init_reterm(opts = {}, &bl) ⇒ Object

Initializes the RETerm subsystem before invoking block. After block is finished regardless of execution state (return, exception, etc) this will cleanup the terminal environment before returning control to the TTY.

This method is the first method the user should invoke after requiring the reterm library.

Examples:

init_reterm {
  win = Window.new :rows => 20, :cols => 40
  # ... more UI logic
}


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
# File 'lib/reterm/init.rb', line 23

def init_reterm(opts={}, &bl)
  @@shutdown = false
  @@reterm_opts = opts

  err = nil

  min = opts[:min_size]

  begin
    # XXX: must load terminal info before changing terminal settings
    #      (else causes crash... not sure why)
    # But we also check min dimensions here
    Terminal.load(min)

    # shorten up the esc delay time
    ENV["ESCDELAY"] = 100.to_s

    stdscr = Ncurses::initscr
    initialized = true
    Ncurses::start_color
    Ncurses::noecho
    Ncurses::cbreak
    disable_cursor! unless !!opts[:cursor]
    Ncurses::keypad(stdscr, true)

    no_mouse = opts[:nomouse] || (opts.key?(:mouse) && !opts[:mouse])
    Ncurses::mousemask(MouseInput::ALL_EVENTS, []) unless no_mouse

    track_resize if opts[:resize]

    bl.call

  rescue => e
    err = e

  ensure
    stop_track_resize
    Ncurses.curs_set(1) if !!initialized
    Window.top.each { |w| w.finalize! }
    CDK::SCREEN.endCDK if cdk_enabled?
    Ncurses.endwin if !!initialized
    #`reset -Q` # XXX only way to guarantee a full reset (see above)
  end

  if err
    puts "Unhandled error:"
    puts "  " + err.to_s
    puts err.backtrace
            .collect { |b|
              "    " + b
            }.join("\n")
  end
end

#load_reterm(str) ⇒ Object

class Loader



123
124
125
# File 'lib/reterm/loader.rb', line 123

def load_reterm(str)
  Loader.new(str).window
end

#on_resize(&bl) ⇒ Object

Set the global callback to be invoked on resize



6
7
8
# File 'lib/reterm/resize.rb', line 6

def on_resize(&bl)
  @on_resize = bl
end

#parse_ansi(str) ⇒ Object

Converts specified ansi string to array of character data & corresponding control logic



37
38
39
40
41
42
43
44
45
46
47
48
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/reterm/util.rb', line 37

def parse_ansi(str)
  require 'strscan'

  r = []
  f = []
  t = []

  a = nil
  s = StringScanner.new(str)

  while(!s.eos?)
    # end of formatting
    if s.scan(/(\e|\[)\[0m/)
      t << f.pop
      t.compact!
      if f.empty?
        r << [a, t]
        t = []
        a = nil
      end

    # basic formatter
    elsif s.scan(/\e\[(3[0-7]|90|1)m/)
      # FIXME need to register formatting for 'a'
      # up to this point (and reset 'a') (and below)
      f << ANSI_COLORS[s[1].to_i]

    # sgr
    # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
    elsif s.scan(/\e\[(([0-9]+;?)+)m/)
      sgr = s[1].split(";").collect { |s| s.to_i }
      f << if (30..37).include?(sgr[0])
             ANSI_COLORS[sgr[1]]

           elsif sgr[0] == 38
             if sgr[1] == 5
              if sgr[2] < 8
                ANSI_COLORS[sgr[2]]

              elsif sgr[2] < 16
                ANSI_COLORS[sgr[2]]

              elsif sgr[2] < 232
                # TODO verify:
                # https://stackoverflow.com/questions/12338015/converting-8-bit-color-into-rgb-value
                re = (sgr[2] >> 5) * 32
                gr = ((sgr[2] & 28) >> 2) * 32
                bl = (sgr[2] & 3) * 64
                [re, gr, bl]

              else # if srg[2] < 256
                # TODO
              end

             else # if sgr[1] == 2
               # TODO
             end

             # TODO other sgr commands
           end

    else
      a  = "" if a.nil?
      a += s.scan(/./m)

    end
  end


  # handle remaining / lingering data
  r << [a, (t + f).compact] unless f.empty?

  r
end

#process_alive?(pid) ⇒ Boolean

Helper returning boolean indicating if specified process is alive

Returns:

  • (Boolean)


3
4
5
6
7
8
9
10
# File 'lib/reterm/util.rb', line 3

def process_alive?(pid)
  begin
    Process.getpgid( pid )
    true
  rescue Errno::ESRCH
    false
  end
end

#reterm_optsObject

Return copy of options specified to init_reterm



104
105
106
# File 'lib/reterm/init.rb', line 104

def reterm_opts
  @@_reterm_opts ||= Hash[@@reterm_opts]
end

#run_sync!Object

Run the sync process, used internally



200
201
202
203
204
205
206
207
208
# File 'lib/reterm/init.rb', line 200

def run_sync!
  return unless sync_enabled?

  Window.all.each { |w|
    w.sync!
  }

  update_reterm
end

#shutdown!Object

Use to halt all operation and cleanup.



213
214
215
# File 'lib/reterm/init.rb', line 213

def shutdown!
  @@shutdown = true
end

#shutdown?Boolean

Boolean indicating if app in being halted

Returns:

  • (Boolean)


218
219
220
# File 'lib/reterm/init.rb', line 218

def shutdown?
  !!@@shutdown
end

#stop_track_resizeObject

Terminate resize tracking thread (if running)



34
35
36
37
# File 'lib/reterm/resize.rb', line 34

def stop_track_resize
  @track_resize = false
  @resize_thread.join if @resize_thread
end

#sync_enabled?Boolean

Boonean indicating if component syncronization is enabled

Returns:

  • (Boolean)


195
196
197
# File 'lib/reterm/init.rb', line 195

def sync_enabled?
  defined?(@@sync_activated) && !!@@sync_activated
end

#track_resizeObject

Internal helper to track size of terminal. This is a simple mechanism that launches a worker thread to periodically poll terminal size.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/reterm/resize.rb', line 13

def track_resize
  @track_resize = true
  d = Terminal.dimensions

  @resize_thread = Thread.new {
    while @track_resize
      Terminal.reset!
      t = Terminal.dimensions

      if t != d && !shutdown?
        Terminal.resize!
        @on_resize.call if !!@on_resize
      end

      d = Terminal.dimensions
      sleep(RESIZE_TIME)
    end
  }
end

#update_reterm(force_refresh = false) ⇒ Object

This method resyncs the visible terminal with the internal RETerm environment. It should be called any time the user needs to update the UI after making changes.

Examples:

init_reterm {
  win = Window.new
  win.border! # draw window border
  update_rti  # resync terminal ouput
  win.getch   # wait for input
}


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

def update_reterm(force_refresh=false)
  if force_refresh
    Window.top.each { |w|
      w.erase
      w.draw!
    }
  else
    Window.top.each { |w| w.noutrefresh }
  end

  #Ncurses::Panel.update_panels
  Ncurses.doupdate
end