123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- """This implements an ANSI (VT100) terminal emulator as a subclass of screen.
- PEXPECT LICENSE
- This license is approved by the OSI and FSF as GPL-compatible.
- http://opensource.org/licenses/isc-license.txt
- Copyright (c) 2012, Noah Spurrier <noah@noah.org>
- PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY
- PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE
- COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES.
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- """
- # references:
- # http://en.wikipedia.org/wiki/ANSI_escape_code
- # http://www.retards.org/terminals/vt102.html
- # http://vt100.net/docs/vt102-ug/contents.html
- # http://vt100.net/docs/vt220-rm/
- # http://www.termsys.demon.co.uk/vtansi.htm
- import screen
- import FSM
- import copy
- import string
- #
- # The 'Do.*' functions are helper functions for the ANSI class.
- #
- def DoEmit (fsm):
- screen = fsm.memory[0]
- screen.write_ch(fsm.input_symbol)
- def DoStartNumber (fsm):
- fsm.memory.append (fsm.input_symbol)
- def DoBuildNumber (fsm):
- ns = fsm.memory.pop()
- ns = ns + fsm.input_symbol
- fsm.memory.append (ns)
- def DoBackOne (fsm):
- screen = fsm.memory[0]
- screen.cursor_back ()
- def DoBack (fsm):
- count = int(fsm.memory.pop())
- screen = fsm.memory[0]
- screen.cursor_back (count)
- def DoDownOne (fsm):
- screen = fsm.memory[0]
- screen.cursor_down ()
- def DoDown (fsm):
- count = int(fsm.memory.pop())
- screen = fsm.memory[0]
- screen.cursor_down (count)
- def DoForwardOne (fsm):
- screen = fsm.memory[0]
- screen.cursor_forward ()
- def DoForward (fsm):
- count = int(fsm.memory.pop())
- screen = fsm.memory[0]
- screen.cursor_forward (count)
- def DoUpReverse (fsm):
- screen = fsm.memory[0]
- screen.cursor_up_reverse()
- def DoUpOne (fsm):
- screen = fsm.memory[0]
- screen.cursor_up ()
- def DoUp (fsm):
- count = int(fsm.memory.pop())
- screen = fsm.memory[0]
- screen.cursor_up (count)
- def DoHome (fsm):
- c = int(fsm.memory.pop())
- r = int(fsm.memory.pop())
- screen = fsm.memory[0]
- screen.cursor_home (r,c)
- def DoHomeOrigin (fsm):
- c = 1
- r = 1
- screen = fsm.memory[0]
- screen.cursor_home (r,c)
- def DoEraseDown (fsm):
- screen = fsm.memory[0]
- screen.erase_down()
- def DoErase (fsm):
- arg = int(fsm.memory.pop())
- screen = fsm.memory[0]
- if arg == 0:
- screen.erase_down()
- elif arg == 1:
- screen.erase_up()
- elif arg == 2:
- screen.erase_screen()
- def DoEraseEndOfLine (fsm):
- screen = fsm.memory[0]
- screen.erase_end_of_line()
- def DoEraseLine (fsm):
- arg = int(fsm.memory.pop())
- screen = fsm.memory[0]
- if arg == 0:
- screen.erase_end_of_line()
- elif arg == 1:
- screen.erase_start_of_line()
- elif arg == 2:
- screen.erase_line()
- def DoEnableScroll (fsm):
- screen = fsm.memory[0]
- screen.scroll_screen()
- def DoCursorSave (fsm):
- screen = fsm.memory[0]
- screen.cursor_save_attrs()
- def DoCursorRestore (fsm):
- screen = fsm.memory[0]
- screen.cursor_restore_attrs()
- def DoScrollRegion (fsm):
- screen = fsm.memory[0]
- r2 = int(fsm.memory.pop())
- r1 = int(fsm.memory.pop())
- screen.scroll_screen_rows (r1,r2)
- def DoMode (fsm):
- screen = fsm.memory[0]
- mode = fsm.memory.pop() # Should be 4
- # screen.setReplaceMode ()
- def DoLog (fsm):
- screen = fsm.memory[0]
- fsm.memory = [screen]
- fout = open ('log', 'a')
- fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n')
- fout.close()
- class term (screen.screen):
- """This class is an abstract, generic terminal.
- This does nothing. This is a placeholder that
- provides a common base class for other terminals
- such as an ANSI terminal. """
- def __init__ (self, r=24, c=80):
- screen.screen.__init__(self, r,c)
- class ANSI (term):
- """This class implements an ANSI (VT100) terminal.
- It is a stream filter that recognizes ANSI terminal
- escape sequences and maintains the state of a screen object. """
- def __init__ (self, r=24,c=80):
- term.__init__(self,r,c)
- #self.screen = screen (24,80)
- self.state = FSM.FSM ('INIT',[self])
- self.state.set_default_transition (DoLog, 'INIT')
- self.state.add_transition_any ('INIT', DoEmit, 'INIT')
- self.state.add_transition ('\x1b', 'INIT', None, 'ESC')
- self.state.add_transition_any ('ESC', DoLog, 'INIT')
- self.state.add_transition ('(', 'ESC', None, 'G0SCS')
- self.state.add_transition (')', 'ESC', None, 'G1SCS')
- self.state.add_transition_list ('AB012', 'G0SCS', None, 'INIT')
- self.state.add_transition_list ('AB012', 'G1SCS', None, 'INIT')
- self.state.add_transition ('7', 'ESC', DoCursorSave, 'INIT')
- self.state.add_transition ('8', 'ESC', DoCursorRestore, 'INIT')
- self.state.add_transition ('M', 'ESC', DoUpReverse, 'INIT')
- self.state.add_transition ('>', 'ESC', DoUpReverse, 'INIT')
- self.state.add_transition ('<', 'ESC', DoUpReverse, 'INIT')
- self.state.add_transition ('=', 'ESC', None, 'INIT') # Selects application keypad.
- self.state.add_transition ('#', 'ESC', None, 'GRAPHICS_POUND')
- self.state.add_transition_any ('GRAPHICS_POUND', None, 'INIT')
- self.state.add_transition ('[', 'ESC', None, 'ELB')
- # ELB means Escape Left Bracket. That is ^[[
- self.state.add_transition ('H', 'ELB', DoHomeOrigin, 'INIT')
- self.state.add_transition ('D', 'ELB', DoBackOne, 'INIT')
- self.state.add_transition ('B', 'ELB', DoDownOne, 'INIT')
- self.state.add_transition ('C', 'ELB', DoForwardOne, 'INIT')
- self.state.add_transition ('A', 'ELB', DoUpOne, 'INIT')
- self.state.add_transition ('J', 'ELB', DoEraseDown, 'INIT')
- self.state.add_transition ('K', 'ELB', DoEraseEndOfLine, 'INIT')
- self.state.add_transition ('r', 'ELB', DoEnableScroll, 'INIT')
- self.state.add_transition ('m', 'ELB', None, 'INIT')
- self.state.add_transition ('?', 'ELB', None, 'MODECRAP')
- self.state.add_transition_list (string.digits, 'ELB', DoStartNumber, 'NUMBER_1')
- self.state.add_transition_list (string.digits, 'NUMBER_1', DoBuildNumber, 'NUMBER_1')
- self.state.add_transition ('D', 'NUMBER_1', DoBack, 'INIT')
- self.state.add_transition ('B', 'NUMBER_1', DoDown, 'INIT')
- self.state.add_transition ('C', 'NUMBER_1', DoForward, 'INIT')
- self.state.add_transition ('A', 'NUMBER_1', DoUp, 'INIT')
- self.state.add_transition ('J', 'NUMBER_1', DoErase, 'INIT')
- self.state.add_transition ('K', 'NUMBER_1', DoEraseLine, 'INIT')
- self.state.add_transition ('l', 'NUMBER_1', DoMode, 'INIT')
- ### It gets worse... the 'm' code can have infinite number of
- ### number;number;number before it. I've never seen more than two,
- ### but the specs say it's allowed. crap!
- self.state.add_transition ('m', 'NUMBER_1', None, 'INIT')
- ### LED control. Same implementation problem as 'm' code.
- self.state.add_transition ('q', 'NUMBER_1', None, 'INIT')
- # \E[?47h switch to alternate screen
- # \E[?47l restores to normal screen from alternate screen.
- self.state.add_transition_list (string.digits, 'MODECRAP', DoStartNumber, 'MODECRAP_NUM')
- self.state.add_transition_list (string.digits, 'MODECRAP_NUM', DoBuildNumber, 'MODECRAP_NUM')
- self.state.add_transition ('l', 'MODECRAP_NUM', None, 'INIT')
- self.state.add_transition ('h', 'MODECRAP_NUM', None, 'INIT')
- #RM Reset Mode Esc [ Ps l none
- self.state.add_transition (';', 'NUMBER_1', None, 'SEMICOLON')
- self.state.add_transition_any ('SEMICOLON', DoLog, 'INIT')
- self.state.add_transition_list (string.digits, 'SEMICOLON', DoStartNumber, 'NUMBER_2')
- self.state.add_transition_list (string.digits, 'NUMBER_2', DoBuildNumber, 'NUMBER_2')
- self.state.add_transition_any ('NUMBER_2', DoLog, 'INIT')
- self.state.add_transition ('H', 'NUMBER_2', DoHome, 'INIT')
- self.state.add_transition ('f', 'NUMBER_2', DoHome, 'INIT')
- self.state.add_transition ('r', 'NUMBER_2', DoScrollRegion, 'INIT')
- ### It gets worse... the 'm' code can have infinite number of
- ### number;number;number before it. I've never seen more than two,
- ### but the specs say it's allowed. crap!
- self.state.add_transition ('m', 'NUMBER_2', None, 'INIT')
- ### LED control. Same problem as 'm' code.
- self.state.add_transition ('q', 'NUMBER_2', None, 'INIT')
- self.state.add_transition (';', 'NUMBER_2', None, 'SEMICOLON_X')
- # Create a state for 'q' and 'm' which allows an infinite number of ignored numbers
- self.state.add_transition_any ('SEMICOLON_X', DoLog, 'INIT')
- self.state.add_transition_list (string.digits, 'SEMICOLON_X', None, 'NUMBER_X')
- self.state.add_transition_any ('NUMBER_X', DoLog, 'INIT')
- self.state.add_transition ('m', 'NUMBER_X', None, 'INIT')
- self.state.add_transition ('q', 'NUMBER_X', None, 'INIT')
- self.state.add_transition (';', 'NUMBER_2', None, 'SEMICOLON_X')
- def process (self, c):
- self.state.process(c)
- def process_list (self, l):
- self.write(l)
- def write (self, s):
- for c in s:
- self.process(c)
- def flush (self):
- pass
- def write_ch (self, ch):
- """This puts a character at the current cursor position. The cursor
- position is moved forward with wrap-around, but no scrolling is done if
- the cursor hits the lower-right corner of the screen. """
- #\r and \n both produce a call to cr() and lf(), respectively.
- ch = ch[0]
- if ch == '\r':
- self.cr()
- return
- if ch == '\n':
- self.crlf()
- return
- if ch == chr(screen.BS):
- self.cursor_back()
- return
- if ch not in string.printable:
- fout = open ('log', 'a')
- fout.write ('Nonprint: ' + str(ord(ch)) + '\n')
- fout.close()
- return
- self.put_abs(self.cur_r, self.cur_c, ch)
- old_r = self.cur_r
- old_c = self.cur_c
- self.cursor_forward()
- if old_c == self.cur_c:
- self.cursor_down()
- if old_r != self.cur_r:
- self.cursor_home (self.cur_r, 1)
- else:
- self.scroll_up ()
- self.cursor_home (self.cur_r, 1)
- self.erase_line()
- # def test (self):
- #
- # import sys
- # write_text = 'I\'ve got a ferret sticking up my nose.\n' + \
- # '(He\'s got a ferret sticking up his nose.)\n' + \
- # 'How it got there I can\'t tell\n' + \
- # 'But now it\'s there it hurts like hell\n' + \
- # 'And what is more it radically affects my sense of smell.\n' + \
- # '(His sense of smell.)\n' + \
- # 'I can see a bare-bottomed mandril.\n' + \
- # '(Slyly eyeing his other nostril.)\n' + \
- # 'If it jumps inside there too I really don\'t know what to do\n' + \
- # 'I\'ll be the proud posessor of a kind of nasal zoo.\n' + \
- # '(A nasal zoo.)\n' + \
- # 'I\'ve got a ferret sticking up my nose.\n' + \
- # '(And what is worst of all it constantly explodes.)\n' + \
- # '"Ferrets don\'t explode," you say\n' + \
- # 'But it happened nine times yesterday\n' + \
- # 'And I should know for each time I was standing in the way.\n' + \
- # 'I\'ve got a ferret sticking up my nose.\n' + \
- # '(He\'s got a ferret sticking up his nose.)\n' + \
- # 'How it got there I can\'t tell\n' + \
- # 'But now it\'s there it hurts like hell\n' + \
- # 'And what is more it radically affects my sense of smell.\n' + \
- # '(His sense of smell.)'
- # self.fill('.')
- # self.cursor_home()
- # for c in write_text:
- # self.write_ch (c)
- # print str(self)
- #
- #if __name__ == '__main__':
- # t = ANSI(6,65)
- # t.test()
|