| OLD | NEW |
| 1 """This implements an ANSI terminal emulator as a subclass of screen. | 1 """This implements an ANSI (VT100) terminal emulator as a subclass of screen. |
| 2 | 2 |
| 3 $Id: ANSI.py 491 2007-12-16 20:04:57Z noah $ | 3 PEXPECT LICENSE |
| 4 |
| 5 This license is approved by the OSI and FSF as GPL-compatible. |
| 6 http://opensource.org/licenses/isc-license.txt |
| 7 |
| 8 Copyright (c) 2012, Noah Spurrier <noah@noah.org> |
| 9 PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY |
| 10 PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE |
| 11 COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. |
| 12 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 13 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 14 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 15 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 16 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 17 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 18 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 19 |
| 4 """ | 20 """ |
| 21 |
| 5 # references: | 22 # references: |
| 23 # http://en.wikipedia.org/wiki/ANSI_escape_code |
| 6 # http://www.retards.org/terminals/vt102.html | 24 # http://www.retards.org/terminals/vt102.html |
| 7 # http://vt100.net/docs/vt102-ug/contents.html | 25 # http://vt100.net/docs/vt102-ug/contents.html |
| 8 # http://vt100.net/docs/vt220-rm/ | 26 # http://vt100.net/docs/vt220-rm/ |
| 9 # http://www.termsys.demon.co.uk/vtansi.htm | 27 # http://www.termsys.demon.co.uk/vtansi.htm |
| 10 | 28 |
| 11 import screen | 29 import screen |
| 12 import FSM | 30 import FSM |
| 13 import copy | 31 import copy |
| 14 import string | 32 import string |
| 15 | 33 |
| 16 def Emit (fsm): | 34 # |
| 35 # The 'Do.*' functions are helper functions for the ANSI class. |
| 36 # |
| 37 def DoEmit (fsm): |
| 17 | 38 |
| 18 screen = fsm.memory[0] | 39 screen = fsm.memory[0] |
| 19 screen.write_ch(fsm.input_symbol) | 40 screen.write_ch(fsm.input_symbol) |
| 20 | 41 |
| 21 def StartNumber (fsm): | 42 def DoStartNumber (fsm): |
| 22 | 43 |
| 23 fsm.memory.append (fsm.input_symbol) | 44 fsm.memory.append (fsm.input_symbol) |
| 24 | 45 |
| 25 def BuildNumber (fsm): | 46 def DoBuildNumber (fsm): |
| 26 | 47 |
| 27 ns = fsm.memory.pop() | 48 ns = fsm.memory.pop() |
| 28 ns = ns + fsm.input_symbol | 49 ns = ns + fsm.input_symbol |
| 29 fsm.memory.append (ns) | 50 fsm.memory.append (ns) |
| 30 | 51 |
| 31 def DoBackOne (fsm): | 52 def DoBackOne (fsm): |
| 32 | 53 |
| 33 screen = fsm.memory[0] | 54 screen = fsm.memory[0] |
| 34 screen.cursor_back () | 55 screen.cursor_back () |
| 35 | 56 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 elif arg == 2: | 128 elif arg == 2: |
| 108 screen.erase_screen() | 129 screen.erase_screen() |
| 109 | 130 |
| 110 def DoEraseEndOfLine (fsm): | 131 def DoEraseEndOfLine (fsm): |
| 111 | 132 |
| 112 screen = fsm.memory[0] | 133 screen = fsm.memory[0] |
| 113 screen.erase_end_of_line() | 134 screen.erase_end_of_line() |
| 114 | 135 |
| 115 def DoEraseLine (fsm): | 136 def DoEraseLine (fsm): |
| 116 | 137 |
| 138 arg = int(fsm.memory.pop()) |
| 117 screen = fsm.memory[0] | 139 screen = fsm.memory[0] |
| 118 if arg == 0: | 140 if arg == 0: |
| 119 screen.end_of_line() | 141 screen.erase_end_of_line() |
| 120 elif arg == 1: | 142 elif arg == 1: |
| 121 screen.start_of_line() | 143 screen.erase_start_of_line() |
| 122 elif arg == 2: | 144 elif arg == 2: |
| 123 screen.erase_line() | 145 screen.erase_line() |
| 124 | 146 |
| 125 def DoEnableScroll (fsm): | 147 def DoEnableScroll (fsm): |
| 126 | 148 |
| 127 screen = fsm.memory[0] | 149 screen = fsm.memory[0] |
| 128 screen.scroll_screen() | 150 screen.scroll_screen() |
| 129 | 151 |
| 130 def DoCursorSave (fsm): | 152 def DoCursorSave (fsm): |
| 131 | 153 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 143 r2 = int(fsm.memory.pop()) | 165 r2 = int(fsm.memory.pop()) |
| 144 r1 = int(fsm.memory.pop()) | 166 r1 = int(fsm.memory.pop()) |
| 145 screen.scroll_screen_rows (r1,r2) | 167 screen.scroll_screen_rows (r1,r2) |
| 146 | 168 |
| 147 def DoMode (fsm): | 169 def DoMode (fsm): |
| 148 | 170 |
| 149 screen = fsm.memory[0] | 171 screen = fsm.memory[0] |
| 150 mode = fsm.memory.pop() # Should be 4 | 172 mode = fsm.memory.pop() # Should be 4 |
| 151 # screen.setReplaceMode () | 173 # screen.setReplaceMode () |
| 152 | 174 |
| 153 def Log (fsm): | 175 def DoLog (fsm): |
| 154 | 176 |
| 155 screen = fsm.memory[0] | 177 screen = fsm.memory[0] |
| 156 fsm.memory = [screen] | 178 fsm.memory = [screen] |
| 157 fout = open ('log', 'a') | 179 fout = open ('log', 'a') |
| 158 fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n') | 180 fout.write (fsm.input_symbol + ',' + fsm.current_state + '\n') |
| 159 fout.close() | 181 fout.close() |
| 160 | 182 |
| 161 class term (screen.screen): | 183 class term (screen.screen): |
| 162 """This is a placeholder. | 184 |
| 163 In theory I might want to add other terminal types. | 185 """This class is an abstract, generic terminal. |
| 164 """ | 186 This does nothing. This is a placeholder that |
| 187 provides a common base class for other terminals |
| 188 such as an ANSI terminal. """ |
| 189 |
| 165 def __init__ (self, r=24, c=80): | 190 def __init__ (self, r=24, c=80): |
| 191 |
| 166 screen.screen.__init__(self, r,c) | 192 screen.screen.__init__(self, r,c) |
| 167 | 193 |
| 168 class ANSI (term): | 194 class ANSI (term): |
| 169 | 195 |
| 170 """This class encapsulates a generic terminal. It filters a stream and | 196 """This class implements an ANSI (VT100) terminal. |
| 171 maintains the state of a screen object. """ | 197 It is a stream filter that recognizes ANSI terminal |
| 198 escape sequences and maintains the state of a screen object. """ |
| 172 | 199 |
| 173 def __init__ (self, r=24,c=80): | 200 def __init__ (self, r=24,c=80): |
| 174 | 201 |
| 175 term.__init__(self,r,c) | 202 term.__init__(self,r,c) |
| 176 | 203 |
| 177 #self.screen = screen (24,80) | 204 #self.screen = screen (24,80) |
| 178 self.state = FSM.FSM ('INIT',[self]) | 205 self.state = FSM.FSM ('INIT',[self]) |
| 179 self.state.set_default_transition (Log, 'INIT') | 206 self.state.set_default_transition (DoLog, 'INIT') |
| 180 self.state.add_transition_any ('INIT', Emit, 'INIT') | 207 self.state.add_transition_any ('INIT', DoEmit, 'INIT') |
| 181 self.state.add_transition ('\x1b', 'INIT', None, 'ESC') | 208 self.state.add_transition ('\x1b', 'INIT', None, 'ESC') |
| 182 self.state.add_transition_any ('ESC', Log, 'INIT') | 209 self.state.add_transition_any ('ESC', DoLog, 'INIT') |
| 183 self.state.add_transition ('(', 'ESC', None, 'G0SCS') | 210 self.state.add_transition ('(', 'ESC', None, 'G0SCS') |
| 184 self.state.add_transition (')', 'ESC', None, 'G1SCS') | 211 self.state.add_transition (')', 'ESC', None, 'G1SCS') |
| 185 self.state.add_transition_list ('AB012', 'G0SCS', None, 'INIT') | 212 self.state.add_transition_list ('AB012', 'G0SCS', None, 'INIT') |
| 186 self.state.add_transition_list ('AB012', 'G1SCS', None, 'INIT') | 213 self.state.add_transition_list ('AB012', 'G1SCS', None, 'INIT') |
| 187 self.state.add_transition ('7', 'ESC', DoCursorSave, 'INIT') | 214 self.state.add_transition ('7', 'ESC', DoCursorSave, 'INIT') |
| 188 self.state.add_transition ('8', 'ESC', DoCursorRestore, 'INIT') | 215 self.state.add_transition ('8', 'ESC', DoCursorRestore, 'INIT') |
| 189 self.state.add_transition ('M', 'ESC', DoUpReverse, 'INIT') | 216 self.state.add_transition ('M', 'ESC', DoUpReverse, 'INIT') |
| 190 self.state.add_transition ('>', 'ESC', DoUpReverse, 'INIT') | 217 self.state.add_transition ('>', 'ESC', DoUpReverse, 'INIT') |
| 191 self.state.add_transition ('<', 'ESC', DoUpReverse, 'INIT') | 218 self.state.add_transition ('<', 'ESC', DoUpReverse, 'INIT') |
| 192 self.state.add_transition ('=', 'ESC', None, 'INIT') # Selects applicati
on keypad. | 219 self.state.add_transition ('=', 'ESC', None, 'INIT') # Selects applicati
on keypad. |
| 193 self.state.add_transition ('#', 'ESC', None, 'GRAPHICS_POUND') | 220 self.state.add_transition ('#', 'ESC', None, 'GRAPHICS_POUND') |
| 194 self.state.add_transition_any ('GRAPHICS_POUND', None, 'INIT') | 221 self.state.add_transition_any ('GRAPHICS_POUND', None, 'INIT') |
| 195 self.state.add_transition ('[', 'ESC', None, 'ELB') | 222 self.state.add_transition ('[', 'ESC', None, 'ELB') |
| 196 # ELB means Escape Left Bracket. That is ^[[ | 223 # ELB means Escape Left Bracket. That is ^[[ |
| 197 self.state.add_transition ('H', 'ELB', DoHomeOrigin, 'INIT') | 224 self.state.add_transition ('H', 'ELB', DoHomeOrigin, 'INIT') |
| 198 self.state.add_transition ('D', 'ELB', DoBackOne, 'INIT') | 225 self.state.add_transition ('D', 'ELB', DoBackOne, 'INIT') |
| 199 self.state.add_transition ('B', 'ELB', DoDownOne, 'INIT') | 226 self.state.add_transition ('B', 'ELB', DoDownOne, 'INIT') |
| 200 self.state.add_transition ('C', 'ELB', DoForwardOne, 'INIT') | 227 self.state.add_transition ('C', 'ELB', DoForwardOne, 'INIT') |
| 201 self.state.add_transition ('A', 'ELB', DoUpOne, 'INIT') | 228 self.state.add_transition ('A', 'ELB', DoUpOne, 'INIT') |
| 202 self.state.add_transition ('J', 'ELB', DoEraseDown, 'INIT') | 229 self.state.add_transition ('J', 'ELB', DoEraseDown, 'INIT') |
| 203 self.state.add_transition ('K', 'ELB', DoEraseEndOfLine, 'INIT') | 230 self.state.add_transition ('K', 'ELB', DoEraseEndOfLine, 'INIT') |
| 204 self.state.add_transition ('r', 'ELB', DoEnableScroll, 'INIT') | 231 self.state.add_transition ('r', 'ELB', DoEnableScroll, 'INIT') |
| 205 self.state.add_transition ('m', 'ELB', None, 'INIT') | 232 self.state.add_transition ('m', 'ELB', None, 'INIT') |
| 206 self.state.add_transition ('?', 'ELB', None, 'MODECRAP') | 233 self.state.add_transition ('?', 'ELB', None, 'MODECRAP') |
| 207 self.state.add_transition_list (string.digits, 'ELB', StartNumber, 'NUMB
ER_1') | 234 self.state.add_transition_list (string.digits, 'ELB', DoStartNumber, 'NU
MBER_1') |
| 208 self.state.add_transition_list (string.digits, 'NUMBER_1', BuildNumber,
'NUMBER_1') | 235 self.state.add_transition_list (string.digits, 'NUMBER_1', DoBuildNumber
, 'NUMBER_1') |
| 209 self.state.add_transition ('D', 'NUMBER_1', DoBack, 'INIT') | 236 self.state.add_transition ('D', 'NUMBER_1', DoBack, 'INIT') |
| 210 self.state.add_transition ('B', 'NUMBER_1', DoDown, 'INIT') | 237 self.state.add_transition ('B', 'NUMBER_1', DoDown, 'INIT') |
| 211 self.state.add_transition ('C', 'NUMBER_1', DoForward, 'INIT') | 238 self.state.add_transition ('C', 'NUMBER_1', DoForward, 'INIT') |
| 212 self.state.add_transition ('A', 'NUMBER_1', DoUp, 'INIT') | 239 self.state.add_transition ('A', 'NUMBER_1', DoUp, 'INIT') |
| 213 self.state.add_transition ('J', 'NUMBER_1', DoErase, 'INIT') | 240 self.state.add_transition ('J', 'NUMBER_1', DoErase, 'INIT') |
| 214 self.state.add_transition ('K', 'NUMBER_1', DoEraseLine, 'INIT') | 241 self.state.add_transition ('K', 'NUMBER_1', DoEraseLine, 'INIT') |
| 215 self.state.add_transition ('l', 'NUMBER_1', DoMode, 'INIT') | 242 self.state.add_transition ('l', 'NUMBER_1', DoMode, 'INIT') |
| 216 ### It gets worse... the 'm' code can have infinite number of | 243 ### It gets worse... the 'm' code can have infinite number of |
| 217 ### number;number;number before it. I've never seen more than two, | 244 ### number;number;number before it. I've never seen more than two, |
| 218 ### but the specs say it's allowed. crap! | 245 ### but the specs say it's allowed. crap! |
| 219 self.state.add_transition ('m', 'NUMBER_1', None, 'INIT') | 246 self.state.add_transition ('m', 'NUMBER_1', None, 'INIT') |
| 220 ### LED control. Same problem as 'm' code. | 247 ### LED control. Same implementation problem as 'm' code. |
| 221 self.state.add_transition ('q', 'NUMBER_1', None, 'INIT') | 248 self.state.add_transition ('q', 'NUMBER_1', None, 'INIT') |
| 222 | 249 |
| 223 # \E[?47h appears to be "switch to alternate screen" | 250 # \E[?47h switch to alternate screen |
| 224 # \E[?47l restores alternate screen... I think. | 251 # \E[?47l restores to normal screen from alternate screen. |
| 225 self.state.add_transition_list (string.digits, 'MODECRAP', StartNumber,
'MODECRAP_NUM') | 252 self.state.add_transition_list (string.digits, 'MODECRAP', DoStartNumber
, 'MODECRAP_NUM') |
| 226 self.state.add_transition_list (string.digits, 'MODECRAP_NUM', BuildNumb
er, 'MODECRAP_NUM') | 253 self.state.add_transition_list (string.digits, 'MODECRAP_NUM', DoBuildNu
mber, 'MODECRAP_NUM') |
| 227 self.state.add_transition ('l', 'MODECRAP_NUM', None, 'INIT') | 254 self.state.add_transition ('l', 'MODECRAP_NUM', None, 'INIT') |
| 228 self.state.add_transition ('h', 'MODECRAP_NUM', None, 'INIT') | 255 self.state.add_transition ('h', 'MODECRAP_NUM', None, 'INIT') |
| 229 | 256 |
| 230 #RM Reset Mode Esc [ Ps l none | 257 #RM Reset Mode Esc [ Ps l none |
| 231 self.state.add_transition (';', 'NUMBER_1', None, 'SEMICOLON') | 258 self.state.add_transition (';', 'NUMBER_1', None, 'SEMICOLON') |
| 232 self.state.add_transition_any ('SEMICOLON', Log, 'INIT') | 259 self.state.add_transition_any ('SEMICOLON', DoLog, 'INIT') |
| 233 self.state.add_transition_list (string.digits, 'SEMICOLON', StartNumber,
'NUMBER_2') | 260 self.state.add_transition_list (string.digits, 'SEMICOLON', DoStartNumbe
r, 'NUMBER_2') |
| 234 self.state.add_transition_list (string.digits, 'NUMBER_2', BuildNumber,
'NUMBER_2') | 261 self.state.add_transition_list (string.digits, 'NUMBER_2', DoBuildNumber
, 'NUMBER_2') |
| 235 self.state.add_transition_any ('NUMBER_2', Log, 'INIT') | 262 self.state.add_transition_any ('NUMBER_2', DoLog, 'INIT') |
| 236 self.state.add_transition ('H', 'NUMBER_2', DoHome, 'INIT') | 263 self.state.add_transition ('H', 'NUMBER_2', DoHome, 'INIT') |
| 237 self.state.add_transition ('f', 'NUMBER_2', DoHome, 'INIT') | 264 self.state.add_transition ('f', 'NUMBER_2', DoHome, 'INIT') |
| 238 self.state.add_transition ('r', 'NUMBER_2', DoScrollRegion, 'INIT') | 265 self.state.add_transition ('r', 'NUMBER_2', DoScrollRegion, 'INIT') |
| 239 ### It gets worse... the 'm' code can have infinite number of | 266 ### It gets worse... the 'm' code can have infinite number of |
| 240 ### number;number;number before it. I've never seen more than two, | 267 ### number;number;number before it. I've never seen more than two, |
| 241 ### but the specs say it's allowed. crap! | 268 ### but the specs say it's allowed. crap! |
| 242 self.state.add_transition ('m', 'NUMBER_2', None, 'INIT') | 269 self.state.add_transition ('m', 'NUMBER_2', None, 'INIT') |
| 243 ### LED control. Same problem as 'm' code. | 270 ### LED control. Same problem as 'm' code. |
| 244 self.state.add_transition ('q', 'NUMBER_2', None, 'INIT') | 271 self.state.add_transition ('q', 'NUMBER_2', None, 'INIT') |
| 272 self.state.add_transition (';', 'NUMBER_2', None, 'SEMICOLON_X') |
| 273 |
| 274 # Create a state for 'q' and 'm' which allows an infinite number of igno
red numbers |
| 275 self.state.add_transition_any ('SEMICOLON_X', DoLog, 'INIT') |
| 276 self.state.add_transition_list (string.digits, 'SEMICOLON_X', None, 'NUM
BER_X') |
| 277 self.state.add_transition_any ('NUMBER_X', DoLog, 'INIT') |
| 278 self.state.add_transition ('m', 'NUMBER_X', None, 'INIT') |
| 279 self.state.add_transition ('q', 'NUMBER_X', None, 'INIT') |
| 280 self.state.add_transition (';', 'NUMBER_2', None, 'SEMICOLON_X') |
| 245 | 281 |
| 246 def process (self, c): | 282 def process (self, c): |
| 247 | 283 |
| 248 self.state.process(c) | 284 self.state.process(c) |
| 249 | 285 |
| 250 def process_list (self, l): | 286 def process_list (self, l): |
| 251 | 287 |
| 252 self.write(l) | 288 self.write(l) |
| 253 | 289 |
| 254 def write (self, s): | 290 def write (self, s): |
| 255 | 291 |
| 256 for c in s: | 292 for c in s: |
| 257 self.process(c) | 293 self.process(c) |
| 258 | 294 |
| 259 def flush (self): | 295 def flush (self): |
| 260 | 296 |
| 261 pass | 297 pass |
| 262 | 298 |
| 263 def write_ch (self, ch): | 299 def write_ch (self, ch): |
| 264 | 300 |
| 265 """This puts a character at the current cursor position. cursor | 301 """This puts a character at the current cursor position. The cursor |
| 266 position if moved forward with wrap-around, but no scrolling is done if | 302 position is moved forward with wrap-around, but no scrolling is done if |
| 267 the cursor hits the lower-right corner of the screen. """ | 303 the cursor hits the lower-right corner of the screen. """ |
| 268 | 304 |
| 269 #\r and \n both produce a call to crlf(). | 305 #\r and \n both produce a call to cr() and lf(), respectively. |
| 270 ch = ch[0] | 306 ch = ch[0] |
| 271 | 307 |
| 272 if ch == '\r': | 308 if ch == '\r': |
| 273 # self.crlf() | 309 self.cr() |
| 274 return | 310 return |
| 275 if ch == '\n': | 311 if ch == '\n': |
| 276 self.crlf() | 312 self.crlf() |
| 277 return | 313 return |
| 278 if ch == chr(screen.BS): | 314 if ch == chr(screen.BS): |
| 279 self.cursor_back() | 315 self.cursor_back() |
| 280 self.put_abs(self.cur_r, self.cur_c, ' ') | |
| 281 return | 316 return |
| 282 | |
| 283 if ch not in string.printable: | 317 if ch not in string.printable: |
| 284 fout = open ('log', 'a') | 318 fout = open ('log', 'a') |
| 285 fout.write ('Nonprint: ' + str(ord(ch)) + '\n') | 319 fout.write ('Nonprint: ' + str(ord(ch)) + '\n') |
| 286 fout.close() | 320 fout.close() |
| 287 return | 321 return |
| 288 self.put_abs(self.cur_r, self.cur_c, ch) | 322 self.put_abs(self.cur_r, self.cur_c, ch) |
| 289 old_r = self.cur_r | 323 old_r = self.cur_r |
| 290 old_c = self.cur_c | 324 old_c = self.cur_c |
| 291 self.cursor_forward() | 325 self.cursor_forward() |
| 292 if old_c == self.cur_c: | 326 if old_c == self.cur_c: |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 325 # '(His sense of smell.)' | 359 # '(His sense of smell.)' |
| 326 # self.fill('.') | 360 # self.fill('.') |
| 327 # self.cursor_home() | 361 # self.cursor_home() |
| 328 # for c in write_text: | 362 # for c in write_text: |
| 329 # self.write_ch (c) | 363 # self.write_ch (c) |
| 330 # print str(self) | 364 # print str(self) |
| 331 # | 365 # |
| 332 #if __name__ == '__main__': | 366 #if __name__ == '__main__': |
| 333 # t = ANSI(6,65) | 367 # t = ANSI(6,65) |
| 334 # t.test() | 368 # t.test() |
| OLD | NEW |