Initial revision
diff --git a/Demo/sgi/gl/README b/Demo/sgi/gl/README
new file mode 100644
index 0000000..7a8caae
--- /dev/null
+++ b/Demo/sgi/gl/README
@@ -0,0 +1,29 @@
+These demos run only on SGI machines and require the 'gl' built-in module.
+The demonstrate the abilities of SGI's GL library as well as the ease of
+GL programming in Python.  Most demos require the Z-buffer (aka
+24-bitplane) option.  Press ESC to get out of any of them.
+
+backface.py	Demonstrates the 'backface' GL function.
+
+kites.py	Show 3 flying kites.  Demonstrates the rendering speed
+		obtainable by Python programs.
+
+mclock.py	A colorful clock with more options than you can
+		remember.  Works on 8-bit machines, but allows more
+		colors on 24-bit machines.  See mclock.doc for more
+		info.
+
+mixing.py	Demonstrates the effect of color mixing: through
+		frequent color switching it gives the effect of white
+		light.
+
+nurbs.py	A simple demonstration of the 'nurbs' GL functions.
+		Press left mouse button to toggle surface trimming.
+
+zrgb.py		Displays a 3-D Gouraud-shaded figure which can be moved
+		around with the mouse.
+
+glstdwin/	This is quite different: a partial STDWIN emulation
+		using GL!  Requires only small changes to Python
+		programs that use STDWIN.  Some features not yet
+		implemented, e.g., scroll bars.
diff --git a/Demo/sgi/gl/backface.py b/Demo/sgi/gl/backface.py
new file mode 100755
index 0000000..594b8ad
--- /dev/null
+++ b/Demo/sgi/gl/backface.py
@@ -0,0 +1,140 @@
+#! /usr/local/python
+
+#   backface
+#
+#   draw a cube that can run with backface() turned on or off.
+#   cube is moved when LEFTMOUSE is pressed and mouse itself is moved.
+
+from gl import *
+from DEVICE import *
+from GL import *
+
+CUBE_SIZE = 200.0
+CUBE_OBJ = 1
+
+def main () :
+	#
+	x = 0
+	y = 0
+	moveit = 0
+	#
+	initialize()
+	#
+	while (1) :
+		#
+		while (qtest()) :
+			dev, val = qread()
+			#
+			if dev == ESCKEY :
+				backface(0)
+				return
+				#
+			elif dev == REDRAW :
+				reshapeviewport()
+				drawcube(x,y)
+				#
+			elif dev == LEFTMOUSE :
+				#
+				# LEFTMOUSE down
+				moveit = val
+				#
+			elif dev == BKEY :
+				backface(1)
+				drawcube(x,y)
+				#
+			elif dev == FKEY :
+				backface(0)
+				drawcube(x,y)
+				#
+		if moveit :
+			x = getvaluator(MOUSEX)
+			y = getvaluator(MOUSEY)
+			drawcube(x,y)
+
+
+def initialize () :
+	foreground ()
+	keepaspect (1, 1)
+	gid = winopen('backface')
+	winset(gid)
+	winconstraints()
+	#
+	doublebuffer()
+	gconfig()
+	shademodel(FLAT)
+	#
+	ortho(-1024.0, 1024.0, -1024.0, 1024.0, -1024.0, 1024.0)
+	#
+	qdevice(ESCKEY)
+	qdevice(REDRAW)
+	qdevice(LEFTMOUSE)
+	qdevice(BKEY)
+	qdevice(FKEY)
+	qenter(REDRAW,gid)
+	#
+	backface(1)
+
+#
+# define a cube
+def cube () :
+	#
+	# front face
+	pushmatrix()
+	translate(0.0,0.0,CUBE_SIZE)
+	color(RED)
+	rectf(-CUBE_SIZE,-CUBE_SIZE,CUBE_SIZE,CUBE_SIZE)
+	popmatrix()
+	#
+	# right face
+	pushmatrix()
+	translate(CUBE_SIZE, 0.0, 0.0)
+	rotate(900, 'y')
+	color(GREEN)
+	rectf(-CUBE_SIZE,-CUBE_SIZE,CUBE_SIZE,CUBE_SIZE)
+	popmatrix()
+	#
+	# back face
+	pushmatrix()
+	translate(0.0, 0.0, -CUBE_SIZE)
+	rotate(1800, 'y')
+	color(BLUE)
+	rectf(-CUBE_SIZE,-CUBE_SIZE,CUBE_SIZE,CUBE_SIZE)
+	popmatrix()
+	#
+	# left face
+	pushmatrix()
+	translate(-CUBE_SIZE, 0.0, 0.0)
+	rotate(-900, 'y')
+	color(CYAN)
+	rectf(-CUBE_SIZE,-CUBE_SIZE,CUBE_SIZE,CUBE_SIZE)
+	popmatrix()
+	#
+	# top face
+	pushmatrix()
+	translate(0.0, CUBE_SIZE, 0.0)
+	rotate(-900, 'x')
+	color(MAGENTA)
+	rectf(-CUBE_SIZE,-CUBE_SIZE,CUBE_SIZE,CUBE_SIZE)
+	popmatrix()
+	#
+	# bottom face
+	pushmatrix()
+	translate(0.0, -CUBE_SIZE, 0.0)
+	rotate(900, 'x')
+	color(YELLOW)
+	rectf(-CUBE_SIZE,-CUBE_SIZE,CUBE_SIZE,CUBE_SIZE)
+	popmatrix()
+
+def drawcube(x,y) :
+	#
+	pushmatrix()
+	rotate(2*x, 'x')
+	rotate(2*y, 'y')
+	color(BLACK)
+	clear()
+	cube()        
+	popmatrix()
+	swapbuffers()
+
+
+main ()
diff --git a/Demo/sgi/gl/glstdwin/fontchart.py b/Demo/sgi/gl/glstdwin/fontchart.py
new file mode 100644
index 0000000..6b58f12
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/fontchart.py
@@ -0,0 +1,34 @@
+import stdwingl
+
+import stdwin
+from stdwinevents import *
+
+def main():
+	size = 12
+	w = stdwin.open('Font chart ' + `size`)
+	while 1:
+		type, window, detail = stdwin.getevent()
+		if type == WE_CLOSE:
+			break
+		if type == WE_DRAW:
+			width, height = w.getwinsize()
+			d = w.begindrawing()
+			d.setsize(size)
+			h, v = 0, 0
+			for c in range(32, 256):
+				ch = chr(c)
+				chw = d.textwidth(ch)
+				if h + chw > width:
+					v = v + d.lineheight()
+					h = 0
+					if v >= height:
+						break
+				d.text((h, v), ch)
+				h = h + chw
+			del d
+		if type == WE_MOUSE_UP:
+			size = size + 1
+			w.settitle('Font chart ' + `size`)
+			w.change((0, 0), (2000, 2000))
+
+main()
diff --git a/Demo/sgi/gl/glstdwin/glstdwdraw.py b/Demo/sgi/gl/glstdwin/glstdwdraw.py
new file mode 100644
index 0000000..0092065
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/glstdwdraw.py
@@ -0,0 +1,135 @@
+# Define drawing operations for GL stdwin
+
+import gl
+import fm
+from GL import LO_XOR, LO_SRC
+from glstdwin import MASK
+
+class DrawingObject:
+	#
+	def _init(self, win):
+		self.fg = win._fg
+		self.bg = win._bg
+		self.font = win._font
+		self.size = win._size
+		self.width, self.height = win._area[1]
+		gl.winset(win._gid)
+		gl.color(self.fg)
+		return self
+	#
+	def setfont(self, fontname):
+		self.font = fm.findfont(fontname).scalefont(self.size)
+	#
+	def setsize(self, size):
+		ratio = float(size) / float(self.size)
+		self.size = size
+		self.font = self.font.scalefont(ratio)
+	#
+	def setfgcolor(self, color):
+		self.fg = color
+		gl.color(self.fg)
+	#
+	def setbgcolor(self, color):
+		self.bg = color
+	#
+	def cliprect(self, area):
+		#print 'cliprect', area
+		(left, top), (right, bottom) = area
+		gl.scrmask(left, right, self.height-bottom, self.height-top)
+	#
+	def noclip(self):
+		#print 'noclip()'
+		gl.scrmask(0, self.width, 0, self.height)
+	#
+	def paint(self, ((left, top), (right, bottom))):
+		gl.rectf(left, top, right, bottom)
+	#
+	def box(self, ((left, top), (right, bottom))):
+		#print 'box', ((left, top), (right, bottom))
+		gl.rect(left, top, right, bottom)
+	#
+	def circle(self, ((h, v), radius)):
+		gl.circ(h, v, radius)
+	#
+	def elarc(self, (center, (rh, rv), a1, a2)):
+		pass # XXX
+	#
+	def erase(self, ((left, top), (right, bottom))):
+		#print 'erase', ((left, top), (right, bottom))
+		gl.color(self.bg)
+		gl.rectf(left, top, right, bottom)
+		gl.color(self.fg)
+	#
+	def invert(self, ((left, top), (right, bottom))):
+		#print 'invert', ((h0, v0), (h1, v1))
+		gl.logicop(LO_XOR)
+		gl.color(self.bg)
+		gl.rectf(left, top, right, bottom)
+		gl.color(self.fg)
+		gl.logicop(LO_SRC)
+	#
+	def line(self, ((h0, v0), (h1, v1))):
+		#print 'line', ((h0, v0), (h1, v1))
+		gl.bgnline()
+		gl.v2i(h0, v0)
+		gl.v2i(h1, v1)
+		gl.endline()
+	#
+	def xorline(self, ((h0, v0), (h1, v1))):
+		#print 'xorline', ((h0, v0), (h1, v1))
+		gl.logicop(LO_XOR)
+		gl.color(self.bg)
+		gl.bgnline()
+		gl.v2i(h0, v0)
+		gl.v2i(h1, v1)
+		gl.endline()
+		gl.color(self.fg)
+		gl.logicop(LO_SRC)
+	#
+	def point(self, (h, v)):
+		#print 'point', (h, v)
+		gl.bgnpoint()
+		gl.v2i(h, v)
+		gl.endpoint()
+	#
+	def text(self, ((h, v), string)):
+		#print 'text', ((h, v), string)
+		if h < 0:
+			# If the point is outside the window
+			# the whole string isn't drawn.
+			# Skip the beginning of the string.
+			# XXX What if the font is bigger than 20 pixels?
+			i, n = 0, len(string)
+			while h < -MASK and i < n:
+				h = h + self.font.getstrwidth(string[i])
+				i = i + 1
+			string = string[i:]
+		gl.cmov2(h, v + self.baseline())
+		self.font.setfont()
+		fm.prstr(string)
+	#
+	def shade(self, ((h, v), percent)):
+		pass # XXX
+	#
+	def baseline(self):
+		(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
+			height, nglyphs) = self.font.getfontinfo()
+		return height - yorig
+	#
+	def lineheight(self):
+		(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
+			height, nglyphs) = self.font.getfontinfo()
+		return height
+	#
+	def textbreak(self, (string, width)):
+		# XXX Slooooow!
+		n = len(string)
+		nwidth = self.textwidth(string[:n])
+		while nwidth > width:
+			n = n-1
+			nwidth = self.textwidth(string[:n])
+		return n
+	#
+	def textwidth(self, string):
+		return self.font.getstrwidth(string)
+	#
diff --git a/Demo/sgi/gl/glstdwin/glstdwin.py b/Demo/sgi/gl/glstdwin/glstdwin.py
new file mode 100644
index 0000000..2228554
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/glstdwin.py
@@ -0,0 +1,400 @@
+# GL STDWIN
+#
+# See stdwingl for a convenient hack to use this instead of built-in stdwin
+# without modifying your application, except for one line in the main file.
+#
+# Intrinsic differences with built-in stdwin (hard or impossible to fix):
+# - Need to call w.close() to close a window !!!
+# - Need to call m.close() to remove a menu !!!
+# - Doesn't enforce the existence of at most one drawing object
+# - No textedit package
+# - No X11 selections
+#
+# Not yet implemented:
+# - shade drawing
+# - elliptical arc drawing (need to play with transformation)
+# - more than one mouse button
+# - scroll bars (need to redo viewport handling to get this)
+# - partial redraws
+# - dialog boxes
+# - timer events
+# - cursors
+#
+# Extra features:
+# - color (for now, you need to know the colormap index)
+
+
+import gl
+import fm
+from GL import *
+from DEVICE import *
+from stdwinevents import *
+
+
+# Customizable constants
+#
+DEF_FONT = 'Times-Roman'		# Default font
+DEF_SIZE = 12				# Default font size (points)
+MASK = 20				# Viewport minus scrmask
+
+
+# A structure to hold global variables
+#
+class Struct: pass
+G = Struct()
+#
+G.queue = []				# Pending STDWIN events
+G.drawqueue = []			# Windows that need WE_REDRAW
+G.windowmap = {}			# Map window id to window object
+G.windowmap['0'] = None			# For convenience
+G.focus = None				# Input focus
+G.fg = BLACK				# Foreground color
+G.bg = WHITE				# Background color
+G.def_size = 0, 0			# Default window size
+G.def_pos = 0, 0			# Default window position
+#
+G.size = DEF_SIZE
+G.font = fm.findfont(DEF_FONT).scalefont(G.size)
+
+
+# Initialize GL
+#
+gl.foreground()
+gl.noport()
+dummygid = gl.winopen('')
+
+# Ask for all sorts of events
+#
+# Both REDRAW (= resize and/or redraw!) and INPUTCHANGE are implicitly queued
+#qdevice(REDRAW)
+#qdevice(INPUTCHANGE)
+#
+# Keyboard
+gl.qdevice(KEYBD)
+gl.qdevice(LEFTARROWKEY)
+gl.qdevice(RIGHTARROWKEY)
+gl.qdevice(UPARROWKEY)
+gl.qdevice(DOWNARROWKEY)
+gl.qdevice(LEFTALTKEY)
+gl.qdevice(RIGHTALTKEY)
+#
+# Mouse
+gl.qdevice(LEFTMOUSE)
+#gl.qdevice(MIDDLEMOUSE)
+gl.qdevice(RIGHTMOUSE)			# Menu button
+# NB MOUSEX, MOUSEY events are queued on button down
+#
+# Window close requests
+gl.qdevice(WINQUIT)
+gl.qdevice(WINSHUT)
+#
+# These aren't needed
+#gl.qdevice(TIMER0)
+#gl.qdevice(WINFREEZE)
+#gl.qdevice(WINTHAW)
+#gl.qdevice(REDRAWICONIC)
+
+
+# STDWIN: create a new window
+#
+def open(title):
+	h, v = G.def_pos
+	width, height = G.def_size
+	if h > 0 or v > 0:
+		# Choose arbitrary defaults
+		if h < 0: h = 10
+		if v < 0: v = 30
+		if width <= 0: width = 400
+		if height <= 0: height = 300
+		gl.prefposition(h, h+width, 1024-v, 1024-v-height)
+	elif width > 0 or height > 0:
+		if width <= 0: width = 400
+		if height <= 0: height = 300
+		gl.prefsize(width, height)
+	from glstdwwin import WindowObject
+	win = WindowObject()._init(title)
+	G.windowmap[`win._gid`] = win
+	return win
+
+
+# STDWIN: set default initial window position (0 means use default)
+#
+def setdefwinpos(h, v):
+	G.def_pos = h, v
+
+
+# STDWIN: set default window size (0 means use default)
+#
+def setdefwinsize(width, height):
+	G.def_size = width, height
+
+
+# STDWIN: beep or ring the bell
+#
+def fleep():
+	gl.ringbell()
+
+
+# STDWIN: set default foreground color
+#
+def setfgcolor(color):
+	G.fg = color
+
+
+# STDWIN: set default background color
+#
+def setbgcolor(color):
+	G.bg = color
+
+
+# STDWIN: get default foreground color
+#
+def getfgcolor():
+	return G.fgcolor
+
+
+# STDWIN: get default background color
+#
+def getbgcolor():
+	return G.bgcolor
+
+
+# Table mapping characters to key codes
+#
+key2code = key = {}
+key['A'] = AKEY
+key['B'] = BKEY
+key['C'] = CKEY
+key['D'] = DKEY
+key['E'] = EKEY
+key['F'] = FKEY
+key['G'] = GKEY
+key['H'] = HKEY
+key['I'] = IKEY
+key['J'] = JKEY
+key['K'] = KKEY
+key['L'] = LKEY
+key['M'] = MKEY
+key['N'] = NKEY
+key['O'] = OKEY
+key['P'] = PKEY
+key['Q'] = QKEY
+key['R'] = RKEY
+key['S'] = SKEY
+key['T'] = TKEY
+key['U'] = UKEY
+key['V'] = VKEY
+key['W'] = WKEY
+key['X'] = XKEY
+key['Y'] = YKEY
+key['Z'] = ZKEY
+key['0'] = ZEROKEY
+key['1'] = ONEKEY
+key['2'] = TWOKEY
+key['3'] = THREEKEY
+key['4'] = FOURKEY
+key['5'] = FIVEKEY
+key['6'] = SIXKEY
+key['7'] = SEVENKEY
+key['8'] = EIGHTKEY
+key['9'] = NINEKEY
+del key
+#
+code2key = {}
+codelist = []
+for key in key2code.keys():
+	code = key2code[key]
+	code2key[`code`] = key
+	codelist.append(code)
+del key
+
+
+# STDWIN: wait for the next event
+#
+commands = {}
+commands['\r'] = WC_RETURN
+commands['\b'] = WC_BACKSPACE
+commands['\t'] = WC_TAB
+#
+def getevent():
+  while 1:
+	#
+	# Get next event from the processed queue, if any
+	#
+	if G.queue:
+		event = G.queue[0]
+		del G.queue[0]
+		#print 'getevent from queue -->', event
+		return event
+	#
+	# Get next event from the draw queue, if any,
+	# but only if there is nothing in the system queue.
+	#
+	if G.drawqueue and not gl.qtest():
+		win = G.drawqueue[0]
+		del G.drawqueue[0]
+		gl.winset(win._gid)
+		gl.color(win._bg)
+		gl.clear()
+		event = WE_DRAW, win, win._area
+		#print 'getevent from drawqueue -->', event
+		return event
+	#
+	# Get next event from system queue, blocking if necessary
+	# until one is available.
+	# Some cases immediately return the event, others do nothing
+	# or append one or more events to the processed queue.
+	#
+	dev, val = gl.qread()
+	#
+	if dev == REDRAW:
+		win = G.windowmap[`val`]
+		old_area = win._area
+		win._fixviewport()
+		win._needredraw()
+		if old_area <> win._area:
+			#print 'getevent --> WE_SIZE'
+			return WE_SIZE, win, None
+	elif dev == KEYBD:
+		if val == 3:
+			raise KeyboardInterrupt # Control-C in window
+		character = chr(val)
+		if commands.has_key(character):
+			return WE_COMMAND, G.focus, commands[character]
+		return WE_CHAR, G.focus, character
+	elif dev == LEFTARROWKEY:
+		if val:
+			return WE_COMMAND, G.focus, WC_LEFT
+	elif dev == RIGHTARROWKEY:
+		if val:
+			return WE_COMMAND, G.focus, WC_RIGHT
+	elif dev == UPARROWKEY:
+		if val:
+			return WE_COMMAND, G.focus, WC_UP
+	elif dev == DOWNARROWKEY:
+		if val:
+			return WE_COMMAND, G.focus, WC_DOWN
+	elif dev in (LEFTALTKEY, RIGHTALTKEY):
+		if val:
+			for code in codelist:
+				gl.qdevice(code)
+		else:
+			for code in codelist:
+				gl.unqdevice(code)
+	elif dev in codelist:
+		if val:
+			event = G.focus._doshortcut(code2key[`dev`])
+			if event:
+				return event
+	elif dev == LEFTMOUSE:
+		G.mousex = gl.getvaluator(MOUSEX)
+		G.mousey = gl.getvaluator(MOUSEY)
+		if val:
+			type = WE_MOUSE_DOWN
+			gl.qdevice(MOUSEX)
+			gl.qdevice(MOUSEY)
+		else:
+			type = WE_MOUSE_UP
+			gl.unqdevice(MOUSEX)
+			gl.unqdevice(MOUSEY)
+		return _mouseevent(type)
+	elif dev == MOUSEX:
+		G.mousex = val
+		return _mouseevent(WE_MOUSE_MOVE)
+	elif dev == MOUSEY:
+		G.mousey = val
+		return _mouseevent(WE_MOUSE_MOVE)
+	elif dev == RIGHTMOUSE:		# Menu button press/release
+		if val:			# Press
+			event = G.focus._domenu()
+			if event:
+				return event
+	elif dev == INPUTCHANGE:
+		if G.focus:
+			G.queue.append(WE_DEACTIVATE, G.focus, None)
+		G.focus = G.windowmap[`val`]
+		if G.focus:
+			G.queue.append(WE_ACTIVATE, G.focus, None)
+	elif dev in (WINSHUT, WINQUIT):
+		return WE_CLOSE, G.windowmap[`val`], None
+	else:
+		print '*** qread() --> dev:', dev, 'val:', val
+
+# Helper routine to construct a mouse (up, move or down) event
+#
+def _mouseevent(type):
+	gl.winset(G.focus._gid)
+	orgx, orgy = gl.getorigin()
+	sizex, sizey = gl.getsize()
+	x = G.mousex - orgx
+	y = G.mousey - orgy
+	return type, G.focus, ((x, sizey-y), 1, 0, 0)
+
+
+
+
+# STDWIN: text measuring functions
+
+def baseline():
+	(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
+		height, nglyphs) = G.font.getfontinfo()
+	return height - yorig
+
+def lineheight():
+	(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
+			height, nglyphs) = G.font.getfontinfo()
+	return height
+
+def textbreak(string, width):
+	# XXX Slooooow!
+	n = len(string)
+	nwidth = textwidth(string[:n])
+	while nwidth > width:
+		n = n-1
+		nwidth = textwidth(string[:n])
+	return n
+
+def textwidth(string):
+	return G.font.getstrwidth(string)
+
+
+# STDWIN: set default font and size
+
+def setfont(fontname):
+	G.font = fm.findfont(fontname).scalefont(G.size)
+
+def setsize(size):
+	ratio = float(size) / float(G.size)
+	G.size = size
+	G.font = G.font.scalefont(ratio)
+
+
+# Utility functions
+
+# Exclusive-or of two BYTES
+#
+def xor(x, y):
+	a = bits(x)
+	b = bits(y)
+	c = [0, 0, 0, 0, 0, 0, 0, 0]
+	for i in range(8):
+		c[i] = (a[i] + b[i]) % 2
+	return stib(c)
+
+# Return the bits of a byte as a list of 8 integers
+#
+def bits(x):
+	b = [0, 0, 0, 0, 0, 0, 0, 0]
+	for i in range(8):
+		x, b[i] = divmod(x, 2)
+	return b
+
+# Convert a list of 8 integers (0|1) to a byte
+#
+def stib(b):
+	x = 0
+	shift = 1
+	for i in range(8):
+		x = x + b[i]*shift
+		shift = shift*2
+	return x
diff --git a/Demo/sgi/gl/glstdwin/glstdwmenu.py b/Demo/sgi/gl/glstdwin/glstdwmenu.py
new file mode 100644
index 0000000..64eb333
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/glstdwmenu.py
@@ -0,0 +1,60 @@
+# Define menu operations for GL stdwin
+
+import gl
+from glstdwin import key2code
+
+class MenuObject:
+	#
+	def _init(self, (win, title)):
+		self._win = win
+		self._title = title
+		self._items = []
+		return self
+	#
+	def close(self):
+		self._win.remove(self)
+		del self._win
+	#
+	def additem(self, arg):
+		if type(arg) == type(()):
+			text, shortcut = arg
+		else:
+			text, shortcut = arg, None
+		self._items.append([text, shortcut, 1, 0])
+	#
+	def setitem(self, (i, text)):
+		self._items[i][0] = text
+	#
+	def enable(self, (i, flag)):
+		self._items[i][2] = flag
+	#
+	def check(self, (i, flag)):
+		self._items[i][3] = flag
+	#
+	def _makepup(self, firstitem):
+		pup = gl.newpup()
+		if self._title:
+			gl.addtopup(pup, self._title + '%t', 0)
+		for item in self._items:
+			text = item[0]
+			if not item[2]: # Disabled
+				text = ' ( ' + text + ' )%x-1'
+			else:
+				if item[3]: # Check mark
+					text = '-> ' + text
+				else:
+					text = '    ' + text
+				if key2code.has_key(item[1]):
+					text = text + '  [Alt-' + item[1] + ']'
+				text = text + '%x' + `firstitem`
+			gl.addtopup(pup, text, 0)
+			firstitem = firstitem + 1
+		return pup
+	#
+	def _checkshortcut(self, char):
+		for i in range(len(self._items)):
+			item = self._items[i]
+			if item[2] and item[1] == char:
+				return i
+		return -1
+	#
diff --git a/Demo/sgi/gl/glstdwin/glstdwwin.py b/Demo/sgi/gl/glstdwin/glstdwwin.py
new file mode 100644
index 0000000..e024545
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/glstdwwin.py
@@ -0,0 +1,139 @@
+# Define window operations for STDWIN
+
+import gl
+from stdwinevents import *
+from glstdwin import G			# Global variables
+from glstdwin import MASK		# Tunable constant
+
+class WindowObject:
+	#
+	def _init(self, title):
+		self._docsize = (0, 0)
+		self._fg = G.fg
+		self._bg = G.bg
+		self._title = title
+		self._font = G.font
+		self._size = G.size
+		self._menus = []
+		self._gid = gl.winopen(title)
+		gl.winconstraints() # To remove prefsize() effect
+		self._fixviewport()
+		self._needredraw()
+		return self
+	#
+	def close(self):
+		del G.windowmap[`self._gid`]
+		gl.winclose(self._gid)
+		self._gid = 0
+	#
+	def _needredraw(self):
+		if self in G.drawqueue:
+			G.drawqueue.remove(self)
+		G.drawqueue.append(self)
+	#
+	def begindrawing(self):
+		from glstdwdraw import DrawingObject
+		return DrawingObject()._init(self)
+	#
+	def change(self, area):
+		self._needredraw()
+		# XXX Should record the area to be drawn?
+	#
+	def gettitle(self):
+		return self._title
+	#
+	def getdocsize(self):
+		return self._docsize
+	#
+	def getorigin(self):
+		return self._area[0]
+	#
+	def getwinsize(self):
+		return self._area[1]
+	#
+	def scroll(self, (area, by)):
+		# XXX ought to use gl.rectcopy()
+		if by <> (0, 0):
+			self.change(area)
+	#
+	def setdocsize(self, docsize):
+		self._docsize = docsize
+	#
+	def setorigin(self, origin):
+		pass # XXX
+	#
+	def settimer(self, decisecs):
+		pass # XXX
+	#
+	def settitle(self, title):
+		self._title = title
+		gl.wintitle(title)
+	#
+	def show(self, area):
+		pass # XXX
+	#
+	def _fixviewport(self):
+		#
+		# Called after redraw or resize, and initially.
+		#
+		# Fix the coordinate system so that (0, 0) is top left,
+		# units are pixels, and positive axes point right and down.
+		#
+		# Make the viewport slightly larger than the window,
+		# and set the screenmask exactly to the window; this
+		# help fixing character clipping.
+		#
+		# Set self._area to the window rectangle in STDWIN coords.
+		#
+		gl.winset(self._gid)
+		gl.reshapeviewport()
+		x0, x1, y0, y1 = gl.getviewport()
+		width, height = x1-x0, y1-y0
+		gl.viewport(x0-MASK, x1+MASK, y0-MASK, y1+MASK)
+		gl.scrmask(x0, x1, y0, y1)
+		gl.ortho2(-MASK, width+MASK, height+MASK, -MASK)
+		self._area = (0, 0), (width, height)
+	#
+	def menucreate(self, title):
+		from glstdwmenu import MenuObject
+		menu = MenuObject()._init(self, title)
+		self._menus.append(menu)
+		return menu
+	#
+	def _domenu(self):
+		if not self._menus:
+			return None
+		if len(self._menus) == 1:
+			pup = self._menus[0]._makepup(0)
+			val = gl.dopup(pup)
+			gl.freepup(pup)
+			if val < 0:
+				return None
+			return WE_MENU, self, (self._menus[0], val)
+		#
+		# More than one menu: use nested menus.
+		#
+		pups = []
+		firstitem = 0
+		for menu in self._menus:
+			pups.append(menu._makepup(firstitem))
+			firstitem = firstitem + 100
+		pup = gl.newpup()
+		for i in range(len(self._menus)):
+			gl.addtopup(pup, self._menus[i]._title + '%m', pups[i])
+		val = gl.dopup(pup)
+		gl.freepup(pup)
+		for pup in pups:
+			gl.freepup(pup)
+		if val < 0:
+			return None
+		i_menu, i_item = divmod(val, 100)
+		return WE_MENU, self, (self._menus[i_menu], i_item)
+	#
+	def _doshortcut(self, char):
+		for menu in self._menus:
+			i = menu._checkshortcut(char)
+			if i >= 0:
+				return WE_MENU, self, (menu, i)
+		return None
+	#
diff --git a/Demo/sgi/gl/glstdwin/stdwingl.py b/Demo/sgi/gl/glstdwin/stdwingl.py
new file mode 100644
index 0000000..4427593
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/stdwingl.py
@@ -0,0 +1,10 @@
+# If you put 'import stdwin_gl' in front of the main program of a program
+# using stdwin (before it has a chance to import the real stdwin!),
+# it will use glstdwin and think it is stdwin.
+
+import sys
+if sys.modules.has_key('stdwin'):
+	raise RuntimeError, 'too late -- stdwin has already been imported'
+
+import glstdwin
+sys.modules['stdwin'] = glstdwin
diff --git a/Demo/sgi/gl/glstdwin/tcolor.py b/Demo/sgi/gl/glstdwin/tcolor.py
new file mode 100644
index 0000000..cf96158
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/tcolor.py
@@ -0,0 +1,43 @@
+# Try colors -- display all 256 possible colors, with their color index
+
+# import stdwingl
+
+import stdwin
+from stdwinevents import *
+
+NROWS = 16
+NCOLS = 16
+
+def main():
+	stdwin.setdefwinsize(NCOLS * stdwin.textwidth('12345'), \
+				NROWS * stdwin.lineheight() * 3)
+	w = stdwin.open('TestColors')
+	#
+	while 1:
+		type, window, detail = stdwin.getevent()
+		if type == WE_CLOSE:
+			print 'Bye.'
+			break
+		elif type == WE_SIZE:
+			w.change((0,0), (10000, 10000))
+		elif type == WE_DRAW:
+			width, height = w.getwinsize()
+			d = w.begindrawing()
+			for row in range(NROWS):
+				for col in range(NCOLS):
+					color = row*NCOLS + col
+					d.setfgcolor(color)
+					p = col*width/NCOLS, row*height/NROWS
+					q = (col+1)*width/NCOLS, \
+						(row+1)*height/NROWS
+					d.paint(p, q)
+					d.setfgcolor(0)
+					d.box(p, q)
+					d.text(p, `color`)
+					p = p[0] , p[1]+ d.lineheight()
+					d.setfgcolor(7)
+					d.text(p, `color`)
+			del d
+	#
+
+main()
diff --git a/Demo/sgi/gl/glstdwin/tglsw.py b/Demo/sgi/gl/glstdwin/tglsw.py
new file mode 100644
index 0000000..c066c4d
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/tglsw.py
@@ -0,0 +1,70 @@
+import sys
+
+if len(sys.argv) < 2:
+	import stdwingl
+	color = 1
+	needclose = 1
+else:
+	color = 0
+	needclose = 0
+
+import stdwin
+import time
+from stdwinevents import *
+from GL import BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE
+
+def main():
+	#
+	stdwin.setdefwinsize(300, 300)
+	stdwin.setdefwinpos(0, 0)
+	if color: stdwin.setbgcolor(YELLOW)
+	w1 = stdwin.open('Hello, world')
+	w1.box = (10, 10), (90, 90)
+	#
+	stdwin.setdefwinsize(0, 0)
+	stdwin.setdefwinpos(50, 50)
+	if color: stdwin.setbgcolor(GREEN)
+	w2 = stdwin.open('Second window')
+	w2.box = (10, 10), (90, 90)
+	#
+	while w1 or w2:
+		type, window, detail = stdwin.getevent()
+		if type == WE_DRAW:
+			d = window.begindrawing()
+			if window == w1:
+				if color: d.setfgcolor(BLACK)
+				d.box((50, 50), (250, 250))
+				if color: d.setfgcolor(RED)
+				d.cliprect((50, 50), (250, 250))
+				d.paint(w1.box)
+				d.noclip()
+				if color: d.setfgcolor(BLUE)
+				d.line((0, 0), w1.box[0])
+			elif window == w2:
+				if color: d.setfgcolor(WHITE)
+				d.box(w2.box)
+				if color: d.setfgcolor(BLACK)
+				d.text(w2.box[0], 'Hello world')
+			else:
+				print 'Strange draw???', window, detail
+			del d
+		elif type == WE_CLOSE:
+			if needclose: window.close()
+			if window == w1:
+				w1 = None
+			elif window == w2:
+				w2 = None
+			else:
+				print 'weird close event???', window, detail
+		elif type in (WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP):
+			h, v = detail[0]
+			window.box = (h, v), (h+80, v+80)
+			window.change((0,0), (2000, 2000))
+		elif type == WE_CHAR:
+			print 'character', `detail`
+		else:
+			print type, window, detail
+	#
+
+main()
+print 'Done.'
diff --git a/Demo/sgi/gl/glstdwin/tmenu.py b/Demo/sgi/gl/glstdwin/tmenu.py
new file mode 100644
index 0000000..97c6bc6
--- /dev/null
+++ b/Demo/sgi/gl/glstdwin/tmenu.py
@@ -0,0 +1,44 @@
+# Test menus
+
+import stdwingl
+
+import stdwin
+from stdwinevents import *
+
+def main():
+	w = stdwin.open('TestMenus')
+	#
+	items1 = 'Aap', 'Noot', 'Mies'
+	m1 = w.menucreate('Menu-1')
+	for item in items1:
+		m1.additem(item, item[0])
+	#
+	items2 = 'Wim', 'Zus', 'Jet', 'Teun', 'Vuur'
+	m2 = w.menucreate('Menu-2')
+	for item in items2:
+		m2.additem(item, `len(item)`)
+	#
+	m1.enable(1, 0)
+	m2.check(1, 1)
+	#
+	while 1:
+		type, window, detail = stdwin.getevent()
+		if type == WE_CLOSE:
+			break
+		elif type == WE_DRAW:
+			d = w.begindrawing()
+			d.box((50,50), (100,100))
+			del d
+		elif type == WE_MENU:
+			mp, i = detail
+			if mp == m1:
+				print 'Choice:', items1[i]
+			elif mp == m2:
+				print 'Choice:', items2[i]
+			else:
+				print 'Not one of my menus!'
+		elif type == WE_CHAR:
+			print 'Character', `detail`
+	#
+
+main()
diff --git a/Demo/sgi/gl/kites.py b/Demo/sgi/gl/kites.py
new file mode 100755
index 0000000..d986833
--- /dev/null
+++ b/Demo/sgi/gl/kites.py
@@ -0,0 +1,194 @@
+#! /usr/local/python
+
+# *** This only works correctly on a 24 bit-plane machine. ***
+#
+# A simple Python program that tests the some parts of the
+# GL library. It shows the speed that can be obtained when
+# doing simple graphics.
+#
+# The bottleneck in this program is NOT Python but the graphics
+# engine; i.e Python can feed the graphics pipeline fast enough
+# on the 4D/25G.
+#
+# This program show 3 kites flying around the screen. It uses
+#
+# 	* bgnpolygon, endpolygon
+# 	* v3, n3
+# 	* lmdef, lmbind
+#
+# Usage :
+# 
+# 	ESC 	-> exit program
+# 	MOUSE3 	-> freeze toggle
+# 	MOUSE2 	-> one step (use this in freeze state)
+
+from GL import *
+from gl import *
+import DEVICE
+from math import *
+
+#
+# viewobj : sets the rotation, translation and scaling
+# set appropiate material, call drawobject()
+#
+def viewobj (r, s, t, mat) :
+	pushmatrix()
+	rot (r * 10.0, 'X')
+	rot (r * 10.0, 'Y')
+	rot (r * 10.0, 'Z')
+	scale (s[0], s[1], s[2])
+	translate (t[0], t[1], t[2])
+	lmbind(MATERIAL, mat)
+	drawobject()
+	popmatrix()
+
+#
+# makeobj : the contructor of the object
+#
+def mkobj () :
+	v0 = (-5.0 ,0.0, 0.0)
+	v1 = (0.0 ,5.0, 0.0)
+	v2 = (5.0 ,0.0, 0.0)
+	v3 = (0.0 ,2.0, 0.0)
+	n0 = (sqrt(2.0)/2.0, sqrt(2.0)/2.0, 0.0)
+	vn = ((v0, n0), (v1, n0), (v2, n0), (v3, n0))
+	#
+	return vn
+
+#
+# the object itself as an array of vertices and normals
+#
+kite = mkobj ()
+
+#
+# drawobject : draw a triangle. with bgnpolygon
+#
+def drawobject () :
+	#
+	bgnpolygon()
+	vnarray (kite)
+	endpolygon()
+
+#
+# identity matrix
+#
+idmat=[1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0]
+
+#
+# the rgb-value of light-blue 
+#
+LightBlue = (43,169,255)
+
+#
+# the different materials.
+#
+m1=[SPECULAR,0.0,0.0,0.6,DIFFUSE,0.0,0.0,0.8,SHININESS,20.0,LMNULL]
+m2=[SPECULAR,0.8,0.0,0.1,DIFFUSE,0.8,0.0,0.3,SHININESS,120.0,LMNULL]
+m3=[SPECULAR,0.0,1.0,0.0,DIFFUSE,0.0,0.6,0.0,SHININESS,120.0,LMNULL]
+
+#
+# lightsources
+#
+light1 = [LCOLOR,1.0,1.0,1.0,POSITION,15.0,15.0,0.0,1.0,LMNULL]
+light2 = [LCOLOR,1.0,1.0,1.0,POSITION,-15.0,15.0,0.0,1.0,LMNULL]
+
+#
+# the lightmodel
+#
+model = [AMBIENT,0.2,0.2,0.2,LMNULL]
+
+#
+# initgl : opens the window, configures the pipeline to 2buf and zbuf,
+# sets the viewing, defines and binds the materials
+#
+def initgl () :
+	#
+	# open window
+	#
+	foreground ()
+	keepaspect (1, 1)
+	prefposition (100, 500, 100, 500)
+	w = winopen ('PYTHON lights')
+	keepaspect (1, 1)
+	winconstraints()
+	#
+	# configure pipeline (zbuf, 2buf, GOURAUD and RGBmode)
+	#
+	zbuffer (1)
+	doublebuffer ()
+	shademodel (GOURAUD)
+	RGBmode ()
+	gconfig ()
+	#
+	# define and bind materials (set perspective BEFORE loadmat !)
+	#
+	mmode(MVIEWING)
+	perspective (900, 1.0, 1.0, 20.0)
+	loadmatrix(idmat)
+	lmdef(DEFMATERIAL, 1, m1)
+	lmdef(DEFMATERIAL, 2, m2)
+	lmdef(DEFMATERIAL, 3, m3)
+	lmdef(DEFLIGHT, 1, light1)
+	lmdef(DEFLIGHT, 2, light2)
+	lmdef(DEFLMODEL, 1, model)
+	lmbind(LIGHT0,1)
+	lmbind(LIGHT1,2)
+	lmbind(LMODEL,1)
+	#
+	# set viewing
+	#
+	lookat (0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0)
+	#
+	# ask for the REDRAW and ESCKEY events
+	#
+	qdevice(DEVICE.MOUSE3)
+	qdevice(DEVICE.MOUSE2)
+	qdevice(DEVICE.REDRAW)
+	qdevice(DEVICE.ESCKEY)
+
+#
+# GoForIT : use 2buf to redraw the object 2n times. index i is used as 
+# the (smoothly changing) rotation angle
+#
+def GoForIt(i) :
+	freeze = 1
+	while 1 :
+		if freeze <> 0 :
+			i = i + 1
+		#
+		# clear z-buffer and clear background to light-blue
+		#
+		zclear()
+		c3i (LightBlue)
+		clear()
+		#
+		# draw the 3 traiangles scaled above each other.
+		#
+		viewobj(float(i),[1.0,1.0,1.0],[1.0,1.0,1.0],1)
+		viewobj(float(i),[0.75,0.75,0.75],[0.0,2.0,2.0],2)
+		viewobj(float(i),[0.5,0.5,0.5],[0.0,4.0,4.0],3)
+		#
+		swapbuffers()
+		#
+		if qtest() <> 0 :
+			dev, val = qread()
+			if dev == DEVICE.ESCKEY :
+				break
+			elif dev == DEVICE.REDRAW :
+				reshapeviewport ()
+			elif dev == DEVICE.MOUSE3 and val <> 0 :
+				freeze = 1 - freeze
+			elif dev == DEVICE.MOUSE2 and val <> 0 :
+				i = i + 1
+
+
+# the main program
+#
+def main () :
+	initgl ()
+	GoForIt (0)
+
+#
+# exec main
+#
+main  ()
diff --git a/Demo/sgi/gl/kunst.py b/Demo/sgi/gl/kunst.py
new file mode 100755
index 0000000..8fb982e
--- /dev/null
+++ b/Demo/sgi/gl/kunst.py
@@ -0,0 +1,446 @@
+#!/ufs/guido/bin/sgi/python
+# Simulate the artwork in the hall.
+# Jack Jansen, Feb 91.
+#
+# Please please please don't try to read this code.
+# It is the first GL program I ever wrote, and used to do
+# very different things before it's current function:-)
+from gl import *
+from GL import *
+from math import *
+from DEVICE import *
+import sys
+import __main__
+main_dict = __main__.__dict__
+
+SPOTDIRECTION = 103
+SPOTLIGHT = 104
+
+#
+# Make a cylinder paralel with the Z axis with center (X,Y,0)
+# and radius 1
+def mkcyl(nslice, nparts, docircle):
+	cyl = []
+	step = 2.0 / float(nslice)
+	z = -1.0
+	for i in range(nslice):
+	    cyl.append(mkslice(z, z+step, nparts, docircle))
+	    z = z + step
+	return drawcylinder(cyl)
+#
+# Make one part of a cylinder
+#
+def mkslice(z1, z2, nparts, docircle):
+	if docircle:
+	    w1 = z1
+	    w2 = z2
+	    w1 = sqrt(1.0-w1*w1)
+	    w2 = sqrt(1.0-w2*w2)
+	    normalz = 1.0
+	else:
+	    w1 = 1.0
+	    w2 = 1.0
+	    normalz = 0.0
+	slice = []
+	step = (2.0*pi)/float(nparts)
+	angle = 0.0
+	for i in range(nparts+1):
+	    vx = cos(angle)
+	    vy = sin(angle)
+	    slice.append( ((vx*w1,vy*w1,z1), (vx*w1, vy*w1, z1*normalz)) )
+	    slice.append( ((vx*w2,vy*w2,z2), (vx*w2, vy*w2, z2*normalz)) )
+	    angle = angle + step
+	return slice
+#
+# Drawcylinder : draw the cylinder
+#
+class struct(): pass
+curobj = struct()
+curobj.curobj = 1
+def drawcylinder(cyl):
+	obj = curobj.curobj
+	curobj.curobj = curobj.curobj+1
+	makeobj(obj)
+	for slice in cyl:
+	    bgntmesh()
+	    vnarray(slice)
+	    endtmesh()
+	closeobj()
+	return obj
+#
+def drawnormals(cyl):
+	for slice in cyl:
+	    for triang in slice:
+		bgnline()
+		v3f(triang[0])
+		v3f(triang[0][0] + triang[1][0], triang[0][1] + triang[1][1], triang[0][2] + triang[1][2])
+		endline()
+def drawfloors():
+	obj = curobj.curobj
+	curobj.curobj = curobj.curobj+1
+	makeobj(obj)
+	bgnpolygon()
+	v3i(4,6,-6)
+	v3i(-6,6,-6)
+	v3i(-6,-6,-6)
+	v3i(4,-6,-6)
+	endpolygon()
+	for floor in range(3):
+	    pos = -1 + 5*floor
+	    bgnpolygon()
+	    v3i(4,4,pos)
+	    v3i(-6,4,pos)
+	    v3i(-6,6,pos)
+	    v3i(4,6,pos)
+	    endpolygon()
+	    bgnpolygon()
+	    v3i(-4,4,pos)
+	    v3i(-4,-4,pos)
+	    v3i(-6,-4,pos)
+	    v3i(-6,4,pos)
+	    endpolygon()
+	    bgnpolygon()
+	    v3i(-6,-4,pos)
+	    v3i(-6,-6,pos)
+	    v3i(4,-6,pos)
+	    v3i(4,-4,pos)
+	    endpolygon()
+	closeobj()
+	return obj
+def drawdoors():
+	obj = curobj.curobj
+	curobj.curobj = curobj.curobj+1
+	makeobj(obj)
+	for floor in range(3):
+	    pos = -1+5*floor
+	    bgnpolygon()
+	    v3i(-2,6,pos)
+	    v3i(-2,6,pos+3)
+	    v3i(0,6,pos+3)
+	    v3i(0,6,pos)
+	    endpolygon()
+	closeobj()
+	return obj
+def drawrailing():
+	obj = curobj.curobj
+	curobj.curobj = curobj.curobj+1
+	makeobj(obj)
+	for floor in range(3):
+	    pos = -1 + 5*floor
+	    bgnpolygon()
+	    v3i(4,4,pos)
+	    v3i(4,4,pos-1)
+	    v3i(-4,4,pos-1)
+	    v3i(-4,4,pos)
+	    endpolygon()
+	    bgnpolygon()
+	    v3i(-4,4,pos)
+	    v3i(-4,4,pos-1)
+	    v3i(-4,-4,pos-1)
+	    v3i(-4,-4,pos)
+	    endpolygon()
+	    bgnpolygon()
+	    v3i(-4,-4,pos)
+	    v3i(-4,-4,pos-1)
+	    v3i(4,-4,pos-1)
+	    v3i(4,-4,pos)
+	    endpolygon()
+	closeobj()
+	return obj
+def drawwalls():
+	obj = curobj.curobj
+	curobj.curobj = curobj.curobj+1
+	makeobj(obj)
+	bgnpolygon()
+	v3i(4,6,-6)
+	v3i(4,6,18)
+	v3i(-6,6,18)
+	v3i(-6,6,-6)
+	endpolygon()
+	bgnpolygon()
+	v3i(-6,6,-6)
+	v3i(-6,6,18)
+	v3i(-6,-6,18)
+	v3i(-6,-6,-6)
+	endpolygon()
+	bgnpolygon()
+	v3i(-6,-6,-6)
+	v3i(-6,-6,18)
+	v3i(4,-6,18)
+	v3i(4,-6,-6)
+	endpolygon()
+	bgnpolygon()
+	v3i(4,-6,-6)
+	v3i(4,-6,18)
+	v3i(4,4,18)
+	v3i(4,4,-6)
+	endpolygon()
+	closeobj()
+	return obj
+def axis():
+	bgnline()
+	cpack(0xff0000)
+	v3i(-1,0,0)
+	v3i(1,0,0)
+	v3f(1.0, 0.1, 0.1)
+	endline()
+	bgnline()
+	cpack(0xff00)
+	v3i(0,-1,0)
+	v3i(0,1,0)
+	v3f(0.1, 1.0, 0.1)
+	endline()
+	bgnline()
+	cpack(0xff)
+	v3i(0,0,-1)
+	v3i(0,0,1)
+	v3f(0.1,0.1,1.0)
+	endline()
+#
+silver = [ DIFFUSE, 0.3, 0.3, 0.3, SPECULAR, 0.9, 0.9, 0.95, \
+	SHININESS, 40.0, LMNULL]
+floormat = [ AMBIENT, 0.5, 0.25, 0.15, DIFFUSE, 0.5, 0.25, 0.15, SPECULAR, 0.6, 0.3, 0.2, SHININESS, 20.0, LMNULL]
+wallmat = [ DIFFUSE, 0.4, 0.2, 0.1, AMBIENT, 0.4, 0.20, 0.10, SPECULAR, 0.0, 0.0, 0.0, SHININESS, 20.0, LMNULL]
+offwhite = [ DIFFUSE, 0.8, 0.8, 0.6, AMBIENT, 0.8, 0.8, 0.6, SPECULAR, 0.9, 0.9, 0.9, SHININESS, 30.0, LMNULL]
+doormat = [ DIFFUSE, 0.1, 0.2, 0.5, AMBIENT, 0.2, 0.4, 1.0, SPECULAR, 0.2, 0.4, 1.0, SHININESS, 60.0, LMNULL]
+
+toplight = [ LCOLOR, 1.0, 1.0, 0.5, \
+	POSITION, 0.0, 0.0, 11.0, 1.0, LMNULL]
+floor1light = [ LCOLOR, 1.0, 1.0, 1.0, POSITION, 3.9, -3.9, 0.0, 1.0, \
+	SPOTDIRECTION, 1.0, 1.0, 0.0, SPOTLIGHT, 10.0, 90.0, LMNULL]
+
+lmodel = [ AMBIENT, 0.92, 0.8, 0.5, LOCALVIEWER, 1.0, LMNULL]
+#
+def lighting():
+	INDIGO=1	# XXXX Seems indigo only has one light.
+	lmdef(DEFMATERIAL, 2, silver)
+	lmdef(DEFMATERIAL, 3, floormat)
+	lmdef(DEFMATERIAL, 4, wallmat)
+	lmdef(DEFMATERIAL, 5, offwhite)
+	lmdef(DEFMATERIAL, 6, doormat)
+	lmdef(DEFLIGHT, 1, toplight)
+	if not INDIGO:
+	    lmdef(DEFLIGHT, 2, floor1light)
+	lmdef(DEFLMODEL, 1, lmodel)
+	lmbind(LIGHT0, 1)
+	if not INDIGO:
+	    lmbind(LIGHT1, 2)
+	lmbind(LMODEL, 1)
+IdMat=[1.0,0.0,0.0,0.0, 0.0,1.0,0.0,0.0, 0.0,0.0,1.0,0.0, 0.0,0.0,0.0,1.0]
+#
+wrongrange='Wrong Range'
+def defun(axis):
+	done = 0
+	res = 0.0	# Hack around exec(...)
+	while not done:
+	    print 'F'+axis+'(t) = ',
+	    s = sys.stdin.readline(100)
+	    print
+	    try:
+		s = 'def f'+axis+'(t): return '+s
+		exec(s, main_dict)
+		exec('res = f'+axis+'(0.0)\n')
+		if res < -10.0 or res > 10.0:
+			raise wrongrange
+		exec('res = f'+axis+'(100.0)\n')
+		if res < -10.0 or res > 10.0:
+			raise wrongrange
+		done = 1
+	    except RuntimeError:
+		print 'Sorry, there is a syntax error in your expression'
+	    except TypeError:
+		print 'Please remember to use floating point numbers'
+	    except wrongrange:
+		print 'Sorry, function values out of range (non-periodic function?)'
+def getfunctions():
+	print 'Welcome to the CWI art simulator. You can now enter X, Y and Z'
+	print 'coordinates as a function of t.'
+	print 'Alternatively, you can specify the name of a python module'
+	print 'defining functions fx(t), fy(t) and fz(t) on the command line'
+	print 'Normal trig functions are available. Please use floating point'
+	print 'values only (so 0.0 for 0). Comments to jack@cwi.nl'
+	defun('x')
+	defun('y')
+	defun('z')
+	print 'Ok, here you go. Use mouse+right button to move up/down,'
+	print 'mouse+middle to speed up/slow down time. type ESC to quit simulation'
+def main():
+	if len(sys.argv) > 1:
+	    exec('from '+sys.argv[1]+' import *\n')
+	else:
+	    getfunctions()
+	foreground()
+	prefposition(100,600,100,600)
+	void = winopen('cyl')
+	qdevice(ESCKEY)
+	qdevice(MOUSE1)
+	qdevice(MOUSE2)
+	qdevice(PKEY)
+	RGBmode()
+	doublebuffer()
+	gconfig()
+	zbuffer(1)
+	mmode(MVIEWING)
+	perspective(600, 1.0, 0.01, 20.0)
+	loadmatrix(IdMat)
+	vx = 0.0
+	vy = -3.9
+	vz = 0.0
+	lookat(0.0, -3.9, 0.0, 0.0, 0.0, 0.0, 0)
+	lighting()
+	t = -1.0
+	step = 0.2
+	bol = mkcyl(12,24, 1)
+	cable = mkcyl(1, 6, 0)
+	floors = drawfloors()
+	walls = drawwalls()
+	pillar = mkcyl(1,4,0)
+	railing = drawrailing()
+	doors = drawdoors()
+	shademodel(GOURAUD)
+	mousing = -1
+	pausing = 0
+	while 1:
+	    #
+	    # Check for some user input
+	    #
+	    if qtest():
+		dev, value = qread()
+		if dev == PKEY and value == 1:
+			pausing = 1
+		if dev == ESCKEY:
+		    break
+		elif (dev==MOUSE1 or dev==MOUSE2) and value == 1:
+		    if mousing > 0:
+			vx = 0.0
+			vy = -3.9
+			vz = 0.0
+		    mousing = dev
+		    oldx = getvaluator(MOUSEX)
+		    oldy = getvaluator(MOUSEY)
+		elif (dev==MOUSE1 or dev==MOUSE2):
+		    mousing = -1
+	    if mousing >= 0:
+		newx = getvaluator(MOUSEX)
+		newy = getvaluator(MOUSEY)
+		if newy <> oldy and mousing==MOUSE1:
+		    vz = vz + float(newy - oldy)/100.0
+		    if vz < -5.99:
+			vz = -5.99
+		    dist = sqrt(vx*vx + vy*vy + vz*vz)
+		    perspective(600, 1.0, 0.01, dist+16.0)
+		    loadmatrix(IdMat)
+		    if vz < 0.0:
+			lookat(vx, vy, vz, 0.0, 0.0, 0.0, 1800)
+		    else:
+			lookat(vx, vy, vz, 0.0, 0.0, 0.0, 0)
+		if newy <> oldy and mousing==MOUSE2:
+		    step = step * exp(float(newy-oldy)/400.0)
+	    if getbutton(CTRLKEY) == 0:
+		t = t + step
+	    else:
+		t = t - step
+	    if getbutton(LEFTSHIFTKEY) == 0:
+		shademodel(GOURAUD)
+	    else:
+		shademodel(FLAT)
+	    #
+	    # Draw background and axis
+	    czclear(0x802020,getgdesc(GD_ZMAX))
+	    #axis()
+	    #
+	    # draw the floors
+	    #
+	    lmbind(MATERIAL, 3)
+	    callobj(floors)
+	    lmbind(MATERIAL, 4)
+	    callobj(walls)
+	    lmbind(MATERIAL, 5)
+	    pushmatrix()
+	    translate(-4.5,4.5,3.0)
+	    scale(0.2,0.2,9.0)
+	    rotate(450,'z')
+	    callobj(pillar)
+	    popmatrix()
+	    callobj(railing)
+	    lmbind(MATERIAL, 6)
+	    pushmatrix()
+	    translate(0.0, -0.01, 0.0)
+	    callobj(doors)
+	    popmatrix()
+	    #
+	    # Draw object
+	    #
+	    bolx = fx(t)
+	    boly = fy(t)
+	    bolz = fz(t)
+	    err = ''
+	    if bolx < -4.0 or bolx > 4.0:
+		err = 'X('+`bolx`+') out of range [-4,4]'
+	    if boly < -4.0 or boly > 4.0:
+		err = 'Y('+`boly`+') out of range [-4,4]'
+	    if bolz < -4.0 or bolz > 8.0:
+		err = 'Z('+`bolz`+') out of range [-4,8]'
+	    if not err:
+		pushmatrix()
+		translate(bolx, boly, bolz)
+		scale(0.3, 0.3, 0.3)
+		lmbind(MATERIAL, 2)
+		blendfunction(BF_ONE, BF_ONE)
+		callobj(bol)
+		blendfunction(BF_ONE, BF_ZERO)
+		popmatrix()
+		#
+		# Draw the cables
+		#
+		bolz = bolz + 0.3
+		pushmatrix()
+		#linesmooth(SML_ON)
+		bgnline()
+		v3i(-4,-4,9)
+		v3f(bolx, boly, bolz)
+		endline()
+		bgnline()
+		v3i(-4,4,9)
+		v3f(bolx, boly, bolz)
+		endline()
+		bgnline()
+		v3i(4,-4,9)
+		v3f(bolx, boly, bolz)
+		endline()
+		bgnline()
+		v3i(4,4,9)
+		v3f(bolx, boly, bolz)
+		endline()
+		popmatrix()
+	    if mousing == MOUSE2 or err:
+		cpack(0xff0000)
+		cmov(0.0, 0.0, 0.4)
+		charstr('t='+`t`)
+	    if mousing == MOUSE2:
+		cpack(0xff0000)
+		cmov(0.0, 0.0, 0.2)
+		charstr('delta-t='+`step`)
+	    if err:
+		cpack(0xff00)
+		cmov(0.0, 0.0, 0.2)
+		charstr(err)
+		pausing = 1
+	    if pausing:
+		cpack(0xff00)
+		cmov(0.0, 0.0, 0.0)
+		charstr('Pausing, type P to continue')
+	    swapbuffers()
+	    if pausing:
+		while 1:
+		    dv=qread()
+		    if dv==(PKEY,1):
+			break
+		    if dv==(ESCKEY,1):
+			sys.exit(0)
+		pausing = 0
+#
+try:
+    main()
+except KeyboardInterrupt:
+    sys.exit(1)
diff --git a/Demo/sgi/gl/mclock.doc b/Demo/sgi/gl/mclock.doc
new file mode 100755
index 0000000..2208f33
--- /dev/null
+++ b/Demo/sgi/gl/mclock.doc
@@ -0,0 +1,60 @@
+Newsgroups: cwi.sgi
+Subject: Re: new clock
+Distribution: cwi.sgi
+References: <2246@charon.cwi.nl>
+
+Last week I wrote:
+
+>For your enjoyment I have implemented a colorful clock.
+
+The clock has now been extended with some new facilities: a menu, an
+alarm and a gong.  These may require some explanation beyond what's in
+the usage message.
+
+Menu
+----
+The right mouse button now pops up a menu that allows you to turn the
+seconds hand on or off and to switch the alarm off.
+
+Alarm
+-----
+
+The left and middle buttons set the alarm.  When it is on, the alarm
+time is displayed as a time on a 24 hour clock in the bottom left
+corner.  It is also indicated by two red triangles, corresponding to the
+little (hours) and big (minutes) hand.  These hands can be moved around:
+the left mouse button moves the minutes hand, the middle button moves
+the hourds hand.  Watch out for differences of twelve hours (always
+check the digital display); these can be corrected by dragging the hours
+hand once around the dial.
+
+When the alarm goes off, two things happen: a shell command specified on
+the command line with the -a option is executed (in the background), and
+the clock's colors change every two seconds, for five minutes.  You can
+also turn the alarm off by using the menu accessible through the right
+mouse button.
+
+There is no default command for the -a option; if it is not specified,
+only the changing of the colors happens.  If you have an 8 ohm speaker
+connected to the audio output of your Personal Iris, a suitable command
+would be:
+
+	mclock -a '/ufs/guido/bin/sgi/play /ufs/guido/lib/sounds/alarm'
+
+Gong
+----
+
+Some people like a clock that makes noises every hour, or even more
+often.  This is supported by the -g and -G options.  With -g you specify
+a shell command to be executed to sound the gong; with -G you can
+specify the interval between gong calls, in seconds (default is one hour).
+The shell command is executed in the background.  It is given two
+arguments: the hours (on a 24 hour clock!) and the minutes.  The
+executable Python script /ufs/guido/bin/sgi/chime is a suitable example.
+Again, this only works if you have installed a speaker (I bet 8 ohm
+speakers are going to be in demand!)
+
+--
+Guido van Rossum, Centre for Mathematics and Computer Science (CWI), Amsterdam
+guido@cwi.nl or ..!hp4nl!cwi.nl!guido or guido%cwi.nl@uunet.uu.net
+"A thing of beauty is a joy till sunrise"
diff --git a/Demo/sgi/gl/mclock.py b/Demo/sgi/gl/mclock.py
new file mode 100755
index 0000000..5a94dcb
--- /dev/null
+++ b/Demo/sgi/gl/mclock.py
@@ -0,0 +1,731 @@
+#! /usr/local/python
+
+#############################################################################
+# NOTA BENE: Before installing, fix TZDIFF to reflect your local time zone! #
+#############################################################################
+
+# "M Clock"
+#
+# An implementation in software of an original design by Rob Juda.
+# Clock implementation: Guido van Rossum.
+# Alarm and Gong features: Sape Mullender.
+#
+# XXX TO DO:
+# add arguments to specify initial window position and size
+# find out local time zone difference automatically
+# add a date indicator
+# allow multiple alarms
+# allow the menu to change more parameters
+
+import sys
+
+from gl import *
+from GL import *
+from DEVICE import *
+import time
+import getopt
+import string
+import os
+from math import pi
+import math
+
+FULLC = 3600		# Full circle in 1/10-ths of a degree
+MIDN = 900		# Angle of the 12 o'clock position
+R, G, B = 0, 1, 2	# Indices of colors in RGB list
+
+HOUR = 3600		# Number of seconds per hour
+MINUTE = 60		# Number of seconds per minute
+
+class struct: pass	# Class to define featureless structures
+Gl = struct()		# Object to hold writable global variables
+
+# Default constants (used in multiple places)
+
+SCREENBG = 127, 156, 191
+NPARTS = 9
+TITLE = 'M Clock'
+TZDIFF = -2*HOUR	# <--- change this to reflect your local time zone
+
+# Default parameters
+
+Gl.foreground = 0	# If set, run in the foreground
+Gl.fullscreen = 0	# If set, run on full screen
+Gl.tzdiff = TZDIFF	# Seconds west of Greenwich (winter time)
+Gl.nparts = NPARTS	# Number of parts each circle is divided in (>= 2)
+Gl.debug = 0		# If set, print debug output
+Gl.doublebuffer = 1	# If set, use double buffering
+Gl.update = 0		# Update interval; seconds hand is suppressed if > 1
+Gl.colorsubset = 0	# If set, display only a subset of the colors
+Gl.cyan = 0		# If set, display cyan overlay (big hand)
+Gl.magenta = 0		# If set, display magenta overlay (little hand)
+Gl.yellow = 0		# If set, display yellow overlay (fixed background)
+Gl.black = 0		# If set, display black overlay (hands)
+Gl.colormap = 0		# If set, use colormap mode instead of RGB mode
+Gl.warnings = 0		# If set, print warnings
+Gl.title = ''		# Window title (default set later)
+Gl.name = 'mclock'	# Window title for resources
+Gl.border = 1		# If set, use a window border (and title)
+Gl.bg = 0, 0, 0		# Background color R, G, B value
+Gl.iconic = 0		# Set in iconic state
+Gl.fg = 255, 0, 0	# Alarm background RGB (either normal or alarm)
+Gl.ox,Gl.oy = 0,0	# Window origin
+Gl.cx,Gl.cy = 0,0	# Window size
+Gl.alarm_set = 0	# Alarm on or off
+Gl.alarm_on = 0		# Alarm is ringing
+Gl.alarm_time = 0	# Alarm time in seconds after midnight
+Gl.alarm_hours = 0	# Alarm hour setting, 24 hour clock
+Gl.alarm_minutes = 0	# Alarm minutes setting
+Gl.alarm_rgb = 0,0,0	# Alarm display RGB colors
+Gl.alarm_cmd = ''	# Command to execute when alarm goes off
+Gl.mouse2down = 0	# Mouse button state
+Gl.mouse3down = 0	# Mouse button state
+Gl.gong_cmd = ''	# Command to execute when chimes go off
+Gl.gong_int = 3600	# Gong interval
+Gl.indices = R, G, B	# Colors (permuted when alarm is on)
+
+def main():
+	#
+	sys.stdout = sys.stderr		# All output is errors/warnings etc.
+	#
+	try:
+		args = getoptions()
+	except string.atoi_error, value:
+		usage(string.atoi_error, value)
+	except getopt.error, msg:
+		usage(getopt.error, msg)
+	#
+	if args:
+		realtime = 0
+		hours = string.atoi(args[0])
+		minutes = seconds = 0
+		if args[1:]: minutes = string.atoi(args[1])
+		if args[2:]: seconds = string.atoi(args[2])
+		localtime = ((hours*60)+minutes)*60+seconds
+	else:
+		realtime = 1
+	#
+	if Gl.title == '':
+		if realtime:
+			Gl.title = TITLE
+		else:
+			title = ''
+			for arg in args: title = title + ' ' + arg
+			Gl.title = title[1:]
+			del title
+	#
+	wid = makewindow()
+	Gl.ox,Gl.oy = getorigin()
+	Gl.cx,Gl.cy = getsize()
+	initmenu()
+	clearall()
+	#
+	if not Gl.update:
+		Gl.update = 60
+	#
+	if Gl.update <= 1:
+		Gl.timernoise = 6
+	else:
+		Gl.timernoise = 60
+	noise(TIMER0, Gl.timernoise)
+	#
+	qdevice(WINSHUT)
+	qdevice(WINQUIT)
+	qdevice(ESCKEY)
+	if realtime:
+		qdevice(TIMER0)
+	qdevice(REDRAW)
+	qdevice(WINFREEZE)
+	qdevice(WINTHAW)
+	qdevice(MENUBUTTON)	# MOUSE1
+	qdevice(MOUSE3)		# Left button
+	qdevice(MOUSE2)		# Middle button
+	unqdevice(INPUTCHANGE)
+	#
+	lasttime = 0
+	Gl.change = 1
+	while 1:
+		if realtime:
+			localtime = time.time() - Gl.tzdiff
+		if Gl.alarm_set:
+			if localtime%(24*HOUR) == Gl.alarm_time:
+				# Ring the alarm!
+				if Gl.debug:
+					print 'Rrrringg!'
+				Gl.alarm_on = 1
+				if Gl.alarm_cmd <> '':
+					d = os.system(Gl.alarm_cmd+' '+`Gl.alarm_time/3600`+' '+`(Gl.alarm_time/60)%60` + ' &')
+				Gl.change = 1
+				clearall()
+		if Gl.alarm_on:
+			if (localtime - Gl.alarm_time) % (24*HOUR) > 300:
+				# More than 5 minutes away from alarm
+				Gl.alarm_on = 0
+				if Gl.debug:
+					print 'Alarm turned off'
+				Gl.change = 1
+				clearall()
+				Gl.indices = R, G, B
+			else:
+				if localtime % 2 == 0:
+				  # Permute color indices
+				  Gl.indices = Gl.indices[2:] + Gl.indices[:2]
+				  Gl.change = 1
+		if Gl.gong_cmd <> '' and localtime%Gl.gong_int == 0:
+			d = os.system(Gl.gong_cmd+' '+`(localtime/3600)%24`+' '+`(localtime/60)%60` + ' &')
+		if localtime/Gl.update <> lasttime/Gl.update:
+			if Gl.debug: print 'new time'
+			Gl.change = 1
+		if Gl.change:
+			if Gl.debug: print 'drawing'
+			doit(localtime)
+			lasttime = localtime
+			Gl.change = 0
+		dev, data = qread()
+		if Gl.debug and dev <> TIMER0:
+			print dev, data
+		if dev == TIMER0:
+			if Gl.debug > 1:
+				print dev, data
+		elif dev == MOUSE3:
+			mousex = getvaluator(MOUSEX)
+			mousey = getvaluator(MOUSEY)
+			if mouseclick(3, data, mousex, mousey):
+				Gl.change = 1
+		elif dev == MOUSE2:
+			mousex = getvaluator(MOUSEX)
+			mousey = getvaluator(MOUSEY)
+			if mouseclick(2, data, mousex, mousey):
+				Gl.change = 1
+		elif dev == MOUSEX:
+			mousex = data
+			if Gl.mouse2down:
+				mouse2track(mousex, mousey)
+			if Gl.mouse3down:
+				mouse3track(mousex, mousey)
+		elif dev == MOUSEY:
+			mousey = data
+			if Gl.mouse2down:
+				mouse2track(mousex, mousey)
+			if Gl.mouse3down:
+				mouse3track(mousex, mousey)
+		elif dev == REDRAW or dev == REDRAWICONIC:
+			if Gl.debug:
+				if dev == REDRAW: print 'REDRAW'
+				else: print 'REDRAWICONIC'
+			reshapeviewport()
+			Gl.ox,Gl.oy = getorigin()
+			Gl.cx,Gl.cy = getsize()
+			Gl.change = 1
+			clearall()
+		elif dev == MENUBUTTON:
+			if Gl.debug: print 'MENUBUTTON'
+			handlemenu()
+		elif dev == WINFREEZE:
+			if Gl.debug: print 'WINFREEZE'
+			Gl.iconic = 1
+			noise(TIMER0, 60*60) # Redraw every 60 seconds only
+		elif dev == WINTHAW:
+			if Gl.debug: print 'WINTHAW'
+			Gl.iconic = 0
+			noise(TIMER0, Gl.timernoise)
+			Gl.change = 1
+		elif dev == ESCKEY or dev == WINSHUT or dev == WINQUIT:
+			if Gl.debug: print 'Exit'
+			sys.exit(0)
+
+def getoptions():
+	optlist, args = getopt.getopt(sys.argv[1:], 'A:a:B:bc:dFfG:g:n:sT:t:u:wCMYK')
+	for optname, optarg in optlist:
+		if optname == '-A':
+			Gl.fg = eval(optarg)	# Should be (r,g,b)
+		elif optname == '-a':
+			Gl.alarm_cmd = optarg
+		elif optname == '-B':
+			Gl.bg = eval(optarg)	# Should be (r,g,b)
+		elif optname == '-b':
+			Gl.border = 0
+		elif optname == '-c':
+			Gl.colormap = string.atoi(optarg)
+		elif optname == '-d':
+			Gl.debug = Gl.debug + 1
+			Gl.warnings = 1
+		elif optname == '-F':
+			Gl.foreground = 1
+		elif optname == '-f':
+			Gl.fullscreen = 1
+		elif optname == '-G':
+			Gl.gong_int = 60*string.atoi(optarg)
+		elif optname == '-g':
+			Gl.gong_cmd = optarg
+		elif optname == '-n':
+			Gl.nparts = string.atoi(optarg)
+		elif optname == '-s':
+			Gl.doublebuffer = 0
+		elif optname == '-T':
+			Gl.title = Gl.name = optarg
+		elif optname == '-t':
+			Gl.tzdiff = string.atoi(optarg)
+		elif optname == '-u':
+			Gl.update = string.atoi(optarg)
+		elif optname == '-w':
+			Gl.warnings = 1
+		elif optname == '-C':
+			Gl.cyan = Gl.colorsubset = 1
+		elif optname == '-M':
+			Gl.magenta = Gl.colorsubset = 1
+		elif optname == '-Y':
+			Gl.yellow = Gl.colorsubset = 1
+		elif optname == '-K':
+			Gl.black = Gl.colorsubset = 1
+		else:
+			print 'Unsupported option', optname
+	return args
+
+def usage(exc, msg):
+	if sys.argv:
+		progname = os.path.basename(sys.argv[0])
+	else:
+		progname = 'mclock'
+	#
+	print progname + ':',
+	if exc == string.atoi_error:
+		print 'non-numeric argument:',
+	print msg
+	#
+	print 'usage:', progname, '[options] [hh [mm [ss]]]'
+	#
+	print '-A r,g,b  : alarm background red,green,blue [255,0,0]'
+	print '-a cmd    : shell command executed when alarm goes off'
+	print '-B r,g,b  : background red,green,blue [0,0,0]'
+	print '            (-B SCREENBG uses the default screen background)'
+	print '-b        : suppress window border and title'
+	print '-c cmapid : select explicit colormap'
+	print '-d        : more debug output (implies -F, -w)'
+	print '-F        : run in foreground'
+	print '-f        : use full screen'
+	print '-G intrvl : interval between chimes in minutes [60]'
+	print '-g cmd    : shell command executed when chimes go off'
+	print '-s        : single buffer mode'
+	print '-w        : print various warnings'
+	print '-n nparts : number of parts [' + `NPARTS` + ']'
+	print '-T title  : alternate window title [\'' + TITLE + '\']'
+	print '-t tzdiff : time zone difference [' + `TZDIFF` + ']'
+	print '-u update : update interval [60]'
+	print '-CMYK     : Cyan, Magenta, Yellow or blacK overlay only'
+	print 'if hh [mm [ss]] is specified, display that time statically'
+	print 'on machines with < 12 bitplanes, -c and -s are forced on'
+	#
+	sys.exit(2)
+
+def doit(localtime):
+	hands = makehands(localtime)
+	list = makelist(hands)
+	render(list, hands)
+
+def makehands(localtime):
+	localtime = localtime % (12*HOUR)
+	seconds_hand = MIDN + FULLC - (localtime*60) % FULLC
+	big_hand = (MIDN + FULLC - (localtime%HOUR)) % FULLC
+	little_hand = (MIDN + FULLC - ((localtime/12) % HOUR)) % FULLC
+	return little_hand, big_hand, seconds_hand
+
+def makelist(little_hand, big_hand, seconds_hand):
+	total = []
+	if Gl.cyan or not Gl.colorsubset:
+		total = total + makesublist(big_hand, Gl.indices[0])
+	if Gl.magenta or not Gl.colorsubset:
+		total = total + makesublist(little_hand, Gl.indices[1])
+	if Gl.yellow or not Gl.colorsubset:
+		total = total + makesublist(MIDN, Gl.indices[2])
+	total.sort()
+	return total
+
+def makesublist(first, icolor):
+	list = []
+	alpha = FULLC/Gl.nparts
+	a = first - alpha/2
+	for i in range(Gl.nparts):
+		angle = (a + i*alpha + FULLC) % FULLC
+		value = 255*(Gl.nparts-1-i)/(Gl.nparts-1)
+		list.append(angle, icolor, value)
+	list.sort()
+	a, icolor, value = list[0]
+	if a <> 0:
+		a, icolor, value = list[len(list)-1]
+		t = 0, icolor, value
+		list.insert(0, t)
+	return list
+
+def rgb_fg():
+	return Gl.fg
+	# Obsolete code:
+	if Gl.alarm_on:
+		return Gl.bg
+	else:
+		return Gl.fg
+
+def rgb_bg():
+	return Gl.bg
+	# Obsolete code:
+	if Gl.alarm_on:
+		return Gl.fg
+	else:
+		return Gl.bg
+
+def clearall():
+	Gl.c3i(rgb_bg())
+	clear()
+	if Gl.doublebuffer:
+		swapbuffers()
+		clear()
+
+def draw_alarm(color):
+	frontbuffer(TRUE)
+	Gl.c3i(color)
+	pushmatrix()
+	rotate(-((Gl.alarm_time/12)%3600), 'z')
+	bgnpolygon()
+	v2f( 0.00,1.00)
+	v2f( 0.04,1.05)
+	v2f(-0.04,1.05)
+	endpolygon()
+	popmatrix()
+	#
+	pushmatrix()
+	rotate(-((Gl.alarm_time)%3600), 'z')
+	bgnpolygon()
+	v2f( 0.00,1.05)
+	v2f( 0.07,1.10)
+	v2f(-0.07,1.10)
+	endpolygon()
+	popmatrix()
+	#
+	cmov2(-1.06, -1.06)
+	charstr(string.rjust(`Gl.alarm_time/3600`,2))
+	charstr(':')
+	charstr(string.zfill((Gl.alarm_time/60)%60,2))
+	frontbuffer(FALSE)
+
+def render(list, (little_hand, big_hand, seconds_hand)):
+	#
+	if Gl.colormap:
+		resetindex()
+	#
+	if not list:
+		Gl.c3i(255, 255, 255) # White
+		circf(0.0, 0.0, 1.0)
+	else:
+		list.append(3600, 0, 255) # Sentinel
+	#
+	rgb = [255, 255, 255]
+	a_prev = 0
+	for a, icolor, value in list:
+		if a <> a_prev:
+			[r, g, b] = rgb
+			if Gl.debug > 1:
+				print rgb, a_prev, a
+			Gl.c3i(r, g, b)
+			arcf(0.0, 0.0, 1.0, a_prev, a)
+		rgb[icolor] = value
+		a_prev = a
+	#
+	if Gl.black or not Gl.colorsubset:
+		#
+		# Draw the hands -- in black
+		#
+		Gl.c3i(0, 0, 0)
+		#
+		if Gl.update == 1 and not Gl.iconic:
+			# Seconds hand is only drawn if we update every second
+			pushmatrix()
+			rotate(seconds_hand, 'z')
+			bgnline()
+			v2f(0.0, 0.0)
+			v2f(1.0, 0.0)
+			endline()
+			popmatrix()
+		#
+		pushmatrix()
+		rotate(big_hand, 'z')
+		rectf(0.0, -0.01, 0.97, 0.01)
+		circf(0.0, 0.0, 0.01)
+		circf(0.97, 0.0, 0.01)
+		popmatrix()
+		#
+		pushmatrix()
+		rotate(little_hand, 'z')
+		rectf(0.04, -0.02, 0.63, 0.02)
+		circf(0.04, 0.0, 0.02)
+		circf(0.63, 0.0, 0.02)
+		popmatrix()
+		#
+		# Draw the alarm time, if set or being set
+		#
+		if Gl.alarm_set:
+			draw_alarm(rgb_fg())
+	#
+	if Gl.doublebuffer: swapbuffers()
+
+def makewindow():
+	#
+	if Gl.debug or Gl.foreground:
+		foreground()
+	#
+	if Gl.fullscreen:
+		scrwidth, scrheight = getgdesc(GD_XPMAX), getgdesc(GD_YPMAX)
+		prefposition(0, scrwidth-1, 0, scrheight-1)
+	else:
+		keepaspect(1, 1)
+		prefsize(100, 100)
+	#
+	if not Gl.border:
+		noborder()
+	wid = winopen(Gl.name)
+	wintitle(Gl.title)
+	#
+	if not Gl.fullscreen:
+		keepaspect(1, 1)
+		minsize(10, 10)
+		maxsize(2000, 2000)
+		iconsize(66, 66)
+		winconstraints()
+	#
+	nplanes = getplanes()
+	nmaps = getgdesc(GD_NMMAPS)
+	if Gl.warnings:
+		print nplanes, 'color planes,', nmaps, 'color maps'
+	#
+	if nplanes < 12 or Gl.colormap:
+		if not Gl.colormap:
+			Gl.colormap = nmaps - 1
+			if Gl.warnings:
+				print 'not enough color planes available',
+				print 'for RGB mode; forcing colormap mode'
+				print 'using color map number', Gl.colormap
+		if not Gl.colorsubset:
+			needed = 3
+		else:
+			needed = Gl.cyan + Gl.magenta + Gl.yellow
+		needed = needed*Gl.nparts
+		if Gl.bg <> (0, 0, 0):
+			needed = needed+1
+		if Gl.fg <> (0, 0, 0):
+			needed = needed+1
+		if Gl.doublebuffer:
+			if needed > available(nplanes/2):
+				Gl.doublebuffer = 0
+				if Gl.warnings:
+					print 'not enough colors available',
+					print 'for double buffer mode;',
+					print 'forcing single buffer mode'
+			else:
+				nplanes = nplanes/2
+		if needed > available(nplanes):
+			# Do this warning always
+			print 'still not enough colors available;',
+			print 'parts will be left white'
+			print '(needed', needed, 'but have only',
+			print available(nplanes), 'colors available)'
+	#
+	if Gl.doublebuffer:
+		doublebuffer()
+		gconfig()
+	#
+	if Gl.colormap:
+		Gl.c3i = pseudo_c3i
+		fixcolormap()
+	else:
+		Gl.c3i = c3i
+		RGBmode()
+		gconfig()
+	#
+	if Gl.fullscreen:
+		# XXX Should find out true screen size using getgdesc()
+		ortho2(-1.1*1.280, 1.1*1.280, -1.1*1.024, 1.1*1.024)
+	else:
+		ortho2(-1.1, 1.1, -1.1, 1.1)
+	#
+	return wid
+
+def available(nplanes):
+	return pow(2, nplanes) - 1	# Reserve one pixel for black
+
+def fixcolormap():
+	multimap()
+	gconfig()
+	nplanes = getplanes()
+	if Gl.warnings:
+		print 'multimap mode has', nplanes, 'color planes'
+	imap = Gl.colormap
+	Gl.startindex = pow(2, nplanes) - 1
+	Gl.stopindex = 1
+	setmap(imap)
+	mapcolor(0, 0, 0, 0) # Fixed entry for black
+	if Gl.bg <> (0, 0, 0):
+		r, g, b = Gl.bg
+		mapcolor(1, r, g, b) # Fixed entry for Gl.bg
+		Gl.stopindex = 2
+	if Gl.fg <> (0, 0, 0):
+		r, g, b = Gl.fg
+		mapcolor(2, r, g, b) # Fixed entry for Gl.fg
+		Gl.stopindex = 3
+	Gl.overflow_seen = 0
+	resetindex()
+
+def resetindex():
+	Gl.index = Gl.startindex
+
+r0g0b0 = (0, 0, 0)
+
+def pseudo_c3i(rgb):
+	if rgb == r0g0b0:
+		index = 0
+	elif rgb == Gl.bg:
+		index = 1
+	elif rgb == Gl.fg:
+		index = 2
+	else:
+		index = definecolor(rgb)
+	color(index)
+
+def definecolor(rgb):
+	index = Gl.index
+	if index < Gl.stopindex:
+		if Gl.debug: print 'definecolor hard case', rgb
+		# First see if we already have this one...
+		for index in range(Gl.stopindex, Gl.startindex+1):
+			if rgb == getmcolor(index):
+				if Gl.debug: print 'return', index
+				return index
+		# Don't clobber reserverd colormap entries
+		if not Gl.overflow_seen:
+			# Shouldn't happen any more, hence no Gl.warnings test
+			print 'mclock: out of colormap entries'
+			Gl.overflow_seen = 1
+		return Gl.stopindex
+	r, g, b = rgb
+	if Gl.debug > 1: print 'mapcolor', (index, r, g, b)
+	mapcolor(index, r, g, b)
+	Gl.index = index - 1
+	return index
+
+# Compute n**i
+def pow(n, i):
+	x = 1
+	for j in range(i): x = x*n
+	return x
+
+def mouseclick(mouse, updown, x, y):
+	if updown == 1:
+		# mouse button came down, start tracking
+		if Gl.debug:
+			print 'mouse', mouse, 'down at', x, y
+		if mouse == 2:
+			Gl.mouse2down = 1
+			mouse2track(x, y)
+		elif mouse == 3:
+			Gl.mouse3down = 1
+			mouse3track(x, y)
+		else:
+			print 'fatal error'
+		qdevice(MOUSEX)
+		qdevice(MOUSEY)
+		return 0
+	else:
+		# mouse button came up, stop tracking
+		if Gl.debug:
+			print 'mouse', mouse, 'up at', x, y
+		unqdevice(MOUSEX)
+		unqdevice(MOUSEY)
+		if mouse == 2:
+			mouse2track(x, y)
+			Gl.mouse2down = 0
+		elif mouse == 3:
+			mouse3track(x, y)
+			Gl.mouse3down = 0
+		else:
+			print 'fatal error'
+		Gl.alarm_set = 1
+		return 1
+
+def mouse3track(x, y):
+	# first compute polar coordinates from x and y
+	cx, cy = Gl.ox + Gl.cx/2, Gl.oy + Gl.cy/2
+	x, y = x - cx, y - cy
+	if (x, y) == (0, 0): return	# would cause an exception
+	minutes = int(30.5 + 30.0*math.atan2(float(-x), float(-y))/pi)
+	if minutes == 60: minutes = 0
+	a,b = Gl.alarm_minutes/15, minutes/15
+	if (a,b) == (0,3):
+		# Moved backward through 12 o'clock:
+		Gl.alarm_hours = Gl.alarm_hours - 1
+		if Gl.alarm_hours < 0: Gl.alarm_hours = Gl.alarm_hours + 24
+	if (a,b) == (3,0):
+		# Moved forward through 12 o'clock:
+		Gl.alarm_hours = Gl.alarm_hours + 1
+		if Gl.alarm_hours >= 24: Gl.alarm_hours = Gl.alarm_hours - 24
+	Gl.alarm_minutes = minutes
+	seconds = Gl.alarm_hours * HOUR + Gl.alarm_minutes * MINUTE
+	if seconds <> Gl.alarm_time:
+		draw_alarm(rgb_bg())
+		Gl.alarm_time = seconds
+		draw_alarm(rgb_fg())
+
+def mouse2track(x, y):
+	# first compute polar coordinates from x and y
+	cx, cy = Gl.ox + Gl.cx/2, Gl.oy + Gl.cy/2
+	x, y = x - cx, y - cy
+	if (x, y) == (0, 0): return	# would cause an exception
+	hours = int(6.5 - float(Gl.alarm_minutes)/60.0 + 6.0*math.atan2(float(-x), float(-y))/pi)
+	if hours == 12: hours = 0
+	if (Gl.alarm_hours,hours) == (0,11):
+		# Moved backward through midnight:
+		Gl.alarm_hours = 23
+	elif (Gl.alarm_hours,hours) == (12,11):
+		# Moved backward through noon:
+		Gl.alarm_hours = 11
+	elif (Gl.alarm_hours,hours) == (11,0):
+		# Moved forward through noon:
+		Gl.alarm_hours = 12
+	elif (Gl.alarm_hours,hours) == (23,0):
+		# Moved forward through midnight:
+		Gl.alarm_hours = 0
+	elif Gl.alarm_hours < 12:
+		Gl.alarm_hours = hours
+	else:
+		Gl.alarm_hours = hours + 12
+	seconds = Gl.alarm_hours * HOUR + Gl.alarm_minutes * MINUTE
+	if seconds <> Gl.alarm_time:
+		draw_alarm(rgb_bg())
+		Gl.alarm_time = seconds
+		draw_alarm(rgb_fg())
+
+def initmenu():
+	Gl.pup = pup = newpup()
+	addtopup(pup, 'M Clock%t|Alarm On/Off|Seconds Hand On/Off|Quit', 0)
+
+def handlemenu():
+	item = dopup(Gl.pup)
+	if item == 1:
+		# Toggle alarm
+		if Gl.alarm_set:
+			Gl.alarm_set = 0
+			Gl.alarm_on = 0
+		else:
+			Gl.alarm_set = 1
+		Gl.change = 1
+		clearall()
+	elif item == 2:
+		# Toggle Seconds Hand
+		if Gl.update == 1:
+			Gl.update = 60
+			Gl.timernoise = 60
+		else:
+			Gl.update = 1
+			Gl.timernoise = 6
+		Gl.change = 1
+	elif item == 3:
+		if Gl.debug: print 'Exit'
+		sys.exit(0)
+
+main()
diff --git a/Demo/sgi/gl/mixing.py b/Demo/sgi/gl/mixing.py
new file mode 100755
index 0000000..d6b9ecf
--- /dev/null
+++ b/Demo/sgi/gl/mixing.py
@@ -0,0 +1,116 @@
+#! /usr/local/python
+
+# Use Gouraud shading to mix colors.  Requires Z-buffer.
+# It changes the color assignments so fast that you see white.
+# Left button pauses, middle rotates the square.  ESC to quit.
+# Experiment with a larger window (too slow) or smaller window (really white).
+
+from GL import *
+from gl import *
+import DEVICE
+from math import *
+
+#
+#  tekenvlak : draw a square. with bgnpolygon
+#
+def tekenvlak (vc) :
+	bgnpolygon()
+	#vcarray (vc)
+	for i in vc :
+		c3f (i[1])
+		v3f (i[0])
+	endpolygon()
+
+#
+# tekendoos : draw a box
+#
+def tekendoos (col) :
+	v = [(-5.0,0.0,0.0),(0.0,5.0,0.0),(5.0,0.0,0.0),(0.0,-5.0,0.0)]
+	vc = [(v[0],col[0]),(v[1],col[1]),(v[2],col[2]),(v[3],col[1])]
+	tekenvlak (vc)
+
+#
+# initialize gl
+#
+def initgl () :
+	#
+	# open window
+	#
+	foreground ()
+	keepaspect (1, 1)
+	prefposition (100, 500, 100, 500)
+	w = winopen ('PYTHON RGB')
+	keepaspect (1, 1)
+	winconstraints()
+	#
+	# configure pipeline (2buf, GOURAUD and RGBmode)
+	#
+	doublebuffer ()
+	zbuffer (1)
+	shademodel (GOURAUD)
+	RGBmode ()
+	gconfig ()
+	#
+	# set viewing
+	#
+	perspective (900, 1, 1.0, 10.0)
+	polarview (10.0, 0, 0, 0)
+	#
+	# ask for the REDRAW and ESCKEY events
+	#
+	qdevice(DEVICE.MOUSE2)
+	qdevice(DEVICE.MOUSE3)
+	qdevice(DEVICE.REDRAW)
+	qdevice(DEVICE.ESCKEY)
+
+
+#
+# the color black
+#
+black = 0
+#
+# GoForIT : use 2buf to redraw the object 2n times. index i is used as 
+# the (smoothly changing) rotation angle
+#
+def GoForIt(i) :
+	col = [(255.0,0.0,0.0), (0.0,255.0,0.0), (0.0,0.0,255.0)]
+	twist = 0
+	freeze = 1
+	while 1 :
+		if freeze <> 0 :
+			col[0],col[1],col[2] = col[1],col[2],col[0]
+		#
+		# clear z-buffer and clear background to light-blue
+		#
+		zclear()
+		cpack (black)
+		clear()
+		#
+		tekendoos (col)
+		#
+		swapbuffers()
+		#
+		if qtest() <> 0 :
+			dev, val = qread()
+			if dev == DEVICE.ESCKEY :
+				break
+			elif dev == DEVICE.REDRAW :
+				reshapeviewport ()
+			elif dev == DEVICE.MOUSE2 and val <> 0 :
+				twist = twist + 30
+				perspective (900, 1, 1.0, 10.0)
+				polarview (10.0, 0, 0, twist)
+			elif dev == DEVICE.MOUSE3 and val <> 0 :
+				freeze = 1 - freeze
+
+
+# the main program
+#
+def main () :
+	initgl ()
+	GoForIt (0)
+
+#
+# exec main
+#
+main  ()
diff --git a/Demo/sgi/gl/nurbs.py b/Demo/sgi/gl/nurbs.py
new file mode 100755
index 0000000..fb76aa9
--- /dev/null
+++ b/Demo/sgi/gl/nurbs.py
@@ -0,0 +1,171 @@
+#! /usr/local/python
+
+# Rotate a 3D surface created using NURBS.
+#
+# Press left mouse button to toggle surface trimming.
+# Press ESC to quit.
+#
+# See the GL manual for an explanation of NURBS.
+
+from gl import *
+from GL import *
+from DEVICE import *
+
+TRUE = 1
+FALSE = 0
+ORDER = 4
+
+idmat = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
+
+surfknots = [-1, -1, -1, -1, 1, 1, 1, 1]
+
+def make_ctlpoints():
+	c = []
+	#
+	ci = []
+	ci.append(-2.5,  -3.7,  1.0)
+	ci.append(-1.5,  -3.7,  3.0)
+	ci.append(1.5,  -3.7, -2.5)
+	ci.append(2.5,  -3.7,  -0.75)
+	c.append(ci)
+	#
+	ci = []
+	ci.append(-2.5,  -2.0,  3.0)
+	ci.append(-1.5,  -2.0,  4.0)
+	ci.append(1.5,  -2.0,  -3.0)
+	ci.append(2.5,  -2.0,  0.0)
+	c.append(ci)
+	#
+	ci = []
+	ci.append(-2.5, 2.0,  1.0)
+	ci.append(-1.5, 2.0,  0.0)
+	ci.append(1.5,  2.0,  -1.0)
+	ci.append(2.5,  2.0,  2.0)
+	c.append(ci)
+	#
+	ci = []
+	ci.append(-2.5,  2.7,  1.25)
+	ci.append(-1.5,  2.7,  0.1)
+	ci.append(1.5,  2.7,  -0.6)
+	ci.append(2.5,  2.7,  0.2)
+	c.append(ci)
+	#
+	return c
+
+ctlpoints = make_ctlpoints()
+
+trimknots = [0., 0., 0.,  1., 1.,  2., 2.,  3., 3.,   4., 4., 4.]
+
+def make_trimpoints():
+	c = []
+	c.append(1.0, 0.0, 1.0)
+	c.append(1.0, 1.0, 1.0)
+	c.append(0.0, 2.0, 2.0)
+	c.append(-1.0, 1.0, 1.0)
+	c.append(-1.0, 0.0, 1.0)
+	c.append(-1.0, -1.0, 1.0)
+	c.append(0.0, -2.0, 2.0)
+	c.append(1.0, -1.0, 1.0) 
+	c.append(1.0, 0.0, 1.0)
+	return c
+
+trimpoints = make_trimpoints()
+
+def main():
+	init_windows()
+	setup_queue()
+	make_lights()
+	init_view()
+	#
+	set_scene()
+	setnurbsproperty( N_ERRORCHECKING, 1.0 )
+	setnurbsproperty( N_PIXEL_TOLERANCE, 50.0 )
+	trim_flag = 0
+	draw_trim_surface(trim_flag)
+	#
+	while 1:
+		while qtest():
+			dev, val = qread()
+			if dev == ESCKEY:
+				return
+			elif dev == WINQUIT:
+				dglclose(-1)	# this for DGL only
+				return
+			elif dev == REDRAW:
+				reshapeviewport()
+				set_scene()
+				draw_trim_surface(trim_flag)
+			elif dev == LEFTMOUSE:
+				if val:
+					trim_flag = (not trim_flag)
+		set_scene()
+		draw_trim_surface(trim_flag)
+
+def init_windows():
+	foreground()
+	#prefposition(0, 500, 0, 500)
+	wid = winopen('nurbs')
+	wintitle('NURBS Surface')
+	doublebuffer()
+	RGBmode()
+	gconfig()
+	lsetdepth(0x000, 0x7fffff)
+	zbuffer( TRUE )
+
+def setup_queue():
+	qdevice(ESCKEY)
+	qdevice(REDRAW)
+	qdevice(RIGHTMOUSE)
+	qdevice(WINQUIT)
+	qdevice(LEFTMOUSE) #trimming
+
+def init_view():
+	mmode(MPROJECTION)
+	ortho( -4., 4., -4., 4., -4., 4. )
+	#
+	mmode(MVIEWING)
+	loadmatrix(idmat)
+	#
+	lmbind(MATERIAL, 1)
+
+def set_scene():
+	lmbind(MATERIAL, 0)
+	RGBcolor(150,150,150)
+	lmbind(MATERIAL, 1)
+	clear()
+	zclear()
+	#
+	rotate( 100, 'y' )
+	rotate( 100, 'z' )
+
+def draw_trim_surface(trim_flag):
+	bgnsurface()
+	nurbssurface(surfknots, surfknots, ctlpoints, ORDER, ORDER, N_XYZ)
+	if trim_flag:
+		bgntrim()
+		nurbscurve(trimknots, trimpoints, ORDER-1, N_STW)
+		endtrim()
+	endsurface()
+	swapbuffers()
+
+def make_lights():
+	lmdef(DEFLMODEL,1,[])
+	lmdef(DEFLIGHT,1,[])
+	#
+	# define material #1
+	#
+	a = []
+	a = a + [EMISSION, 0.0, 0.0, 0.0]
+	a = a + [AMBIENT,  0.1, 0.1, 0.1]
+	a = a + [DIFFUSE,  0.6, 0.3, 0.3]
+	a = a + [SPECULAR,  0.0, 0.6, 0.0]
+	a = a + [SHININESS, 2.0]
+	a = a + [LMNULL]
+	lmdef(DEFMATERIAL, 1, a)
+	#
+	# turn on lighting
+	#
+	lmbind(LIGHT0, 1)
+	lmbind(LMODEL, 1)
+
+main()
diff --git a/Demo/sgi/gl/zrgb.py b/Demo/sgi/gl/zrgb.py
new file mode 100755
index 0000000..fa01840
--- /dev/null
+++ b/Demo/sgi/gl/zrgb.py
@@ -0,0 +1,168 @@
+#! /usr/local/python
+
+#   zrgb  (Requires Z buffer.)
+#
+# This program demostrates zbuffering 3 intersecting RGB polygons while
+# in doublebuffer mode where, movement of the mouse with the LEFTMOUSE 
+# button depressed will, rotate the 3 polygons. This is done by compound
+# rotations allowing continuous screen-oriented rotations. 
+#
+#    Press the "Esc" key to exit.  
+
+from gl import *
+from GL import *
+from DEVICE import *
+
+
+idmat=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]
+
+def main() :
+	#
+	# old and new mouse position
+	#
+	#
+	mode = 0
+	omx = 0
+	mx = 0
+	omy = 0
+	my = 0
+	#
+	objmat=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]
+	#
+	initialize ()
+	#
+	draw_scene (objmat)
+	#
+	while (1) :
+		#
+		dev, val = qread()
+		#
+		if dev == ESCKEY :
+			if val :
+				break	
+			# exit when key is going up, not down
+			# this avoids the scenario where a window 
+			# underneath this program's window
+			# would otherwise "eat up" the up-
+			# event of the Esc key being released
+			return        
+			#
+		elif dev == REDRAW :
+			reshapeviewport()
+			draw_scene(objmat)
+			#
+		elif dev == LEFTMOUSE:
+			omx = mx
+			omy = my
+			if val :
+				mode = 1
+			else :
+				mode = 0
+		elif dev == MOUSEX :
+			omx = mx
+			mx = val
+			#print omx, mx
+			objmat = update_scene(objmat,mx,my,omx,omy,mode)
+			#
+		elif dev == MOUSEY :
+			omy = my
+			my = val
+			#print omy, my
+			objmat = update_scene(objmat,mx,my,omx,omy,mode)
+			#
+
+
+def initialize () :
+	#
+	foreground ()
+	keepaspect(5, 4)
+	w = winopen('Zbuffered RGB')
+	#
+	doublebuffer()
+	RGBmode()
+	gconfig()
+	zbuffer(1)
+	lsetdepth(0x0, 0x7FFFFF)
+	#
+	qdevice(ESCKEY)
+	qdevice(LEFTMOUSE)
+	qdevice(MOUSEX)
+	qdevice(MOUSEY)
+
+def update_scene (mat, mx, my, omx, omy, mode) :
+	#
+	if mode == 1 :
+		mat = orient(mat, mx, my, omx, omy)
+		draw_scene(mat)
+	return mat
+
+def orient (mat, mx, my, omx, omy) :
+	#
+	#
+	pushmatrix()
+	loadmatrix(idmat)
+	#
+	if mx - omx : rot (float (mx - omx), 'y')
+	if omy - my : rot (float (omy - my), 'x')
+	#
+	multmatrix(mat)
+	mat = getmatrix()
+	#
+	popmatrix()
+	#
+	return mat
+
+def draw_scene (mat) :
+	RGBcolor(40, 100, 200)
+	clear()
+	zclear()
+	#
+	perspective(400, 1.25, 30.0, 60.0)
+	translate(0.0, 0.0, -40.0)
+	multmatrix(mat)
+	#
+	# skews original view to show all polygons
+	#
+	rotate(-580, 'y')
+	draw_polys()
+	#
+	swapbuffers()
+
+polygon1 = [(-10.0,-10.0,0.0),(10.0,-10.0,0.0),(-10.0,10.0,0.0)]
+
+polygon2 = [(0.0,-10.0,-10.0),(0.0,-10.0,10.0),(0.0,5.0,-10.0)]
+
+polygon3 = [(-10.0,6.0,4.0),(-10.0,3.0,4.0),(4.0,-9.0,-10.0),(4.0,-6.0,-10.0)]
+
+def draw_polys():
+	bgnpolygon()
+	cpack(0x0)
+	v3f(polygon1[0])
+	cpack(0x007F7F7F)
+	v3f(polygon1[1])
+	cpack(0x00FFFFFF)
+	v3f(polygon1[2])
+	endpolygon()
+	#
+	bgnpolygon()
+	cpack(0x0000FFFF)
+	v3f(polygon2[0])
+	cpack(0x007FFF00)
+	v3f(polygon2[1])
+	cpack(0x00FF0000)
+	v3f(polygon2[2])
+	endpolygon()
+	#
+	bgnpolygon()
+	cpack(0x0000FFFF)
+	v3f(polygon3[0])
+	cpack(0x00FF00FF)
+	v3f(polygon3[1])
+	cpack(0x00FF0000)
+	v3f(polygon3[2])
+	cpack(0x00FF00FF)
+	v3f(polygon3[3])
+	endpolygon()
+
+
+main ()