blob: 003bab270e6f9079e09811525cad7ddcd397316a [file] [log] [blame]
Johnny Chenb7cfba42011-03-11 20:13:06 +00001"""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
11import screen
12import FSM
13import copy
14import string
15
Kate Stoneb9c1b512016-09-06 20:57:50 +000016
17def Emit(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000018
19 screen = fsm.memory[0]
20 screen.write_ch(fsm.input_symbol)
21
Johnny Chenb7cfba42011-03-11 20:13:06 +000022
Kate Stoneb9c1b512016-09-06 20:57:50 +000023def StartNumber(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000024
Kate Stoneb9c1b512016-09-06 20:57:50 +000025 fsm.memory.append(fsm.input_symbol)
26
27
28def BuildNumber(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000029
30 ns = fsm.memory.pop()
31 ns = ns + fsm.input_symbol
Kate Stoneb9c1b512016-09-06 20:57:50 +000032 fsm.memory.append(ns)
Johnny Chenb7cfba42011-03-11 20:13:06 +000033
Kate Stoneb9c1b512016-09-06 20:57:50 +000034
35def DoBackOne(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000036
37 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000038 screen.cursor_back()
Johnny Chenb7cfba42011-03-11 20:13:06 +000039
Kate Stoneb9c1b512016-09-06 20:57:50 +000040
41def DoBack(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000042
43 count = int(fsm.memory.pop())
44 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000045 screen.cursor_back(count)
Johnny Chenb7cfba42011-03-11 20:13:06 +000046
Kate Stoneb9c1b512016-09-06 20:57:50 +000047
48def DoDownOne(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000049
50 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000051 screen.cursor_down()
Johnny Chenb7cfba42011-03-11 20:13:06 +000052
Kate Stoneb9c1b512016-09-06 20:57:50 +000053
54def DoDown(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000055
56 count = int(fsm.memory.pop())
57 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000058 screen.cursor_down(count)
Johnny Chenb7cfba42011-03-11 20:13:06 +000059
Kate Stoneb9c1b512016-09-06 20:57:50 +000060
61def DoForwardOne(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000062
63 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000064 screen.cursor_forward()
Johnny Chenb7cfba42011-03-11 20:13:06 +000065
Kate Stoneb9c1b512016-09-06 20:57:50 +000066
67def DoForward(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000068
69 count = int(fsm.memory.pop())
70 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000071 screen.cursor_forward(count)
Johnny Chenb7cfba42011-03-11 20:13:06 +000072
Kate Stoneb9c1b512016-09-06 20:57:50 +000073
74def DoUpReverse(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000075
76 screen = fsm.memory[0]
77 screen.cursor_up_reverse()
78
Kate Stoneb9c1b512016-09-06 20:57:50 +000079
80def DoUpOne(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000081
82 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000083 screen.cursor_up()
Johnny Chenb7cfba42011-03-11 20:13:06 +000084
Kate Stoneb9c1b512016-09-06 20:57:50 +000085
86def DoUp(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000087
88 count = int(fsm.memory.pop())
89 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000090 screen.cursor_up(count)
Johnny Chenb7cfba42011-03-11 20:13:06 +000091
Kate Stoneb9c1b512016-09-06 20:57:50 +000092
93def DoHome(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +000094
95 c = int(fsm.memory.pop())
96 r = int(fsm.memory.pop())
97 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +000098 screen.cursor_home(r, c)
Johnny Chenb7cfba42011-03-11 20:13:06 +000099
Kate Stoneb9c1b512016-09-06 20:57:50 +0000100
101def DoHomeOrigin(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000102
103 c = 1
104 r = 1
105 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +0000106 screen.cursor_home(r, c)
Johnny Chenb7cfba42011-03-11 20:13:06 +0000107
Kate Stoneb9c1b512016-09-06 20:57:50 +0000108
109def DoEraseDown(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000110
111 screen = fsm.memory[0]
112 screen.erase_down()
113
Kate Stoneb9c1b512016-09-06 20:57:50 +0000114
115def DoErase(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000116
117 arg = int(fsm.memory.pop())
118 screen = fsm.memory[0]
119 if arg == 0:
120 screen.erase_down()
121 elif arg == 1:
122 screen.erase_up()
123 elif arg == 2:
124 screen.erase_screen()
125
Kate Stoneb9c1b512016-09-06 20:57:50 +0000126
127def DoEraseEndOfLine(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000128
129 screen = fsm.memory[0]
130 screen.erase_end_of_line()
131
Kate Stoneb9c1b512016-09-06 20:57:50 +0000132
133def DoEraseLine(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000134
135 screen = fsm.memory[0]
136 if arg == 0:
137 screen.end_of_line()
138 elif arg == 1:
139 screen.start_of_line()
140 elif arg == 2:
141 screen.erase_line()
142
Kate Stoneb9c1b512016-09-06 20:57:50 +0000143
144def DoEnableScroll(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000145
146 screen = fsm.memory[0]
147 screen.scroll_screen()
148
Kate Stoneb9c1b512016-09-06 20:57:50 +0000149
150def DoCursorSave(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000151
152 screen = fsm.memory[0]
153 screen.cursor_save_attrs()
154
Kate Stoneb9c1b512016-09-06 20:57:50 +0000155
156def DoCursorRestore(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000157
158 screen = fsm.memory[0]
159 screen.cursor_restore_attrs()
160
Kate Stoneb9c1b512016-09-06 20:57:50 +0000161
162def DoScrollRegion(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000163
164 screen = fsm.memory[0]
165 r2 = int(fsm.memory.pop())
166 r1 = int(fsm.memory.pop())
Kate Stoneb9c1b512016-09-06 20:57:50 +0000167 screen.scroll_screen_rows(r1, r2)
Johnny Chenb7cfba42011-03-11 20:13:06 +0000168
Kate Stoneb9c1b512016-09-06 20:57:50 +0000169
170def DoMode(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000171
172 screen = fsm.memory[0]
Kate Stoneb9c1b512016-09-06 20:57:50 +0000173 mode = fsm.memory.pop() # Should be 4
Johnny Chenb7cfba42011-03-11 20:13:06 +0000174 # screen.setReplaceMode ()
175
Kate Stoneb9c1b512016-09-06 20:57:50 +0000176
177def Log(fsm):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000178
179 screen = fsm.memory[0]
180 fsm.memory = [screen]
Kate Stoneb9c1b512016-09-06 20:57:50 +0000181 fout = open('log', 'a')
182 fout.write(fsm.input_symbol + ',' + fsm.current_state + '\n')
Johnny Chenb7cfba42011-03-11 20:13:06 +0000183 fout.close()
184
Kate Stoneb9c1b512016-09-06 20:57:50 +0000185
Johnny Chenb7cfba42011-03-11 20:13:06 +0000186class term (screen.screen):
Kate Stoneb9c1b512016-09-06 20:57:50 +0000187 """This is a placeholder.
Johnny Chenb7cfba42011-03-11 20:13:06 +0000188 In theory I might want to add other terminal types.
189 """
Kate Stoneb9c1b512016-09-06 20:57:50 +0000190
191 def __init__(self, r=24, c=80):
192 screen.screen.__init__(self, r, c)
193
Johnny Chenb7cfba42011-03-11 20:13:06 +0000194
195class ANSI (term):
196
197 """This class encapsulates a generic terminal. It filters a stream and
198 maintains the state of a screen object. """
199
Kate Stoneb9c1b512016-09-06 20:57:50 +0000200 def __init__(self, r=24, c=80):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000201
Kate Stoneb9c1b512016-09-06 20:57:50 +0000202 term.__init__(self, r, c)
Johnny Chenb7cfba42011-03-11 20:13:06 +0000203
204 #self.screen = screen (24,80)
Kate Stoneb9c1b512016-09-06 20:57:50 +0000205 self.state = FSM.FSM('INIT', [self])
206 self.state.set_default_transition(Log, 'INIT')
207 self.state.add_transition_any('INIT', Emit, 'INIT')
208 self.state.add_transition('\x1b', 'INIT', None, 'ESC')
209 self.state.add_transition_any('ESC', Log, 'INIT')
210 self.state.add_transition('(', 'ESC', None, 'G0SCS')
211 self.state.add_transition(')', 'ESC', None, 'G1SCS')
212 self.state.add_transition_list('AB012', 'G0SCS', None, 'INIT')
213 self.state.add_transition_list('AB012', 'G1SCS', None, 'INIT')
214 self.state.add_transition('7', 'ESC', DoCursorSave, 'INIT')
215 self.state.add_transition('8', 'ESC', DoCursorRestore, 'INIT')
216 self.state.add_transition('M', 'ESC', DoUpReverse, 'INIT')
217 self.state.add_transition('>', 'ESC', DoUpReverse, 'INIT')
218 self.state.add_transition('<', 'ESC', DoUpReverse, 'INIT')
219 # Selects application keypad.
220 self.state.add_transition('=', 'ESC', None, 'INIT')
221 self.state.add_transition('#', 'ESC', None, 'GRAPHICS_POUND')
222 self.state.add_transition_any('GRAPHICS_POUND', None, 'INIT')
223 self.state.add_transition('[', 'ESC', None, 'ELB')
Johnny Chenb7cfba42011-03-11 20:13:06 +0000224 # ELB means Escape Left Bracket. That is ^[[
Kate Stoneb9c1b512016-09-06 20:57:50 +0000225 self.state.add_transition('H', 'ELB', DoHomeOrigin, 'INIT')
226 self.state.add_transition('D', 'ELB', DoBackOne, 'INIT')
227 self.state.add_transition('B', 'ELB', DoDownOne, 'INIT')
228 self.state.add_transition('C', 'ELB', DoForwardOne, 'INIT')
229 self.state.add_transition('A', 'ELB', DoUpOne, 'INIT')
230 self.state.add_transition('J', 'ELB', DoEraseDown, 'INIT')
231 self.state.add_transition('K', 'ELB', DoEraseEndOfLine, 'INIT')
232 self.state.add_transition('r', 'ELB', DoEnableScroll, 'INIT')
233 self.state.add_transition('m', 'ELB', None, 'INIT')
234 self.state.add_transition('?', 'ELB', None, 'MODECRAP')
235 self.state.add_transition_list(
236 string.digits, 'ELB', StartNumber, 'NUMBER_1')
237 self.state.add_transition_list(
238 string.digits, 'NUMBER_1', BuildNumber, 'NUMBER_1')
239 self.state.add_transition('D', 'NUMBER_1', DoBack, 'INIT')
240 self.state.add_transition('B', 'NUMBER_1', DoDown, 'INIT')
241 self.state.add_transition('C', 'NUMBER_1', DoForward, 'INIT')
242 self.state.add_transition('A', 'NUMBER_1', DoUp, 'INIT')
243 self.state.add_transition('J', 'NUMBER_1', DoErase, 'INIT')
244 self.state.add_transition('K', 'NUMBER_1', DoEraseLine, 'INIT')
245 self.state.add_transition('l', 'NUMBER_1', DoMode, 'INIT')
246 # It gets worse... the 'm' code can have infinite number of
247 # number;number;number before it. I've never seen more than two,
248 # but the specs say it's allowed. crap!
249 self.state.add_transition('m', 'NUMBER_1', None, 'INIT')
250 # LED control. Same problem as 'm' code.
251 self.state.add_transition('q', 'NUMBER_1', None, 'INIT')
252
Johnny Chenb7cfba42011-03-11 20:13:06 +0000253 # \E[?47h appears to be "switch to alternate screen"
254 # \E[?47l restores alternate screen... I think.
Kate Stoneb9c1b512016-09-06 20:57:50 +0000255 self.state.add_transition_list(
256 string.digits, 'MODECRAP', StartNumber, 'MODECRAP_NUM')
257 self.state.add_transition_list(
258 string.digits,
259 'MODECRAP_NUM',
260 BuildNumber,
261 'MODECRAP_NUM')
262 self.state.add_transition('l', 'MODECRAP_NUM', None, 'INIT')
263 self.state.add_transition('h', 'MODECRAP_NUM', None, 'INIT')
Johnny Chenb7cfba42011-03-11 20:13:06 +0000264
Kate Stoneb9c1b512016-09-06 20:57:50 +0000265# RM Reset Mode Esc [ Ps l none
266 self.state.add_transition(';', 'NUMBER_1', None, 'SEMICOLON')
267 self.state.add_transition_any('SEMICOLON', Log, 'INIT')
268 self.state.add_transition_list(
269 string.digits, 'SEMICOLON', StartNumber, 'NUMBER_2')
270 self.state.add_transition_list(
271 string.digits, 'NUMBER_2', BuildNumber, 'NUMBER_2')
272 self.state.add_transition_any('NUMBER_2', Log, 'INIT')
273 self.state.add_transition('H', 'NUMBER_2', DoHome, 'INIT')
274 self.state.add_transition('f', 'NUMBER_2', DoHome, 'INIT')
275 self.state.add_transition('r', 'NUMBER_2', DoScrollRegion, 'INIT')
276 # It gets worse... the 'm' code can have infinite number of
277 # number;number;number before it. I've never seen more than two,
278 # but the specs say it's allowed. crap!
279 self.state.add_transition('m', 'NUMBER_2', None, 'INIT')
280 # LED control. Same problem as 'm' code.
281 self.state.add_transition('q', 'NUMBER_2', None, 'INIT')
Johnny Chenb7cfba42011-03-11 20:13:06 +0000282
Kate Stoneb9c1b512016-09-06 20:57:50 +0000283 def process(self, c):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000284
285 self.state.process(c)
286
Kate Stoneb9c1b512016-09-06 20:57:50 +0000287 def process_list(self, l):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000288
289 self.write(l)
290
Kate Stoneb9c1b512016-09-06 20:57:50 +0000291 def write(self, s):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000292
293 for c in s:
294 self.process(c)
295
Kate Stoneb9c1b512016-09-06 20:57:50 +0000296 def flush(self):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000297
298 pass
299
Kate Stoneb9c1b512016-09-06 20:57:50 +0000300 def write_ch(self, ch):
Johnny Chenb7cfba42011-03-11 20:13:06 +0000301 """This puts a character at the current cursor position. cursor
302 position if moved forward with wrap-around, but no scrolling is done if
303 the cursor hits the lower-right corner of the screen. """
304
305 #\r and \n both produce a call to crlf().
306 ch = ch[0]
307
308 if ch == '\r':
Kate Stoneb9c1b512016-09-06 20:57:50 +0000309 # self.crlf()
Johnny Chenb7cfba42011-03-11 20:13:06 +0000310 return
311 if ch == '\n':
312 self.crlf()
313 return
314 if ch == chr(screen.BS):
315 self.cursor_back()
316 self.put_abs(self.cur_r, self.cur_c, ' ')
317 return
318
319 if ch not in string.printable:
Kate Stoneb9c1b512016-09-06 20:57:50 +0000320 fout = open('log', 'a')
321 fout.write('Nonprint: ' + str(ord(ch)) + '\n')
Johnny Chenb7cfba42011-03-11 20:13:06 +0000322 fout.close()
323 return
324 self.put_abs(self.cur_r, self.cur_c, ch)
325 old_r = self.cur_r
326 old_c = self.cur_c
327 self.cursor_forward()
328 if old_c == self.cur_c:
329 self.cursor_down()
330 if old_r != self.cur_r:
Kate Stoneb9c1b512016-09-06 20:57:50 +0000331 self.cursor_home(self.cur_r, 1)
Johnny Chenb7cfba42011-03-11 20:13:06 +0000332 else:
Kate Stoneb9c1b512016-09-06 20:57:50 +0000333 self.scroll_up()
334 self.cursor_home(self.cur_r, 1)
Johnny Chenb7cfba42011-03-11 20:13:06 +0000335 self.erase_line()
336
337# def test (self):
338#
339# import sys
340# write_text = 'I\'ve got a ferret sticking up my nose.\n' + \
341# '(He\'s got a ferret sticking up his nose.)\n' + \
342# 'How it got there I can\'t tell\n' + \
343# 'But now it\'s there it hurts like hell\n' + \
344# 'And what is more it radically affects my sense of smell.\n' + \
345# '(His sense of smell.)\n' + \
346# 'I can see a bare-bottomed mandril.\n' + \
347# '(Slyly eyeing his other nostril.)\n' + \
348# 'If it jumps inside there too I really don\'t know what to do\n' + \
349# 'I\'ll be the proud posessor of a kind of nasal zoo.\n' + \
350# '(A nasal zoo.)\n' + \
351# 'I\'ve got a ferret sticking up my nose.\n' + \
352# '(And what is worst of all it constantly explodes.)\n' + \
353# '"Ferrets don\'t explode," you say\n' + \
354# 'But it happened nine times yesterday\n' + \
355# 'And I should know for each time I was standing in the way.\n' + \
356# 'I\'ve got a ferret sticking up my nose.\n' + \
357# '(He\'s got a ferret sticking up his nose.)\n' + \
358# 'How it got there I can\'t tell\n' + \
359# 'But now it\'s there it hurts like hell\n' + \
360# 'And what is more it radically affects my sense of smell.\n' + \
361# '(His sense of smell.)'
362# self.fill('.')
363# self.cursor_home()
364# for c in write_text:
365# self.write_ch (c)
366# print str(self)
367#
Kate Stoneb9c1b512016-09-06 20:57:50 +0000368# if __name__ == '__main__':
Johnny Chenb7cfba42011-03-11 20:13:06 +0000369# t = ANSI(6,65)
370# t.test()