From 74b3f8a9e3368ad10a8d4aebd2dbaffdd3ca3585 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 28 Oct 1993 09:53:13 +0000 Subject: Initial revision --- Demo/cwilib/cwilib.py | 226 ++++++++++++++++++++++++++++++++ Demo/cwilib/form.py | 170 ++++++++++++++++++++++++ Demo/cwilib/telnetlib.py | 181 ++++++++++++++++++++++++++ Demo/cwilib/vt100.py | 326 +++++++++++++++++++++++++++++++++++++++++++++++ Demo/cwilib/vt100win.py | 68 ++++++++++ 5 files changed, 971 insertions(+) create mode 100755 Demo/cwilib/cwilib.py create mode 100755 Demo/cwilib/form.py create mode 100755 Demo/cwilib/telnetlib.py create mode 100755 Demo/cwilib/vt100.py create mode 100755 Demo/cwilib/vt100win.py 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 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() -- cgit v0.12