]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Initial revision
authorGuido van Rossum <guido@python.org>
Thu, 28 Oct 1993 09:53:13 +0000 (09:53 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 28 Oct 1993 09:53:13 +0000 (09:53 +0000)
Demo/cwilib/cwilib.py [new file with mode: 0755]
Demo/cwilib/form.py [new file with mode: 0755]
Demo/cwilib/telnetlib.py [new file with mode: 0755]
Demo/cwilib/vt100.py [new file with mode: 0755]
Demo/cwilib/vt100win.py [new file with mode: 0755]

diff --git a/Demo/cwilib/cwilib.py b/Demo/cwilib/cwilib.py
new file mode 100755 (executable)
index 0000000..25e0622
--- /dev/null
@@ -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 (executable)
index 0000000..8dd6ef9
--- /dev/null
@@ -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 (executable)
index 0000000..5c862e7
--- /dev/null
@@ -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 (executable)
index 0000000..e802389
--- /dev/null
@@ -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 (executable)
index 0000000..dcaf17b
--- /dev/null
@@ -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()