Home | History | Annotate | Download | only in pexpect-2.4
      1 """This implements an ANSI terminal emulator as a subclass of screen.
      2 
      3 $Id: ANSI.py 491 2007-12-16 20:04:57Z noah $
      4 """
      5 # references:
      6 #     http://www.retards.org/terminals/vt102.html
      7 #     http://vt100.net/docs/vt102-ug/contents.html
      8 #     http://vt100.net/docs/vt220-rm/
      9 #     http://www.termsys.demon.co.uk/vtansi.htm
     10 
     11 import screen
     12 import FSM
     13 import copy
     14 import string
     15 
     16 def Emit (fsm):
     17 
     18     screen = fsm.memory[0]
     19     screen.write_ch(fsm.input_symbol)
     20 
     21 def StartNumber (fsm):
     22 
     23     fsm.memory.append (fsm.input_symbol)
     24 
     25 def BuildNumber (fsm):
     26 
     27     ns = fsm.memory.pop()
     28     ns = ns + fsm.input_symbol
     29     fsm.memory.append (ns)
     30 
     31 def DoBackOne (fsm):
     32 
     33     screen = fsm.memory[0]
     34     screen.cursor_back ()
     35 
     36 def DoBack (fsm):
     37 
     38     count = int(fsm.memory.pop())
     39     screen = fsm.memory[0]
     40     screen.cursor_back (count)
     41 
     42 def DoDownOne (fsm):
     43 
     44     screen = fsm.memory[0]
     45     screen.cursor_down ()
     46 
     47 def DoDown (fsm):
     48 
     49     count = int(fsm.memory.pop())
     50     screen = fsm.memory[0]
     51     screen.cursor_down (count)
     52 
     53 def DoForwardOne (fsm):
     54 
     55     screen = fsm.memory[0]
     56     screen.cursor_forward ()
     57 
     58 def DoForward (fsm):
     59 
     60     count = int(fsm.memory.pop())
     61     screen = fsm.memory[0]
     62     screen.cursor_forward (count)
     63 
     64 def DoUpReverse (fsm):
     65 
     66     screen = fsm.memory[0]
     67     screen.cursor_up_reverse()
     68 
     69 def DoUpOne (fsm):
     70 
     71     screen = fsm.memory[0]
     72     screen.cursor_up ()
     73 
     74 def DoUp (fsm):
     75 
     76     count = int(fsm.memory.pop())
     77     screen = fsm.memory[0]
     78     screen.cursor_up (count)
     79 
     80 def DoHome (fsm):
     81 
     82     c = int(fsm.memory.pop())
     83     r = int(fsm.memory.pop())
     84     screen = fsm.memory[0]
     85     screen.cursor_home (r,c)
     86 
     87 def DoHomeOrigin (fsm):
     88 
     89     c = 1
     90     r = 1
     91     screen = fsm.memory[0]
     92     screen.cursor_home (r,c)
     93 
     94 def DoEraseDown (fsm):
     95 
     96     screen = fsm.memory[0]
     97     screen.erase_down()
     98 
     99 def DoErase (fsm):
    100 
    101     arg = int(fsm.memory.pop())
    102     screen = fsm.memory[0]
    103     if arg == 0:
    104         screen.erase_down()
    105     elif arg == 1:
    106         screen.erase_up()
    107     elif arg == 2:
    108         screen.erase_screen()
    109 
    110 def DoEraseEndOfLine (fsm):
    111 
    112     screen = fsm.memory[0]
    113     screen.erase_end_of_line()
    114 
    115 def DoEraseLine (fsm):
    116 
    117     screen = fsm.memory[0]
    118     if arg == 0:
    119         screen.end_of_line()
    120     elif arg == 1:
    121         screen.start_of_line()
    122     elif arg == 2:
    123         screen.erase_line()
    124 
    125 def DoEnableScroll (fsm):
    126 
    127     screen = fsm.memory[0]
    128     screen.scroll_screen()
    129 
    130 def DoCursorSave (fsm):
    131 
    132     screen = fsm.memory[0]
    133     screen.cursor_save_attrs()
    134 
    135 def DoCursorRestore (fsm):
    136 
    137     screen = fsm.memory[0]
    138     screen.cursor_restore_attrs()
    139 
    140 def DoScrollRegion (fsm):
    141 
    142     screen = fsm.memory[0]
    143     r2 = int(fsm.memory.pop())
    144     r1 = int(fsm.memory.pop())
    145     screen.scroll_screen_rows (r1,r2)
    146 
    147 def DoMode (fsm):
    148 
    149     screen = fsm.memory[0]
    150     mode = fsm.memory.pop() # Should be 4
    151     # screen.setReplaceMode ()
    152 
    153 def Log (fsm):
    154 
    155     screen = fsm.memory[0]
    156     fsm.memory = [screen]
    157     fout = open ('log', 'a')
    158     fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n')
    159     fout.close()
    160 
    161 class term (screen.screen):
    162     """This is a placeholder. 
    163     In theory I might want to add other terminal types.
    164     """
    165     def __init__ (self, r=24, c=80):
    166         screen.screen.__init__(self, r,c)
    167 
    168 class ANSI (term):
    169 
    170     """This class encapsulates a generic terminal. It filters a stream and
    171     maintains the state of a screen object. """
    172 
    173     def __init__ (self, r=24,c=80):
    174 
    175         term.__init__(self,r,c)
    176 
    177         #self.screen = screen (24,80)
    178         self.state = FSM.FSM ('INIT',[self])
    179         self.state.set_default_transition (Log, 'INIT')
    180         self.state.add_transition_any ('INIT', Emit, 'INIT')
    181         self.state.add_transition ('\x1b', 'INIT', None, 'ESC')
    182         self.state.add_transition_any ('ESC', Log, 'INIT')
    183         self.state.add_transition ('(', 'ESC', None, 'G0SCS')
    184         self.state.add_transition (')', 'ESC', None, 'G1SCS')
    185         self.state.add_transition_list ('AB012', 'G0SCS', None, 'INIT')
    186         self.state.add_transition_list ('AB012', 'G1SCS', None, 'INIT')
    187         self.state.add_transition ('7', 'ESC', DoCursorSave, 'INIT')
    188         self.state.add_transition ('8', 'ESC', DoCursorRestore, 'INIT')
    189         self.state.add_transition ('M', 'ESC', DoUpReverse, 'INIT')
    190         self.state.add_transition ('>', 'ESC', DoUpReverse, 'INIT')
    191         self.state.add_transition ('<', 'ESC', DoUpReverse, 'INIT')
    192         self.state.add_transition ('=', 'ESC', None, 'INIT') # Selects application keypad.
    193         self.state.add_transition ('#', 'ESC', None, 'GRAPHICS_POUND')
    194         self.state.add_transition_any ('GRAPHICS_POUND', None, 'INIT')
    195         self.state.add_transition ('[', 'ESC', None, 'ELB')
    196         # ELB means Escape Left Bracket. That is ^[[
    197         self.state.add_transition ('H', 'ELB', DoHomeOrigin, 'INIT')
    198         self.state.add_transition ('D', 'ELB', DoBackOne, 'INIT')
    199         self.state.add_transition ('B', 'ELB', DoDownOne, 'INIT')
    200         self.state.add_transition ('C', 'ELB', DoForwardOne, 'INIT')
    201         self.state.add_transition ('A', 'ELB', DoUpOne, 'INIT')
    202         self.state.add_transition ('J', 'ELB', DoEraseDown, 'INIT')
    203         self.state.add_transition ('K', 'ELB', DoEraseEndOfLine, 'INIT')
    204         self.state.add_transition ('r', 'ELB', DoEnableScroll, 'INIT')
    205         self.state.add_transition ('m', 'ELB', None, 'INIT')
    206         self.state.add_transition ('?', 'ELB', None, 'MODECRAP')
    207         self.state.add_transition_list (string.digits, 'ELB', StartNumber, 'NUMBER_1')
    208         self.state.add_transition_list (string.digits, 'NUMBER_1', BuildNumber, 'NUMBER_1')
    209         self.state.add_transition ('D', 'NUMBER_1', DoBack, 'INIT')
    210         self.state.add_transition ('B', 'NUMBER_1', DoDown, 'INIT')
    211         self.state.add_transition ('C', 'NUMBER_1', DoForward, 'INIT')
    212         self.state.add_transition ('A', 'NUMBER_1', DoUp, 'INIT')
    213         self.state.add_transition ('J', 'NUMBER_1', DoErase, 'INIT')
    214         self.state.add_transition ('K', 'NUMBER_1', DoEraseLine, 'INIT')
    215         self.state.add_transition ('l', 'NUMBER_1', DoMode, 'INIT')
    216         ### It gets worse... the 'm' code can have infinite number of
    217         ### number;number;number before it. I've never seen more than two,
    218         ### but the specs say it's allowed. crap!
    219         self.state.add_transition ('m', 'NUMBER_1', None, 'INIT')
    220         ### LED control. Same problem as 'm' code.
    221         self.state.add_transition ('q', 'NUMBER_1', None, 'INIT') 
    222         
    223         # \E[?47h appears to be "switch to alternate screen"
    224         # \E[?47l restores alternate screen... I think.
    225         self.state.add_transition_list (string.digits, 'MODECRAP', StartNumber, 'MODECRAP_NUM')
    226         self.state.add_transition_list (string.digits, 'MODECRAP_NUM', BuildNumber, 'MODECRAP_NUM')
    227         self.state.add_transition ('l', 'MODECRAP_NUM', None, 'INIT')
    228         self.state.add_transition ('h', 'MODECRAP_NUM', None, 'INIT')
    229 
    230 #RM   Reset Mode                Esc [ Ps l                   none
    231         self.state.add_transition (';', 'NUMBER_1', None, 'SEMICOLON')
    232         self.state.add_transition_any ('SEMICOLON', Log, 'INIT')
    233         self.state.add_transition_list (string.digits, 'SEMICOLON', StartNumber, 'NUMBER_2')
    234         self.state.add_transition_list (string.digits, 'NUMBER_2', BuildNumber, 'NUMBER_2')
    235         self.state.add_transition_any ('NUMBER_2', Log, 'INIT')
    236         self.state.add_transition ('H', 'NUMBER_2', DoHome, 'INIT')
    237         self.state.add_transition ('f', 'NUMBER_2', DoHome, 'INIT')
    238         self.state.add_transition ('r', 'NUMBER_2', DoScrollRegion, 'INIT')
    239         ### It gets worse... the 'm' code can have infinite number of
    240         ### number;number;number before it. I've never seen more than two,
    241         ### but the specs say it's allowed. crap!
    242         self.state.add_transition ('m', 'NUMBER_2', None, 'INIT')
    243         ### LED control. Same problem as 'm' code.
    244         self.state.add_transition ('q', 'NUMBER_2', None, 'INIT') 
    245 
    246     def process (self, c):
    247 
    248         self.state.process(c)
    249 
    250     def process_list (self, l):
    251 
    252         self.write(l)
    253 
    254     def write (self, s):
    255 
    256         for c in s:
    257             self.process(c)
    258 
    259     def flush (self):
    260 
    261         pass
    262 
    263     def write_ch (self, ch):
    264 
    265         """This puts a character at the current cursor position. cursor
    266         position if moved forward with wrap-around, but no scrolling is done if
    267         the cursor hits the lower-right corner of the screen. """
    268 
    269         #\r and \n both produce a call to crlf().
    270         ch = ch[0]
    271 
    272         if ch == '\r':
    273         #    self.crlf()
    274             return
    275         if ch == '\n':
    276             self.crlf()
    277             return
    278         if ch == chr(screen.BS):
    279             self.cursor_back()
    280             self.put_abs(self.cur_r, self.cur_c, ' ')
    281             return
    282 
    283         if ch not in string.printable:
    284             fout = open ('log', 'a')
    285             fout.write ('Nonprint: ' + str(ord(ch)) + '\n')
    286             fout.close()
    287             return
    288         self.put_abs(self.cur_r, self.cur_c, ch)
    289         old_r = self.cur_r
    290         old_c = self.cur_c
    291         self.cursor_forward()
    292         if old_c == self.cur_c:
    293             self.cursor_down()
    294             if old_r != self.cur_r:
    295                 self.cursor_home (self.cur_r, 1)
    296             else:
    297                 self.scroll_up ()
    298                 self.cursor_home (self.cur_r, 1)
    299                 self.erase_line()
    300 
    301 #    def test (self):
    302 #
    303 #        import sys
    304 #        write_text = 'I\'ve got a ferret sticking up my nose.\n' + \
    305 #        '(He\'s got a ferret sticking up his nose.)\n' + \
    306 #        'How it got there I can\'t tell\n' + \
    307 #        'But now it\'s there it hurts like hell\n' + \
    308 #        'And what is more it radically affects my sense of smell.\n' + \
    309 #        '(His sense of smell.)\n' + \
    310 #        'I can see a bare-bottomed mandril.\n' + \
    311 #        '(Slyly eyeing his other nostril.)\n' + \
    312 #        'If it jumps inside there too I really don\'t know what to do\n' + \
    313 #        'I\'ll be the proud posessor of a kind of nasal zoo.\n' + \
    314 #        '(A nasal zoo.)\n' + \
    315 #        'I\'ve got a ferret sticking up my nose.\n' + \
    316 #        '(And what is worst of all it constantly explodes.)\n' + \
    317 #        '"Ferrets don\'t explode," you say\n' + \
    318 #        'But it happened nine times yesterday\n' + \
    319 #        'And I should know for each time I was standing in the way.\n' + \
    320 #        'I\'ve got a ferret sticking up my nose.\n' + \
    321 #        '(He\'s got a ferret sticking up his nose.)\n' + \
    322 #        'How it got there I can\'t tell\n' + \
    323 #        'But now it\'s there it hurts like hell\n' + \
    324 #        'And what is more it radically affects my sense of smell.\n' + \
    325 #        '(His sense of smell.)'
    326 #        self.fill('.')
    327 #        self.cursor_home()
    328 #        for c in write_text:
    329 #            self.write_ch (c)
    330 #        print str(self)
    331 #
    332 #if __name__ == '__main__':
    333 #    t = ANSI(6,65)
    334 #    t.test()
    335