OLD | NEW |
(Empty) | |
| 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 applicati
on 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, 'NUMB
ER_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', BuildNumb
er, '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() |
OLD | NEW |