Redesigned option processing and added several new options.
Added optional read-ahead thread.
Changed mouse interface.
diff --git a/Demo/sgi/video/Vplay.py b/Demo/sgi/video/Vplay.py
index 1ad6713..5ab623d 100755
--- a/Demo/sgi/video/Vplay.py
+++ b/Demo/sgi/video/Vplay.py
@@ -1,77 +1,124 @@
+#! /ufs/guido/bin/sgi/python-405
+#! /ufs/guido/bin/sgi/python
 #! /usr/local/python
 
 # Play CMIF movie files
 
 
-# Usage:
-#
-# Vplay [-L] [-M maginfy] [-m msec] [-q] [-r msec] [-s factor] [file] ...
+# Help function
+
+def help():
+	print 'Usage: Vplay [options] [file] ...'
+	print
+	print 'Options:'
+	print '-M magnify : magnify the image by the given factor'
+	print '-d         : write some debug stuff on stderr'
+	print '-l         : loop, playing the movie over and over again'
+	print '-m delta   : drop frames closer than delta msec (default 0)'
+	print '-q         : quiet, no informative messages'
+	print '-r delta   : regenerate input time base delta msec apart'
+	print '-s speed   : speed change factor (default 1.0)'
+	print '-t         : use a 2nd thread for read-ahead'
+	print '-x left    : window offset from left of screen'
+	print '-y top     : window offset from top of screen'
+	print 'file ...   : file(s) to play; default film.video'
+	print
+	print 'User interface:'
+	print 'Press the left mouse button to stop or restart the movie.'
+	print 'Press ESC or use the window manager Close or Quit command'
+	print 'to close the window and play the next file (if any).'
 
 
-# Options:
-#
-# -L         : loop, playing the movie over and over again
-# -M magnify : magnify the image by the given factor
-# -m n       : drop frames closer than n msec (default 0)
-# -q         : quiet, no informative messages
-# -r n       : regenerate input time base n msec apart
-# -s speed   : speed change factor after other processing (default 1.0)
-# file ...   : file(s) to play; default film.video
-
-
-# User interface:
-#
-# Place the windo where you want it.  The size is determined by the
-# movie file and the -m option.
-#
-# Press ESC or select the window manager Quit or Close window option
-# to close a window; if more files are given the window for the next
-# file now pops up.  Unless looping, the window closes automatically
-# at the end of the movie.
-
+# Imported modules
 
 import sys
-sys.path.append('/ufs/guido/src/video')
+sys.path.append('/ufs/guido/src/video') # Increase chance of finding VFile
 import VFile
 import time
 import gl, GL
-from DEVICE import *
+from DEVICE import REDRAW, ESCKEY, LEFTMOUSE, WINSHUT, WINQUIT
 import getopt
 import string
 
 
 # Global options
 
-magnify = 1
+debug = 0
 looping = 0
-speed = 1.0
+magnify = 1
 mindelta = 0
+nowait = 0
 quiet = 0
 regen = None
+speed = 1.0
+threading = 0
+xoff = yoff = None
 
 
 # Main program -- mostly command line parsing
 
 def main():
-	global magnify, looping, speed, mindelta, quiet, regen
-	opts, args = getopt.getopt(sys.argv[1:], 'LM:m:qr:s:')
-	for opt, arg in opts:
-		if opt == '-L':
-			looping = 1
-		elif opt == '-M':
-			magnify = string.atoi(arg)
-		elif opt == '-m':
-			mindelta = string.atoi(arg)
-		elif opt == '-q':
-			quiet = 1
-		elif opt == '-r':
-			regen = string.atoi(arg)
-		elif opt == '-s':
-			speed = float(eval(arg))
-	if not args:
-		args = ['film.video']
+	global debug, looping, magnify, mindelta, nowait, quiet, regen, speed
+	global threading, xoff, yoff
+
+	# Parse command line
+	try:
+		opts, args = getopt.getopt(sys.argv[1:], 'M:dlm:nqr:s:tx:y:')
+	except getopt.error, msg:
+		sys.stdout = sys.stderr
+		print 'Error:', msg, '\n'
+		help()
+		sys.exit(2)
+
+	# Interpret options
+	try:
+		for opt, arg in opts:
+			if opt == '-M': magnify = string.atoi(arg)
+			if opt == '-d': debug = debug + 1
+			if opt == '-l': looping = 1
+			if opt == '-m': mindelta = string.atoi(arg)
+			if opt == '-n': nowait = 1
+			if opt == '-q': quiet = 1
+			if opt == '-r': regen = string.atoi(arg)
+			if opt == '-s':
+				try:
+					speed = float(eval(arg))
+				except:
+					sys.stdout = sys.stderr
+					print 'Option -s needs float argument'
+					sys.exit(2)
+			if opt == '-t':
+				try:
+					import thread
+					threading = 1
+				except ImportError:
+					print 'Sorry, this version of Python',
+					print 'does not support threads:',
+					print '-t ignored'
+			if opt == '-x': xoff = string.atoi(arg)
+			if opt == '-y': yoff = string.atoi(arg)
+	except string.atoi_error:
+		sys.stdout = sys.stderr
+		print 'Option', opt, 'require integer argument'
+		sys.exit(2)
+
+	# Check validity of certain options combinations
+	if nowait and looping:
+		print 'Warning: -n and -l are mutually exclusive; -n ignored'
+		nowait = 0
+	if xoff <> None and yoff == None:
+		print 'Warning: -x without -y ignored'
+	if xoff == None and yoff <> None:
+		print 'Warning: -y without -x ignored'
+
+	# Process all files
+	if not args: args = ['film.video']
+	sts = 0
 	for filename in args:
-		process(filename)
+		sts = (process(filename) or sts)
+
+	# Exit with proper exit status
+	sys.exit(sts)
 
 
 # Process one movie file
@@ -81,13 +128,13 @@
 		vin = VFile.VinFile().init(filename)
 	except IOError, msg:
 		sys.stderr.write(filename + ': I/O error: ' + `msg` + '\n')
-		return
+		return 1
 	except VFile.Error, msg:
 		sys.stderr.write(msg + '\n')
-		return
+		return 1
 	except EOFError:
 		sys.stderr.write(filename + ': EOF in video header\n')
-		return
+		return 1
 
 	if not quiet:
 		print 'File:    ', filename
@@ -99,108 +146,145 @@
 		print 'Offset:  ', vin.offset
 	
 	gl.foreground()
-	gl.prefsize(vin.width * magnify, vin.height * magnify)
+
+	width, height = vin.width * magnify, vin.height * magnify
+	if xoff <> None and yoff <> None:
+		scrheight = gl.getgdesc(GL.GD_YPMAX)
+		gl.prefposition(xoff, xoff+width-1, \
+			scrheight-yoff-height, scrheight-yoff-1)
+	else:
+		gl.prefsize(width, height)
+
 	win = gl.winopen(filename)
-	if quiet:
-		vin.quiet = 1
+	gl.clear()
+
+	if quiet: vin.quiet = 1
 	vin.initcolormap()
 
 	gl.qdevice(ESCKEY)
 	gl.qdevice(WINSHUT)
 	gl.qdevice(WINQUIT)
+	gl.qdevice(LEFTMOUSE)
 
-	while 1:
-		stop = playonce(vin)
-		if stop or not looping:
-			break
-
-	gl.wintitle('(done) ' + filename)
+	stop = 0
 
 	while not stop:
-		dev, val = gl.qread()
-		if dev in (ESCKEY, WINSHUT, WINQUIT):
-			stop = 1
+		gl.wintitle(filename)
+		stop = (playonce(vin) or nowait)
+		gl.wintitle('(done) ' + filename)
+		if not looping:
+			while not stop:
+				dev, val = gl.qread()
+				if dev == LEFTMOUSE and val == 1:
+					break # Continue outer loop
+				if dev == ESCKEY and val == 1 or \
+						dev in (WINSHUT, WINQUIT):
+					stop = 1
 
+	# Set xoff, yoff for the next window from the current window
+	global xoff, yoff
+	xoff, yoff = gl.getorigin()
+	width, height = gl.getsize()
+	scrheight = gl.getgdesc(GL.GD_YPMAX)
+	yoff = scrheight - yoff - height
 	gl.winclose(win)
 
+	return 0
+
 
 # Play a movie once; return 1 if user wants to stop, 0 if not
 
 def playonce(vin):
-	if vin.hascache:
-		vin.rewind()
+	vin.rewind()
 	vin.colormapinited = 1
 	vin.magnify = magnify
 
+	if threading:
+		import thread
+		queue = []
+		stop = []
+		thread.start_new_thread(read_ahead, (vin, queue, stop))
+		# Get the read-ahead thread going
+		while len(queue) < 5 and None not in queue:
+			time.millisleep(10)
+
 	tin = 0
 	told = 0
 	nin = 0
 	nout = 0
-	nmissed = 0
+	nlate = 0
 	nskipped = 0
+	data = None
 
-	t0 = time.millitimer()
+	tlast = t0 = time.millitimer()
 
 	while 1:
-		try:
-			tin, size, csize = vin.getnextframeheader()
-		except EOFError:
-			break
-		nin = nin+1
-		if regen:
-			tout = nin * regen
-		else:
-			tout = tin
-		tout = int(tout / speed)
-		if tout - told < mindelta:
-			try:
-				vin.skipnextframedata(size, csize)
-			except EOFError:
-				print '[short data at EOF]'
-				break
-			nskipped = nskipped + 1
-		else:
-			told = tout
-			dt = tout + t0 - time.millitimer()
-			if dt < 0:
-				try:
-					vin.skipnextframedata(size, csize)
-				except EOFError:
-					print '[short data at EOF]'
-					break
-				nmissed = nmissed + 1
-			else:
-				try:
-					data, cdata = vin.getnextframedata \
-						(size, csize)
-				except EOFError:
-					print '[short data at EOF]'
-					break
-				dt = tout + t0 - time.millitimer()
-				if dt > 0:
-					time.millisleep(dt)
-				vin.showframe(data, cdata)
-				nout = nout + 1
 		if gl.qtest():
 			dev, val = gl.qread()
-			if dev in (ESCKEY, WINSHUT, WINQUIT):
-				return 1
+			if dev == ESCKEY and val == 1 or \
+					dev in (WINSHUT, WINQUIT) or \
+					dev == LEFTMOUSE and val == 1:
+				if debug: sys.stderr.write('\n')
+				if threading:
+					stop.append(None)
+					while len(stop) < 2:
+						time.millisleep(10)
+				return (dev != LEFTMOUSE)
 			if dev == REDRAW:
 				gl.reshapeviewport()
-				if data:
-					vin.showframe(data, cdata)
+				if data: vin.showframe(data, cdata)
+		if threading:
+			if not queue:
+				if debug: sys.stderr.write('.')
+				time.millisleep(10)
+				continue
+			q0 = queue[0]
+			if q0 == None: break
+			del queue[0]
+			tin, data, cdata = q0
+		else:
+			try:
+				tin, size, csize = vin.getnextframeheader()
+			except EOFError:
+				break
+		nin = nin+1
+		if regen: tout = nin * regen
+		else: tout = tin
+		tout = int(tout / speed)
+		if tout - told < mindelta:
+			nskipped = nskipped + 1
+		else:
+			if not threading:
+				try:
+					data, cdata = \
+					  vin.getnextframedata(size, csize)
+				except EOFError:
+					if not quiet:
+						print '[incomplete last frame]'
+					break
+			now = time.millitimer()
+			dt = (tout-told) - (now-tlast)
+			told = tout
+			if debug: sys.stderr.write(`dt/10` + ' ')
+			if dt < 0: nlate = nlate + 1
+			if dt > 0:
+				time.millisleep(dt)
+				now = now + dt
+			tlast = now
+			vin.showframe(data, cdata)
+			nout = nout + 1
 
 	t1 = time.millitimer()
 
-	if quiet:
-		return 0
+	if debug: sys.stderr.write('\n')
+
+	if quiet: return 0
 
 	print 'Recorded:', nin, 'frames in', tin*0.001, 'sec.',
 	if tin: print '-- average', int(nin*10000.0/tin)*0.1, 'frames/sec',
 	print
 
-	if nskipped:
-		print 'Skipped', nskipped, 'frames'
+	if nskipped: print 'Skipped', nskipped, 'frames'
 
 	tout = t1-t0
 	print 'Played:', nout,
@@ -208,12 +292,25 @@
 	if tout: print '-- average', int(nout*10000.0/tout)*0.1, 'frames/sec',
 	print
 
-	if nmissed:
-		print 'Missed', nmissed, 'frames'
+	if nlate: print 'There were', nlate, 'late frames'
 
 	return 0
 
 
+# Read-ahead thread
+
+def read_ahead(vin, queue, stop):
+	try:
+		while not stop: queue.append(vin.getnextframe())
+	except EOFError:
+		queue.append(None)
+	stop.append(None)
+
+
+
 # Don't forget to call the main program
 
-main()
+try:
+	main()
+except KeyboardInterrupt:
+	print '[Interrupt]'