forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			350 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			350 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
"""This implements a virtual screen. This is used to support ANSI terminal
 | 
						|
emulation. The screen representation and state is implemented in this class.
 | 
						|
Most of the methods are inspired by ANSI screen control codes. The ANSI class
 | 
						|
extends this class to add parsing of ANSI escape codes.
 | 
						|
 | 
						|
$Id: screen.py 486 2007-07-13 01:04:16Z noah $
 | 
						|
"""
 | 
						|
 | 
						|
import copy
 | 
						|
 | 
						|
NUL = 0    # Fill character; ignored on input.
 | 
						|
ENQ = 5    # Transmit answerback message.
 | 
						|
BEL = 7    # Ring the bell.
 | 
						|
BS = 8    # Move cursor left.
 | 
						|
HT = 9    # Move cursor to next tab stop.
 | 
						|
LF = 10    # Line feed.
 | 
						|
VT = 11    # Same as LF.
 | 
						|
FF = 12    # Same as LF.
 | 
						|
CR = 13    # Move cursor to left margin or newline.
 | 
						|
SO = 14    # Invoke G1 character set.
 | 
						|
SI = 15    # Invoke G0 character set.
 | 
						|
XON = 17   # Resume transmission.
 | 
						|
XOFF = 19  # Halt transmission.
 | 
						|
CAN = 24   # Cancel escape sequence.
 | 
						|
SUB = 26   # Same as CAN.
 | 
						|
ESC = 27   # Introduce a control sequence.
 | 
						|
DEL = 127  # Fill character; ignored on input.
 | 
						|
SPACE = chr(32)  # Space or blank character.
 | 
						|
 | 
						|
 | 
						|
def constrain(n, min, max):
 | 
						|
    """This returns a number, n constrained to the min and max bounds. """
 | 
						|
 | 
						|
    if n < min:
 | 
						|
        return min
 | 
						|
    if n > max:
 | 
						|
        return max
 | 
						|
    return n
 | 
						|
 | 
						|
 | 
						|
class screen:
 | 
						|
 | 
						|
    """This object maintains the state of a virtual text screen as a
 | 
						|
    rectangluar array. This maintains a virtual cursor position and handles
 | 
						|
    scrolling as characters are added. This supports most of the methods needed
 | 
						|
    by an ANSI text screen. Row and column indexes are 1-based (not zero-based,
 | 
						|
    like arrays). """
 | 
						|
 | 
						|
    def __init__(self, r=24, c=80):
 | 
						|
        """This initializes a blank scree of the given dimentions."""
 | 
						|
 | 
						|
        self.rows = r
 | 
						|
        self.cols = c
 | 
						|
        self.cur_r = 1
 | 
						|
        self.cur_c = 1
 | 
						|
        self.cur_saved_r = 1
 | 
						|
        self.cur_saved_c = 1
 | 
						|
        self.scroll_row_start = 1
 | 
						|
        self.scroll_row_end = self.rows
 | 
						|
        self.w = [[SPACE] * self.cols for c in range(self.rows)]
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        """This returns a printable representation of the screen. The end of
 | 
						|
        each screen line is terminated by a newline. """
 | 
						|
 | 
						|
        return '\n'.join([''.join(c) for c in self.w])
 | 
						|
 | 
						|
    def dump(self):
 | 
						|
        """This returns a copy of the screen as a string. This is similar to
 | 
						|
        __str__ except that lines are not terminated with line feeds. """
 | 
						|
 | 
						|
        return ''.join([''.join(c) for c in self.w])
 | 
						|
 | 
						|
    def pretty(self):
 | 
						|
        """This returns a copy of the screen as a string with an ASCII text box
 | 
						|
        around the screen border. This is similar to __str__ except that it
 | 
						|
        adds a box. """
 | 
						|
 | 
						|
        top_bot = '+' + '-' * self.cols + '+\n'
 | 
						|
        return top_bot + \
 | 
						|
            '\n'.join(['|' + line + '|' for line in str(self).split('\n')]) + '\n' + top_bot
 | 
						|
 | 
						|
    def fill(self, ch=SPACE):
 | 
						|
 | 
						|
        self.fill_region(1, 1, self.rows, self.cols, ch)
 | 
						|
 | 
						|
    def fill_region(self, rs, cs, re, ce, ch=SPACE):
 | 
						|
 | 
						|
        rs = constrain(rs, 1, self.rows)
 | 
						|
        re = constrain(re, 1, self.rows)
 | 
						|
        cs = constrain(cs, 1, self.cols)
 | 
						|
        ce = constrain(ce, 1, self.cols)
 | 
						|
        if rs > re:
 | 
						|
            rs, re = re, rs
 | 
						|
        if cs > ce:
 | 
						|
            cs, ce = ce, cs
 | 
						|
        for r in range(rs, re + 1):
 | 
						|
            for c in range(cs, ce + 1):
 | 
						|
                self.put_abs(r, c, ch)
 | 
						|
 | 
						|
    def cr(self):
 | 
						|
        """This moves the cursor to the beginning (col 1) of the current row.
 | 
						|
        """
 | 
						|
 | 
						|
        self.cursor_home(self.cur_r, 1)
 | 
						|
 | 
						|
    def lf(self):
 | 
						|
        """This moves the cursor down with scrolling.
 | 
						|
        """
 | 
						|
 | 
						|
        old_r = self.cur_r
 | 
						|
        self.cursor_down()
 | 
						|
        if old_r == self.cur_r:
 | 
						|
            self.scroll_up()
 | 
						|
            self.erase_line()
 | 
						|
 | 
						|
    def crlf(self):
 | 
						|
        """This advances the cursor with CRLF properties.
 | 
						|
        The cursor will line wrap and the screen may scroll.
 | 
						|
        """
 | 
						|
 | 
						|
        self.cr()
 | 
						|
        self.lf()
 | 
						|
 | 
						|
    def newline(self):
 | 
						|
        """This is an alias for crlf().
 | 
						|
        """
 | 
						|
 | 
						|
        self.crlf()
 | 
						|
 | 
						|
    def put_abs(self, r, c, ch):
 | 
						|
        """Screen array starts at 1 index."""
 | 
						|
 | 
						|
        r = constrain(r, 1, self.rows)
 | 
						|
        c = constrain(c, 1, self.cols)
 | 
						|
        ch = str(ch)[0]
 | 
						|
        self.w[r - 1][c - 1] = ch
 | 
						|
 | 
						|
    def put(self, ch):
 | 
						|
        """This puts a characters at the current cursor position.
 | 
						|
        """
 | 
						|
 | 
						|
        self.put_abs(self.cur_r, self.cur_c, ch)
 | 
						|
 | 
						|
    def insert_abs(self, r, c, ch):
 | 
						|
        """This inserts a character at (r,c). Everything under
 | 
						|
        and to the right is shifted right one character.
 | 
						|
        The last character of the line is lost.
 | 
						|
        """
 | 
						|
 | 
						|
        r = constrain(r, 1, self.rows)
 | 
						|
        c = constrain(c, 1, self.cols)
 | 
						|
        for ci in range(self.cols, c, -1):
 | 
						|
            self.put_abs(r, ci, self.get_abs(r, ci - 1))
 | 
						|
        self.put_abs(r, c, ch)
 | 
						|
 | 
						|
    def insert(self, ch):
 | 
						|
 | 
						|
        self.insert_abs(self.cur_r, self.cur_c, ch)
 | 
						|
 | 
						|
    def get_abs(self, r, c):
 | 
						|
 | 
						|
        r = constrain(r, 1, self.rows)
 | 
						|
        c = constrain(c, 1, self.cols)
 | 
						|
        return self.w[r - 1][c - 1]
 | 
						|
 | 
						|
    def get(self):
 | 
						|
 | 
						|
        self.get_abs(self.cur_r, self.cur_c)
 | 
						|
 | 
						|
    def get_region(self, rs, cs, re, ce):
 | 
						|
        """This returns a list of lines representing the region.
 | 
						|
        """
 | 
						|
 | 
						|
        rs = constrain(rs, 1, self.rows)
 | 
						|
        re = constrain(re, 1, self.rows)
 | 
						|
        cs = constrain(cs, 1, self.cols)
 | 
						|
        ce = constrain(ce, 1, self.cols)
 | 
						|
        if rs > re:
 | 
						|
            rs, re = re, rs
 | 
						|
        if cs > ce:
 | 
						|
            cs, ce = ce, cs
 | 
						|
        sc = []
 | 
						|
        for r in range(rs, re + 1):
 | 
						|
            line = ''
 | 
						|
            for c in range(cs, ce + 1):
 | 
						|
                ch = self.get_abs(r, c)
 | 
						|
                line = line + ch
 | 
						|
            sc.append(line)
 | 
						|
        return sc
 | 
						|
 | 
						|
    def cursor_constrain(self):
 | 
						|
        """This keeps the cursor within the screen area.
 | 
						|
        """
 | 
						|
 | 
						|
        self.cur_r = constrain(self.cur_r, 1, self.rows)
 | 
						|
        self.cur_c = constrain(self.cur_c, 1, self.cols)
 | 
						|
 | 
						|
    def cursor_home(self, r=1, c=1):  # <ESC>[{ROW};{COLUMN}H
 | 
						|
 | 
						|
        self.cur_r = r
 | 
						|
        self.cur_c = c
 | 
						|
        self.cursor_constrain()
 | 
						|
 | 
						|
    def cursor_back(self, count=1):  # <ESC>[{COUNT}D (not confused with down)
 | 
						|
 | 
						|
        self.cur_c = self.cur_c - count
 | 
						|
        self.cursor_constrain()
 | 
						|
 | 
						|
    def cursor_down(self, count=1):  # <ESC>[{COUNT}B (not confused with back)
 | 
						|
 | 
						|
        self.cur_r = self.cur_r + count
 | 
						|
        self.cursor_constrain()
 | 
						|
 | 
						|
    def cursor_forward(self, count=1):  # <ESC>[{COUNT}C
 | 
						|
 | 
						|
        self.cur_c = self.cur_c + count
 | 
						|
        self.cursor_constrain()
 | 
						|
 | 
						|
    def cursor_up(self, count=1):  # <ESC>[{COUNT}A
 | 
						|
 | 
						|
        self.cur_r = self.cur_r - count
 | 
						|
        self.cursor_constrain()
 | 
						|
 | 
						|
    def cursor_up_reverse(self):  # <ESC> M   (called RI -- Reverse Index)
 | 
						|
 | 
						|
        old_r = self.cur_r
 | 
						|
        self.cursor_up()
 | 
						|
        if old_r == self.cur_r:
 | 
						|
            self.scroll_up()
 | 
						|
 | 
						|
    def cursor_force_position(self, r, c):  # <ESC>[{ROW};{COLUMN}f
 | 
						|
        """Identical to Cursor Home."""
 | 
						|
 | 
						|
        self.cursor_home(r, c)
 | 
						|
 | 
						|
    def cursor_save(self):  # <ESC>[s
 | 
						|
        """Save current cursor position."""
 | 
						|
 | 
						|
        self.cursor_save_attrs()
 | 
						|
 | 
						|
    def cursor_unsave(self):  # <ESC>[u
 | 
						|
        """Restores cursor position after a Save Cursor."""
 | 
						|
 | 
						|
        self.cursor_restore_attrs()
 | 
						|
 | 
						|
    def cursor_save_attrs(self):  # <ESC>7
 | 
						|
        """Save current cursor position."""
 | 
						|
 | 
						|
        self.cur_saved_r = self.cur_r
 | 
						|
        self.cur_saved_c = self.cur_c
 | 
						|
 | 
						|
    def cursor_restore_attrs(self):  # <ESC>8
 | 
						|
        """Restores cursor position after a Save Cursor."""
 | 
						|
 | 
						|
        self.cursor_home(self.cur_saved_r, self.cur_saved_c)
 | 
						|
 | 
						|
    def scroll_constrain(self):
 | 
						|
        """This keeps the scroll region within the screen region."""
 | 
						|
 | 
						|
        if self.scroll_row_start <= 0:
 | 
						|
            self.scroll_row_start = 1
 | 
						|
        if self.scroll_row_end > self.rows:
 | 
						|
            self.scroll_row_end = self.rows
 | 
						|
 | 
						|
    def scroll_screen(self):  # <ESC>[r
 | 
						|
        """Enable scrolling for entire display."""
 | 
						|
 | 
						|
        self.scroll_row_start = 1
 | 
						|
        self.scroll_row_end = self.rows
 | 
						|
 | 
						|
    def scroll_screen_rows(self, rs, re):  # <ESC>[{start};{end}r
 | 
						|
        """Enable scrolling from row {start} to row {end}."""
 | 
						|
 | 
						|
        self.scroll_row_start = rs
 | 
						|
        self.scroll_row_end = re
 | 
						|
        self.scroll_constrain()
 | 
						|
 | 
						|
    def scroll_down(self):  # <ESC>D
 | 
						|
        """Scroll display down one line."""
 | 
						|
 | 
						|
        # Screen is indexed from 1, but arrays are indexed from 0.
 | 
						|
        s = self.scroll_row_start - 1
 | 
						|
        e = self.scroll_row_end - 1
 | 
						|
        self.w[s + 1:e + 1] = copy.deepcopy(self.w[s:e])
 | 
						|
 | 
						|
    def scroll_up(self):  # <ESC>M
 | 
						|
        """Scroll display up one line."""
 | 
						|
 | 
						|
        # Screen is indexed from 1, but arrays are indexed from 0.
 | 
						|
        s = self.scroll_row_start - 1
 | 
						|
        e = self.scroll_row_end - 1
 | 
						|
        self.w[s:e] = copy.deepcopy(self.w[s + 1:e + 1])
 | 
						|
 | 
						|
    def erase_end_of_line(self):  # <ESC>[0K -or- <ESC>[K
 | 
						|
        """Erases from the current cursor position to the end of the current
 | 
						|
        line."""
 | 
						|
 | 
						|
        self.fill_region(self.cur_r, self.cur_c, self.cur_r, self.cols)
 | 
						|
 | 
						|
    def erase_start_of_line(self):  # <ESC>[1K
 | 
						|
        """Erases from the current cursor position to the start of the current
 | 
						|
        line."""
 | 
						|
 | 
						|
        self.fill_region(self.cur_r, 1, self.cur_r, self.cur_c)
 | 
						|
 | 
						|
    def erase_line(self):  # <ESC>[2K
 | 
						|
        """Erases the entire current line."""
 | 
						|
 | 
						|
        self.fill_region(self.cur_r, 1, self.cur_r, self.cols)
 | 
						|
 | 
						|
    def erase_down(self):  # <ESC>[0J -or- <ESC>[J
 | 
						|
        """Erases the screen from the current line down to the bottom of the
 | 
						|
        screen."""
 | 
						|
 | 
						|
        self.erase_end_of_line()
 | 
						|
        self.fill_region(self.cur_r + 1, 1, self.rows, self.cols)
 | 
						|
 | 
						|
    def erase_up(self):  # <ESC>[1J
 | 
						|
        """Erases the screen from the current line up to the top of the
 | 
						|
        screen."""
 | 
						|
 | 
						|
        self.erase_start_of_line()
 | 
						|
        self.fill_region(self.cur_r - 1, 1, 1, self.cols)
 | 
						|
 | 
						|
    def erase_screen(self):  # <ESC>[2J
 | 
						|
        """Erases the screen with the background color."""
 | 
						|
 | 
						|
        self.fill()
 | 
						|
 | 
						|
    def set_tab(self):  # <ESC>H
 | 
						|
        """Sets a tab at the current position."""
 | 
						|
 | 
						|
        pass
 | 
						|
 | 
						|
    def clear_tab(self):  # <ESC>[g
 | 
						|
        """Clears tab at the current position."""
 | 
						|
 | 
						|
        pass
 | 
						|
 | 
						|
    def clear_all_tabs(self):  # <ESC>[3g
 | 
						|
        """Clears all tabs."""
 | 
						|
 | 
						|
        pass
 | 
						|
 | 
						|
#        Insert line             Esc [ Pn L
 | 
						|
#        Delete line             Esc [ Pn M
 | 
						|
#        Delete character        Esc [ Pn P
 | 
						|
#        Scrolling region        Esc [ Pn(top);Pn(bot) r
 |