Initial revision
diff --git a/Demo/stdwin/lpwin.py b/Demo/stdwin/lpwin.py
new file mode 100755
index 0000000..07a2bf7
--- /dev/null
+++ b/Demo/stdwin/lpwin.py
@@ -0,0 +1,197 @@
+#! /usr/local/python
+
+# Watch line printer queues (only works with BSD 4.3 lpq).
+#
+# This brings up a window containing one line per printer argument.
+#
+# Each line gives a small summary of the printer's status and queue.
+# The status tries to give as much relevant information as possible,
+# and gives extra info if you have jobs in the queue.
+#
+# The line's background color gives a hint at the status: navajo white
+# for idle, green if your job is now printing, yellow/orange for
+# small/large queue, red for errors.
+#
+# To reduce the duration of the unresponsive time while it is waiting
+# for an lpq subprocess to finish, it polls one printer every
+# delay/len(printers) seconds.  A tiny dot indicates the last printer
+# updated.  Hit the mouse button in the window to update the next one.
+#
+# To do:
+# - add an argument to override the default delay
+# - add arguments to override the default colors
+# - better heuristic for small/large queue (and more colors!)
+# - mouse clicks should update the printer clicked in
+# - better visual appearance, e.g., boxes around the lines?
+
+import posix
+import sys
+import time
+import string
+
+import stdwin
+from stdwinevents import *
+import mainloop
+
+# Default parameters
+DEF_PRINTER = 'oce' # This is CWI specific!
+DEF_DELAY = 10
+
+# Color assignments
+c_unknown = stdwin.fetchcolor('white')
+c_idle = stdwin.fetchcolor('navajo white')
+c_ontop = stdwin.fetchcolor('green')
+c_smallqueue = stdwin.fetchcolor('yellow')
+c_bigqueue = stdwin.fetchcolor('orange')
+c_error = stdwin.fetchcolor('red')
+
+def main():
+	delay = DEF_DELAY
+	#
+	try:
+		thisuser = posix.environ['LOGNAME']
+	except:
+		thisuser = posix.environ['USER']
+	#
+	printers = sys.argv[1:]
+	if printers:
+		# Strip '-P' from printer names just in case
+		# the user specified it...
+		for i in range(len(printers)):
+			if printers[i][:2] == '-P':
+				printers[i] = printers[i][2:]
+	else:
+		if posix.environ.has_key('PRINTER'):
+			printers = [posix.environ['PRINTER']]
+		else:
+			printers = [DEF_PRINTER]
+	#
+	width = stdwin.textwidth('in')*20
+	height = len(printers) * stdwin.lineheight() + 5
+	stdwin.setdefwinsize(width, height)
+	stdwin.setdefscrollbars(0, 0)
+	#
+	win = stdwin.open('lpwin')
+	#
+	win.printers = printers
+	win.colors = [c_unknown] * len(printers)
+	win.texts = printers[:]
+	win.next = 0
+	win.delay = DEF_DELAY
+	win.thisuser = thisuser
+	win.dispatch = lpdispatch
+	#
+	win.settimer(1)
+	#
+	mainloop.register(win)
+	mainloop.mainloop()
+
+def lpdispatch(type, win, detail):
+	if type == WE_CLOSE or type == WE_CHAR and detail in ('q', 'Q'):
+		mainloop.unregister(win)
+	elif type == WE_DRAW:
+		drawproc(win)
+	elif type == WE_TIMER:
+		update(win)
+		win.change((0,0), (10000, 10000))
+	elif type == WE_MOUSE_UP:
+		win.settimer(1)
+
+def drawproc(win):
+	d = win.begindrawing()
+	offset = d.textwidth('.')
+	h, v = 0, 0
+	for i in range(len(win.printers)):
+		text = win.texts[i]
+		color = win.colors[i]
+		d.setbgcolor(color)
+		d.erase((h, v), (h+10000, v+d.lineheight()))
+		if (i+1) % len(win.printers) == win.next and color <> c_unknown:
+			d.text((h, v), '.')
+		d.text((h+offset, v), text)
+		v = v + d.lineheight()
+
+def update(win):
+	i = win.next
+	win.next = (i+1) % len(win.printers)
+	win.texts[i], win.colors[i] = makestatus(win.printers[i], win.thisuser)
+	win.settimer(int(win.delay * 10.0 / len(win.printers)))
+
+def makestatus(name, thisuser):
+	pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r')
+	lines = []
+	users = {}
+	aheadbytes = 0
+	aheadjobs = 0
+	userseen = 0
+	totalbytes = 0
+	totaljobs = 0
+	color = c_unknown
+	while 1:
+		line = pipe.readline()
+		if not line: break
+		fields = string.split(line)
+		n = len(fields)
+		if len(fields) >= 6 and fields[n-1] == 'bytes':
+			rank = fields[0]
+			user = fields[1]
+			job = fields[2]
+			files = fields[3:-2]
+			bytes = eval(fields[n-2])
+			if user == thisuser:
+				userseen = 1
+				if aheadjobs == 0:
+					color = c_ontop
+			elif not userseen:
+				aheadbytes = aheadbytes + bytes
+				aheadjobs = aheadjobs + 1
+			totalbytes = totalbytes + bytes
+			totaljobs = totaljobs + 1
+			if color == c_unknown:
+				color = c_smallqueue
+			elif color == c_smallqueue:
+				color = c_bigqueue
+			if users.has_key(user):
+				ujobs, ubytes = users[user]
+			else:
+				ujobs, ubytes = 0, 0
+			ujobs = ujobs + 1
+			ubytes = ubytes + bytes
+			users[user] = ujobs, ubytes
+		else:
+			if fields and fields[0] <> 'Rank':
+				line = string.strip(line)
+				if line == 'no entries':
+					line = name + ': idle'
+					if color == c_unknown:
+						color = c_idle
+				elif line[-22:] == ' is ready and printing':
+					line = line[:-22]
+				else:
+					line = name + ': ' + line
+					color = c_error
+				lines.append(line)
+	#
+	if totaljobs:
+		line = `(totalbytes+1023)/1024` + ' K'
+		if totaljobs <> len(users):
+			line = line + ' (' + `totaljobs` + ' jobs)'
+		if len(users) == 1:
+			line = line + ' for ' + users.keys()[0]
+		else:
+			line = line + ' for ' + `len(users)` + ' users'
+			if userseen:
+				if aheadjobs == 0:
+				  line =  line + ' (' + thisuser + ' first)'
+				else:
+				  line = line + ' (' + `(aheadbytes+1023)/1024`
+				  line = line + ' K before ' + thisuser + ')'
+		lines.append(line)
+	#
+	sts = pipe.close()
+	if sts:
+		lines.append('lpq exit status ' + `sts`)
+		color = c_error
+	return string.joinfields(lines, ': '), color
+
+main()