class Ansi
require 'io/console' # for stdin.raw (cur_pos?)
def initialize
{
cls: proc { → '2J' }, # clear entire screen
cls_l: proc { → '1J' }, # clear screen left of cursor
cls_r: proc { → '0J' }, # clear screen right of cursor
cl_l: proc { → '1K' }, # clear line left of cursor
cl_r: proc { → '0K' }, # clear line right of cursor
cl_ln: proc { → '2K' }, # clear line
# hide or show cursor
cur: proc {|show=true| → show ? '?25h' : '?25l' },
cur_n: proc {|step=1| → '%sA' % [step] }, # cursor North
cur_s: proc {|step=1| → '%sB' % [step] }, # cursor South
cur_e: proc {|step=1| → '%sC' % [step] }, # cursor East
cur_w: proc {|step=1| → '%sD' % [step] }, # cursor West
cur_u: proc {|step=1| X.cur_n(step) }, # aliases Up
cur_d: proc {|step=1| X.cur_s(step) }, # Down
cur_r: proc {|step=1| X.cur_e(step) }, # Right
cur_l: proc {|step=1| X.cur_w(step) }, # Left
cur_col: proc {|col=0| → '%sG' % [col] }, # move to column
cur_at: proc {|ln, col| → '%s;%sH' % [ln, col] },
# print at
pat: proc {|ln, col, str| → '%s;%sH' % [ln, col]; print str },
cur_pos?: proc { # get cursor position
res = ''
$stdin.raw {|stdin|
$stdout << "\e[6n"; $stdout.flush
while (c = stdin.getc) != 'R' do; res << c if c; end
}
m = res.match /(?<ln>\d+);(?<col>\d+)/
Struct.new(:ln, :col).new(*m.to_a[1..].map(&:to_i))
}, # :bar, :line, :block or default (not given)
# shape can be :block, :line or :bar. blink: true or FALSE
cur_shape: proc {|shape, blink: true| # cursor shape and blink
val = shape ?
(%i(block line bar).index(shape) * 2 + (blink ? 1 : 2)) : 0
→ '%s q' % [val]
},
cur_color: proc {|col| # cursor color
print "\e]12;#{col}\a" if col[/^#[0-9A-F]{6}$/i]
},
cur_sto: proc { system('tput sc') }, # store cursor position
cur_rec: proc { system('tput rc') }, # recall cursor position
scr_sto: proc { system('tput smcup') }, # store screen
scr_rec: proc { system('tput rmcup') }, # recall screen
# columns and lines of terminal window
scr_size: proc { [`tput cols`.to_i, `tput lines`.to_i] },
# init and de-init (not required)
init: proc { X.scr_sto; X.cls; X.cur(false) },
de_init: proc {
X.scr_rec; X.cur;
X.cur_shape; X.cur_color('#FFFFFF')
},
}.each {|k,v|
define_singleton_method(k) {|*args, **kw| v.call(*args, **kw) }
}
end
# print ESC method
def →(s) = ( print "\e[#{s}" )
end
# =============================================================================
# Usage:
X = Ansi.new
X.init # save screen, clear screen, hide cursor
cols, lines = X.scr_size # get screen size (columns, lines)
X.cur_at(10,10); print 'HEJ' # position cursor
X.pat(12,10, 'HEJ!!') # position cursor and print
pos = X.cur_pos? # get cursor position
print ' (%s, %s)' %
[pos.ln, pos.col]
sleep(2)
print ' red, blinking bar cursor'
X.cur(true) # show cursor
X.cur_color('#FF0000') # set cursor color
X.cur_shape(:line, blink: false)# set cursor to steady bar
sleep(2)
X.de_init # restore screen, standard cursor visible