#!/usr/bin/env python import os import empty import cmd_ut import io_ut def esc_seq_new(): esc = lambda s: chr(0x1b) + '[' + s esc_ex = lambda d, v: esc( '?{}{}'.format(d, 'h' if v else 'l' ) ) cursor_show = lambda v: esc_ex( 25, v ) mov = lambda x, y: esc( '{};{}H'.format(y+1, x+1) ) scroll = lambda m: esc( '{}{}'.format( abs(m), 'S' if m > 0 else 'T' ) ) cls = esc('2J') reset = esc('0m') rev = esc('7m') uline = esc('4m') return empty.new( locals() ) def get_wh(): w = int( cmd_ut.call('tput cols').strip() ) h = int( cmd_ut.call('tput lines').strip() ) return (w, h) dev = '/dev/tty' def stty(s, v=True): if not v: s = '-' + s cmd = 'stty {} < {}'.format(s, dev) cmd_ut.call(cmd) def new(): esc_seq = esc_seq_new() fr = open(dev, 'r') fw = open(dev, 'w') def get_xy(): cmds = [ #'echo -en "\e[6n"', 'echo -en "\x1b[6n"', 'read -sd"[" dmy', 'read -sd"R" row_col', 'echo -n $row_col >&2', ] cmd = "bash -c '{}'".format( '\n'.join(cmds) ) proc = cmd_ut.proc_new(cmd, stderr=cmd_ut.PIPE) s = proc.communicate()[1].decode() proc.wait() (row, col) = map( int, s.split(';') ) (x, y) = (col-1, row-1) return (x, y) def disp_new(): (w, h) = wh = get_wh() def out(s): fw.write(s) fw.flush() return s cursor_show = lambda v: out( esc_seq.cursor_show(v) ) mov = lambda x, y: out( esc_seq.mov(x, y) ) scroll = lambda m: out( esc_seq.scroll(m) ) cls = lambda : out( esc_seq.cls ) reset = lambda : out( esc_seq.reset ) rev = lambda : out( esc_seq.rev ) uline = lambda : out( esc_seq.uline ) return empty.new( locals(), get_xy=get_xy ) disp = disp_new() def key_new(): def init(): stty( 'echo', False ) stty( 'icanon', False ) disp.cursor_show( False ) def fini(): disp.cursor_show( True ) stty( 'icanon', True ) stty( 'echo', True ) readable = lambda : io_ut.readable( fr ) def get_block(): s = os.read( fr.fileno(), 10 ) if type(s) != str: s = ''.join( map( chr, s ) ) # !!! decode()? return s get = lambda : get_block() if readable() else '' ctl = lambda s: chr( 1 + ord(s) - ord('a') ) # control key esc = esc_seq.esc # esc key allow_tbls = (( ( 'u', 'd', 'l', 'r' ), ( esc('A'), esc('B'), esc('D'), esc('C') ), # allow ( ctl('p'), ctl('n'), ctl('b'), ctl('f') ), # emacs ( 'k', 'j', 'h', 'l' ), # vi ), ( # PAGE ( 'pu', 'pd', 'pl', 'pr' ), ( esc('5~'), esc('6~'), esc('8~'), esc('7~') ), # allow ( chr(0xf6), ctl('v'), ctl('a'), ctl('e') ), # emacs ( 'b', 'f', '^', '$' ), # less, vi )) allow_dic = {} for tbl in allow_tbls: lst0 = tbl[0] for lst in tbl[1:]: for (k, v) in zip(lst, lst0): allow_dic[k] = v k_to_allow = lambda k, dv='': allow_dic.get( k, dv ) # return 'u', 'd', 'l', 'r', 'pu', 'pd', pl', 'pr' dxy_dic = { 'l': (-1,0), 'r': (1,0), 'u': (0,-1), 'd': (0,1) } d_to_dxy = lambda d, dv=None: dxy_dic.get( d, dv ) k_to_dxy = lambda k, dv=None: d_to_dxy( k_to_allow(k), dv ) is_ok = lambda k: k in ('\n', ' ') is_cancel = lambda k: k in ( chr(0x1b) ) is_allow = lambda k: k_to_allow(k) != '' return empty.new( locals() ) key = key_new() def buff_new(w, h): def c_new(c=' ', rev=False, uline=False): def set_attr(prev): if rev == prev.rev and uline == prev.uline: return if rev == False or uline == False: disp.reset() if rev: disp.rev() if uline: disp.uline() def reset(disp): if rev or uline: disp.reset() return empty.new( locals() ) def arr_xy_new(w, h): arr = list( map( lambda y: list( map( lambda x: c_new(), range( w ) ) ), range( h ) ) ) get = lambda x, y: arr[y][x] def set(x, y, c): arr[y][x] = c return c return empty.new( locals() ) arr_xy = arr_xy_new( w, h ) def ids_new(w, h): xy_to_id = lambda x, y: y * w + x id_to_xy = lambda id: ( id % w, id // w ) lst = [] def add(x, y): id = xy_to_id( x, y ) if id not in lst: lst.append( id ) lst.sort() get = lambda : list( map( id_to_xy, lst ) ) def clear(): del lst[:] return empty.new( locals() ) ids = ids_new( w, h ) def init_by(): (bx, by) = disp.get_xy() sc = max( h - ( disp.h - by ), 0 ) if sc > 0: disp.scroll( sc ) by -= sc return by by = init_by() def putc(x, y, c): prev = arr_xy.get( x, y ) if vars( prev ) != vars( c ): arr_xy.set( x, y, c ) ids.add( x, y ) def fill(x, y, w_, h_, c): for j in range( h_ ): for i in range( w_ ): x_ = ( x + i ) % w y_ = ( y + j ) % h putc( x_, y_, c ) def puts(x, y, s): # \tr1 \tr0 \tu0 \tu1 lst = s.split('\n') (rev, uline) = ( False, False ) for (j, s) in enumerate( lst ): i = 0 while s: (c, s) = ( s[0], s[1:] ) if c == '\t' and len(s) >= 2: (k, v, s) = ( s[0], s[1], s[2:] ) v = ( v != '0' ) if k == 'r': rev = v elif k == 'u': uline = v continue putc( x + i, y + j, c_new( c, rev, uline ) ) i += 1 def update(): disp.reset() (cx, cy, cc) = ( w, h, c_new() ) for (x, y) in ids.get(): if not( y == cy and x == cx ): disp.mov( x, by + y ) (cx, cy) = ( x, y ) c = arr_xy.get( x, y ) c.set_attr( cc ) disp.out( c.c ) (cx, cy, cc) = ( cx + 1, y, c ) ids.clear() cc.reset( disp ) def fini(): disp.reset() disp.mov( w - 1, by + h - 1 ) disp.out( '\n' ) return empty.new( locals() ) return empty.new( locals() ) # EOF