3
# openipmi generic terminal handling for SoL
5
# Author: MontaVista Software, Inc.
6
# Corey Minyard <minyard@mvista.com>
9
# Copyright 2006 MontaVista Software Inc.
11
# This program is free software; you can redistribute it and/or
12
# modify it under the terms of the GNU Lesser General Public License
13
# as published by the Free Software Foundation; either version 2 of
14
# the License, or (at your option) any later version.
17
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
18
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
# You should have received a copy of the GNU Lesser General Public
29
# License along with this program; if not, write to the Free
30
# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
48
class TerminalEmulator:
51
# Mode is a 2-d array of [ cflags, bg color, fg color ]
53
# Style is (fg, bg, flags)
54
for i in range(0, 24):
56
self.modes.append([ ])
57
for j in range(0, 80):
58
self.buf[i].append(" ")
59
self.modes[i].append( [0, 0, 7] )
69
self.InputHandler = self.Input0
71
self.saved_pos = [1, 1]
72
self.scroll_region = [0, self.height-1]
73
self.keypad_alt = False
76
def check_scroll_down(self):
78
starty = self.GetStartY()
81
old = self.buf[starty]
83
self.buf.insert(endy, old)
84
old = self.modes[starty]
85
del(self.modes[starty])
86
self.modes.insert(endy, old)
87
for j in range(0, self.width):
88
self.buf[endy][j] = " "
89
self.modes[endy][j][0] = 0
90
self.modes[endy][j][1] = black
91
self.modes[endy][j][2] = white
94
self.ScrollLines(starty, endy)
100
def check_scroll_up(self):
101
starty = self.GetStartY()
102
endy = self.GetEndY()
103
if (self.y == starty):
106
self.buf.insert(starty, old)
107
for j in range(0, self.width):
108
self.buf[starty][j] = ' '
109
self.modes[starty][j][0] = 0
110
self.modes[starty][j][1] = black
111
self.modes[starty][j][2] = white
114
self.ScrollLinesUp(starty, endy)
120
def output_at(self, x, y, slen):
123
last_mode = [ 0, 0, 7 ]
127
if (last_mode != self.modes[y][x+i]):
128
# Character mode change, output what we have and switch.
129
self.DrawText(last_mode[2], last_mode[1], last_mode[0],
135
last_mode = self.modes[y][x+i]
138
s += self.buf[y][x+i]
141
self.DrawText(last_mode[2], last_mode[1], last_mode[0],
145
def handle_cursor(self):
146
if (self.x == self.width):
151
mode = self.modes[self.y][xpos]
152
self.DrawCursor(mode[2], mode[1], mode[0], xpos, self.y,
153
self.buf[self.y][xpos])
156
def restore_cursor(self):
157
if (self.x == self.width):
158
self.output_at(self.x-1, self.y, 1)
161
self.output_at(self.x, self.y, 1)
165
def output_str(self, s):
169
if (self.x == self.width):
170
# Cursor is at the end of the line, redraw the char where
171
# the cursor is sitting.
172
self.restore_cursor()
174
self.check_scroll_down()
176
while ((self.x + len(s)) > self.width):
177
# The string exceeds the line length, do it in pieces
178
outlen = self.width - self.x
180
self.buf[self.y][self.x:self.x+outlen] = s[0:outlen]
181
self.output_at(self.x, self.y, outlen)
184
self.check_scroll_down()
187
for i in range(0, outlen):
188
self.buf[self.y][self.x+i] = s[i]
189
for i in range(self.x, self.x+outlen):
190
self.modes[self.y][i][0] = self.cflags
191
self.modes[self.y][i][1] = self.bg_color
192
self.modes[self.y][i][2] = self.fg_color
194
self.output_at(self.x, self.y, outlen)
199
def GetParm(self, n, default = 1):
200
if (self.parms == None):
202
elif (len(self.parms) <= n):
206
# Get the current upper bound of the cursor, used for scroll regions.
208
if ((self.y >= self.scroll_region[0])
209
and (self.y <= self.scroll_region[1])):
210
return self.scroll_region[0]
213
# Get the current lower bound of the cursor, used for scroll regions.
215
if ((self.y >= self.scroll_region[0])
216
and (self.y <= self.scroll_region[1])):
217
return self.scroll_region[1]
218
return self.height - 1
221
def Input2(self, c, s):
223
count = self.GetParm(0)
224
starty = self.GetStartY()
225
self.restore_cursor()
226
if (count > (self.y - starty)):
231
self.handle_cursor();
233
elif (c == 'B'): # Down
234
count = self.GetParm(0)
235
endy = self.GetEndY()
236
self.restore_cursor()
237
if (count > (endy - self.y)):
242
self.handle_cursor();
244
elif (c == 'C'): # Right
245
count = self.GetParm(0)
246
self.restore_cursor()
247
if (count > (self.width - self.x - 1)):
248
self.x = self.width - 1
252
self.handle_cursor();
254
elif (c == 'D'): # Right
255
count = self.GetParm(0)
256
self.restore_cursor()
262
self.handle_cursor();
264
elif (c == '?'): # FIXME: Not sure what this does
265
return "" # Stay in Input2
266
elif ((c >= '0') and (c <= '9')):
267
if (self.parms == None):
268
self.parms = [ int(c) ]
270
currparm = len(self.parms) - 1
271
self.parms[currparm] *= 10
272
self.parms[currparm] += int(c)
274
return "" # Stay in Input2
275
elif (c == ';'): # Next parm
276
if (self.parms == None):
277
self.parms = [ 0, 0 ]
281
return "" # Stay in Input2
282
elif (c == 'r'): # Scroll region
283
y1 = self.GetParm(0, -1)
284
y2 = self.GetParm(1, -1)
285
if ((y1 == -1) or (y2 == -1)):
286
if ((y1 == -1) and (y2 == -1)):
287
self.scroll_region[0] = 0
288
self.scroll_region[1] = self.height - 1
291
elif ((y1 > y2) or (y1 < 1) or (y2 < 1)
292
or (y1 >= (self.height+1)) or (y2 >= (self.height+1))):
293
# Bogus values, just ignre them.
296
self.scroll_region[0] = y1 - 1
297
self.scroll_region[1] = y2 - 1
300
elif ((c == 'H') or (c == 'f')): # Move to position specified
305
elif (x > self.width):
308
self.restore_cursor()
311
self.handle_cursor();
313
elif (c == 's'): # save cursor position
314
self.saved_pos[0] = self.x
315
self.saved_pos[1] = self.y
317
elif (c == 'u'): # Restore cursor position
318
self.restore_cursor()
319
self.x = self.saved_pos[0]
320
self.y = self.saved_pos[1]
321
self.handle_cursor();
323
elif (c == 'J'): # Clear screen area
324
mode = self.GetParm(0, -1)
327
length = self.height - self.y
341
for y in range(starty, starty + length):
342
for x in range(0, self.width):
344
self.modes[y][x][0] = 0
345
self.modes[y][x][1] = black
346
self.modes[y][x][2] = white
348
self.output_at(0, y, self.width)
350
self.handle_cursor();
352
elif (c == 'K'): # Clear line
353
mode = self.GetParm(0, 0)
355
if (mode == 0): # To end of line
357
length = self.width - self.x
359
elif (mode == 1): # To start of line
363
elif (mode == 2): # Whole line
371
for x in range(startx, startx+length):
373
self.modes[y][x][0] = 0
374
self.modes[y][x][1] = black
375
self.modes[y][x][2] = white
377
self.output_at(startx, y, length)
378
self.handle_cursor();
379
elif (c == 'm'): # Graphics mode
381
val = self.GetParm(i, 0)
385
self.bg_color = black
386
self.fg_color = white
390
self.cflags |= UNDERLINE
394
self.cflags |= INVERSE
396
self.cflags |= CONCEALED
397
elif ((val >= 30) and (val <= 37)):
398
self.fg_color = val - 30
399
elif ((val >= 40) and (val <= 47)):
400
self.bg_color = val - 40
403
val = self.GetParm(i, -1)
407
# FIXME: \e[2g means clear tabs, so does 3g, 0g or just g
410
elif (c == 'P'): # delete parm characters (1 default)
411
count = self.GetParm(0, 1)
412
if (count > (self.width - self.x)):
413
count = self.width - self.x
417
for i in range(0, count):
419
self.buf[y].append(' ')
420
old = self.modes[y][x]
425
self.modes[y].append(old)
427
self.DeleteChars(x, y, count)
430
elif (c == 'M'): # delete parm lines (1 default)
431
count = self.GetParm(0, 1)
432
if (count > (self.height - self.y)):
433
count = self.height - self.y
435
for i in range(0, count):
436
old = self.buf[self.y]
439
old = self.modes[self.y]
440
del self.modes[self.y]
441
self.modes.append(old)
442
for j in range(0, self.width):
443
self.buf[self.height-1][j] = ' '
444
self.modes[self.height-1][j][0] = 0
445
self.modes[self.height-1][j][1] = black
446
self.modes[self.height-1][j][2] = white
448
self.ScrollLines(self.y, self.height-1)
452
elif (c == 'L'): # insert parm lines (1 default)
453
self.restore_cursor()
454
count = self.GetParm(0, 1)
455
if (count > (self.height - self.y)):
456
count = self.height - self.y
458
for i in range(0, count):
459
old = self.buf[self.height-1]
460
del self.buf[self.height-1]
461
self.buf.insert(self.y, old)
462
for j in range(0, self.width):
463
self.buf[self.y][j] = ' '
464
self.modes[self.y][j][0] = 0
465
self.modes[self.y][j][1] = black
466
self.modes[self.y][j][2] = white
468
self.ScrollLinesUp(self.y, self.height-1)
472
elif (c == 'Z'): # Move to previous tab stop
473
self.restore_cursor()
474
self.x = ((self.x-1) / 8) * 8
475
self.handle_cursor();
477
elif (c == '@'): # insert parm chars (1 default)
478
self.restore_cursor()
479
count = self.GetParm(0, 1)
480
if (count > (self.width - self.x)):
481
count = self.width - self.x
485
for i in range(0, count):
486
del self.buf[y][self.width-1]
487
old = self.modes[y][self.width-1]
488
del self.modes[y][self.width-1]
492
self.buf[y].insert(self.x, ' ')
493
self.modes[y].insert(self.x, old)
495
self.InsertChars(x, y, count)
498
elif (c == 'S'): # scroll forward parm lines (1 default)
499
self.restore_cursor()
500
count = self.GetParm(0, 1)
501
if (count > self.height):
504
for i in range(0, count):
510
self.modes.append(old)
511
for j in range(0, self.width):
512
self.buf[self.height-1][j] = ' '
513
self.modes[self.height-1][j][0] = 0
514
self.modes[self.height-1][j][1] = black
515
self.modes[self.height-1][j][2] = white
517
self.ScrollLines(0, self.height-1)
521
elif (c == 'T'): # scroll back parm lines (1 default)
522
self.restore_cursor()
523
count = self.GetParm(0, 1)
524
if (count > self.height):
527
for i in range(0, count):
528
old = self.buf[self.height-1]
529
del self.buf[self.height-1]
530
self.buf.insert(0, old)
531
old = self.modes[self.height-1]
532
del self.modes[self.height-1]
533
self.modes.insert(0, old)
534
for j in range(0, self.width):
536
self.modes[0][j][0] = 0
537
self.modes[0][j][1] = black
538
self.modes[0][j][2] = white
540
self.ScrollLinesUp(0, self.height-1)
544
elif (c == 'G'): # Move cursor to column parm (1 default)
545
self.restore_cursor()
546
pos = self.GetParm(0, 1)
547
if (pos > self.width):
555
elif (c == 'd'): # Move cursor to line parm (1 default)
556
self.restore_cursor()
557
pos = self.GetParm(0, 1)
558
if (pos > self.height):
566
elif (c == 'X'): # erase parm characters (1 default) (no cursor move)
567
count = self.GetParm(0, 1)
568
if (count > (self.height - self.y)):
569
count = self.height - self.y
571
for i in range(0, count):
572
self.buf[self.y][self.x+i] = " "
573
self.modes[self.y][self.x+i][0] = 0
574
self.modes[self.y][self.x+i][1] = black
575
self.modes[self.y][self.x+i][2] = white
577
self.output_at(self.x, self.y, count)
580
elif (c == 'c'): # Identify terminal
581
# Identify ourself as "linux"
582
self.HandleTerminalOutput("\x1b[?62;9;c")
584
self.InputHandler = self.Input0
588
def Input1(self, c, s):
590
self.InputHandler = self.Input2
592
elif (c == 'D'): # Scroll down
593
self.restore_cursor()
594
self.check_scroll_down()
595
self.handle_cursor();
596
elif (c == 'M'): # Scroll up
597
self.restore_cursor()
598
self.check_scroll_up()
599
self.handle_cursor();
601
elif (c == 'H'): # FIXME: Set tabulator stop in all rows at current column
603
elif (c == 'c'): # reset terminal
604
for y in range(0, self.height):
605
for x in range(0, self.width):
607
self.modes[y][x][0] = 0
608
self.modes[y][x][1] = black
609
self.modes[y][x][2] = white
611
self.output_at(0, y, self.width)
616
self.bg_color = black
617
self.fg_color = white
618
self.handle_cursor();
620
elif ((c >= '0') and (c <= '9')):
621
if (self.parms == None):
622
self.parms = [ int(c) ]
624
currparm = len(self.parms) - 1
625
self.parms[currparm] *= 10
626
self.parms[currparm] += int(c)
628
return "" # Stay in Input1
629
elif (c == 'n'): # Terminal state
630
op = self.GetParm(0, 0)
631
if (op == 5): # Requesting terminal status
632
self.HandleTerminalOutput("\x1b0n") # We are ok
633
elif (op == 6): # Current cursor position
634
self.HandleTerminalOutput("\x1b%d;%dR" % (self.y+1, self.x+1))
636
elif (c == '='): # alternate keypad mode
637
self.keypat_alt = True
639
elif (c == '>'): # alternate keypad mode off
640
self.keypat_alt = False
642
self.InputHandler = self.Input0
645
# "normal" input mode
646
def Input0(self, c, s):
647
if ((c >= ' ') and (c <= '~')):
652
self.restore_cursor()
653
self.check_scroll_down()
654
self.handle_cursor();
657
self.restore_cursor()
659
self.handle_cursor();
662
self.restore_cursor()
663
if (self.x >= self.width-8):
664
self.x = self.width-1
666
self.x = ((self.x / 8) * 8) + 8
668
self.handle_cursor();
670
elif (c == '\007'): #bell
672
elif (c == '\010'): #backspace
674
self.restore_cursor()
676
self.handle_cursor();
681
self.InputHandler = self.Input1
686
def ProcessInput(self, data):
689
s = self.InputHandler(c, s)
694
def ResizeTerminal(self, w, h):