summaryrefslogtreecommitdiffstats
path: root/Demo/cwilib
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1993-10-28 09:53:13 (GMT)
committerGuido van Rossum <guido@python.org>1993-10-28 09:53:13 (GMT)
commit74b3f8a9e3368ad10a8d4aebd2dbaffdd3ca3585 (patch)
tree48baa4720d284e11ed119b35c6f915c71876d394 /Demo/cwilib
parentdc4b93db7097a34cee492b188ec2fd65c5b9bc4b (diff)
downloadcpython-74b3f8a9e3368ad10a8d4aebd2dbaffdd3ca3585.zip
cpython-74b3f8a9e3368ad10a8d4aebd2dbaffdd3ca3585.tar.gz
cpython-74b3f8a9e3368ad10a8d4aebd2dbaffdd3ca3585.tar.bz2
Initial revision
Diffstat (limited to 'Demo/cwilib')
-rwxr-xr-xDemo/cwilib/cwilib.py226
-rwxr-xr-xDemo/cwilib/form.py170
-rwxr-xr-xDemo/cwilib/telnetlib.py181
-rwxr-xr-xDemo/cwilib/vt100.py326
-rwxr-xr-xDemo/cwilib/vt100win.py68
5 files changed, 971 insertions, 0 deletions
diff --git a/Demo/cwilib/cwilib.py b/Demo/cwilib/cwilib.py
new file mode 100755
index 0000000..25e0622
--- /dev/null
+++ b/Demo/cwilib/cwilib.py
@@ -0,0 +1,226 @@
+# Interface to the interactive CWI library catalog.
+
+import sys
+import stdwin
+from stdwinevents import *
+import select
+import telnetlib
+import vt100win
+from form import Form
+
+
+# Main program
+
+def main():
+ vt = vt100win.VT100win()
+ #
+ host = 'biefstuk.cwi.nl'
+ port = 0
+ timeout = 10.0
+ tn = telnetlib.Telnet(host, port)
+ tn.set_timeout(timeout)
+ #
+ try:
+ vt.send(tn.read_until('login: '))
+ tn.write('cwilib\r')
+ #
+ vt.send(tn.read_until('Hit <RETURN> to continue...'))
+ tn.write('\r')
+ #
+ vt.send(tn.read_until('QUIT'))
+ except EOFError:
+ sys.stderr.write('Connection closed prematurely\n')
+ sys.exit(1)
+ #
+ define_screens(vt)
+ matches = vt.which_screens()
+ if 'menu' not in matches:
+ sys.stderr.write('No main menu within %g seconds\n' % timeout)
+ sys.exit(1)
+ #
+ tn.set_timeout(0)
+ tn.write('\r\r')
+ vt.open('Progress -- CWI Library')
+ ui = UserInterface()
+ #
+ while 1:
+ event = stdwin.pollevent()
+ if not event:
+ rfd, wfd, xfd = select.select([stdwin, tn], [], [])
+ if stdwin in rfd:
+ event = stdwin.getevent()
+ if event:
+ type, window, detail = event
+ if window == None and type == WE_LOST_SEL:
+ window = ui.queryform.window
+ event = type, window, detail
+ if type == WE_CLOSE:
+ break
+ if window in ui.windows:
+ ui.dispatch(type, window, detail)
+ elif window == vt.window:
+ if type == WE_NULL:
+ pass
+ elif type == WE_COMMAND:
+ if detail == WC_RETURN:
+ tn.write('\r')
+ elif detail == WC_BACKSPACE:
+ tn.write('\b')
+ elif detail == WC_TAB:
+ tn.write('\t')
+ elif detail == WC_UP:
+ tn.write('\033[A')
+ elif detail == WC_DOWN:
+ tn.write('\033[B')
+ elif detail == WC_RIGHT:
+ tn.write('\033[C')
+ elif detail == WC_LEFT:
+ tn.write('\033[D')
+ else:
+ print '*** Command:', detail
+ elif type == WE_CHAR:
+ tn.write(detail)
+ elif type == WE_DRAW:
+ vt.draw(detail)
+ elif type in (WE_ACTIVATE, WE_DEACTIVATE):
+ pass
+ else:
+ print '*** VT100 event:', type, detail
+ else:
+ print '*** Alien event:', type, window, detail
+ elif tn in rfd:
+ vt.window.setwincursor('watch')
+ try:
+ data = tn.read_now()
+ except EOFError:
+ stdwin.message('Connection closed--goodbye')
+ break
+ print 'send...'
+ vt.send(data)
+ print 'send...done'
+ vt.window.setwincursor('arrow')
+ matches = vt.which_screens()
+ if 'timelimit' in matches:
+ stdwin.message('Time limit--goodbye')
+ break
+ print '*** Matches:', matches
+ else:
+ print '*** Weird return from select:', rfd, wfd, xfd
+
+
+# Subroutine to define our screen recognition patterns
+
+def define_screens(vt):
+ vt.define_screen('menu', {
+ 'title': ('search', 0, 0, 80,
+ ' SEARCH FUNCTIONS +OTHER FUNCTIONS '),
+ })
+ vt.define_screen('search', {
+ 'title': ('search', 0, 0, 80, ' Search '),
+ })
+ vt.define_screen('shortlist', {'title': ('search', 0, 0, 80,
+ ' Short-list')})
+ vt.define_screen('showrecord', {
+ 'title': ('search', 0, 0, 80, ' Show record '),
+ })
+ vt.define_screen('timelimit', {
+ 'limit': ('search', 12, 0, 80, ' TIME LIMIT '),
+ })
+ vt.define_screen('attention', {
+ 'BASE': ('copy', 0, 0, 0, 'search'),
+ 'title': ('search', 10, 0, 80, ' ATTENTION ')})
+ vt.define_screen('syntaxerror', {
+ 'BASE': ('copy', 0, 0, 0, 'attention'),
+ 'message': ('search', 12, 0, 80, ' Syntax error'),
+ })
+ vt.define_screen('emptyerror', {
+ 'BASE': ('copy', 0, 0, 0, 'attention'),
+ 'message': ('search', 12, 0, 80,
+ ' Check your input. Search at least one term'),
+ })
+ vt.define_screen('unsortedwarning', {
+ 'BASE': ('copy', 0, 0, 0, 'attention'),
+ 'message': ('search', 12, 0, 80,
+ ' Number of records exceeds sort limit'),
+ })
+ vt.define_screen('thereismore', {
+ 'BASE': ('copy', 0, 0, 0, 'showrecord'),
+ 'message': ('search', 15, 0, 80,
+ 'There is more within this record. Use the arrow keys'),
+ })
+ vt.define_screen('nofurther', {
+ 'BASE': ('copy', 0, 0, 0, 'showrecord'),
+ 'message': ('search', 17, 0, 80, 'You cannot go further\.'),
+ })
+ vt.define_screen('nofurtherback', {
+ 'BASE': ('copy', 0, 0, 0, 'showrecord'),
+ 'message': ('search', 17, 0, 80,
+ 'You cannot go further back'),
+ })
+
+
+# Class to implement our user interface.
+
+class UserInterface:
+
+ def __init__(self):
+ stdwin.setfont('7x14')
+ self.queryform = QueryForm()
+ self.listform = ListForm()
+ self.recordform = RecordForm()
+ self.forms = [self.queryform, self.listform, self.recordform]
+ define_query_fields(self.queryform)
+ self.windows = []
+ for form in self.forms:
+ if form.formheight > 0:
+ form.open()
+ self.windows.append(form.window)
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ for form in self.forms:
+ form.close()
+
+ def dispatch(self, type, window, detail):
+ for form in self.forms:
+ if window == form.window:
+ form.dispatch(type, detail)
+
+
+def define_query_fields(f):
+ f.define_field('name', 'Name auth./ed.', 1, 60)
+ f.define_field('title', 'Title', 4, 60)
+ f.define_field('shelfmark', 'Shelf mark', 1, 60)
+ f.define_field('class', 'Prim. classif.', 1, 60)
+ f.define_field('series', 'Series', 1, 60)
+ f.define_field('congress', 'Congr. pl./year', 1, 60)
+ f.define_field('type', 'Type', 1, 60)
+
+
+class QueryForm(Form):
+
+ def __init__(self):
+ Form.__init__(self, 'Query form -- CWI Library')
+
+ def dispatch(self, type, detail):
+ if type == WE_COMMAND and detail == WC_RETURN:
+ print '*** SUBMIT ***'
+ else:
+ Form.dispatch(self, type, detail)
+
+
+class ListForm(Form):
+
+ def __init__(self):
+ Form.__init__(self, 'Short list -- CWI Library')
+
+
+class RecordForm(Form):
+
+ def __init__(self):
+ Form.__init__(self, 'Record detail -- CWI Library')
+
+
+main()
diff --git a/Demo/cwilib/form.py b/Demo/cwilib/form.py
new file mode 100755
index 0000000..8dd6ef9
--- /dev/null
+++ b/Demo/cwilib/form.py
@@ -0,0 +1,170 @@
+# Fill-out form window
+
+import stdwin
+from stdwinevents import *
+
+
+class Form:
+
+ def __init__(self, title):
+ self.title = title
+ self.window = None
+ self.fields = {}
+ self.fieldnames = []
+ self.formwidth = self.formheight = 0
+ self.focusname = None
+ self.tefocus = None
+
+ def define_field(self, name, label, lines, chars):
+ self.fieldnames.append(name)
+ lh = stdwin.lineheight()
+ cw = stdwin.textwidth('m')
+ left = 20*cw
+ top = self.formheight + 4
+ right = left + chars*cw
+ bottom = top + lines*lh
+ te = None
+ self.fields[name] = (label, left, top, right, bottom, te)
+ self.formheight = bottom + 2
+ self.formwidth = max(self.formwidth, right + 4)
+
+ def open(self):
+ if self.window: return
+ self.formwidth = max(100, self.formwidth)
+ self.formheight = max(50, self.formheight)
+ stdwin.setdefwinsize(self.formwidth, self.formheight)
+ stdwin.setdefscrollbars(0, 0)
+ self.window = stdwin.open(self.title)
+ self.window.setdocsize(self.formwidth, self.formheight)
+ for name in self.fieldnames:
+ label, left, top, right, bottom, te = \
+ self.fields[name]
+ rect = (left, top), (right, bottom)
+ te = self.window.textcreate(rect)
+ te.setactive(0)
+ te.setview(rect)
+ self.fields[name] = \
+ label, left, top, right, bottom, te
+ if self.fieldnames:
+ self.setfocus(self.fieldnames[0])
+
+ def setfocus(self, name):
+ if name <> self.focusname and self.tefocus:
+ self.tefocus.setactive(0)
+ self.focusname = name
+ if self.focusname:
+ self.tefocus = self.fields[self.focusname][-1]
+ self.tefocus.setactive(1)
+ else:
+ self.tefocus = None
+
+ def dispatch(self, type, detail):
+ event = type, self.window, detail
+ if type == WE_NULL:
+ pass
+ elif type == WE_DRAW:
+ self.draw(detail)
+ elif type == WE_MOUSE_DOWN:
+ x, y = detail[0]
+ for name in self.fieldnames:
+ label, left, top, right, bottom, te = \
+ self.fields[name]
+ if left <= x < right and \
+ top <= y < bottom:
+ self.setfocus(name)
+ break
+ else:
+ stdwin.fleep()
+ return
+ if self.tefocus:
+ (left, top), (right, bottom) = \
+ self.tefocus.getrect()
+ if x < left: x = left
+ if x >= right: x = right-1
+ if y < top: y = top
+ if y >= bottom:
+ y = bottom-1
+ x = right-1
+ event = type, self.window, ((x,y),)+detail[1:]
+ if not self.tefocus.event(event):
+ stdwin.fleep()
+ elif type in (WE_MOUSE_MOVE, WE_MOUSE_UP, WE_CHAR):
+ if not self.tefocus or not self.tefocus.event(event):
+ stdwin.fleep()
+ elif type == WE_MOUSE_UP:
+ button = detail[2]
+ if button == 2:
+ self.paste_selection()
+ else:
+ self.make_selection()
+ elif type == WE_COMMAND:
+ if detail in (WC_BACKSPACE, WC_UP, WC_DOWN,
+ WC_LEFT, WC_RIGHT):
+ if not self.tefocus or \
+ not self.tefocus.event(event):
+ stdwin.fleep()
+ elif detail == WC_RETURN:
+ print '*** Submit query'
+ elif detail == WC_TAB:
+ if not self.fields:
+ stdwin.fleep()
+ return
+ if not self.focusname:
+ i = 0
+ else:
+ i = self.fieldnames.index(
+ self.focusname)
+ i = (i+1) % len(self.fieldnames)
+ self.setfocus(self.fieldnames[i])
+ self.tefocus.setfocus(0, 0x7fff)
+ self.make_selection()
+ elif type in (WE_ACTIVATE, WE_DEACTIVATE):
+ pass
+ elif type == WE_LOST_SEL:
+ if self.tefocus:
+ a, b = self.tefocus.getfocus()
+ self.tefocus.setfocus(a, a)
+ else:
+ print 'Form.dispatch(%d, %s)' % (type, `detail`)
+
+ def draw(self, detail):
+ d = self.window.begindrawing()
+ d.cliprect(detail)
+ d.erase(detail)
+ self.drawform(d, detail)
+ d.noclip()
+ d.close()
+ # Stupid textedit objects can't draw with open draw object...
+ self.drawtextedit(detail)
+
+ def drawform(self, d, detail):
+ for name in self.fieldnames:
+ label, left, top, right, bottom, te = self.fields[name]
+ d.text((0, top), label)
+ d.box((left-3, top-2), (right+4, bottom+2))
+
+ def drawtextedit(self, detail):
+ for name in self.fieldnames:
+ label, left, top, right, bottom, te = self.fields[name]
+ te.draw(detail)
+
+ def make_selection(self):
+ s = self.tefocus.getfocustext()
+ if not s:
+ return
+ stdwin.rotatecutbuffers(1)
+ stdwin.setcutbuffer(0, s)
+ if not self.window.setselection(WS_PRIMARY, s):
+ stdwin.fleep()
+
+ def paste_selection(self):
+ if not self.tefocus:
+ stdwin.fleep()
+ return
+ s = stdwin.getselection(WS_PRIMARY)
+ if not s:
+ s = stdwin.getcutbuffer(0)
+ if not s:
+ stdwin.fleep()
+ return
+ self.tefocus.replace(s)
diff --git a/Demo/cwilib/telnetlib.py b/Demo/cwilib/telnetlib.py
new file mode 100755
index 0000000..5c862e7
--- /dev/null
+++ b/Demo/cwilib/telnetlib.py
@@ -0,0 +1,181 @@
+# Telnet client library
+
+import socket
+import select
+import string
+import regsub
+
+# Tunable parameters
+TIMEOUT = 30.0
+DEBUGLEVEL = 1
+
+# Telnet protocol defaults
+TELNET_PORT = 23
+
+# Telnet protocol characters (don't change)
+IAC = chr(255) # "Interpret As Command"
+DONT = chr(254)
+DO = chr(253)
+WONT = chr(252)
+WILL = chr(251)
+
+
+# Telnet interface class
+
+class Telnet:
+
+ # Constructor
+ def __init__(self, host, port):
+ self.debuglevel = DEBUGLEVEL
+ self.host = host
+ if not port: port = TELNET_PORT
+ self.port = port
+ self.timeout = TIMEOUT
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.connect((self.host, self.port))
+ self.rawq = ''
+ self.irawq = 0
+ self.cookedq = ''
+
+ # Destructor
+ def __del__(self):
+ self.close()
+
+ # Print debug message
+ def msg(self, msg, *args):
+ if self.debuglevel > 0:
+ print 'TELNET:', msg%args
+
+ # Set debug level
+ def set_debuglevel(self, debuglevel):
+ self.debuglevel = debuglevel
+
+ # Set time-out on certain reads
+ def set_timeout(self, timeout):
+ self.timeout = float(timeout)
+
+ # Explicit close
+ def close(self):
+ if self.sock:
+ self.sock.close()
+ self.sock = None
+
+ # Return socket (e.g. for select)
+ def get_socket(self):
+ return self.sock
+
+ # Return socket's fileno (e.g. for select)
+ def fileno(self):
+ return self.sock.fileno()
+
+ # Write a string to the socket, doubling any IAC characters
+ def write(self, buffer):
+ if IAC in buffer:
+ buffer = regsub.gsub(IAC, IAC+IAC, buffer)
+ self.sock.send(buffer)
+
+ # Read until a given string is encountered or until timeout
+ def read_until(self, match):
+## self.msg('read_until(%s)' % `match`)
+ n = len(match)
+ self.process_rawq()
+ i = string.find(self.cookedq, match)
+ if i < 0:
+ i = max(0, len(self.cookedq)-n)
+ self.fill_cookedq()
+ i = string.find(self.cookedq, match, i)
+ if i >= 0:
+ i = i+n
+ buf = self.cookedq[:i]
+ self.cookedq = self.cookedq[i:]
+## self.msg('read_until(%s) -> %s' % (`match`, `buf`))
+ return buf
+ while select.select([self], [], [], self.timeout) == \
+ ([self], [], []):
+ i = max(0, len(self.cookedq)-n)
+ self.fill_rawq()
+ self.process_rawq()
+ i = string.find(self.cookedq, match, i)
+ if i >= 0:
+ i = i+n
+ buf = self.cookedq[:i]
+ self.cookedq = self.cookedq[i:]
+## self.msg('read_until(%s) -> %s' %
+## (`match`, `buf`))
+ return buf
+ buf = self.cookedq
+ self.cookedq = ''
+## self.msg('read_until(%s) -> %s' % (`match`, `buf`))
+ return buf
+
+ # Read everything that's possible without really blocking
+ def read_now(self):
+ self.fill_cookedq()
+ buf = self.cookedq
+ self.cookedq = ''
+## self.msg('read_now() --> %s' % `buf`)
+ return buf
+
+ # Fill cooked queue without blocking
+ def fill_cookedq(self):
+ self.process_rawq()
+ while select.select([self], [], [], 0) == ([self], [], []):
+ self.fill_rawq()
+ if not self.rawq:
+ raise EOFError
+ self.process_rawq()
+
+ # Transfer from raw queue to cooked queue
+ def process_rawq(self):
+ # There is some silliness going on here in an attempt
+ # to avoid quadratic behavior with large inputs...
+ buf = ''
+ while self.rawq:
+ c = self.rawq_getchar()
+ if c != IAC:
+ buf = buf + c
+ if len(buf) >= 44:
+## self.msg('transfer: %s' % `buf`)
+ self.cookedq = self.cookedq + buf
+ buf = ''
+ continue
+ c = self.rawq_getchar()
+ if c == IAC:
+ buf = buf + c
+ elif c in (DO, DONT):
+ opt = self.rawq_getchar()
+ self.msg('IAC %s %d',
+ c == DO and 'DO' or 'DONT',
+ ord(c))
+ self.sock.send(IAC + WONT + opt)
+ elif c in (WILL, WONT):
+ opt = self.rawq_getchar()
+ self.msg('IAC %s %d',
+ c == WILL and 'WILL' or 'WONT',
+ ord(c))
+ else:
+ self.msg('IAC %s not recognized' % `c`)
+## self.msg('transfer: %s' % `buf`)
+ self.cookedq = self.cookedq + buf
+
+ # Get next char from raw queue, blocking if necessary
+ def rawq_getchar(self):
+ if not self.rawq:
+ self.fill_rawq()
+ if self.irawq >= len(self.rawq):
+ raise EOFError
+ c = self.rawq[self.irawq]
+ self.irawq = self.irawq + 1
+ if self.irawq >= len(self.rawq):
+ self.rawq = ''
+ self.irawq = 0
+ return c
+
+ # Fill raw queue
+ def fill_rawq(self):
+ if self.irawq >= len(self.rawq):
+ self.rawq = ''
+ self.irawq = 0
+ buf = self.sock.recv(50)
+## self.msg('fill_rawq(): %s' % `buf`)
+ self.rawq = self.rawq + buf
diff --git a/Demo/cwilib/vt100.py b/Demo/cwilib/vt100.py
new file mode 100755
index 0000000..e802389
--- /dev/null
+++ b/Demo/cwilib/vt100.py
@@ -0,0 +1,326 @@
+# VT100 terminal emulator.
+# This is incomplete and slow, but will do for now...
+# It shouldn't be difficult to extend it to be a more-or-less complete
+# VT100 emulator. And little bit of profiling could go a long way...
+
+from array import array
+import regex
+import string
+
+# Tunable parameters
+DEBUGLEVEL = 1
+
+# Symbolic constants
+ESC = '\033'
+
+
+# VT100 emulation class
+
+class VT100:
+
+ def __init__(self):
+ self.debuglevel = DEBUGLEVEL
+ # Unchangeable parameters (for now)
+ self.width = 80
+ self.height = 24
+ self.blankline = array('c', ' '*self.width)
+ self.blankattr = array('b', '\0'*self.width)
+ # Set mutable display state
+ self.reset()
+ # Set parser state
+ self.unfinished = ''
+ # Set screen recognition state
+ self.reset_recognizer()
+
+ def msg(self, msg, *args):
+ if self.debuglevel > 0:
+ print 'VT100:', msg%args
+
+ def set_debuglevel(self, debuglevel):
+ self.debuglevel = debuglevel
+
+ def reset(self):
+ self.lines = []
+ self.attrs = []
+ self.fill_bottom()
+ self.x = 0
+ self.y = 0
+ self.curattrs = []
+
+ def show(self):
+ lineno = 0
+ for line in self.lines:
+ lineno = lineno + 1
+ i = len(line)
+ while i > 0 and line[i-1] == ' ': i = i-1
+ print line[:i]
+ print 'CURSOR:', self.x, self.y
+
+ def fill_bottom(self):
+ while len(self.lines) < self.height:
+ self.lines.append(self.blankline[:])
+ self.attrs.append(self.blankattr[:])
+
+ def fill_top(self):
+ while len(self.lines) < self.height:
+ self.lines.insert(0, self.blankline[:])
+ self.attrs.insert(0, self.blankattr[:])
+
+ def clear_all(self):
+ self.lines = []
+ self.attrs = []
+ self.fill_bottom()
+
+ def clear_below(self):
+ del self.lines[self.y:]
+ del self.attrs[self.y:]
+ self.fill_bottom()
+
+ def clear_above(self):
+ del self.lines[:self.y]
+ del self.attrs[:self.y]
+ self.fill_top()
+
+ def send(self, buffer):
+ self.unfinished = self.unfinished + buffer
+ i = 0
+ n = len(self.unfinished)
+ while i < n:
+ c = self.unfinished[i]
+ i = i+1
+ if c != ESC:
+ self.add_char(c)
+ continue
+ if i >= n:
+ i = i-1
+ break
+ c = self.unfinished[i]
+ i = i+1
+ if c == 'c':
+ self.reset()
+ continue
+ if c <> '[':
+ self.msg('unrecognized: ESC %s', `c`)
+ continue
+ argstr = ''
+ while i < n:
+ c = self.unfinished[i]
+ i = i+1
+ if c not in '0123456789;':
+ break
+ argstr = argstr + c
+ else:
+ i = i - len(argstr)
+ break
+## self.msg('found ESC [ %s %s' % (`argstr`, `c`))
+ args = string.splitfields(argstr, ';')
+ for j in range(len(args)):
+ s = args[j]
+ while s[:1] == '0': s = s[1:]
+ if s: args[j] = eval(s)
+ else: args[j] = 0
+ p1 = p2 = 0
+ if args: p1 = args[0]
+ if args[1:]: p2 = args[1]
+ if c in '@ABCDH':
+ if not p1: p1 = 1
+ if c in 'H':
+ if not p2: p2 = 1
+ if c == '@':
+ for j in range(p1):
+ self.add_char(' ')
+ elif c == 'A':
+ self.move_by(0, -p1)
+ elif c == 'B':
+ self.move_by(0, p1)
+ elif c == 'C':
+ self.move_by(p1, 0)
+ elif c == 'D':
+ self.move_by(-p1, 0)
+ elif c == 'H':
+ self.move_to(p2-1, p1-1)
+ elif c == 'J':
+ if p1 == 0: self.clear_above()
+ elif p1 == 1: self.clear_below()
+ elif p1 == 2: self.clear_all()
+ else: self.msg('weird ESC [ %d J', p1)
+ elif c == 'K':
+ if p1 == 0: self.erase_right()
+ elif p1 == 1: self.erase_left()
+ elif p1 == 2: self.erase_line()
+ else: self.msg('weird ESC [ %d K', p1)
+ elif c == 'm':
+ if p1 == 0:
+ self.curattrs = []
+ else:
+ if p1 not in self.curattrs:
+ self.curattrs.append(p1)
+ self.curattrs.sort()
+ else:
+ self.msg('unrecognized: ESC [ %s', `argstr+c`)
+ self.unfinished = self.unfinished[i:]
+
+ def add_char(self, c):
+ if c == '\r':
+ self.move_to(0, self.y)
+ return
+ if c in '\n\f\v':
+ self.move_to(self.x, self.y + 1)
+ if self.y >= self.height:
+ self.scroll_up(1)
+ self.move_to(self.x, self.height - 1)
+ return
+ if c == '\b':
+ self.move_by(-1, 0)
+ return
+ if c == '\a':
+ self.msg('BELL')
+ return
+ if c == '\t':
+ self.move_to((self.x+8)/8*8, self.y)
+ return
+ if c == '\0':
+ return
+ if c < ' ' or c > '~':
+ self.msg('ignored control char: %s', `c`)
+ return
+ if self.x >= self.width:
+ self.move_to(0, self.y + 1)
+ if self.y >= self.height:
+ self.scroll_up(1)
+ self.move_to(self.x, self.height - 1)
+ self.lines[self.y][self.x] = c
+ if self.curattrs:
+ self.attrs[self.y][self.x] = max(self.curattrs)
+ else:
+ self.attrs[self.y][self.x] = 0
+ self.move_by(1, 0)
+
+ def move_to(self, x, y):
+ self.x = min(max(0, x), self.width)
+ self.y = min(max(0, y), self.height)
+
+ def move_by(self, dx, dy):
+ self.move_to(self.x + dx, self.y + dy)
+
+ def scroll_up(self, nlines):
+ del self.lines[:max(0, nlines)]
+ del self.attrs[:max(0, nlines)]
+ self.fill_bottom()
+
+ def scroll_down(self, nlines):
+ del self.lines[-max(0, nlines):]
+ del self.attrs[-max(0, nlines):]
+ self.fill_top()
+
+ def erase_left(self):
+ x = min(self.width-1, x)
+ y = min(self.height-1, y)
+ self.lines[y][:x] = self.blankline[:x]
+ self.attrs[y][:x] = self.blankattr[:x]
+
+ def erase_right(self):
+ x = min(self.width-1, x)
+ y = min(self.height-1, y)
+ self.lines[y][x:] = self.blankline[x:]
+ self.attrs[y][x:] = self.blankattr[x:]
+
+ def erase_line(self):
+ self.lines[y][:] = self.blankline
+ self.attrs[y][:] = self.blankattr
+
+ # The following routines help automating the recognition of
+ # standard screens. A standard screen is characterized by
+ # a number of fields. A field is part of a line,
+ # characterized by a (lineno, begin, end) tuple;
+ # e.g. the first 10 characters of the second line are
+ # specified by the tuple (1, 0, 10). Fields can be:
+ # - regex: desired contents given by a regular expression,
+ # - extract: can be extracted,
+ # - cursor: screen is only valid if cursor in field,
+ # - copy: identical to another screen (position is ignored).
+ # A screen is defined as a dictionary full of fields. Screens
+ # also have names and are placed in a dictionary.
+
+ def reset_recognizer(self):
+ self.screens = {}
+
+ def define_screen(self, screenname, fields):
+ fieldscopy = {}
+ # Check if the fields make sense
+ for fieldname in fields.keys():
+ field = fields[fieldname]
+ ftype, lineno, begin, end, extra = field
+ if ftype in ('match', 'search'):
+ extra = regex.compile(extra)
+ elif ftype == 'extract':
+ extra = None
+ elif ftype == 'cursor':
+ extra = None
+ elif ftype == 'copy':
+ if not self.screens.has_key(extra):
+ raise ValueError, 'bad copy ref'
+ else:
+ raise ValueError, 'bad ftype: %s' % `ftype`
+ fieldscopy[fieldname] = (
+ ftype, lineno, begin, end, extra)
+ self.screens[screenname] = fieldscopy
+
+ def which_screens(self):
+ self.busy = []
+ self.okay = []
+ self.fail = []
+ for name in self.screens.keys():
+ ok = self.match_screen(name)
+ return self.okay[:]
+
+ def match_screen(self, name):
+ if name in self.busy: raise RuntimeError, 'recursive match'
+ if name in self.okay: return 1
+ if name in self.fail: return 0
+ self.busy.append(name)
+ fields = self.screens[name]
+ ok = 0
+ for key in fields.keys():
+ field = fields[key]
+ ftype, lineno, begin, end, extra = field
+ if ftype == 'copy':
+ if not self.match_screen(extra): break
+ elif ftype == 'search':
+ text = self.lines[lineno][begin:end].tostring()
+ if extra.search(text) < 0:
+ break
+ elif ftype == 'match':
+ text = self.lines[lineno][begin:end].tostring()
+ if extra.match(text) < 0:
+ break
+ elif ftype == 'cursor':
+ if self.x != lineno or not \
+ begin <= self.y < end:
+ break
+ else:
+ ok = 1
+ if ok:
+ self.okay.append(name)
+ else:
+ self.fail.append(name)
+ self.busy.remove(name)
+ return ok
+
+ def extract_field(self, screenname, fieldname):
+ ftype, lineno, begin, end, extra = \
+ self.screens[screenname][fieldname]
+ return stripright(self.lines[lineno][begin:end].tostring())
+
+ def extract_rect(self, left, top, right, bottom):
+ lines = []
+ for i in range(top, bottom):
+ lines.append(stripright(self.lines[i][left:right])
+ .tostring())
+ return lines
+
+
+def stripright(line):
+ i = len(line)
+ while i > 0 and line[i-1] in string.whitespace: i = i-1
+ return line[:i]
diff --git a/Demo/cwilib/vt100win.py b/Demo/cwilib/vt100win.py
new file mode 100755
index 0000000..dcaf17b
--- /dev/null
+++ b/Demo/cwilib/vt100win.py
@@ -0,0 +1,68 @@
+# VT100 terminal emulator in a STDWIN window.
+
+import stdwin
+from stdwinevents import *
+from vt100 import VT100
+
+class VT100win(VT100):
+
+ def __init__(self):
+ VT100.__init__(self)
+ self.window = None
+## self.last_x = -1
+## self.last_y = -1
+
+ def __del__(self):
+ self.close()
+
+ def open(self, title):
+ stdwin.setfont('7x14')
+ self.docwidth = self.width * stdwin.textwidth('m')
+ self.docheight = self.height * stdwin.lineheight()
+ stdwin.setdefwinsize(self.docwidth + 2, self.docheight + 2)
+ stdwin.setdefscrollbars(0, 0)
+ self.window = stdwin.open(title)
+ self.window.setdocsize(self.docwidth + 2, self.docheight + 2)
+
+ def close(self):
+ if self.window:
+ self.window.close()
+ self.window = None
+
+ def show(self):
+ if not self.window: return
+ self.draw(((-10, -10), (self.docwidth+10, self.docheight+10)))
+
+ def draw(self, detail):
+ d = self.window.begindrawing()
+ fg = stdwin.getfgcolor()
+ red = stdwin.fetchcolor('red')
+ d.cliprect(detail)
+ d.erase(detail)
+ lh = d.lineheight()
+ cw = d.textwidth('m')
+ for y in range(self.height):
+ d.text((0, y*lh), self.lines[y].tostring())
+ if self.attrs[y] <> self.blankattr:
+ for x in range(len(self.attrs[y])):
+ if self.attrs[y][x] == 7:
+ p1 = x*cw, y*lh
+ p2 = (x+1)*cw, (y+1)*lh
+ d.invert((p1, p2))
+ x = self.x * cw
+ y = self.y * lh
+ d.setfgcolor(red)
+ d.invert((x, y), (x+cw, y+lh))
+ d.setfgcolor(fg)
+ d.close()
+
+## def move_to(self, x, y):
+## VT100.move_to(self, x, y)
+## if self.y != self.last_y:
+## self.show()
+## self.last_x = self.x
+## self.last_y = y
+
+ def send(self, str):
+ VT100.send(self, str)
+ self.show()