mirror of
https://github.com/python/cpython.git
synced 2025-11-02 03:01:58 +00:00
Initial revision
This commit is contained in:
parent
dc4b93db70
commit
74b3f8a9e3
5 changed files with 971 additions and 0 deletions
226
Demo/cwilib/cwilib.py
Executable file
226
Demo/cwilib/cwilib.py
Executable file
|
|
@ -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()
|
||||
170
Demo/cwilib/form.py
Executable file
170
Demo/cwilib/form.py
Executable file
|
|
@ -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)
|
||||
181
Demo/cwilib/telnetlib.py
Executable file
181
Demo/cwilib/telnetlib.py
Executable file
|
|
@ -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
|
||||
326
Demo/cwilib/vt100.py
Executable file
326
Demo/cwilib/vt100.py
Executable file
|
|
@ -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]
|
||||
68
Demo/cwilib/vt100win.py
Executable file
68
Demo/cwilib/vt100win.py
Executable file
|
|
@ -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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue