http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
authorZygo Blaxell <zblaxell@hungrycats.org>
Mon, 2 Mar 2009 05:43:14 +0000 (00:43 -0500)
committerZygo Blaxell <zblaxell@faye.furryterror.org>
Fri, 8 Feb 2013 17:54:08 +0000 (12:54 -0500)
-rw-r--r-- 1 zblaxell zblaxell 3988251 Oct 30  2003 xscreensaver-4.14.tar.gz
4996718deaceeb578953f400e518b745c1dd32c4  xscreensaver-4.14.tar.gz

188 files changed:
Makefile.in
README
configure
configure.in
driver/Makefile.in
driver/XScreenSaver.ad.in
driver/XScreenSaver_ad.h
driver/demo-Gtk-conf.c
driver/demo-Gtk.c
driver/passwd-kerberos.c
driver/pdf2jpeg.m [new file with mode: 0644]
driver/pdf2jpeg.man [new file with mode: 0644]
driver/xscreensaver-command.man
driver/xscreensaver-demo.man
driver/xscreensaver-getimage-desktop [new file with mode: 0755]
driver/xscreensaver-getimage-desktop.man [new file with mode: 0644]
driver/xscreensaver-getimage-file.man
driver/xscreensaver-getimage-video
driver/xscreensaver-getimage-video.man
driver/xscreensaver-getimage.c
driver/xscreensaver-getimage.man
driver/xscreensaver.c
driver/xscreensaver.man
hacks/Makefile.in
hacks/analogtv.c [new file with mode: 0644]
hacks/analogtv.h [new file with mode: 0644]
hacks/ant.c
hacks/apollonian.c
hacks/apple2-main.c [new file with mode: 0644]
hacks/apple2.c [new file with mode: 0644]
hacks/apple2.h [new file with mode: 0644]
hacks/apple2.man [new file with mode: 0644]
hacks/blitspin.c
hacks/bouboule.c
hacks/braid.c
hacks/bsod.c
hacks/bsod.man
hacks/bumps.c
hacks/compile_axp.com
hacks/compile_decc.com
hacks/config/README
hacks/config/apple2.xml [new file with mode: 0644]
hacks/config/blinkbox.xml [new file with mode: 0644]
hacks/config/bsod.xml
hacks/config/fontglide.xml [new file with mode: 0644]
hacks/config/gleidescope.xml [new file with mode: 0644]
hacks/config/glslideshow.xml
hacks/config/mirrorblob.xml [new file with mode: 0644]
hacks/config/pong.xml [new file with mode: 0644]
hacks/config/strange.xml
hacks/config/xanalogtv.xml [new file with mode: 0644]
hacks/crystal.c
hacks/decayscreen.c
hacks/deluxe.c
hacks/demon.c
hacks/discrete.c
hacks/distort.c
hacks/drift.c
hacks/euler2d.c
hacks/fadeplot.c
hacks/flag.c
hacks/flow.c
hacks/fluidballs.c
hacks/fontglide.c [new file with mode: 0644]
hacks/fontglide.man [new file with mode: 0644]
hacks/galaxy.c
hacks/glx/Makefile.in
hacks/glx/antspotlight.c
hacks/glx/atlantis.c
hacks/glx/atunnel.c
hacks/glx/b_draw.c
hacks/glx/b_lockglue.c
hacks/glx/b_sphere.c
hacks/glx/blinkbox.c [new file with mode: 0644]
hacks/glx/blinkbox.man [new file with mode: 0644]
hacks/glx/boxed.c
hacks/glx/bubble3d.c
hacks/glx/buildlwo.c
hacks/glx/cage.c
hacks/glx/chessgames.h
hacks/glx/dolphin.c
hacks/glx/endgame.c
hacks/glx/engine.c
hacks/glx/flipscreen3d.c
hacks/glx/flurry.c
hacks/glx/fps.c
hacks/glx/gears.c
hacks/glx/gflux.c
hacks/glx/gleidescope.c [new file with mode: 0644]
hacks/glx/gleidescope.man [new file with mode: 0644]
hacks/glx/glforestfire.c
hacks/glx/glknots.c
hacks/glx/glslideshow.c
hacks/glx/glslideshow.man
hacks/glx/glsnake.c
hacks/glx/gltext.c
hacks/glx/grab-ximage.c
hacks/glx/grab-ximage.h
hacks/glx/hypertorus.c
hacks/glx/lavalite.c
hacks/glx/mirrorblob.c [new file with mode: 0644]
hacks/glx/mirrorblob.man [new file with mode: 0644]
hacks/glx/moebius.c
hacks/glx/morph3d.c
hacks/glx/pipeobjs.c
hacks/glx/pipes.c
hacks/glx/polytopes.c
hacks/glx/rubik.c
hacks/glx/sballs.c
hacks/glx/shark.c
hacks/glx/sierpinski3d.c
hacks/glx/spheremonics.c
hacks/glx/sproingies.c
hacks/glx/sproingiewrap.c
hacks/glx/stairs.c
hacks/glx/starwars.c
hacks/glx/starwars.man
hacks/glx/stonerview.c
hacks/glx/superquadrics.c
hacks/glx/swim.c
hacks/glx/tunnel_draw.c
hacks/glx/whale.c
hacks/grav.c
hacks/halo.c
hacks/helix.c
hacks/hopalong.c
hacks/ifs.c
hacks/imsmap.c
hacks/jigsaw.c
hacks/juggle.c
hacks/julia.c
hacks/laser.c
hacks/lightning.c
hacks/lisa.c
hacks/lissie.c
hacks/ljlatest
hacks/ljlatest.man
hacks/loop.c
hacks/metaballs.c
hacks/mountain.c
hacks/penrose.c
hacks/phosphor.c
hacks/phosphor.man
hacks/polyominoes.c
hacks/pong.c [new file with mode: 0644]
hacks/pong.man [new file with mode: 0644]
hacks/ripples.c
hacks/rotor.c
hacks/rotzoomer.c
hacks/sierpinski.c
hacks/slidescreen.c
hacks/slip.c
hacks/sonar.c
hacks/sphere.c
hacks/spiral.c
hacks/spotlight.c
hacks/strange.c
hacks/swirl.c
hacks/thornbird.c
hacks/triangle.c
hacks/twang.c
hacks/vines.c
hacks/webcollage-helper.c
hacks/whirlygig.c
hacks/worm.c
hacks/xanalogtv.c [new file with mode: 0644]
hacks/xanalogtv.man [new file with mode: 0644]
hacks/xflame.c
hacks/xflame.man
hacks/xsublim.c
hacks/xteevee.c
hacks/xteevee.man
hacks/zoom.c
po/Makefile.in.in
po/POTFILES.in
po/ca.po
po/fi.po
po/it.po
po/pt_BR.po
setup.com
utils/grabclient.c
utils/grabscreen.c
utils/grabscreen.h
utils/usleep.c
utils/version.h
xscreensaver.lsm
xscreensaver.lsm.sh
xscreensaver.spec

index f6f2efb57659ed5f86f3c872f704aafa6ad0e2f6..67c88cfc70e9c235b2dd46711ab3129ffbc3d2ea 100644 (file)
@@ -319,4 +319,5 @@ count::
     ( cd hacks/glx; make -s INSTALL=true install-program ) ) | \
     grep true | \
     grep -v helper | \
+    grep -v ljlatest | \
     wc -l
diff --git a/README b/README
index 1a27506e84a3eb14a8f24c925fc83bdb7bfc154b..036284795f75a2d4334ea1a0e45a2d1953da0ba2 100644 (file)
--- a/README
+++ b/README
@@ -65,7 +65,7 @@ there's no need to recompile or reinstall anything.
 Along with the xscreensaver daemon itself, this package also includes
 numerous graphics hacks for use as screensavers.  There is nothing
 magic about these: they are just programs that draw on the root window.
-More than 160 such programs are included.  For details, see the
+More than 175 such programs are included.  For details, see the
 xscreensaver web page, or the enclosed manual pages.
 
 The latest version of xscreensaver is always available on the web at
@@ -76,6 +76,13 @@ the XScreenSaver FAQ about that: http://www.jwz.org/xscreensaver/faq.html
 
                               ============
 
+Changes since 4.13:   * New hacks, `fontglide', `apple2', `xanalogtv', `pong',
+                        `gleidescope', `mirrorblob', and `blinkbox'.
+                      * New version of `glsnake' (with many more models.)
+                      * Another Windows crash in `bsod'; also HVX/GCOS6/TPS6.
+                      * New version of `endgame'.
+                      * Screen grabbing works on MacOS X.
+                      * Various minor fixes.
 Changes since 4.12:   * On Xinerama systems, xscreensaver now runs one hack on
                         each monitor (just like in "real" multi-head mode)
                         instead of running one hack stretching across all the
index 11e238a0f21910d126e050f65b859e7694888185..e0126736f83a85d49c20ce59f5249864f2af55eb 100755 (executable)
--- a/configure
+++ b/configure
@@ -308,7 +308,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SET_MAKE EGREP PERL X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS INTLTOOL_DESKTOP_RULE INTLTOOL_DIRECTORY_RULE INTLTOOL_KEYS_RULE INTLTOOL_OAF_RULE INTLTOOL_PONG_RULE INTLTOOL_SERVER_RULE INTLTOOL_SHEET_RULE INTLTOOL_SOUNDLIST_RULE INTLTOOL_UI_RULE INTLTOOL_XML_RULE INTLTOOL_CAVES_RULE INTLTOOL_EXTRACT INTLTOOL_MERGE INTLTOOL_UPDATE INTLTOOL_PERL GETTEXT_PACKAGE RANLIB ac_ct_RANLIB ALLOCA USE_NLS MSGFMT GMSGFMT XGETTEXT CATALOGS CATOBJEXT DATADIRNAME GMOFILES INSTOBJEXT INTLDEPS INTLLIBS INTLOBJS POFILES POSUB MKINSTALLDIRS pkg_config glib_config gtk_config gnome_config have_gnome_help xml_config gdk_pixbuf_config fortune_tmp INCLUDES PREFERRED_DEMO_PROGRAM ALL_DEMO_PROGRAMS SAVER_LIBS MOTIF_LIBS GTK_LIBS XML_LIBS JPEG_LIBS HACK_LIBS XPM_LIBS GL_LIBS GLE_LIBS XDPMS_LIBS PASSWD_LIBS INSTALL_SETUID SETUID_HACKS INSTALL_DIRS NEED_SETUID INSTALL_PAM PASSWD_SRCS PASSWD_OBJS XMU_SRCS XMU_OBJS XMU_LIBS SAVER_GL_SRCS SAVER_GL_OBJS SAVER_GL_LIBS LOCK_SRCS LOCK_OBJS JPEG_EXES GL_EXES GL_UTIL_EXES GL_MEN GL_KLUDGE GLE_EXES GLE_KLUDGE GNOMEHELP_Y GNOMEHELP_N HACKDIR GNOME_DATADIR GLADE_DATADIR PO_DATADIR GNOME_PANELDIR HACK_CONF_DIR GTK_EXTRA_OBJS APPDEFAULTS DEPEND DEPEND_FLAGS DEPEND_DEFINES LIBOBJS LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SET_MAKE EGREP PERL X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS INTLTOOL_DESKTOP_RULE INTLTOOL_DIRECTORY_RULE INTLTOOL_KEYS_RULE INTLTOOL_OAF_RULE INTLTOOL_PONG_RULE INTLTOOL_SERVER_RULE INTLTOOL_SHEET_RULE INTLTOOL_SOUNDLIST_RULE INTLTOOL_UI_RULE INTLTOOL_XML_RULE INTLTOOL_CAVES_RULE INTLTOOL_EXTRACT INTLTOOL_MERGE INTLTOOL_UPDATE INTLTOOL_PERL GETTEXT_PACKAGE RANLIB ac_ct_RANLIB ALLOCA USE_NLS MSGFMT GMSGFMT XGETTEXT CATALOGS CATOBJEXT DATADIRNAME GMOFILES INSTOBJEXT INTLDEPS INTLLIBS INTLOBJS POFILES POSUB MKINSTALLDIRS pkg_config glib_config gtk_config gnome_config have_gnome_help xml_config gdk_pixbuf_config fortune_tmp INCLUDES PREFERRED_DEMO_PROGRAM ALL_DEMO_PROGRAMS SAVER_LIBS MOTIF_LIBS GTK_LIBS XML_LIBS JPEG_LIBS HACK_LIBS XPM_LIBS GL_LIBS GLE_LIBS XDPMS_LIBS PASSWD_LIBS INSTALL_SETUID SETUID_HACKS INSTALL_DIRS NEED_SETUID INSTALL_PAM OBJCC EXES_OSX SCRIPTS_OSX MEN_OSX PASSWD_SRCS PASSWD_OBJS XMU_SRCS XMU_OBJS XMU_LIBS SAVER_GL_SRCS SAVER_GL_OBJS SAVER_GL_LIBS LOCK_SRCS LOCK_OBJS JPEG_EXES GL_EXES GL_UTIL_EXES GL_MEN GL_KLUDGE GLE_EXES GLE_KLUDGE GNOMEHELP_Y GNOMEHELP_N HACKDIR GNOME_DATADIR GLADE_DATADIR PO_DATADIR GNOME_PANELDIR HACK_CONF_DIR GTK_EXTRA_OBJS APPDEFAULTS DEPEND DEPEND_FLAGS DEPEND_DEFINES LIBOBJS LTLIBOBJS'
 ac_subst_files=''
 
 # Initialize some variables set by options.
@@ -2523,6 +2523,8 @@ echo "${ECHO_T}no idea" >&6
     esac
   fi
 
+  OBJCC="$CC"
+
   echo "$as_me:$LINENO: checking whether the compiler works on ANSI C" >&5
 echo $ECHO_N "checking whether the compiler works on ANSI C... $ECHO_C" >&6
   if test "$cross_compiling" = yes; then
@@ -2571,6 +2573,7 @@ fi
     echo "$as_me:$LINENO: result: Turning on gcc compiler warnings." >&5
 echo "${ECHO_T}Turning on gcc compiler warnings." >&6
     CC="$CC -pedantic -Wall -Wstrict-prototypes -Wnested-externs"
+    OBJCC="$OBJCC -Wall"
     # supposedly gcc 3.4 will have "-Wdeclaration-after-statement"
     # and then perhaps we can do without -pedantic?
   else
@@ -6448,6 +6451,18 @@ _ACEOF
 
   fi
 
+echo "$as_me:$LINENO: checking whether this is MacOS X" >&5
+echo $ECHO_N "checking whether this is MacOS X... $ECHO_C" >&6
+  ac_macosx=no
+  case "$host" in
+    *-apple-darwin* )
+      ac_macosx=yes
+    ;;
+  esac
+echo "$as_me:$LINENO: result: $ac_macosx" >&5
+echo "${ECHO_T}$ac_macosx" >&6
+
+
 
 ###############################################################################
 #
@@ -11076,6 +11091,19 @@ else
   exit 1
 fi
 
+# We can't lock on MacOS X, so don't even bother compiling in support for it.
+#
+if test "$ac_macosx" = yes; then
+  if test "$enable_locking" = yes; then
+    echo "$as_me:$LINENO: result: locking disabled: it doesn't work on MacOS X" >&5
+echo "${ECHO_T}locking disabled: it doesn't work on MacOS X" >&6
+    enable_locking=no
+    cat >>confdefs.h <<\_ACEOF
+#define NO_LOCKING 1
+_ACEOF
+
+  fi
+fi
 
 
 ###############################################################################
@@ -15364,7 +15392,7 @@ if test "${ac_cv_mesagl_version_string+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 15373 "configure"
+#line 15401 "configure"
 #include "confdefs.h"
 #include <GL/gl.h>
 #ifndef MESA_MAJOR_VERSION
@@ -18514,6 +18542,17 @@ else
   LOCK_OBJS='$(NOLOCK_OBJS_1)'
 fi
 
+if test "$ac_macosx" = yes; then
+  EXES_OSX='$(EXES_OSX)'
+  SCRIPTS_OSX='$(SCRIPTS_OSX)'
+  MEN_OSX='$(MEN_OSX)'
+else
+  EXES_OSX=
+  SCRIPTS_OSX=
+  MEN_OSX=
+fi
+
+
 INSTALL_SETUID='$(INSTALL_PROGRAM) $(SUID_FLAGS)'
 
 if test "$need_setuid" = yes; then
@@ -18700,6 +18739,11 @@ INCLUDES=`echo "$INCLUDES" | sed 's@ -I${prefix}/include@@g;'`
 
 
 
+
+
+
+
+
 
 
 
@@ -19440,6 +19484,10 @@ s,@SETUID_HACKS@,$SETUID_HACKS,;t t
 s,@INSTALL_DIRS@,$INSTALL_DIRS,;t t
 s,@NEED_SETUID@,$NEED_SETUID,;t t
 s,@INSTALL_PAM@,$INSTALL_PAM,;t t
+s,@OBJCC@,$OBJCC,;t t
+s,@EXES_OSX@,$EXES_OSX,;t t
+s,@SCRIPTS_OSX@,$SCRIPTS_OSX,;t t
+s,@MEN_OSX@,$MEN_OSX,;t t
 s,@PASSWD_SRCS@,$PASSWD_SRCS,;t t
 s,@PASSWD_OBJS@,$PASSWD_OBJS,;t t
 s,@XMU_SRCS@,$XMU_SRCS,;t t
 if test "$gtk2_halfassed" != no ; then
   warnL "GTK version $gtk2_halfassed was found, but at least one supporting"
   warn2 "library ($gtk2_halfassed_lib) was not, so GTK 2.x can't be used."
-  v="$ac_gtk_version_string"
-  warn2 "GTK $v is also installed, so it will be used instead."
-  warn2 "Please read the above output and the \`config.log' file"
-  warn2 "for more details."
+  if test "$have_gtk" = yes ; then
+    v="$ac_gtk_version_string"
+    warn2 "GTK $v is also installed, so it will be used instead."
+    warn2 "Please read the above output and the \`config.log' file"
+    warn2 "for more details."
+  fi
 fi
 
 
@@ -20526,7 +20576,7 @@ if test "$do_dir_warning" = yes; then
   echo '      "xscreensaver-demo", and "xscreensaver-command" executables'
   echo "      will be installed in ${bindir}/."
   echo ""
-  echo "      The various graphics demos (160+ different executables) will"
+  echo "      The various graphics demos (175+ different executables) will"
   echo "      be installed in ${HACKDIR}/."
   echo ""
   echo "      If you would prefer the demos to be installed elsewhere,"
index 51c8e52673fce78ad435628803690df56fb31376..8397156d7df721751e7d875ea2236f4ed0705b2d 100644 (file)
@@ -56,6 +56,8 @@ AC_DEFUN(AC_PROG_CC_ANSI,
     esac
   fi
 
+  OBJCC="$CC"
+
   AC_MSG_CHECKING([whether the compiler works on ANSI C])
   AC_TRY_RUN([ main(int ac, char **av) { return 0; } ],
      AC_MSG_RESULT(yes),
@@ -66,6 +68,7 @@ AC_DEFUN(AC_PROG_CC_ANSI,
   if test -n "$GCC"; then
     AC_MSG_RESULT(Turning on gcc compiler warnings.)
     CC="$CC -pedantic -Wall -Wstrict-prototypes -Wnested-externs"
+    OBJCC="$OBJCC -Wall"
     # supposedly gcc 3.4 will have "-Wdeclaration-after-statement"
     # and then perhaps we can do without -pedantic?
   else
@@ -849,6 +852,16 @@ AC_PATH_X_APP_DEFAULTS
 AC_X_RANDOM_PATHS
 AC_XPOINTER
 
+AC_MSG_CHECKING(whether this is MacOS X)
+  ac_macosx=no
+  case "$host" in
+    *-apple-darwin* )
+      ac_macosx=yes
+    ;;
+  esac
+AC_MSG_RESULT($ac_macosx)
+
+
 
 ###############################################################################
 #
@@ -1459,6 +1472,15 @@ else
   exit 1
 fi
 
+# We can't lock on MacOS X, so don't even bother compiling in support for it.
+#
+if test "$ac_macosx" = yes; then
+  if test "$enable_locking" = yes; then
+    AC_MSG_RESULT(locking disabled: it doesn't work on MacOS X)
+    enable_locking=no
+    AC_DEFINE(NO_LOCKING)
+  fi
+fi
 
 
 ###############################################################################
@@ -3407,6 +3429,17 @@ else
   LOCK_OBJS='$(NOLOCK_OBJS_1)'
 fi
 
+if test "$ac_macosx" = yes; then
+  EXES_OSX='$(EXES_OSX)'
+  SCRIPTS_OSX='$(SCRIPTS_OSX)'
+  MEN_OSX='$(MEN_OSX)'
+else
+  EXES_OSX=
+  SCRIPTS_OSX=
+  MEN_OSX=
+fi
+
+
 INSTALL_SETUID='$(INSTALL_PROGRAM) $(SUID_FLAGS)'
 
 if test "$need_setuid" = yes; then
@@ -3570,6 +3603,11 @@ AC_SUBST(INSTALL_DIRS)
 AC_SUBST(NEED_SETUID)
 AC_SUBST(INSTALL_PAM)
 
+AC_SUBST(OBJCC)
+AC_SUBST(EXES_OSX)
+AC_SUBST(SCRIPTS_OSX)
+AC_SUBST(MEN_OSX)
+
 AC_SUBST(PASSWD_SRCS)
 AC_SUBST(PASSWD_OBJS)
 AC_SUBST(XMU_SRCS)
@@ -3761,10 +3799,12 @@ fi
 if test "$gtk2_halfassed" != no ; then
   warnL "GTK version $gtk2_halfassed was found, but at least one supporting"
   warn2 "library ($gtk2_halfassed_lib) was not, so GTK 2.x can't be used."
-  v="$ac_gtk_version_string"
-  warn2 "GTK $v is also installed, so it will be used instead."
-  warn2 "Please read the above output and the \`config.log' file"
-  warn2 "for more details."
+  if test "$have_gtk" = yes ; then
+    v="$ac_gtk_version_string"
+    warn2 "GTK $v is also installed, so it will be used instead."
+    warn2 "Please read the above output and the \`config.log' file"
+    warn2 "for more details."
+  fi
 fi
 
 
@@ -4113,7 +4153,7 @@ if test "$do_dir_warning" = yes; then
   echo '      "xscreensaver-demo", and "xscreensaver-command" executables'
   echo "      will be installed in ${bindir}/."
   echo ""
-  echo "      The various graphics demos (160+ different executables) will"
+  echo "      The various graphics demos (175+ different executables) will"
   echo "      be installed in ${HACKDIR}/."
   echo ""
   echo "      If you would prefer the demos to be installed elsewhere,"
index 42d9da23b2564d2330dfcc1c4039e9bdc4615161..b7d308280470f7ef75405eee9210210ad760c0b5 100644 (file)
@@ -3,7 +3,7 @@
 
 @SET_MAKE@
 .SUFFIXES:
-.SUFFIXES: .c .o
+.SUFFIXES: .c .m .o
 
 srcdir         = @srcdir@
 VPATH          = @srcdir@
@@ -36,6 +36,7 @@ GNOME_BINDIR  = $(bindir)
 HACK_CONF_DIR  = @HACK_CONF_DIR@
 
 CC             = @CC@
+OBJCC          = @OBJCC@
 CFLAGS         = @CFLAGS@
 LDFLAGS                = @LDFLAGS@
 DEFS           = @DEFS@
@@ -192,6 +193,10 @@ DEMO_OBJS_1        = prefs.o dpms.o $(XMU_OBJS)
 DEMO_SRCS      = prefs.c dpms.c remote.c exec.c $(DEMO_UTIL_SRCS)
 DEMO_OBJS      = prefs.o dpms.o remote.o exec.o $(DEMO_UTIL_OBJS)
 
+PDF2JPEG_SRCS  = pdf2jpeg.m
+PDF2JPEG_OBJS  = pdf2jpeg.o
+PDF2JPEG_LIBS  = -framework Cocoa
+
 SAVER_LIBS     = $(LIBS) $(X_LIBS) $(XMU_LIBS) @SAVER_LIBS@ \
                  $(XDPMS_LIBS) $(GL_LIBS) $(X_PRE_LIBS) \
                  -lXt -lX11 -lXext $(X_EXTRA_LIBS) \
@@ -204,18 +209,25 @@ GETIMG_LIBS       = $(LIBS) $(X_LIBS) $(XPM_LIBS) $(JPEG_LIBS) \
                  $(X_PRE_LIBS) -lXt -lX11 $(XMU_LIBS) -lXext $(X_EXTRA_LIBS)
 
 EXES           = xscreensaver xscreensaver-command xscreensaver-demo \
-                 xscreensaver-getimage
+                 xscreensaver-getimage @EXES_OSX@
 EXES2          = @ALL_DEMO_PROGRAMS@
-SCRIPTS                = xscreensaver-getimage-file xscreensaver-getimage-video
+EXES_OSX       = pdf2jpeg
+
+SCRIPTS_1      = xscreensaver-getimage-file xscreensaver-getimage-video
+SCRIPTS_OSX    = xscreensaver-getimage-desktop
+SCRIPTS                = $(SCRIPTS_1) @SCRIPTS_OSX@
 
 HDRS           = XScreenSaver_ad.h xscreensaver.h prefs.h remote.h \
                  demo-Gtk-widgets.h demo-Gtk-stubs.h demo-Gtk-support.h \
                  demo-Gtk-conf.h
-MEN            = xscreensaver.man xscreensaver-demo.man \
+MEN_1          = xscreensaver.man xscreensaver-demo.man \
                  xscreensaver-command.man \
                  xscreensaver-getimage.man \
                  xscreensaver-getimage-file.man \
                  xscreensaver-getimage-video.man
+MEN_OSX                = xscreensaver-getimage-desktop.man pdf2jpeg.man
+MEN            = $(MEN_1) @MEN_OSX@
+
 EXTRAS         = README Makefile.in XScreenSaver.ad.in xscreensaver.pam \
                  xscreensaver-demo.glade xscreensaver-demo.glade2 \
                  xscreensaver-demo.glade2p \
@@ -230,8 +242,9 @@ VMSFILES    = compile_axp.com compile_decc.com link_axp.com link_decc.com \
 TARFILES       = $(EXTRAS) $(VMSFILES) $(SAVER_SRCS_1) \
                  $(MOTIF_SRCS) $(GTK_SRCS) $(PWENT_SRCS) \
                  $(KERBEROS_SRCS) $(PAM_SRCS) $(LOCK_SRCS_1) $(DEMO_SRCS_1) \
-                 $(CMD_SRCS) $(GETIMG_SRCS_1) $(HDRS) $(SCRIPTS) \
-                 $(TEST_SRCS) $(MEN)
+                 $(CMD_SRCS) $(GETIMG_SRCS_1) $(PDF2JPEG_SRCS) $(HDRS) \
+                 $(SCRIPTS_1) $(SCRIPTS_OSX) $(MEN_1) $(MEN_OSX) \
+                 $(TEST_SRCS)
 
 
 default: $(EXES)
@@ -274,18 +287,11 @@ install-program: $(EXES)
        fi ;                                                                  \
        echo $$inst xscreensaver $(install_prefix)$(bindir)/xscreensaver ;    \
        $$inst xscreensaver $(install_prefix)$(bindir)/xscreensaver
-       @echo $(INSTALL_PROGRAM) xscreensaver-command \
-         $(install_prefix)$(bindir)/xscreensaver-command ; \
-       $(INSTALL_PROGRAM) xscreensaver-command \
-         $(install_prefix)$(bindir)/xscreensaver-command
-       @echo $(INSTALL_PROGRAM) xscreensaver-demo \
-         $(install_prefix)$(bindir)/xscreensaver-demo ; \
-       $(INSTALL_PROGRAM) xscreensaver-demo \
-         $(install_prefix)$(bindir)/xscreensaver-demo
-       @echo $(INSTALL_PROGRAM) xscreensaver-getimage \
-         $(install_prefix)$(bindir)/xscreensaver-getimage ; \
-       $(INSTALL_PROGRAM) xscreensaver-getimage \
-         $(install_prefix)$(bindir)/xscreensaver-getimage
+       @for exe in xscreensaver-command xscreensaver-demo                 \
+                   xscreensaver-getimage @EXES_OSX@ ; do                  \
+         echo $(INSTALL_PROGRAM) $$exe $(install_prefix)$(bindir)/$$exe ; \
+              $(INSTALL_PROGRAM) $$exe $(install_prefix)$(bindir)/$$exe ; \
+        done
 
 install-ad: XScreenSaver.ad
        @if [ ! -d $(install_prefix)$(AD_DIR) ]; then                         \
@@ -831,7 +837,10 @@ $(SAVER_UTIL_OBJS):
 
 # How we build object files in this directory.
 .c.o:
-       $(CC) -c $(INCLUDES) $(DEFS)  $(CFLAGS) $(X_CFLAGS) $<
+       $(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(X_CFLAGS) $<
+
+.m.o:
+       $(OBJCC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(X_CFLAGS) $<
 
 # subprocs takes an extra -D option.
 subprocs.o: subprocs.c
@@ -889,6 +898,9 @@ xscreensaver-demo-Gtk: $(DEMO_OBJS) $(GTK_OBJS)
 xscreensaver-getimage: $(GETIMG_OBJS)
        $(CC) $(LDFLAGS) -o $@ $(GETIMG_OBJS) $(GETIMG_LIBS) -lm
 
+pdf2jpeg: $(PDF2JPEG_OBJS)
+       $(OBJCC) $(LDFLAGS) -o $@ $(PDF2JPEG_OBJS) $(PDF2JPEG_LIBS) -lm
+
 
 TEST_PASSWD_OBJS = test-passwd.o $(LOCK_OBJS_1) $(PASSWD_OBJS) \
         subprocs.o setuid.o splash.o prefs.o \
index 109f6403449982146dae0eb48719086b5716ea45..fde6eeec352fa3f90b6ba14e37aa826a1c59aa13 100644 (file)
@@ -4,8 +4,8 @@
 !            a screen saver and locker for the X window system
 !                            by Jamie Zawinski
 !
-!                              version 4.13
-!                              07-Sep-2003
+!                              version 4.14
+!                              25-Oct-2003
 !
 ! See "man xscreensaver" for more info.  The latest version is always
 ! available at http://www.jwz.org/xscreensaver/
           "Qix (transparent)"  qix -root -count 4 -solid -transparent      \n\
                "Qix (linear)"  qix -root -count 5 -solid -transparent        \
                                  -linear -segments 250 -size 100           \n\
-- mono:           "Qix (xor)"  qix -root -linear -count 5 -size 200          \
+-                 "Qix (xor)"  qix -root -linear -count 5 -size 200          \
                                  -spread 30 -segments 75 -solid -xor       \n\
                                                                              \
          "Attraction (balls)"  attraction -root -mode balls                \n\
                                blaster -root                               \n\
                                bumps -root                                 \n\
                                xteevee -root                               \n\
+                               xanalogtv -root                             \n\
                                xspirograph -root                           \n\
                                nerverot -root                              \n\
 -          "NerveRot (dense)"  nerverot -root -count 1000                  \n\
                                barcode -root                               \n\
                                piecewise -root                             \n\
                                cloudlife -root                             \n\
-  color:                       bubbles -root                               \n\
+                  "FontGlide"  fontglide -root -page                       \n\
+       "FontGlide (scroller)"  fontglide -root -scroll                     \n\
+                               apple2 -root                                \n\
+                                bubbles -root                              \n\
+                               pong -root                                  \n\
 - default-n:                   webcollage -root                            \n\
 - default-n:  "WebCollage (whacked)"                                         \
                                webcollage -root -filter                      \
 @GL_KLUDGE@ GL:                        blocktube -root                             \n\
 @GL_KLUDGE@ GL:                        flipflop -root                              \n\
 @GL_KLUDGE@ GL:                        antspotlight -root                          \n\
-@GL_KLUDGE@ GL:                        polytopes -root                             \n\
 -         GL:                  glslideshow -root                           \n\
+@GL_KLUDGE@ GL:                        polytopes -root                             \n\
+@GL_KLUDGE@ GL:                        gleidescope -root                           \n\
+-         GL:                  mirrorblob -root                            \n\
+@GL_KLUDGE@ GL:            "MirrorBlob (color only)"                                 \
+                                mirrorblob -root -colour -no-texture       \n\
+@GL_KLUDGE@ GL:                        blinkbox -root                              \n\
                                                                              \
 -                              xdaliclock -root -builtin3 -cycle           \n\
 - default-n:                   xearth -nofork -nostars -ncolors 50           \
 ! colors would be all wrong.  "default-i" may also be used as a visual name
 ! (meaning, "-visual default -install") but you probably won't ever need
 ! to use that.
-!
-!
-! Some of the GL demos that SGI ships work with XScreenSaver; most don't.
-! XScreenSaver includes a program (not built or installed by default)
-! called "xscreensaver-sgigl".  To use the SGI demos with XScreenSaver,
-! build that program, and use it to launch the SGI demos.  For example,
-! on Irix 6.2, you can do this:
-!
-!     xscreensaver-sgigl /usr/demos/bin/ep -S
-!     xscreensaver-sgigl /usr/demos/bin/bongo
-!
-! On Irix 6.3, things have moved, so you need to do it like this:
-!
-!     xscreensaver-sgigl /usr/sbin/ep -S
-!
-! (But note that, on non-SGIs, the bundled "stonerview" hack is a decent
-! clone of "ep".  Yay!)
-!
-! You can also use the "ant" demo, but first you need to wrap a shell script
-! around it that cds to its home directory, so that it can find its files;
-! and also pass it the -S argument, to prevent it from forking.  What a mess!
-! Basically, the SGI demo writers went out of their way to make my life hell.
 
 
 
@@ -471,7 +459,7 @@ XScreenSaver.bourneShell:           /bin/sh
 *passwd.thermometer.width:     8
 
 *splash.heading.label:         XScreenSaver %s
-*splash.body.label:            Copyright © 1991-2002 by
+*splash.body.label:            Copyright © 1991-2003 by
 *splash.body2.label:           Jamie Zawinski <jwz@jwz.org>
 *splash.demo.label:            Settings
 *splash.help.label:            Help
@@ -624,6 +612,7 @@ XScreenSaver*doc.fontList:       *-helvetica-medium-r-*-*-*-100-*-*-*-iso8859-1
 *hacks.shadebobs.name:      ShadeBobs
 *hacks.ccurve.name:         C Curve
 *hacks.xteevee.name:        XTeeVee
+*hacks.xanalogtv.name:      XAnalogTV
 *hacks.xspirograph.name:    XSpiroGraph
 *hacks.nerverot.name:       NerveRot
 *hacks.webcollage.name:     WebCollage
@@ -657,6 +646,9 @@ XScreenSaver*doc.fontList:       *-helvetica-medium-r-*-*-*-100-*-*-*-iso8859-1
 *hacks.blocktube.name:      BlockTube
 *hacks.flipflop.name:       FlipFlop
 *hacks.antspotlight.name:   AntSpotlight
+*hacks.fontglide.name:      FontGlide
+*hacks.mirrorblob.name:     MirrorBlob
+*hacks.blinkbox.name:       BlinkBox
 
 ! obsolete, but still used by xscreensaver-demo-Xm.
 *hacks.documentation.isInstalled: True
index e40784b53211def37212ecdd3d1f5c0c9c492c7a..b6ec5815256055605809d6183850aabea59ce10b 100644 (file)
@@ -48,7 +48,7 @@
           \"Qix (transparent)\"        qix -root -count 4 -solid -transparent      \\n\
                \"Qix (linear)\"        qix -root -count 5 -solid -transparent        \
                                  -linear -segments 250 -size 100           \\n\
-- mono:           \"Qix (xor)\"        qix -root -linear -count 5 -size 200          \
+-                 \"Qix (xor)\"        qix -root -linear -count 5 -size 200          \
                                  -spread 30 -segments 75 -solid -xor       \\n\
                                                                              \
          \"Attraction (balls)\"        attraction -root -mode balls                \\n\
                                blaster -root                               \\n\
                                bumps -root                                 \\n\
                                xteevee -root                               \\n\
+                               xanalogtv -root                             \\n\
                                xspirograph -root                           \\n\
                                nerverot -root                              \\n\
 -          \"NerveRot (dense)\"        nerverot -root -count 1000                  \\n\
                                barcode -root                               \\n\
                                piecewise -root                             \\n\
                                cloudlife -root                             \\n\
-  color:                       bubbles -root                               \\n\
+                  \"FontGlide\"        fontglide -root -page                       \\n\
+       \"FontGlide (scroller)\"        fontglide -root -scroll                     \\n\
+                               apple2 -root                                \\n\
+                                bubbles -root                              \\n\
+                               pong -root                                  \\n\
 - default-n:                   webcollage -root                            \\n\
 - default-n:  \"WebCollage (whacked)\"                                       \
                                webcollage -root -filter                      \
           GL:                  blocktube -root                             \\n\
           GL:                  flipflop -root                              \\n\
           GL:                  antspotlight -root                          \\n\
-          GL:                  polytopes -root                             \\n\
 -         GL:                  glslideshow -root                           \\n\
+          GL:                  polytopes -root                             \\n\
+          GL:                  gleidescope -root                           \\n\
+-         GL:                  mirrorblob -root                            \\n\
+          GL:      \"MirrorBlob (color only)\"                               \
+                                mirrorblob -root -colour -no-texture       \\n\
+          GL:                  blinkbox -root                              \\n\
                                                                              \
 -                              xdaliclock -root -builtin3 -cycle           \\n\
 - default-n:                   xearth -nofork -nostars -ncolors 50           \
 "*passwd.passwdFont:           *-courier-medium-r-*-*-*-140-*-*-*-iso8859-1",
 "*passwd.thermometer.width:    8",
 "*splash.heading.label:                XScreenSaver %s",
-"*splash.body.label:           Copyright © 1991-2002 by",
+"*splash.body.label:           Copyright © 1991-2003 by",
 "*splash.body2.label:          Jamie Zawinski <jwz@jwz.org>",
 "*splash.demo.label:           Settings",
 "*splash.help.label:           Help",
 "*hacks.shadebobs.name:      ShadeBobs",
 "*hacks.ccurve.name:         C Curve",
 "*hacks.xteevee.name:        XTeeVee",
+"*hacks.xanalogtv.name:      XAnalogTV",
 "*hacks.xspirograph.name:    XSpiroGraph",
 "*hacks.nerverot.name:       NerveRot",
 "*hacks.webcollage.name:     WebCollage",
 "*hacks.blocktube.name:      BlockTube",
 "*hacks.flipflop.name:       FlipFlop",
 "*hacks.antspotlight.name:   AntSpotlight",
+"*hacks.fontglide.name:      FontGlide",
+"*hacks.mirrorblob.name:     MirrorBlob",
+"*hacks.blinkbox.name:       BlinkBox",
 "*hacks.documentation.isInstalled: True",
index 608e488c898e225617434fa03258e1d7a50f2cb2..396803755dee25987a778d125ecf71a98f858e57 100644 (file)
@@ -1433,8 +1433,8 @@ parse_command_line_into_parameters_1 (const char *filename,
 {
   GList *p;
   parameter *match = 0;
-  int which = -1;
-  int index = 0;
+  gint which = -1;
+  gint index = 0;
 
   for (p = parms; p; p = p->next)
     {
@@ -1502,11 +1502,11 @@ parse_command_line_into_parameters_1 (const char *filename,
       break;
     case BOOLEAN:
       if (which != 0 && which != 1) abort();
-      parameter_set_switch (match, (gpointer) which);
+      parameter_set_switch (match, GINT_TO_POINTER(which));
       break;
     case SELECT_OPTION:
       if (which != 1) abort();
-      parameter_set_switch (parent, (gpointer) index);
+      parameter_set_switch (parent, GINT_TO_POINTER(index));
       break;
     default:
       break;
@@ -1553,13 +1553,13 @@ parameter_set_switch (parameter *p, gpointer value)
     case BOOLEAN:
       {
         GtkToggleButton *b = GTK_TOGGLE_BUTTON (p->widget);
-        gtk_toggle_button_set_active (b, (int) value);
+        gtk_toggle_button_set_active (b, GPOINTER_TO_INT(value));
         break;
       }
     case SELECT:
       {
         gtk_option_menu_set_history (GTK_OPTION_MENU (p->widget),
-                                     (int) value);
+                                     GPOINTER_TO_INT(value));
         break;
       }
     default:
index 13033fa731ad5b914024003400f3aebe5c8f13ae..9c2f0ca8dc9b1dc9666f39527afef4ac4de9a917 100644 (file)
@@ -1578,7 +1578,6 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
   GList *menu_items = gtk_container_children (GTK_CONTAINER (widget->parent));
   int menu_index = 0;
   saver_mode new_mode;
-  int old_selected = p->selected_hack;
 
   while (menu_items)
     {
@@ -1592,17 +1591,12 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
   new_mode = mode_menu_order[menu_index];
 
   /* Keep the same list element displayed as before; except if we're
-     switching *to* "one screensaver" mode from any other mode, scroll
-     to and select "the one".
+     switching *to* "one screensaver" mode from any other mode, set
+     "the one" to be that which is currently selected.
    */
-  list_elt = -1;
+  list_elt = selected_list_element (s);
   if (new_mode == ONE_HACK)
-    list_elt = (p->selected_hack >= 0
-                ? s->hack_number_to_list_elt[p->selected_hack]
-                : -1);
-
-  if (list_elt < 0)
-    list_elt = selected_list_element (s);
+    p->selected_hack = s->list_elt_to_hack_number[list_elt];
 
   {
     saver_mode old_mode = p->mode;
@@ -1613,9 +1607,6 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
   }
 
   pref_changed_cb (widget, user_data);
-
-  if (old_selected != p->selected_hack)
-    abort();    /* dammit, not again... */
 }
 
 
@@ -2374,7 +2365,7 @@ populate_prefs_page (state *s)
 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
   THROTTLE (timeout);
   THROTTLE (cycle);
-  THROTTLE (passwd_timeout);
+  /* THROTTLE (passwd_timeout); */  /* GUI doesn't set this; leave it alone */
 # undef THROTTLE
 
 # define FMT_MINUTES(NAME,N) \
@@ -4540,9 +4531,11 @@ main (int argc, char **argv)
   free (window_title);
   window_title = 0;
 
+#ifdef HAVE_GTK2
   /* After picking the default size, allow -geometry to override it. */
   if (geom)
     gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
+#endif
 
   gtk_widget_show (s->toplevel_widget);
   init_icon (GTK_WIDGET (s->toplevel_widget)->window);  /* after `show' */
index 196e3bc59a494e98c463f926d307027dc6eb6496..cdb22b595d0ade04aa309caacd861e080f2bf9b7 100644 (file)
@@ -161,12 +161,14 @@ kerberos_lock_init (int argc, char **argv, Bool verbose_p)
    we are. Calling it ive_got_your_local_function_right_here_buddy()
    would have been rude.
  */
+#ifndef HAVE_DARWIN
 static int 
 key_to_key(char *user, char *instance, char *realm, char *passwd, C_Block key)
 {
   memcpy(key, passwd, sizeof(des_cblock));
   return (0);
 }
+#endif /* !HAVE_DARWIN */
 
 /* Called to see if the user's typed password is valid. We do this by asking
    the kerberos server for a ticket and checking to see if it gave us one.
diff --git a/driver/pdf2jpeg.m b/driver/pdf2jpeg.m
new file mode 100644 (file)
index 0000000..87e8903
--- /dev/null
@@ -0,0 +1,132 @@
+/* pdf2jpeg -- converts a PDF file to a JPEG file, using Cocoa
+ *
+ * Copyright (c) 2001, 2002, 2003 by Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ * Inspired by clues provided by Jan Kujawa and Jonathan Hendry.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (int argc, char** argv)
+{
+  const char *progname = argv[0];
+  const char *infile = 0, *outfile = 0;
+  double compression = 0.85;
+  int verbose = 0;
+  int i;
+
+  for (i = 1; i < argc; i++)
+    {
+      char c;
+      if (argv[i][0] == '-' && argv[i][1] == '-')
+        argv[i]++;
+      if (!strcmp (argv[i], "-q") ||
+          !strcmp (argv[i], "-qual") ||
+          !strcmp (argv[i], "-quality"))
+        {
+          int q;
+          if (1 != sscanf (argv[++i], " %d %c", &q, &c) ||
+              q < 5 || q > 100)
+            {
+              fprintf (stderr, "%s: quality must be 5 - 100 (%d)\n",
+                       progname, q);
+              goto USAGE;
+            }
+          compression = q / 100.0;
+        }
+      else if (!strcmp (argv[i], "-verbose"))
+        verbose++;
+      else if (!strcmp (argv[i], "-v") ||
+               !strcmp (argv[i], "-vv") ||
+               !strcmp (argv[i], "-vvv"))
+        verbose += strlen(argv[i])-1;
+      else if (argv[i][0] == '-')
+        {
+          fprintf (stderr, "%s: unknown option %s\n", progname, argv[i]);
+          goto USAGE;
+        }
+      else if (!infile)
+        infile  = argv[i];
+      else if (!outfile)
+        outfile = argv[i];
+      else
+        {
+        USAGE:
+          fprintf (stderr,
+                   "usage: %s [-verbose] [-quality NN] "
+                   "infile.pdf outfile.jpg\n",
+                   progname);
+          exit (1);
+        }
+    }
+
+  if (!infile || !outfile)
+    goto USAGE;
+
+
+  // Much of Cocoa needs one of these to be available.
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+  //Need an NSApp instance to make [NSImage TIFFRepresentation] work
+  NSApp = [NSApplication sharedApplication];
+  [NSApp autorelease];
+
+  if (verbose)
+    fprintf (stderr, "%s: reading %s...\n", progname, infile);
+
+  // Load the PDF file into an NSData object:
+  NSData *pdf_data = [NSData dataWithContentsOfFile:
+                               [NSString stringWithCString:infile]];
+
+  // Create an NSPDFImageRep from the data:
+  NSPDFImageRep *pdf_rep = [NSPDFImageRep imageRepWithData:pdf_data];
+
+  // Create an NSImage instance
+  NSImage *image = [[NSImage alloc] initWithSize:[pdf_rep size]];
+
+  // Draw the PDFImageRep in the NSImage
+  [image lockFocus];
+  [pdf_rep drawAtPoint:NSMakePoint(0.0,0.0)];
+  [image unlockFocus];
+
+  // Load the NSImage's contents into an NSBitmapImageRep:
+  NSBitmapImageRep *bit_rep = [NSBitmapImageRep
+                                imageRepWithData:[image TIFFRepresentation]];
+
+  // Write the bitmapImageRep to a JPEG file:
+  if (bit_rep == nil)
+    {
+      fprintf (stderr, "%s: error converting image?\n", argv[0]);
+      exit (1);
+    }
+
+  if (verbose)
+    fprintf (stderr, "%s: writing %s (%d%% quality)...\n",
+             progname, outfile, (int) (compression * 100));
+
+  NSDictionary *props = [NSDictionary
+                          dictionaryWithObject:
+                            [NSNumber numberWithFloat:compression]
+                          forKey:NSImageCompressionFactor];
+  NSData *jpeg_data = [bit_rep representationUsingType:NSJPEGFileType
+                               properties:props];
+
+  [jpeg_data writeToFile:
+               [NSString stringWithCString:outfile]
+             atomically:YES];
+  [image release];
+
+  [pool release];
+  exit (0);
+}
diff --git a/driver/pdf2jpeg.man b/driver/pdf2jpeg.man
new file mode 100644 (file)
index 0000000..9d80dd7
--- /dev/null
@@ -0,0 +1,43 @@
+.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.SH NAME
+pdf2jpeg - converts a PDF file to a JPEG file using Cocoa
+.SH SYNOPSIS
+.B pdf2jpeg
+[\--verbose] [\--quality \fINN\fP] infile.pdf outfile.jpg
+.SH DESCRIPTION
+This reads a PDF file (for example, as written by the
+.BR screencapture (1)
+program) and writes a JPEG file.
+.SH OPTIONS
+.I pdf2jpeg
+accepts the following options:
+.TP 4
+.B --verbose
+Print diagnostics.
+.TP 4
+.B --quality \fINN\fP
+JPEG compression factor.  Default 85%.
+.SH BUGS
+The input and output files must be files: pipes don't work.
+
+This program is Cocoa-specific, so it won't work on non-MacOS systems.
+
+This shouldn't need to be a part of the XScreenSaver distribution at
+all, but Apple is COMPLETELY INSANE and made
+.BR screencapture (1)
+only write PDFs, with no simple way to convert that to something
+less crazy.
+.SH SEE ALSO
+.BR screencapture (1),
+.BR xscreensaver\-getimage\-desktop (1)
+.SH COPYRIGHT
+Copyright \(co 2003 by Jamie Zawinski.  Permission to use, copy,
+modify, distribute, and sell this software and its documentation for
+any purpose is hereby granted without fee, provided that the above
+copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation.
+No representations are made about the suitability of this software for
+any purpose.  It is provided "as is" without express or implied
+warranty.
+.SH AUTHOR
+Jamie Zawinski <jwz@jwz.org>, 20-Oct-03.
index ff266911dbecfeecd9f9d9cd6c09ed455bdf763e..d8dd718b5965e843c1934d01d1720dab9c614dd6 100644 (file)
@@ -11,7 +11,7 @@
 .if n .sp 1
 .if t .sp .5
 ..
-.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11"
 .SH NAME
 xscreensaver-command - control a running xscreensaver process
 .SH SYNOPSIS
index 8acc3f858ac87f046cc9980d00f1bdceb3c9ee49..22c60d50f72ea622650096183b2c11e0e2c8265d 100644 (file)
@@ -11,7 +11,7 @@
 .if n .sp 1
 .if t .sp .5
 ..
-.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11"
 .SH NAME
 xscreensaver-demo - interactively control the background xscreensaver daemon
 .SH SYNOPSIS
diff --git a/driver/xscreensaver-getimage-desktop b/driver/xscreensaver-getimage-desktop
new file mode 100755 (executable)
index 0000000..f7d9e76
--- /dev/null
@@ -0,0 +1,184 @@
+#!/usr/bin/perl -w
+# Copyright © 2003 Jamie Zawinski <jwz@jwz.org>.
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation.  No representations are made about the suitability of this
+# software for any purpose.  It is provided "as is" without express or 
+# implied warranty.
+#
+# This program attempts to grab an image of the desktop, and then load
+# it on to the root window using the "xscreensaver-getimage-file"
+# program.  Various frame-grabbing programs are known, and the first
+# one found is used.
+#
+# NOTE:  This script is only used on MacOS X / XDarwin systems, because
+#        on those systems, it's necessary to use the "screencapture"
+#        program to get an image of the desktop -- the usual X11
+#        mechanism for grabbing the screen doesn't work on OSX.
+#
+# The various xscreensaver hacks that manipulate images ("slidescreen",
+# "jigsaw", etc.) get the image to manipulate by running the
+# "xscreensaver-getimage" program.
+#
+# "xscreensaver-getimage" will invoke this program, depending on the
+# value of the "grabDesktopImages" setting in the ~/.xscreensaver file
+# (or in /usr/lib/X11/app-defaults/XScreenSaver).
+#
+# Created: 20-Oct-2003.
+
+require 5;
+use diagnostics;
+use strict;
+
+my $progname = $0; $progname =~ s@.*/@@g;
+my $version = q{ $Revision: 1.1 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
+
+my @grabber   = ("screencapture", "-x");
+my @converter = ("pdf2jpeg");
+
+my $verbose = 0;
+my $use_stdout_p = 0;
+my $return_filename_p = 0;
+
+
+sub error {
+  ($_) = @_;
+  print STDERR "$progname: $_\n";
+  exit 1;
+}
+
+# returns the full path of the named program, or undef.
+#
+sub which {
+  my ($prog) = @_;
+  foreach (split (/:/, $ENV{PATH})) {
+    if (-x "$_/$prog") {
+      return $prog;
+    }
+  }
+  return undef;
+}
+
+sub check_path {
+  my $ok = 1;
+  foreach ($grabber[0], $converter[0]) {
+    if (! which ($_)) {
+      print STDERR "$progname: \"$_\" not found on \$PATH.\n";
+      $ok = 0;
+    }
+  }
+  exit (1) unless $ok;
+}
+
+
+sub grab_image {
+
+  check_path();
+
+  my $tmpdir = $ENV{TMPDIR};
+  $tmpdir = "/tmp" unless $tmpdir;
+
+  my $tmpfile = "$tmpdir/xssgrab.$$.pdf";
+  my @cmd     = (@grabber, $tmpfile);
+
+  unlink $tmpfile;
+
+  print STDERR "$progname: executing \"" . join(' ', @cmd) . "\"\n"
+    if ($verbose);
+  system (@cmd);
+
+  my @st = stat($tmpfile);
+  my $size = (@st ? $st[7] : 0);
+  if ($size <= 2048) {
+    unlink $tmpfile;
+    if ($size == 0) {
+      error "\"" . join(' ', @cmd) . "\" produced no data.";
+    } else {
+      error "\"" . join(' ', @cmd) . "\" produced only $size bytes.";
+    }
+  }
+
+  # Convert the PDF to a JPEG
+  {
+    my $jpgfile = $tmpfile;
+    $jpgfile =~ s/\.[^.]+$//;
+    $jpgfile .= ".jpg";
+
+    @cmd = (@converter, $tmpfile, $jpgfile);
+    push @cmd, "--verbose" if ($verbose);
+
+    print STDERR "$progname: executing \"" . join(' ', @cmd) . "\"\n"
+      if ($verbose);
+    system (@cmd);
+    unlink $tmpfile;
+    $tmpfile = $jpgfile;
+  }
+
+  @st = stat($tmpfile);
+  $size = (@st ? $st[7] : 0);
+  if ($size <= 2048) {
+    unlink $tmpfile;
+    if ($size == 0) {
+      error "\"" . join(' ', @cmd) . "\" produced no data.";
+    } else {
+      error "\"" . join(' ', @cmd) . "\" produced only $size bytes.";
+    }
+  }
+
+  if ($return_filename_p) {
+    print STDERR "$progname: wrote \"$tmpfile\"\n" if ($verbose);
+    print STDOUT "$tmpfile\n";
+
+  } elsif ($use_stdout_p) {
+    local *IN;
+    my $ppm = "";
+    my $reader = "djpeg $tmpfile";
+    $reader .= " 2>/dev/null" if ($verbose <= 1);
+    $reader .= " |";
+
+    open(IN, $reader) || error "reading $tmpfile: $!";
+    print STDERR "$progname: reading $tmpfile\n" if ($verbose > 1);
+    while (<IN>) { $ppm .= $_; }
+    close IN;
+    unlink $tmpfile;
+    print STDOUT $ppm;
+
+  } else {
+
+    @cmd = ("xscreensaver-getimage-file");
+    push @cmd, "--verbose" if ($verbose);
+    push @cmd, $tmpfile;
+
+    print STDERR "$progname: executing \"" . join(' ', @cmd) . "\"\n"
+      if ($verbose);
+    system (@cmd);
+
+    unlink $tmpfile;
+  }
+}
+
+
+sub usage {
+  print STDERR "usage: $progname [--verbose] [--name | --stdout]\n";
+  exit 1;
+}
+
+sub main {
+  while ($_ = $ARGV[0]) {
+    shift @ARGV;
+    if ($_ eq "--verbose") { $verbose++; }
+    elsif (m/^-v+$/) { $verbose += length($_)-1; }
+    elsif (m/^--?stdout$/) { $use_stdout_p = 1; }
+    elsif (m/^--?name$/)   { $return_filename_p = 1; }
+    elsif (m/^-./) { usage; }
+    else { usage; }
+  }
+
+  grab_image();
+}
+
+main;
+exit 0;
diff --git a/driver/xscreensaver-getimage-desktop.man b/driver/xscreensaver-getimage-desktop.man
new file mode 100644 (file)
index 0000000..1974525
--- /dev/null
@@ -0,0 +1,55 @@
+.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.SH NAME
+xscreensaver-getimage-desktop - put a desktop image on the root window
+.SH SYNOPSIS
+.B xscreensaver-getimage-desktop
+[\-display \fIhost:display.screen\fP] [\--verbose] [\--stdout]
+.SH DESCRIPTION
+The \fIxscreensaver\-getimage\-desktop\fP program is a helper program
+for the xscreensaver hacks that manipulate images.  Specifically, it
+is invoked by
+.BR xscreensaver\-getimage (1)
+as needed.  This is not a user-level command.
+
+This program is only used on MacOS X / XDarwin systems, because
+on those systems, it's necessary to use the
+.BR screencapture (1)
+program to get an image of the desktop -- the usual X11
+mechanism for grabbing the screen doesn't work on OSX.
+
+This script works by running
+.BR screencapture (1)
+to get a PDF, then converting it to a JPEG with
+.BR pdf2jpeg (1),
+then loading it onto the window with
+.BR xscreensaver\-getimage\-file (1).
+.SH OPTIONS
+.I xscreensaver-getimage-desktop
+accepts the following options:
+.TP 4
+.B --verbose
+Print diagnostics.
+.TP 4
+.B --stdout
+Instead of loading the image onto the root window, write it to stdout
+as a PBM file.
+.SH SEE ALSO
+.BR screencapture (1),
+.BR pdf2jpeg (1),
+.BR X (1),
+.BR xscreensaver (1),
+.BR xscreensaver\-demo (1),
+.BR xscreensaver\-getimage (1),
+.BR xscreensaver\-getimage\-file (1),
+.BR xscreensaver\-getimage\-video (1),
+.SH COPYRIGHT
+Copyright \(co 2003 by Jamie Zawinski.  Permission to use, copy,
+modify, distribute, and sell this software and its documentation for
+any purpose is hereby granted without fee, provided that the above
+copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation.
+No representations are made about the suitability of this software for
+any purpose.  It is provided "as is" without express or implied
+warranty.
+.SH AUTHOR
+Jamie Zawinski <jwz@jwz.org>, 20-Oct-03.
index 62deae30f1e3adf0066c12e689bb7ce915fa1f8f..92a53d9c83c013cdcb32f5e911552e24a522d933 100644 (file)
@@ -1,4 +1,4 @@
-.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11"
 .SH NAME
 xscreensaver-getimage-file - put a randomly-selected image on the root window
 .SH SYNOPSIS
index d4e9e865671a9c4235e715440a1b75ddfab5ebff..d35d0ca730476ead33a91473f070235d47e1fd26 100755 (executable)
@@ -29,7 +29,7 @@ use diagnostics;
 use strict;
 
 my $progname = $0; $progname =~ s@.*/@@g;
-my $version = q{ $Revision: 1.11 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
+my $version = q{ $Revision: 1.12 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
 
 my $verbose = 0;
 
@@ -55,7 +55,7 @@ my @programs = (
   "bttvgrab -d q -Q -l 1 -o ppm -f $tmpfile",  # BTTV
   "qcam > $tmpfile",                           # Connectix Qcam
   "gqcam -t PPM -d $tmpfile",                  # GTK+ Qcam clone
-  "streamer -a -s 768x576 -o $tmpfile",                # XawTV
+  "streamer -s 768x576 -o $tmpfile",           # XawTV
   "atitv snap $tmpfile",                       # ATI video capture card
 
   "grab -type ppm -format ntsc -source 1 " .
index 84ffbe13577e88c3998cc4aa26822973dae384f5..86c38230402b3c3e8b66be3a528b2778df891983 100644 (file)
@@ -1,4 +1,4 @@
-.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11"
 .SH NAME
 xscreensaver-getimage-video - put a video frame on the root window
 .SH SYNOPSIS
index b0ce3fa20ce7933eeec677160c949e61828bcff5..e46bc5c97262a6e9d55ce566c0142ec8c4b25228 100644 (file)
 #endif
 
 
+#ifdef __APPLE__
+  /* On MacOSX / XDarwin, the usual X11 mechanism of getting a screen shot
+     doesn't work, and we need to use an external program. */
+# define USE_EXTERNAL_SCREEN_GRABBER
+#endif
+
+
 #ifdef __GNUC__
  __extension__     /* shut up about "string length is greater than the length
                       ISO C89 compilers are required to support" when including
@@ -85,9 +92,14 @@ XtAppContext app;
 
 extern void grabscreen_verbose (void);
 
+typedef enum {
+  GRAB_DESK, GRAB_VIDEO, GRAB_FILE, GRAB_BARS
+} grab_type;
+
 
-#define GETIMAGE_VIDEO_PROGRAM "xscreensaver-getimage-video"
-#define GETIMAGE_FILE_PROGRAM  "xscreensaver-getimage-file"
+#define GETIMAGE_VIDEO_PROGRAM   "xscreensaver-getimage-video"
+#define GETIMAGE_FILE_PROGRAM    "xscreensaver-getimage-file"
+#define GETIMAGE_SCREEN_PROGRAM  "xscreensaver-getimage-desktop"
 
 const char *
 blurb (void)
@@ -269,6 +281,60 @@ compute_image_scaling (int src_w, int src_h,
 }
 
 
+/* Scales an XImage, modifying it in place.
+   This doesn't do dithering or smoothing, so it might have artifacts.
+   If out of memory, returns False, and the XImage will have been
+   destroyed and freed.
+ */
+static Bool
+scale_ximage (Screen *screen, Visual *visual,
+              XImage *ximage, int new_width, int new_height)
+{
+  Display *dpy = DisplayOfScreen (screen);
+  int depth = visual_depth (screen, visual);
+  int x, y;
+  double xscale, yscale;
+
+  XImage *ximage2 = XCreateImage (dpy, visual, depth,
+                                  ZPixmap, 0, 0,
+                                  new_width, new_height, 8, 0);
+  ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
+
+  if (!ximage2->data)
+    {
+      fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
+               progname,
+               ximage->width, ximage->height,
+               ximage2->width, ximage2->height);
+      if (ximage->data) free (ximage->data);
+      if (ximage2->data) free (ximage2->data);
+      ximage->data = 0;
+      ximage2->data = 0;
+      XDestroyImage (ximage);
+      XDestroyImage (ximage2);
+      return False;
+    }
+
+  /* Brute force scaling... */
+  xscale = (double) ximage->width  / ximage2->width;
+  yscale = (double) ximage->height / ximage2->height;
+  for (y = 0; y < ximage2->height; y++)
+    for (x = 0; x < ximage2->width; x++)
+      XPutPixel (ximage2, x, y,
+                 XGetPixel (ximage, x * xscale, y * yscale));
+
+  free (ximage->data);
+  ximage->data = 0;
+
+  (*ximage) = (*ximage2);
+
+  ximage2->data = 0;
+  XDestroyImage (ximage2);
+
+  return True;
+}
+
+
 #ifdef HAVE_GDK_PIXBUF
 
 /* Reads the given image file and renders it on the Drawable, using GDK.
@@ -869,59 +935,6 @@ read_jpeg_ximage (Screen *screen, Visual *visual, Drawable drawable,
 }
 
 
-/* Scales an XImage, modifying it in place.
-   If out of memory, returns False, and the XImage will have been
-   destroyed and freed.
- */
-static Bool
-scale_ximage (Screen *screen, Visual *visual,
-              XImage *ximage, int new_width, int new_height)
-{
-  Display *dpy = DisplayOfScreen (screen);
-  int depth = visual_depth (screen, visual);
-  int x, y;
-  double xscale, yscale;
-
-  XImage *ximage2 = XCreateImage (dpy, visual, depth,
-                                  ZPixmap, 0, 0,
-                                  new_width, new_height, 8, 0);
-  ximage2->data = (char *) calloc (ximage2->height, ximage2->bytes_per_line);
-
-  if (!ximage2->data)
-    {
-      fprintf (stderr, "%s: out of memory scaling %dx%d image to %dx%d\n",
-               progname,
-               ximage->width, ximage->height,
-               ximage2->width, ximage2->height);
-      if (ximage->data) free (ximage->data);
-      if (ximage2->data) free (ximage2->data);
-      ximage->data = 0;
-      ximage2->data = 0;
-      XDestroyImage (ximage);
-      XDestroyImage (ximage2);
-      return False;
-    }
-
-  /* Brute force scaling... */
-  xscale = (double) ximage->width  / ximage2->width;
-  yscale = (double) ximage->height / ximage2->height;
-  for (y = 0; y < ximage2->height; y++)
-    for (x = 0; x < ximage2->width; x++)
-      XPutPixel (ximage2, x, y,
-                 XGetPixel (ximage, x * xscale, y * yscale));
-
-  free (ximage->data);
-  ximage->data = 0;
-
-  (*ximage) = (*ximage2);
-
-  ximage2->data = 0;
-  XDestroyImage (ximage2);
-
-  return True;
-}
-
-
 /* Reads the given image file and renders it on the Drawable, using JPEG lib.
    Returns False if it fails.
  */
@@ -1058,7 +1071,7 @@ display_file (Screen *screen, Window window, Drawable drawable,
    to run.
  */
 static char *
-get_filename_1 (Screen *screen, const char *directory, Bool video_p,
+get_filename_1 (Screen *screen, const char *directory, grab_type type,
                 Bool verbose_p)
 {
   Display *dpy = DisplayOfScreen (screen);
@@ -1069,20 +1082,34 @@ get_filename_1 (Screen *screen, const char *directory, Bool video_p,
   char *av[20];
   int ac = 0;
 
-  if (!video_p)
+  switch (type)
     {
+    case GRAB_FILE:
       av[ac++] = GETIMAGE_FILE_PROGRAM;
       if (verbose_p)
         av[ac++] = "--verbose";
       av[ac++] = "--name";
       av[ac++] = (char *) directory;
-    }
-  else
-    {
+      break;
+
+    case GRAB_VIDEO:
       av[ac++] = GETIMAGE_VIDEO_PROGRAM;
       if (verbose_p)
         av[ac++] = "--verbose";
       av[ac++] = "--name";
+      break;
+
+# ifdef USE_EXTERNAL_SCREEN_GRABBER
+    case GRAB_DESK:
+      av[ac++] = GETIMAGE_SCREEN_PROGRAM;
+      if (verbose_p)
+        av[ac++] = "--verbose";
+      av[ac++] = "--name";
+      break;
+# endif
+
+    default:
+      abort();
     }
   av[ac] = 0;
 
@@ -1171,7 +1198,7 @@ get_filename_1 (Screen *screen, const char *directory, Bool video_p,
 static char *
 get_filename (Screen *screen, const char *directory, Bool verbose_p)
 {
-  return get_filename_1 (screen, directory, False, verbose_p);
+  return get_filename_1 (screen, directory, GRAB_FILE, verbose_p);
 }
 
 
@@ -1181,9 +1208,20 @@ get_filename (Screen *screen, const char *directory, Bool verbose_p)
 static char *
 get_video_filename (Screen *screen, Bool verbose_p)
 {
-  return get_filename_1 (screen, 0, True, verbose_p);
+  return get_filename_1 (screen, 0, GRAB_VIDEO, verbose_p);
 }
 
+/* Grabs a desktop image to a file, and returns a pathname to that file.
+   Delete that file when you are done with it (and free the string.)
+ */
+# ifdef USE_EXTERNAL_SCREEN_GRABBER
+static char *
+get_desktop_filename (Screen *screen, Bool verbose_p)
+{
+  return get_filename_1 (screen, 0, GRAB_DESK, verbose_p);
+}
+#endif /* USE_EXTERNAL_SCREEN_GRABBER */
+
 
 /* Grabs a video frame, and renders it on the Drawable.
    Returns False if it fails;
@@ -1218,6 +1256,118 @@ display_video (Screen *screen, Window window, Drawable drawable,
 }
 
 
+/* Grabs a desktop screen shot onto the window and the drawable.
+   If the window and drawable are not the same size, the image in
+   the drawable is scaled to fit.
+   Returns False if it fails.
+ */
+static Bool
+display_desktop (Screen *screen, Window window, Drawable drawable,
+                 Bool verbose_p)
+{
+# ifdef USE_EXTERNAL_SCREEN_GRABBER
+
+  Display *dpy = DisplayOfScreen (screen);
+  Bool top_p = top_level_window_p (screen, window);
+  char *filename;
+  Bool status;
+
+  if (top_p)
+    {
+      if (verbose_p)
+        fprintf (stderr, "%s: unmapping 0x%lx.\n", progname,
+                 (unsigned long) window);
+      XUnmapWindow (dpy, window);
+      XSync (dpy, False);
+    }
+
+  filename = get_desktop_filename (screen, verbose_p);
+
+  if (top_p)
+    {
+      if (verbose_p)
+        fprintf (stderr, "%s: mapping 0x%lx.\n", progname,
+                 (unsigned long) window);
+      XMapRaised (dpy, window);
+      XSync (dpy, False);
+    }
+
+  if (!filename)
+    {
+      if (verbose_p)
+        fprintf (stderr, "%s: desktop grab failed.\n", progname);
+      return False;
+    }
+
+  status = display_file (screen, window, drawable, filename, verbose_p);
+
+  if (unlink (filename))
+    {
+      char buf[512];
+      sprintf (buf, "%s: rm %.100s", progname, filename);
+      perror (buf);
+    }
+  else if (verbose_p)
+    fprintf (stderr, "%s: rm %s\n", progname, filename);
+
+  if (filename) free (filename);
+  return status;
+
+# else /* !USE_EXTERNAL_SCREEN_GRABBER */
+
+  Display *dpy = DisplayOfScreen (screen);
+  XGCValues gcv;
+  XWindowAttributes xgwa;
+  Window root;
+  int px, py;
+  unsigned int pw, ph, pbw, pd;
+  int srcx, srcy, destx, desty, w2, h2;
+
+  if (verbose_p)
+    {
+      fprintf (stderr, "%s: grabbing desktop image\n", progname);
+      grabscreen_verbose();
+    }
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+  XGetGeometry (dpy, drawable, &root, &px, &py, &pw, &ph, &pbw, &pd);
+
+  grab_screen_image_internal (screen, window);
+
+  compute_image_scaling (xgwa.width, xgwa.height,
+                         pw, ph, verbose_p,
+                         &srcx, &srcy, &destx, &desty, &w2, &h2);
+
+  if (pw == w2 && ph == h2)  /* it fits -- just copy server-side pixmaps */
+    {
+      GC gc = XCreateGC (dpy, drawable, 0, &gcv);
+      XCopyArea (dpy, window, drawable, gc,
+                 0, 0, xgwa.width, xgwa.height, 0, 0);
+      XFreeGC (dpy, gc);
+    }
+  else  /* size mismatch -- must scale client-side images to fit drawable */
+    {
+      XImage *ximage = XGetImage (dpy, window, 0, 0, xgwa.width, xgwa.height,
+                                  ~0L, ZPixmap);
+      GC gc;
+      if (!ximage ||
+          !scale_ximage (xgwa.screen, xgwa.visual, ximage, w2, h2))
+        return False;
+      gc = XCreateGC (dpy, drawable, 0, &gcv);
+      clear_drawable (screen, drawable);
+      XPutImage (dpy, drawable, gc, ximage, 
+                 srcx, srcy, destx, desty, ximage->width, ximage->height);
+      XDestroyImage (ximage);
+      XFreeGC (dpy, gc);
+    }
+
+  XSync (dpy, False);
+  return True;
+
+# endif /* !USE_EXTERNAL_SCREEN_GRABBER */
+}
+
+
 /* Grabs an image (from a file, video, or the desktop) and renders it on
    the Drawable.  If `file' is specified, always use that file.  Otherwise,
    select randomly, based on the other arguments.
@@ -1233,9 +1383,10 @@ get_image (Screen *screen,
            const char *file)
 {
   Display *dpy = DisplayOfScreen (screen);
-  enum { do_desk, do_video, do_image, do_bars } which = do_bars;
+  grab_type which = GRAB_BARS;
   int count = 0;
   struct stat st;
+  const char *file_prop = 0;
 
   if (! drawable_window_p (dpy, window))
     {
@@ -1271,6 +1422,10 @@ get_image (Screen *screen,
 
 # if !(defined(HAVE_GDK_PIXBUF) || defined(HAVE_JPEGLIB))
   image_p = False;    /* can't load images from files... */
+#  ifdef USE_EXTERNAL_SCREEN_GRABBER
+  desk_p = False;     /* ...or from desktops grabbed to files. */
+#  endif
+
   if (file)
     {
       fprintf (stderr,
@@ -1302,12 +1457,15 @@ get_image (Screen *screen,
 #  error Error!  This file definitely needs vroot.h!
 # endif
 
-  /* We can grab desktop images if:
+  /* We can grab desktop images (using the normal X11 method) if:
        - the window is the real root window;
        - the window is a toplevel window.
-     We cannot grab desktop images if:
+     We cannot grab desktop images that way if:
        - the window is a non-top-level window.
+
+     Using the MacOS X way, desktops are just like loaded image files.
    */
+# ifndef USE_EXTERNAL_SCREEN_GRABBER
   if (desk_p)
     {
       if (!top_level_window_p (screen, window))
@@ -1319,6 +1477,7 @@ get_image (Screen *screen,
                      progname, (unsigned int) window);
         }
     }
+# endif /* !USE_EXTERNAL_SCREEN_GRABBER */
 
   count = 0;
   if (desk_p)  count++;
@@ -1326,16 +1485,16 @@ get_image (Screen *screen,
   if (image_p) count++;
 
   if (count == 0)
-    which = do_bars;
+    which = GRAB_BARS;
   else
     {
       int i = 0;
       while (1)  /* loop until we get one that's permitted */
         {
           which = (random() % 3);
-          if (which == do_desk  && desk_p)  break;
-          if (which == do_video && video_p) break;
-          if (which == do_image && image_p) break;
+          if (which == GRAB_DESK  && desk_p)  break;
+          if (which == GRAB_VIDEO && video_p) break;
+          if (which == GRAB_FILE  && image_p) break;
           if (++i > 200) abort();
         }
     }
@@ -1343,12 +1502,12 @@ get_image (Screen *screen,
 
   /* If we're to search a directory to find an image file, do so now.
    */
-  if (which == do_image && !file)
+  if (which == GRAB_FILE && !file)
     {
       file = get_filename (screen, dir, verbose_p);
       if (!file)
         {
-          which = do_bars;
+          which = GRAB_BARS;
           if (verbose_p)
             fprintf (stderr, "%s: no image files found.\n", progname);
         }
@@ -1356,48 +1515,54 @@ get_image (Screen *screen,
 
   /* Now actually render something.
    */
-  if (which == do_bars)
-    {
-      XWindowAttributes xgwa;
-    COLORBARS:
-      if (verbose_p)
-        fprintf (stderr, "%s: drawing colorbars.\n", progname);
-      XGetWindowAttributes (dpy, window, &xgwa);
-      draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
-                      0, 0, 0, 0);
-      XSync (dpy, False);
-    }
-  else if (which == do_desk)
+  switch (which)
     {
-      GC gc;
-      XGCValues gcv;
-      XWindowAttributes xgwa;
+    case GRAB_BARS:
+      {
+        XWindowAttributes xgwa;
+      COLORBARS:
+        if (verbose_p)
+          fprintf (stderr, "%s: drawing colorbars.\n", progname);
+        XGetWindowAttributes (dpy, window, &xgwa);
+        draw_colorbars (screen, xgwa.visual, drawable, xgwa.colormap,
+                        0, 0, 0, 0);
+        XSync (dpy, False);
+      }
+      break;
 
-      if (verbose_p)
-        {
-          fprintf (stderr, "%s: grabbing desktop image\n", progname);
-          grabscreen_verbose();
-        }
-      gc = XCreateGC (dpy, drawable, 0, &gcv);
-      XGetWindowAttributes (dpy, window, &xgwa);
-      grab_screen_image (screen, window);
-      XCopyArea (dpy, window, drawable, gc,
-                 0, 0, xgwa.width, xgwa.height, 0, 0);
-      XFreeGC (dpy, gc);
-      XSync (dpy, False);
-    }
-  else if (which == do_image)
-    {
+    case GRAB_DESK:
+      if (! display_desktop (screen, window, drawable, verbose_p))
+        goto COLORBARS;
+      file_prop = "desktop";
+      break;
+
+    case GRAB_FILE:
       if (! display_file (screen, window, drawable, file, verbose_p))
         goto COLORBARS;
-    }
-  else if (which == do_video)
-    {
+      file_prop = file;
+      break;
+
+    case GRAB_VIDEO:
       if (! display_video (screen, window, drawable, verbose_p))
         goto COLORBARS;
+      file_prop = "video";
+      break;
+
+    default:
+      abort();
+      break;
     }
-  else
-    abort();
+
+  {
+    Atom a = XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME, False);
+    if (file_prop && *file_prop)
+      XChangeProperty (dpy, window, a, XA_STRING, 8, PropModeReplace, 
+                       (unsigned char *) file_prop, strlen(file_prop));
+    else
+      XDeleteProperty (dpy, window, a);
+  }
+
+  XSync (dpy, False);
 }
 
 
index 4b773b7b357b4ab2e30b47372faa62dca046c857..f83aed8933bd3c64e04eb86a2bb72508339819d6 100644 (file)
@@ -1,18 +1,23 @@
-.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11"
 .SH NAME
 xscreensaver-getimage - put some randomly-selected image on the root window
 .SH SYNOPSIS
 .B xscreensaver-getimage
-[\-display \fIhost:display.screen\fP] [\--verbose] window-id
+[\-display \fIhost:display.screen\fP] [\--verbose] window-id [pixmap-id]
 .SH DESCRIPTION
 The \fIxscreensaver\-getimage\fP program is a helper program for the
 xscreensaver hacks that manipulate images.  This is not a user-level
 command.
 
 This program selects a random image, and puts it on the specified
-window.  This image might be a snapshot of the desktop; or a frame
-captured from the system's video input; or a randomly-selected image
-from disk.
+window or pixmap.  This image might be a snapshot of the desktop; or
+a frame captured from the system's video input; or a randomly-selected
+image from disk.
+
+If only a window ID is specified, the image will be painted there.
+If both a window ID and a pixmap ID are specified, then the image will
+be painted on the pixmap; and the window \fImay\fP be modified as a
+side-effect.
 .SH OPTIONS
 .I xscreensaver-getimage
 reads the \fI~/.xscreensaver\fP file for configuration information.
@@ -43,12 +48,9 @@ assumed to be images.
 If none of the three options are set to True, then video
 colorbars will be displayed instead.
 .SH BUGS
-If the target window is not the root window, then this program 
-will \fIalways\fP grab a desktop image.  This means that if an 
-image-manipulating xscreensaver mode is run in a normal window,
-it will always operate on a desktop image; it's only when it is
-run in \fI-root\fP mode (or when invoked by xscreensaver) that
-it will load image files or grab video.
+When grabbing desktop images, the \fIwindow\fP argument will be unmapped
+and have its contents modified, causing flicker.  (This does not happen
+when loading image files or video frames.)
 .SH SEE ALSO
 .BR X (1),
 .BR xscreensaver (1)
index de42a100c9980e0ae03fc68f087f24d4c392dc27..09665b665de43e7c4cff488992de83749510e1a2 100644 (file)
@@ -1667,6 +1667,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
     {
       if (until_idle_p)
        {
+          if (p->mode == DONT_BLANK)
+            {
+              clientmessage_response(si, window, True,
+                         "ACTIVATE ClientMessage received in DONT_BLANK mode.",
+                                     "screen blanking is currently disabled.");
+              return False;
+            }
+
          clientmessage_response(si, window, False,
                                 "ACTIVATE ClientMessage received.",
                                 "activating.");
@@ -1771,6 +1779,14 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
       char buf2 [255];
       long which = event->xclient.data.l[1];
 
+      if (p->mode == DONT_BLANK)
+        {
+          clientmessage_response(si, window, True,
+                           "SELECT ClientMessage received in DONT_BLANK mode.",
+                                 "screen blanking is currently disabled.");
+          return False;
+        }
+
       sprintf (buf, "SELECT %ld ClientMessage received.", which);
       sprintf (buf2, "activating (%ld).", which);
       clientmessage_response (si, window, False, buf, buf2);
@@ -1890,7 +1906,11 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                              "not compiled with support for locking.",
                              "locking not enabled.");
 #else /* !NO_LOCKING */
-      if (si->locking_disabled_p)
+      if (p->mode == DONT_BLANK)
+        clientmessage_response(si, window, True,
+                             "LOCK ClientMessage received in DONT_BLANK mode.",
+                               "screen blanking is currently disabled.");
+      else if (si->locking_disabled_p)
        clientmessage_response (si, window, True,
                      "LOCK ClientMessage received, but locking is disabled.",
                              "locking not enabled.");
index 9fb8f6b9a051dd1da0c0dfffbb4764d734565028..7d627791665d6df45735b6d56129cf3b986e59ef 100644 (file)
@@ -11,7 +11,7 @@
 .if n .sp 1
 .if t .sp .5
 ..
-.TH XScreenSaver 1 "07-Sep-2003 (4.13)" "X Version 11"
+.TH XScreenSaver 1 "25-Oct-2003 (4.14)" "X Version 11"
 .SH NAME
 xscreensaver - extensible screen saver framework, plus locking
 .SH SYNOPSIS
index 5ae3dad696386de5acdd7519948b40d3f3b1fb7d..c818b1fc2f322e5bb05bd2126c9da70f4be6d5a4 100644 (file)
@@ -73,7 +73,8 @@ UTIL_OBJS     = $(UTILS_BIN)/alpha.o $(UTILS_BIN)/colors.o \
                  $(UTILS_BIN)/spline.o $(UTILS_BIN)/usleep.o \
                  $(UTILS_BIN)/visual.o $(UTILS_BIN)/logo.o \
                  $(UTILS_BIN)/yarandom.o $(UTILS_BIN)/erase.o \
-                 $(UTILS_SRC)/xshm.o $(UTILS_SRC)/xdbe.o
+                 $(UTILS_BIN)/xshm.o $(UTILS_BIN)/xdbe.o \
+                 $(UTILS_BIN)/colorbars.o
 
 SRCS           = attraction.c blitspin.c bouboule.c braid.c bubbles.c \
                  bubbles-default.c decayscreen.c deco.c drift.c flag.c \
@@ -97,7 +98,8 @@ SRCS          = attraction.c blitspin.c bouboule.c braid.c bubbles.c \
                  xpm-pixmap.c webcollage-helper.c twang.c apollonian.c \
                  euler2d.c juggle.c polyominoes.c thornbird.c fluidballs.c \
                  anemone.c halftone.c metaballs.c eruption.c popsquares.c \
-                 barcode.c piecewise.c cloudlife.c
+                 barcode.c piecewise.c cloudlife.c fontglide.c apple2.c \
+                 apple2-main.c analogtv.c xanalogtv.c pong.c
 SCRIPTS                = vidwhacker webcollage ljlatest
 
 OBJS           = attraction.o blitspin.o bouboule.o braid.o bubbles.o \
@@ -122,7 +124,8 @@ OBJS                = attraction.o blitspin.o bouboule.o braid.o bubbles.o \
                  xpm-pixmap.o webcollage-helper.o twang.o apollonian.o \
                  euler2d.o juggle.o polyominoes.o thornbird.o fluidballs.o \
                  anemone.o halftone.o metaballs.o eruption.o popsquares.o \
-                 barcode.o piecewise.o cloudlife.o
+                 barcode.o piecewise.o cloudlife.o fontglide.o apple2.o \
+                 apple2-main.o analogtv.o xanalogtv.o pong.o
 
 NEXES          = attraction blitspin bouboule braid bubbles decayscreen deco \
                  drift flag flame forest vines galaxy grav greynetic halo \
@@ -140,6 +143,7 @@ NEXES               = attraction blitspin bouboule braid bubbles decayscreen deco \
                  whirlygig speedmine vermiculate twang apollonian euler2d \
                  juggle polyominoes thornbird  fluidballs anemone halftone \
                  metaballs eruption popsquares barcode piecewise cloudlife \
+                 fontglide apple2 xanalogtv pong \
                  @JPEG_EXES@
 SEXES          = sonar
 JPEG_EXES      = webcollage-helper
@@ -155,7 +159,7 @@ XSHM_OBJS   = $(UTILS_BIN)/xshm.o
 XDBE_OBJS      = $(UTILS_BIN)/xdbe.o
 
 HDRS           = bubbles.h screenhack.h xlockmore.h xlockmoreI.h automata.h \
-                 bumps.h xpm-pixmap.h
+                 bumps.h xpm-pixmap.h apple2.h analogtv.h
 MEN            = anemone.man ant.man apollonian.man attraction.man \
                  blaster.man blitspin.man bouboule.man braid.man bsod.man \
                  bubbles.man bumps.man ccurve.man compass.man coral.man \
@@ -181,7 +185,8 @@ MEN         = anemone.man ant.man apollonian.man attraction.man \
                  worm.man xflame.man xjack.man xlyap.man xmatrix.man \
                  xrayswarm.man xspirograph.man xsublim.man xteevee.man \
                  zoom.man halftone.man eruption.man metaballs.man \
-                 barcode.man piecewise.man cloudlife.man ljlatest.man
+                 barcode.man piecewise.man cloudlife.man ljlatest.man \
+                 fontglide.man apple2.man xanalogtv.man pong.man
 STAR           = *
 EXTRAS         = README Makefile.in xlock_23.h xml2man.pl .gdbinit \
                  config/README \
@@ -505,6 +510,8 @@ COL         = $(COLOR_OBJS)
 SHM            = $(XSHM_OBJS)
 DBE            = $(XDBE_OBJS)
 BARS           = $(UTILS_BIN)/colorbars.o $(LOGO)
+ATV             = analogtv.o $(SHM)
+APPLE2          = apple2.o $(ATV)
 
 CC_HACK                = $(CC) $(LDFLAGS)
 
@@ -624,8 +631,14 @@ interference:  interference.o      $(HACK_OBJS) $(COL) $(SHM) $(DBE)
 truchet:        truchet.o      $(HACK_OBJS) $(COL)
        $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(COL) $(HACK_LIBS)
 
-bsod:          bsod.o          $(HACK_OBJS) $(GRAB) $(SHM) $(XPM)
-       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(GRAB) $(SHM) $(XPM) $(XPM_LIBS)
+bsod:          bsod.o          $(HACK_OBJS) $(GRAB) $(APPLE2) $(XPM)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(GRAB) $(APPLE2) $(XPM) $(XPM_LIBS)
+
+apple2:                apple2.o apple2-main.o  $(HACK_OBJS) $(ATV) $(GRAB)
+       $(CC_HACK) -o $@ $@.o   apple2-main.o $(HACK_OBJS) $(ATV) $(GRAB) $(XPM_LIBS)
+
+xanalogtv:     xanalogtv.o     $(HACK_OBJS) $(ATV) $(GRAB) $(XPM)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(ATV) $(GRAB) $(XPM) $(XPM_LIBS) $(HACK_LIBS)
 
 distort:       distort.o       $(HACK_OBJS) $(GRAB) $(SHM)
        $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(GRAB) $(SHM) $(HACK_LIBS)
@@ -750,6 +763,13 @@ piecewise: piecewise.o     $(HACK_OBJS) $(COL) $(DBE)
 cloudlife:     cloudlife.o     $(HACK_OBJS) $(COL) $(DBE)
        $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(COL) $(DBE) $(HACK_LIBS)
 
+fontglide:     fontglide.o     $(HACK_OBJS) $(DBE)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(DBE) $(HACK_LIBS)
+
+pong:  pong.o  $(HACK_OBJS) $(ATV) $(GRAB) $(XPM)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(ATV) $(GRAB) $(XPM) $(XPM_LIBS) $(HACK_LIBS)
+
+
 # The rules for those hacks which follow the `xlockmore' API.
 #
 
@@ -881,6 +901,8 @@ webcollage-helper: webcollage-helper.o
 #
 # DO NOT DELETE: updated by make distdepend
 
+analogtv.o: $(srcdir)/analogtv.h
+analogtv.o: ../config.h
 anemone.o: ../config.h
 anemone.o: $(srcdir)/screenhack.h
 ant.o: $(srcdir)/automata.h
@@ -892,6 +914,14 @@ apollonian.o: ../config.h
 apollonian.o: $(srcdir)/screenhack.h
 apollonian.o: $(srcdir)/xlockmore.h
 apollonian.o: $(srcdir)/xlockmoreI.h
+apple2-main.o: $(srcdir)/analogtv.h
+apple2-main.o: $(srcdir)/apple2.h
+apple2-main.o: ../config.h
+apple2-main.o: $(srcdir)/screenhack.h
+apple2.o: $(srcdir)/analogtv.h
+apple2.o: $(srcdir)/apple2.h
+apple2.o: ../config.h
+apple2.o: $(srcdir)/screenhack.h
 attraction.o: ../config.h
 attraction.o: $(srcdir)/screenhack.h
 barcode.o: ../config.h
@@ -910,6 +940,8 @@ braid.o: ../config.h
 braid.o: $(srcdir)/screenhack.h
 braid.o: $(srcdir)/xlockmore.h
 braid.o: $(srcdir)/xlockmoreI.h
+bsod.o: $(srcdir)/analogtv.h
+bsod.o: $(srcdir)/apple2.h
 bsod.o: ../config.h
 bsod.o: $(srcdir)/images/amiga.xpm
 bsod.o: $(srcdir)/images/atari.xbm
@@ -1034,6 +1066,8 @@ flow.o: $(srcdir)/xlockmore.h
 flow.o: $(srcdir)/xlockmoreI.h
 fluidballs.o: ../config.h
 fluidballs.o: $(srcdir)/screenhack.h
+fontglide.o: ../config.h
+fontglide.o: $(srcdir)/screenhack.h
 forest.o: ../config.h
 forest.o: $(srcdir)/screenhack.h
 forest.o: $(srcdir)/xlockmore.h
@@ -1190,6 +1224,9 @@ polyominoes.o: ../config.h
 polyominoes.o: $(srcdir)/screenhack.h
 polyominoes.o: $(srcdir)/xlockmore.h
 polyominoes.o: $(srcdir)/xlockmoreI.h
+pong.o: $(srcdir)/analogtv.h
+pong.o: ../config.h
+pong.o: $(srcdir)/screenhack.h
 popsquares.o: ../config.h
 popsquares.o: $(srcdir)/screenhack.h
 pyro.o: ../config.h
@@ -1281,6 +1318,10 @@ worm.o: ../config.h
 worm.o: $(srcdir)/screenhack.h
 worm.o: $(srcdir)/xlockmore.h
 worm.o: $(srcdir)/xlockmoreI.h
+xanalogtv.o: $(srcdir)/analogtv.h
+xanalogtv.o: ../config.h
+xanalogtv.o: $(srcdir)/screenhack.h
+xanalogtv.o: $(srcdir)/xpm-pixmap.h
 xflame.o: ../config.h
 xflame.o: $(srcdir)/images/bob.xbm
 xflame.o: $(srcdir)/screenhack.h
diff --git a/hacks/analogtv.c b/hacks/analogtv.c
new file mode 100644 (file)
index 0000000..86761b7
--- /dev/null
@@ -0,0 +1,2138 @@
+/* analogtv, Copyright (c) 2003 Trevor Blackwell <tlb@tlb.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/*
+
+  This is the code for implementing something that looks like a conventional
+  analog TV set. It simulates the following characteristics of standard
+  televisions:
+
+  - Realistic rendering of a composite video signal
+  - Compression & brightening on the right, as the scan gets truncated
+    because of saturation in the flyback transformer
+  - Blooming of the picture dependent on brightness
+  - Overscan, cutting off a few pixels on the left side.
+  - Colored text in mixed graphics/text modes
+
+  It's amazing how much it makes your high-end monitor look like at large
+  late-70s TV. All you need is to put a big "Solid State" logo in curly script
+  on it and you'd be set.
+
+  In DirectColor or TrueColor modes, it generates pixel values
+  directly from RGB values it calculates across each scan line. In
+  PseudoColor mode, it consider each possible pattern of 5 preceding
+  bit values in each possible position modulo 4 and allocates a color
+  for each. A few things, like the brightening on the right side as
+  the horizontal trace slows down, aren't done in PseudoColor.
+
+  I originally wrote it for the Apple ][ emulator, and generalized it
+  here for use with a rewrite of xteevee and possibly others.
+
+  A maxim of technology is that failures reveal underlying mechanism.
+  A good way to learn how something works is to push it to failure.
+  The way it fails will usually tell you a lot about how it works. The
+  corollary for this piece of software is that in order to emulate
+  realistic failures of a TV set, it has to work just like a TV set.
+  So there is lots of DSP-style emulation of analog circuitry for
+  things like color decoding, H and V sync following, and more. In
+  2003, computers are just fast enough to do this at television signal
+  rates. We use a 14 MHz sample rate here, so we can do on the order
+  of a couple hundred instructions per sample and keep a good frame
+  rate.
+
+  Trevor Blackwell <tlb@tlb.org>
+*/
+
+#include <X11/Xutil.h>
+#include <X11/Intrinsic.h>
+#include <assert.h>
+#include "utils.h"
+#include "resources.h"
+#include "analogtv.h"
+#include "yarandom.h"
+#include "grabscreen.h"
+
+/* #define DEBUG 1 */
+
+#ifdef DEBUG
+/* only works on linux + freebsd */
+#include <machine/cpufunc.h>
+
+#define DTIME_DECL u_int64_t dtimes[100]; int n_dtimes
+#define DTIME_START do {n_dtimes=0; dtimes[n_dtimes++]=rdtsc(); } while (0)
+#define DTIME dtimes[n_dtimes++]=rdtsc()
+#define DTIME_SHOW(DIV) \
+do { \
+  double _dtime_div=(DIV); \
+  printf("time/%.1f: ",_dtime_div); \
+  for (i=1; i<n_dtimes; i++) \
+    printf(" %0.9f",(dtimes[i]-dtimes[i-1])* 1e-9 / _dtime_div); \
+  printf("\n"); \
+} while (0)
+
+#else
+
+#define DTIME_DECL
+#define DTIME_START  do { } while (0)
+#define DTIME  do { } while (0)
+#define DTIME_SHOW(DIV)  do { } while (0)
+
+#endif
+
+
+#define FASTRND (fastrnd = fastrnd*1103515245+12345)
+
+static void analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
+                                 int start, int end);
+
+static double puramp(analogtv *it, double tc, double start, double over)
+{
+  double pt=it->powerup-start;
+  double ret;
+  if (pt<0.0) return 0.0;
+  if (pt>900.0 || pt/tc>8.0) return 1.0;
+
+  ret=(1.0-exp(-pt/tc))*over;
+  if (ret>1.0) return 1.0;
+  return ret*ret;
+}
+
+/*
+  There are actual standards for TV signals: NTSC and RS-170A describe the
+  system used in the US and Japan. Europe has slightly different systems, but
+  not different enough to make substantially different screensaver displays.
+  Sadly, the standards bodies don't do anything so useful as publish the spec on
+  the web. Best bets are:
+
+    http://www.ee.washington.edu/conselec/CE/kuhn/ntsc/95x4.htm
+    http://www.ntsc-tv.com/ntsc-index-02.htm
+
+  In DirectColor or TrueColor modes, it generates pixel values directly from RGB
+  values it calculates across each scan line. In PseudoColor mode, it consider
+  each possible pattern of 5 preceding bit values in each possible position
+  modulo 4 and allocates a color for each. A few things, like the brightening on
+  the right side as the horizontal trace slows down, aren't done in PseudoColor.
+
+  I'd like to add a bit of visible retrace, but it conflicts with being able to
+  bitcopy the image when fast scrolling. After another couple of CPU
+  generations, we could probably regenerate the whole image from scratch every
+  time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
+  looks too slow.
+*/
+
+/* localbyteorder is MSBFirst or LSBFirst */
+static int localbyteorder;
+static const double float_low8_ofs=8388608.0;
+static int float_extraction_works;
+
+typedef union {
+  float f;
+  int i;
+} float_extract_t;
+
+static void
+analogtv_init(void)
+{
+  int i;
+  {
+    unsigned int localbyteorder_loc = (MSBFirst<<24) | (LSBFirst<<0);
+    localbyteorder=*(char *)&localbyteorder_loc;
+  }
+
+  if (1) {
+    float_extract_t fe;
+    int ans;
+
+    float_extraction_works=1;
+    for (i=0; i<256*4; i++) {
+      fe.f=float_low8_ofs+(double)i;
+      ans=fe.i&0x3ff;
+      if (ans != i) {
+#ifdef DEBUG
+        printf("Float extraction failed for %d => %d\n",i,ans);
+#endif
+        float_extraction_works=0;
+        break;
+      }
+    }
+  }
+
+}
+
+void
+analogtv_set_defaults(analogtv *it, char *prefix)
+{
+  char buf[256];
+
+  sprintf(buf,"%sTVTint",prefix);
+  it->tint_control = get_float_resource(buf,"TVTint");
+  sprintf(buf,"%sTVColor",prefix);
+  it->color_control = get_float_resource(buf,"TVColor")/100.0;
+  sprintf(buf,"%sTVBrightness",prefix);
+  it->brightness_control = get_float_resource(buf,"TVBrightness") / 100.0;
+  sprintf(buf,"%sTVContrast",prefix);
+  it->contrast_control = get_float_resource(buf,"TVContrast") / 100.0;
+  it->height_control = 1.0;
+  it->width_control = 1.0;
+  it->squish_control = 0.0;
+  it->powerup=1000.0;
+
+  it->hashnoise_rpm=0;
+  it->hashnoise_on=0;
+  it->hashnoise_enable=1;
+
+  it->horiz_desync=frand(10.0)-5.0;
+  it->squeezebottom=frand(5.0)-1.0;
+
+#ifdef DEBUG
+  printf("analogtv: prefix=%s\n",prefix);
+  printf("  use: shm=%d cmap=%d color=%d\n",
+         it->use_shm,it->use_cmap,it->use_color);
+  printf("  controls: tint=%g color=%g brightness=%g contrast=%g\n",
+         it->tint_control, it->color_control, it->brightness_control,
+         it->contrast_control);
+  printf("  freq_error %g: %g %d\n",
+         it->freq_error, it->freq_error_inc, it->flutter_tint);
+  printf("  desync: %g %d\n",
+         it->horiz_desync, it->flutter_horiz_desync);
+  printf("  hashnoise rpm: %g\n",
+         it->hashnoise_rpm);
+  printf("  vis: %d %d %d\n",
+         it->visclass, it->visbits, it->visdepth);
+  printf("  shift: %d-%d %d-%d %d-%d\n",
+         it->red_invprec,it->red_shift,
+         it->green_invprec,it->green_shift,
+         it->blue_invprec,it->blue_shift);
+  printf("  size: %d %d  %d %d  xrepl=%d\n",
+         it->usewidth, it->useheight,
+         it->screen_xo, it->screen_yo, it->xrepl);
+
+  printf("    ANALOGTV_V=%d\n",ANALOGTV_V);
+  printf("    ANALOGTV_TOP=%d\n",ANALOGTV_TOP);
+  printf("    ANALOGTV_VISLINES=%d\n",ANALOGTV_VISLINES);
+  printf("    ANALOGTV_BOT=%d\n",ANALOGTV_BOT);
+  printf("    ANALOGTV_H=%d\n",ANALOGTV_H);
+  printf("    ANALOGTV_SYNC_START=%d\n",ANALOGTV_SYNC_START);
+  printf("    ANALOGTV_BP_START=%d\n",ANALOGTV_BP_START);
+  printf("    ANALOGTV_CB_START=%d\n",ANALOGTV_CB_START);
+  printf("    ANALOGTV_PIC_START=%d\n",ANALOGTV_PIC_START);
+  printf("    ANALOGTV_PIC_LEN=%d\n",ANALOGTV_PIC_LEN);
+  printf("    ANALOGTV_FP_START=%d\n",ANALOGTV_FP_START);
+  printf("    ANALOGTV_PIC_END=%d\n",ANALOGTV_PIC_END);
+  printf("    ANALOGTV_HASHNOISE_LEN=%d\n",ANALOGTV_HASHNOISE_LEN);
+
+#endif
+
+}
+
+extern Bool mono_p; /* shoot me */
+
+void
+analogtv_free_image(analogtv *it)
+{
+  if (it->image) {
+    if (it->use_shm) {
+#ifdef HAVE_XSHM_EXTENSION
+      destroy_xshm_image(it->dpy, it->image, &it->shm_info);
+#endif
+    } else {
+      XDestroyImage(it->image);
+    }
+    it->image=NULL;
+  }
+}
+
+void
+analogtv_alloc_image(analogtv *it)
+{
+  if (it->use_shm) {
+#ifdef HAVE_XSHM_EXTENSION
+    it->image=create_xshm_image(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0,
+                                &it->shm_info, it->usewidth, it->useheight);
+#endif
+    if (!it->image) it->use_shm=0;
+  }
+  if (!it->image) {
+    it->image = XCreateImage(it->dpy, it->xgwa.visual, it->xgwa.depth, ZPixmap, 0, 0,
+                             it->usewidth, it->useheight, 8, 0);
+    it->image->data = (char *)calloc(it->image->height, it->image->bytes_per_line);
+  }
+}
+
+
+void
+analogtv_configure(analogtv *it)
+{
+  int oldwidth=it->usewidth;
+  int oldheight=it->useheight;
+  int wlim,hlim,ohlim;
+
+  hlim=it->xgwa.height;
+  if (hlim<ANALOGTV_VISLINES) hlim = ANALOGTV_VISLINES;
+
+  wlim = it->xgwa.width;
+  if (wlim<300) wlim = 300;
+
+  /* require 3:4 aspect ratio */
+  if (wlim > hlim*4/3) wlim=hlim*4/3;
+  if (hlim > wlim*3/4) hlim=wlim*3/4;
+
+  /* height must be a multiple of VISLINES */
+  ohlim=hlim;
+  hlim = (hlim/ANALOGTV_VISLINES)*ANALOGTV_VISLINES;
+
+  /* Scale width proportionally */
+  wlim=wlim*hlim/ohlim;
+
+  {
+    FILE *fp=fopen("/tmp/analogtv.size","w");
+    fprintf(fp,"wlim=%d hlim=%d\n", wlim, hlim);
+    fclose(fp);
+  }
+
+  /* Most times this doesn't change */
+  if (wlim != oldwidth || hlim != oldheight) {
+
+    it->usewidth=wlim;
+    it->useheight=hlim;
+
+    it->xrepl=1+it->usewidth/640;
+    if (it->xrepl>2) it->xrepl=2;
+    it->subwidth=it->usewidth/it->xrepl;
+
+    analogtv_free_image(it);
+    analogtv_alloc_image(it);
+  }
+
+  it->screen_xo = (it->xgwa.width-it->usewidth)/2;
+  it->screen_yo = (it->xgwa.height-it->useheight)/2;
+  it->need_clear=1;
+}
+
+void
+analogtv_reconfigure(analogtv *it)
+{
+  XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
+  analogtv_configure(it);
+}
+
+analogtv *
+analogtv_allocate(Display *dpy, Window window)
+{
+  XGCValues gcv;
+  analogtv *it=NULL;
+  int i;
+
+  analogtv_init();
+
+  it=(analogtv *)calloc(1,sizeof(analogtv));
+  it->dpy=dpy;
+  it->window=window;
+
+  it->shrinkpulse=-1;
+
+  it->n_colors=0;
+
+#ifdef HAVE_XSHM_EXTENSION
+  it->use_shm=1;
+#else
+  it->use_shm=0;
+#endif
+
+  XGetWindowAttributes (it->dpy, it->window, &it->xgwa);
+
+  it->screen=it->xgwa.screen;
+  it->colormap=it->xgwa.colormap;
+  it->visclass=it->xgwa.visual->class;
+  it->visbits=it->xgwa.visual->bits_per_rgb;
+  it->visdepth=it->xgwa.depth;
+  if (it->visclass == TrueColor || it->visclass == DirectColor) {
+    if (get_integer_resource ("use_cmap", "Integer")) {
+      it->use_cmap=1;
+    } else {
+      it->use_cmap=0;
+    }
+    it->use_color=!mono_p;
+  }
+  else if (it->visclass == PseudoColor || it->visclass == StaticColor) {
+    it->use_cmap=1;
+    it->use_color=!mono_p;
+  }
+  else {
+    it->use_cmap=1;
+    it->use_color=0;
+  }
+
+  it->red_mask=it->xgwa.visual->red_mask;
+  it->green_mask=it->xgwa.visual->green_mask;
+  it->blue_mask=it->xgwa.visual->blue_mask;
+  it->red_shift=it->red_invprec=-1;
+  it->green_shift=it->green_invprec=-1;
+  it->blue_shift=it->blue_invprec=-1;
+  if (!it->use_cmap) {
+    /* Is there a standard way to do this? Does this handle all cases? */
+    int shift, prec;
+    for (shift=0; shift<32; shift++) {
+      for (prec=1; prec<16 && prec<32-shift; prec++) {
+        unsigned long mask=(0xffffUL>>(16-prec)) << shift;
+        if (it->red_shift<0 && mask==it->red_mask)
+          it->red_shift=shift, it->red_invprec=16-prec;
+        if (it->green_shift<0 && mask==it->green_mask)
+          it->green_shift=shift, it->green_invprec=16-prec;
+        if (it->blue_shift<0 && mask==it->blue_mask)
+          it->blue_shift=shift, it->blue_invprec=16-prec;
+      }
+    }
+    if (it->red_shift<0 || it->green_shift<0 || it->blue_shift<0) {
+      if (0) fprintf(stderr,"Can't figure out color space\n");
+      goto fail;
+    }
+
+    for (i=0; i<ANALOGTV_CV_MAX; i++) {
+      int intensity=pow(i/256.0, 0.8)*65535.0; /* gamma correction */
+      if (intensity>65535) intensity=65535;
+      it->red_values[i]=((intensity>>it->red_invprec)<<it->red_shift);
+      it->green_values[i]=((intensity>>it->green_invprec)<<it->green_shift);
+      it->blue_values[i]=((intensity>>it->blue_invprec)<<it->blue_shift);
+    }
+
+  }
+
+  gcv.background=get_pixel_resource("background", "Background",
+                                    it->dpy, it->colormap);
+
+  it->gc = XCreateGC(it->dpy, it->window, GCBackground, &gcv);
+  XSetWindowBackground(it->dpy, it->window, gcv.background);
+  XClearWindow(dpy,window);
+
+  analogtv_configure(it);
+
+  return it;
+
+ fail:
+  if (it) free(it);
+  return NULL;
+}
+
+void
+analogtv_release(analogtv *it)
+{
+  if (it->image) {
+    if (it->use_shm) {
+#ifdef HAVE_XSHM_EXTENSION
+      destroy_xshm_image(it->dpy, it->image, &it->shm_info);
+#endif
+    } else {
+      XDestroyImage(it->image);
+    }
+    it->image=NULL;
+  }
+  if (it->gc) XFreeGC(it->dpy, it->gc);
+  it->gc=NULL;
+  if (it->n_colors) XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
+  it->n_colors=0;
+}
+
+
+/*
+  First generate the I and Q reference signals, which we'll multiply
+  the input signal by to accomplish the demodulation. Normally they
+  are shifted 33 degrees from the colorburst. I think this was convenient
+  for inductor-capacitor-vacuum tube implementation.
+
+  The tint control, FWIW, just adds a phase shift to the chroma signal,
+  and the color control controls the amplitude.
+
+  In text modes (colormode==0) the system disabled the color burst, and no
+  color was detected by the monitor.
+
+  freq_error gives a mismatch between the built-in oscillator and the
+  TV's colorbust. Some II Plus machines seemed to occasionally get
+  instability problems -- the crystal oscillator was a single
+  transistor if I remember correctly -- and the frequency would vary
+  enough that the tint would change across the width of the screen.
+  The left side would be in correct tint because it had just gotten
+  resynchronized with the color burst.
+
+  If we're using a colormap, set it up.
+*/
+int
+analogtv_set_demod(analogtv *it)
+{
+  int y_levels=10,i_levels=5,q_levels=5;
+
+  /*
+    In principle, we might be able to figure out how to adjust the
+    color map frame-by-frame to get some nice color bummage. But I'm
+    terrified of changing the color map because we'll get flashing.
+
+    I can hardly believe we still have to deal with colormaps. They're
+    like having NEAR PTRs: an enormous hassle for the programmer just
+    to save on memory. They should have been deprecated by 1995 or
+    so. */
+
+ cmap_again:
+  if (it->use_cmap && !it->n_colors) {
+
+    if (it->n_colors) {
+      XFreeColors(it->dpy, it->colormap, it->colors, it->n_colors, 0L);
+      it->n_colors=0;
+    }
+
+    {
+      int yli,qli,ili;
+      for (yli=0; yli<y_levels; yli++) {
+        for (ili=0; ili<i_levels; ili++) {
+          for (qli=0; qli<q_levels; qli++) {
+            double interpy,interpi,interpq;
+            double levelmult=700.0;
+            int r,g,b;
+            XColor col;
+
+            interpy=100.0 * ((double)yli/y_levels);
+            interpi=50.0 * (((double)ili-(0.5*i_levels))/(double)i_levels);
+            interpq=50.0 * (((double)qli-(0.5*q_levels))/(double)q_levels);
+
+            r=(int)((interpy + 1.04*interpi + 0.624*interpq)*levelmult);
+            g=(int)((interpy - 0.276*interpi - 0.639*interpq)*levelmult);
+            b=(int)((interpy - 1.105*interpi + 1.729*interpq)*levelmult);
+            if (r<0) r=0;
+            if (r>65535) r=65535;
+            if (g<0) g=0;
+            if (g>65535) g=65535;
+            if (b<0) b=0;
+            if (b>65535) b=65535;
+
+#ifdef DEBUG
+            printf("%0.2f %0.2f %0.2f => %02x%02x%02x\n",
+                   interpy, interpi, interpq,
+                   r/256,g/256,b/256);
+#endif
+
+            col.red=r;
+            col.green=g;
+            col.blue=b;
+            col.pixel=0;
+            if (!XAllocColor(it->dpy, it->colormap, &col)) {
+              if (q_levels > y_levels*4/12)
+                q_levels--;
+              else if (i_levels > y_levels*5/12)
+                i_levels--;
+              else
+                y_levels--;
+
+              if (y_levels<2)
+                return -1;
+              goto cmap_again;
+            }
+            it->colors[it->n_colors++]=col.pixel;
+          }
+        }
+      }
+
+      it->cmap_y_levels=y_levels;
+      it->cmap_i_levels=i_levels;
+      it->cmap_q_levels=q_levels;
+    }
+  }
+
+  return 0;
+}
+
+#if 0
+unsigned int
+analogtv_line_signature(analogtv_input *input, int lineno)
+{
+  int i;
+  char *origsignal=&input->signal[(lineno+input->vsync)
+                                  %ANALOGTV_V][input->line_hsync[lineno]];
+  unsigned int hash=0;
+
+  /* probably lame */
+  for (i=0; i<ANALOGTV_PIC_LEN; i++) {
+    int c=origsignal[i];
+    hash = hash + (hash<<17) + c;
+  }
+
+  hash += input->line_hsync[lineno];
+  hash ^= hash >> 2;
+  /*
+  hash += input->hashnoise_times[lineno];
+  hash ^= hash >> 2;
+  */
+
+  return hash;
+}
+#endif
+
+
+/* Here we model the analog circuitry of an NTSC television.
+   Basically, it splits the signal into 3 signals: Y, I and Q. Y
+   corresponds to luminance, and you get it by low-pass filtering the
+   input signal to below 3.57 MHz.
+
+   I and Q are the in-phase and quadrature components of the 3.57 MHz
+   subcarrier. We get them by multiplying by cos(3.57 MHz*t) and
+   sin(3.57 MHz*t), and low-pass filtering. Because the eye has less
+   resolution in some colors than others, the I component gets
+   low-pass filtered at 1.5 MHz and the Q at 0.5 MHz. The I component
+   is approximately orange-blue, and Q is roughly purple-green. See
+   http://www.ntsc-tv.com for details.
+
+   We actually do an awful lot to the signal here. I suspect it would
+   make sense to wrap them all up together by calculating impulse
+   response and doing FFT convolutions.
+
+*/
+
+static void
+analogtv_ntsc_to_yiq(analogtv *it, int lineno, double *signal,
+                     int start, int end)
+{
+  enum {MAXDELAY=32};
+  int i;
+  double *sp;
+  int phasecorr=(signal-it->rx_signal)&3;
+  struct analogtv_yiq_s *yiq;
+  int colormode;
+  double agclevel=it->agclevel;
+  double brightadd=it->brightness_control*100.0 - ANALOGTV_BLACK_LEVEL;
+  double delay[MAXDELAY+ANALOGTV_PIC_LEN], *dp;
+  double multiq2[4];
+
+  {
+
+    double cb_i=(it->line_cb_phase[lineno][(2+phasecorr)&3]-
+                 it->line_cb_phase[lineno][(0+phasecorr)&3])/16.0;
+    double cb_q=(it->line_cb_phase[lineno][(3+phasecorr)&3]-
+                 it->line_cb_phase[lineno][(1+phasecorr)&3])/16.0;
+
+    colormode = (cb_i * cb_i + cb_q * cb_q) > 2.8;
+
+    if (colormode) {
+      double tint_i = -cos((103 + it->color_control)*3.1415926/180);
+      double tint_q = sin((103 + it->color_control)*3.1415926/180);
+
+      multiq2[0] = (cb_i*tint_i - cb_q*tint_q) * it->color_control;
+      multiq2[1] = (cb_q*tint_i + cb_i*tint_q) * it->color_control;
+      multiq2[2]=-multiq2[0];
+      multiq2[3]=-multiq2[1];
+    }
+  }
+
+#if 0
+  if (lineno==100) {
+    printf("multiq = [%0.3f %0.3f %0.3f %0.3f] ",
+           it->multiq[60],it->multiq[61],it->multiq[62],it->multiq[63]);
+    printf("it->line_cb_phase = [%0.3f %0.3f %0.3f %0.3f]\n",
+           it->line_cb_phase[lineno][0],it->line_cb_phase[lineno][1],
+           it->line_cb_phase[lineno][2],it->line_cb_phase[lineno][3]);
+    printf("multiq2 = [%0.3f %0.3f %0.3f %0.3f]\n",
+           multiq2[0],multiq2[1],multiq2[2],multiq2[3]);
+  }
+#endif
+
+  dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
+  for (i=0; i<5; i++) dp[i]=0.0;
+
+  assert(start>=0);
+  assert(end < ANALOGTV_PIC_LEN+10);
+
+  dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
+  for (i=0; i<24; i++) dp[i]=0.0;
+  for (i=start, yiq=it->yiq+start, sp=signal+start;
+       i<end;
+       i++, dp--, yiq++, sp++) {
+
+    /* Now filter them. These are infinite impulse response filters
+       calculated by the script at
+       http://www-users.cs.york.ac.uk/~fisher/mkfilter. This is
+       fixed-point integer DSP, son. No place for wimps. We do it in
+       integer because you can count on integer being faster on most
+       CPUs. We care about speed because we need to recalculate every
+       time we blink text, and when we spew random bytes into screen
+       memory. This is roughly 16.16 fixed point arithmetic, but we
+       scale some filter values up by a few bits to avoid some nasty
+       precision errors. */
+
+    /* Filter Y with a 4-pole low-pass Butterworth filter at 3.5 MHz
+       with an extra zero at 3.5 MHz, from
+       mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
+       Delay about 2 */
+
+    dp[0] = sp[0] * 0.0469904257251935 * agclevel;
+    dp[8] = (+1.0*(dp[6]+dp[0])
+             +4.0*(dp[5]+dp[1])
+             +7.0*(dp[4]+dp[2])
+             +8.0*(dp[3])
+             -0.0176648*dp[12]
+             -0.4860288*dp[10]);
+    yiq->y = dp[8] + brightadd;
+  }
+
+  if (colormode) {
+    dp=delay+ANALOGTV_PIC_LEN-MAXDELAY;
+    for (i=0; i<27; i++) dp[i]=0.0;
+
+    for (i=start, yiq=it->yiq+start, sp=signal+start;
+         i<end;
+         i++, dp--, yiq++, sp++) {
+      double sig=*sp;
+
+      /* Filter I and Q with a 3-pole low-pass Butterworth filter at
+         1.5 MHz with an extra zero at 3.5 MHz, from
+         mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 -Z 2.5000000000e-01 -l
+         Delay about 3.
+      */
+
+      dp[0] = sig*multiq2[i&3] * 0.0833333333333;
+      yiq->i=dp[8] = (dp[5] + dp[0]
+                      +3.0*(dp[4] + dp[1])
+                      +4.0*(dp[3] + dp[2])
+                      -0.3333333333 * dp[10]);
+
+      dp[16] = sig*multiq2[(i+3)&3] * 0.0833333333333;
+      yiq->q=dp[24] = (dp[16+5] + dp[16+0]
+                       +3.0*(dp[16+4] + dp[16+1])
+                       +4.0*(dp[16+3] + dp[16+2])
+                       -0.3333333333 * dp[24+2]);
+    }
+  } else {
+    for (i=start, yiq=it->yiq+start; i<end; i++, yiq++) {
+      yiq->i = yiq->q = 0.0;
+    }
+  }
+}
+
+void
+analogtv_setup_teletext(analogtv_input *input)
+{
+  int x,y;
+  int teletext=ANALOGTV_BLACK_LEVEL;
+
+  /* Teletext goes in line 21. But I suspect there are other things
+     in the vertical retrace interval */
+
+  for (y=19; y<22; y++) {
+    for (x=ANALOGTV_PIC_START; x<ANALOGTV_PIC_END; x++) {
+      if ((x&7)==0) {
+        teletext=(random()&1) ? ANALOGTV_WHITE_LEVEL : ANALOGTV_BLACK_LEVEL;
+      }
+      input->signal[y][x]=teletext;
+    }
+  }
+}
+
+void
+analogtv_setup_frame(analogtv *it)
+{
+  int i,x,y;
+
+  it->redraw_all=0;
+
+  if (it->flutter_horiz_desync) {
+    /* Horizontal sync during vertical sync instability. */
+    it->horiz_desync += -0.10*(it->horiz_desync-3.0) +
+      ((int)(random()&0xff)-0x80) *
+      ((int)(random()&0xff)-0x80) *
+      ((int)(random()&0xff)-0x80) * 0.000001;
+  }
+
+  for (i=0; i<ANALOGTV_V; i++) {
+    it->hashnoise_times[i]=0;
+  }
+
+  if (it->hashnoise_enable && !it->hashnoise_on) {
+    if (random()%10000==0) {
+      it->hashnoise_on=1;
+      it->shrinkpulse=random()%ANALOGTV_V;
+    }
+  }
+  if (random()%1000==0) {
+    it->hashnoise_on=0;
+  }
+  if (it->hashnoise_on) {
+    it->hashnoise_rpm += (15000.0 - it->hashnoise_rpm)*0.05 +
+      ((int)(random()%2000)-1000)*0.1;
+  } else {
+    it->hashnoise_rpm -= 100 + 0.01*it->hashnoise_rpm;
+    if (it->hashnoise_rpm<0.0) it->hashnoise_rpm=0.0;
+  }
+  if (it->hashnoise_rpm >= 0.0) {
+    int hni;
+    int hnc=it->hashnoise_counter; /* in 24.8 format */
+
+    /* Convert rpm of a 16-pole motor into dots in 24.8 format */
+    hni = (int)(ANALOGTV_V * ANALOGTV_H * 256.0 /
+                (it->hashnoise_rpm * 16.0 / 60.0 / 60.0));
+
+    while (hnc < (ANALOGTV_V * ANALOGTV_H)<<8) {
+      y=(hnc>>8)/ANALOGTV_H;
+      x=(hnc>>8)%ANALOGTV_H;
+
+      if (x>0 && x<ANALOGTV_H - ANALOGTV_HASHNOISE_LEN) {
+        it->hashnoise_times[y]=x;
+      }
+      hnc += hni + (int)(random()%65536)-32768;
+    }
+    hnc -= (ANALOGTV_V * ANALOGTV_H)<<8;
+  }
+
+  it->agclevel = 1.0/it->rx_signal_level;
+
+
+#ifdef DEBUG2
+  printf("filter: ");
+  for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
+    printf(" %0.3f",it->ghostfir[i]);
+  }
+  printf(" siglevel=%g agc=%g\n", siglevel, it->agclevel);
+#endif
+}
+
+void
+analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi)
+{
+  int i,lineno,vsync;
+  char *sig;
+
+  int synclevel = do_ssavi ? ANALOGTV_WHITE_LEVEL : ANALOGTV_SYNC_LEVEL;
+
+  for (lineno=0; lineno<ANALOGTV_V; lineno++) {
+    vsync=lineno>=3 && lineno<7;
+
+    sig=input->signal[lineno];
+
+    i=ANALOGTV_SYNC_START;
+    if (vsync) {
+      while (i<ANALOGTV_BP_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
+      while (i<ANALOGTV_H) sig[i++]=synclevel;
+    } else {
+      while (i<ANALOGTV_BP_START) sig[i++]=synclevel;
+      while (i<ANALOGTV_PIC_START) sig[i++]=ANALOGTV_BLANK_LEVEL;
+      while (i<ANALOGTV_FP_START) sig[i++]=ANALOGTV_BLACK_LEVEL;
+    }
+    while (i<ANALOGTV_H) sig[i++]=ANALOGTV_BLANK_LEVEL;
+
+    if (do_cb) {
+      /* 9 cycles of colorburst */
+      for (i=ANALOGTV_CB_START; i<ANALOGTV_CB_START+36; i+=4) {
+        sig[i+1] += ANALOGTV_CB_LEVEL;
+        sig[i+3] -= ANALOGTV_CB_LEVEL;
+      }
+    }
+  }
+}
+
+void
+analogtv_sync(analogtv *it)
+{
+  int cur_hsync=it->cur_hsync;
+  int cur_vsync=it->cur_vsync;
+  int lineno;
+  int i,j;
+  double osc,filt;
+  double *sp;
+  double cbfc=1.0/128.0;
+
+  sp = it->rx_signal + lineno*ANALOGTV_H + cur_hsync;
+  for (i=-32; i<32; i++) {
+    lineno = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
+    sp = it->rx_signal + lineno*ANALOGTV_H;
+    filt=0.0;
+    for (j=0; j<ANALOGTV_H; j+=ANALOGTV_H/16) {
+      filt += sp[j];
+    }
+    filt *= it->agclevel;
+
+    osc = (double)(ANALOGTV_V+i)/(double)ANALOGTV_V;
+
+    if (osc >= 1.05+0.0002 * filt) break;
+  }
+  cur_vsync = (cur_vsync + i + ANALOGTV_V) % ANALOGTV_V;
+
+  for (lineno=0; lineno<ANALOGTV_V; lineno++) {
+
+    if (lineno>5 && lineno<ANALOGTV_V-3) { /* ignore vsync interval */
+
+      sp = it->rx_signal + ((lineno + cur_vsync + ANALOGTV_V)%ANALOGTV_V
+                            )*ANALOGTV_H + cur_hsync;
+      for (i=-8; i<8; i++) {
+        osc = (double)(ANALOGTV_H+i)/(double)ANALOGTV_H;
+        filt=(sp[i-3]+sp[i-2]+sp[i-1]+sp[i]) * it->agclevel;
+
+        if (osc >= 1.005 + 0.0001*filt) break;
+      }
+      cur_hsync = (cur_hsync + i + ANALOGTV_H) % ANALOGTV_H;
+    }
+
+    it->line_hsync[lineno]=(cur_hsync + ANALOGTV_PIC_START +
+                            ANALOGTV_H) % ANALOGTV_H;
+
+    /* Now look for the colorburst, which is a few cycles after the H
+       sync pulse, and store its phase.
+       The colorburst is 9 cycles long, and we look at the middle 5
+       cycles.
+    */
+
+    if (lineno>15) {
+      sp = it->rx_signal + lineno*ANALOGTV_H + (cur_hsync&~3);
+      for (i=ANALOGTV_CB_START+8; i<ANALOGTV_CB_START+36-8; i++) {
+        it->cb_phase[i&3] = it->cb_phase[i&3]*(1.0-cbfc) +
+          sp[i]*it->agclevel*cbfc;
+      }
+    }
+
+    {
+      double tot=0.1;
+      double cbgain;
+
+      for (i=0; i<4; i++) {
+        tot += it->cb_phase[i]*it->cb_phase[i];
+      }
+      cbgain = 32.0/sqrt(tot);
+
+      for (i=0; i<4; i++) {
+        it->line_cb_phase[lineno][i]=it->cb_phase[i]*cbgain;
+      }
+    }
+
+#ifdef DEBUG
+    if (0) printf("hs=%d cb=[%0.3f %0.3f %0.3f %0.3f]\n",
+                  cur_hsync,
+                  it->cb_phase[0], it->cb_phase[1],
+                  it->cb_phase[2], it->cb_phase[3]);
+#endif
+
+    /* if (random()%2000==0) cur_hsync=random()%ANALOGTV_H; */
+  }
+
+  it->cur_hsync = cur_hsync;
+  it->cur_vsync = cur_vsync;
+}
+
+static double
+analogtv_levelmult(analogtv *it, int level)
+{
+  static double levelfac[3]={-7.5, 5.5, 24.5};
+  return (40.0 + levelfac[level]*puramp(it, 3.0, 6.0, 1.0))/256.0;
+}
+
+static int
+analogtv_level(analogtv *it, int y, int ytop, int ybot)
+{
+  int level;
+  if (ybot-ytop>=7) {
+    if (y==ytop || y==ybot-1) level=0;
+    else if (y==ytop+1 || y==ybot-2) level=1;
+    else level=2;
+  }
+  else if (ybot-ytop>=5) {
+    if (y==ytop || y==ybot-1) level=0;
+    else level=2;
+  }
+  else if (ybot-ytop>=3) {
+    if (y==ytop) level=0;
+    else level=2;
+  }
+  else {
+    level=2;
+  }
+  return level;
+}
+
+static void
+analogtv_blast_imagerow(analogtv *it,
+                        float *rgbf, float *rgbf_end,
+                        int ytop, int ybot)
+{
+  int i,j,x,y;
+  float *rpf;
+  char *level_copyfrom[3];
+  int xrepl=it->xrepl;
+  for (i=0; i<3; i++) level_copyfrom[i]=NULL;
+
+  for (y=ytop; y<ybot; y++) {
+    int level=analogtv_level(it, y, ytop, ybot);
+    char *rowdata;
+
+    rowdata=it->image->data + y*it->image->bytes_per_line;
+
+    /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes to show
+       why standard graphics sw has to be fast, or else people will have to
+       work around it and risk incompatibility. The quickdraw folks
+       understood this. The other answer would be for X11 to have fewer
+       formats for bitm.. oh, never mind. If neither of these cases work
+       (they probably cover 99% of setups) it falls back on the Xlib
+       routines. */
+
+    if (level_copyfrom[level]) {
+      memcpy(rowdata, level_copyfrom[level], it->image->bytes_per_line);
+    }
+    else {
+      double levelmult=analogtv_levelmult(it, level);
+      level_copyfrom[level] = rowdata;
+
+      if (0) {
+      }
+      else if (it->image->format==ZPixmap &&
+               it->image->bits_per_pixel==32 &&
+               sizeof(unsigned int)==4 &&
+               it->image->byte_order==localbyteorder) {
+        /* int is more likely to be 32 bits than long */
+        unsigned int *pixelptr=(unsigned int *)rowdata;
+        unsigned int pix;
+
+        for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
+          int ntscri=rpf[0]*levelmult;
+          int ntscgi=rpf[1]*levelmult;
+          int ntscbi=rpf[2]*levelmult;
+          if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
+          if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
+          if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
+          pix = (it->red_values[ntscri] |
+                 it->green_values[ntscgi] |
+                 it->blue_values[ntscbi]);
+          pixelptr[0] = pix;
+          if (xrepl>=2) {
+            pixelptr[1] = pix;
+            if (xrepl>=3) pixelptr[2] = pix;
+          }
+          pixelptr+=xrepl;
+        }
+      }
+      else if (it->image->format==ZPixmap &&
+               it->image->bits_per_pixel==16 &&
+               sizeof(unsigned short)==2 &&
+               float_extraction_works &&
+               it->image->byte_order==localbyteorder) {
+        unsigned short *pixelptr=(unsigned short *)rowdata;
+        double r2,g2,b2;
+        float_extract_t r1,g1,b1;
+        unsigned short pix;
+
+        for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
+          r2=rpf[0]; g2=rpf[1]; b2=rpf[2];
+          r1.f=r2 * levelmult+float_low8_ofs;
+          g1.f=g2 * levelmult+float_low8_ofs;
+          b1.f=b2 * levelmult+float_low8_ofs;
+          pix = (it->red_values[r1.i & 0x3ff] |
+                 it->green_values[g1.i & 0x3ff] |
+                 it->blue_values[b1.i & 0x3ff]);
+          pixelptr[0] = pix;
+          if (xrepl>=2) {
+            pixelptr[1] = pix;
+            if (xrepl>=3) pixelptr[2] = pix;
+          }
+          pixelptr+=xrepl;
+        }
+      }
+      else if (it->image->format==ZPixmap &&
+               it->image->bits_per_pixel==16 &&
+               sizeof(unsigned short)==2 &&
+               it->image->byte_order==localbyteorder) {
+        unsigned short *pixelptr=(unsigned short *)rowdata;
+        unsigned short pix;
+
+        for (rpf=rgbf; rpf!=rgbf_end; rpf+=3) {
+          int r1=rpf[0] * levelmult;
+          int g1=rpf[1] * levelmult;
+          int b1=rpf[2] * levelmult;
+          if (r1>=ANALOGTV_CV_MAX) r1=ANALOGTV_CV_MAX-1;
+          if (g1>=ANALOGTV_CV_MAX) g1=ANALOGTV_CV_MAX-1;
+          if (b1>=ANALOGTV_CV_MAX) b1=ANALOGTV_CV_MAX-1;
+          pix = it->red_values[r1] | it->green_values[g1] | it->blue_values[b1];
+          pixelptr[0] = pix;
+          if (xrepl>=2) {
+            pixelptr[1] = pix;
+            if (xrepl>=3) pixelptr[2] = pix;
+          }
+          pixelptr+=xrepl;
+        }
+      }
+      else {
+        for (x=0, rpf=rgbf; rpf!=rgbf_end ; x++, rpf+=3) {
+          int ntscri=rpf[0]*levelmult;
+          int ntscgi=rpf[1]*levelmult;
+          int ntscbi=rpf[2]*levelmult;
+          if (ntscri>=ANALOGTV_CV_MAX) ntscri=ANALOGTV_CV_MAX-1;
+          if (ntscgi>=ANALOGTV_CV_MAX) ntscgi=ANALOGTV_CV_MAX-1;
+          if (ntscbi>=ANALOGTV_CV_MAX) ntscbi=ANALOGTV_CV_MAX-1;
+          for (j=0; j<xrepl; j++) {
+            XPutPixel(it->image, x*xrepl + j, y,
+                      it->red_values[ntscri] | it->green_values[ntscgi] |
+                      it->blue_values[ntscbi]);
+          }
+        }
+      }
+    }
+  }
+}
+
+void
+analogtv_draw(analogtv *it)
+{
+  int i,j,x,y,lineno;
+  int scanstart_i,scanend_i,squishright_i,squishdiv,pixrate;
+  float *rgb_start, *rgb_end;
+  double pixbright;
+  int pixmultinc;
+  int bigloadchange,drawcount;
+  double baseload;
+  double puheight;
+  int overall_top, overall_bot;
+
+  float *raw_rgb_start=(float *)calloc(it->subwidth*3, sizeof(float));
+  float *raw_rgb_end=raw_rgb_start+3*it->subwidth;
+  float *rrp;
+
+  analogtv_setup_frame(it);
+  analogtv_set_demod(it);
+
+  /* rx_signal has an extra 2 lines at the end, where we copy the
+     first 2 lines so we can index into it while only worrying about
+     wraparound on a per-line level */
+  memcpy(&it->rx_signal[ANALOGTV_SIGNAL_LEN],
+         &it->rx_signal[0],
+         2*ANALOGTV_H*sizeof(it->rx_signal[0]));
+
+  analogtv_sync(it);
+
+  baseload=0.5;
+  /* if (it->hashnoise_on) baseload=0.5; */
+
+  bigloadchange=1;
+  drawcount=0;
+  it->crtload[ANALOGTV_TOP-1]=baseload;
+  puheight = puramp(it, 2.0, 1.0, 1.3) * it->height_control *
+    (1.125 - 0.125*puramp(it, 2.0, 2.0, 1.1));
+
+  overall_top=it->useheight;
+  overall_bot=0;
+
+  for (lineno=ANALOGTV_TOP; lineno<ANALOGTV_BOT; lineno++) {
+    int slineno=lineno-ANALOGTV_TOP;
+    int ytop=(int)((slineno*it->useheight/ANALOGTV_VISLINES -
+                    it->useheight/2)*puheight) + it->useheight/2;
+    int ybot=(int)(((slineno+1)*it->useheight/ANALOGTV_VISLINES -
+                    it->useheight/2)*puheight) + it->useheight/2;
+#if 0
+    int linesig=analogtv_line_signature(input,lineno)
+      + it->hashnoise_times[lineno];
+#endif
+    double *signal=(it->rx_signal + ((lineno + it->cur_vsync +
+                                      ANALOGTV_V)%ANALOGTV_V) * ANALOGTV_H +
+                    it->line_hsync[lineno]);
+
+    if (ytop==ybot) continue;
+    if (ybot<0 || ytop>it->useheight) continue;
+    if (ytop<0) ytop=0;
+    if (ybot>it->useheight) ybot=it->useheight;
+
+    if (ytop < overall_top) overall_top=ytop;
+    if (ybot > overall_bot) overall_bot=ybot;
+
+    if (lineno==it->shrinkpulse) {
+      baseload += 0.4;
+      bigloadchange=1;
+      it->shrinkpulse=-1;
+    }
+
+#if 0
+    if (it->hashnoise_rpm>0.0 &&
+        !(bigloadchange ||
+          it->redraw_all ||
+          (slineno<20 && it->flutter_horiz_desync) ||
+          it->gaussiannoise_level>30 ||
+          ((it->gaussiannoise_level>2.0 ||
+            it->multipath) && random()%4) ||
+          linesig != it->onscreen_signature[lineno])) {
+      continue;
+    }
+    it->onscreen_signature[lineno] = linesig;
+#endif
+    drawcount++;
+
+    /*
+      Interpolate the 600-dotclock line into however many horizontal
+      screen pixels we're using, and convert to RGB.
+
+      We add some 'bloom', variations in the horizontal scan width with
+      the amount of brightness, extremely common on period TV sets. They
+      had a single oscillator which generated both the horizontal scan and
+      (during the horizontal retrace interval) the high voltage for the
+      electron beam. More brightness meant more load on the oscillator,
+      which caused an decrease in horizontal deflection. Look for
+      (bloomthisrow).
+
+      Also, the A2 did a bad job of generating horizontal sync pulses
+      during the vertical blanking interval. This, and the fact that the
+      horizontal frequency was a bit off meant that TVs usually went a bit
+      out of sync during the vertical retrace, and the top of the screen
+      would be bent a bit to the left or right. Look for (shiftthisrow).
+
+      We also add a teeny bit of left overscan, just enough to be
+      annoying, but you can still read the left column of text.
+
+      We also simulate compression & brightening on the right side of the
+      screen. Most TVs do this, but you don't notice because they overscan
+      so it's off the right edge of the CRT. But the A2 video system used
+      so much of the horizontal scan line that you had to crank the
+      horizontal width down in order to not lose the right few characters,
+      and you'd see the compression on the right edge. Associated with
+      compression is brightening; since the electron beam was scanning
+      slower, the same drive signal hit the phosphor harder. Look for
+      (squishright_i) and (squishdiv).
+    */
+
+    {
+      int totsignal=0;
+      double ncl,diff;
+
+      for (i=0; i<ANALOGTV_PIC_LEN; i++) {
+        totsignal += signal[i];
+      }
+      totsignal *= it->agclevel;
+      ncl = 0.95 * it->crtload[lineno-1] +
+        0.05*(baseload +
+              (totsignal-30000)/100000.0 +
+              (slineno>184 ? (slineno-184)*(lineno-184)*0.001 * it->squeezebottom
+               : 0.0));
+      diff=ncl - it->crtload[lineno];
+      bigloadchange = (diff>0.01 || diff<-0.01);
+      it->crtload[lineno]=ncl;
+    }
+
+    {
+      double bloomthisrow,shiftthisrow;
+      double viswidth,middle;
+      double scanwidth;
+      int scw,scl,scr;
+
+      bloomthisrow = -10.0 * it->crtload[lineno];
+      if (bloomthisrow<-10.0) bloomthisrow=-10.0;
+      if (bloomthisrow>2.0) bloomthisrow=2.0;
+      if (slineno<16) {
+        shiftthisrow=it->horiz_desync * (exp(-0.17*slineno) *
+                                         (0.7+cos(slineno*0.6)));
+      } else {
+        shiftthisrow=0.0;
+      }
+
+      viswidth=ANALOGTV_PIC_LEN * 0.79 - 5.0*bloomthisrow;
+      middle=ANALOGTV_PIC_LEN/2 - shiftthisrow;
+
+      scanwidth=it->width_control * puramp(it, 0.5, 0.3, 1.0);
+
+      scw=it->subwidth*scanwidth;
+      if (scw>it->subwidth) scw=it->usewidth;
+      scl=it->subwidth/2 - scw/2;
+      scr=it->subwidth/2 + scw/2;
+
+      pixrate=(int)((viswidth*65536.0*1.0)/it->subwidth)/scanwidth;
+      scanstart_i=(int)((middle-viswidth*0.5)*65536.0);
+      scanend_i=(ANALOGTV_PIC_LEN-1)*65536;
+      squishright_i=(int)((middle+viswidth*(0.25 + 0.25*puramp(it, 2.0, 0.0, 1.1)
+                                            - it->squish_control)) *65536.0);
+      squishdiv=it->subwidth/15;
+
+      rgb_start=raw_rgb_start+scl*3;
+      rgb_end=raw_rgb_start+scr*3;
+
+      assert(scanstart_i>=0);
+
+#ifdef DEBUG
+      if (0) printf("scan %d: %0.3f %0.3f %0.3f scl=%d scr=%d scw=%d\n",
+                    lineno,
+                    scanstart_i/65536.0,
+                    squishright_i/65536.0,
+                    scanend_i/65536.0,
+                    scl,scr,scw);
+#endif
+    }
+
+    if (it->use_cmap) {
+      for (y=ytop; y<ybot; y++) {
+        int level=analogtv_level(it, y, ytop, ybot);
+        double levelmult=analogtv_levelmult(it, level);
+        double levelmult_y = levelmult * it->contrast_control
+          * puramp(it, 1.0, 0.0, 1.0) / (0.5+0.5*puheight) * 0.070;
+        double levelmult_iq = levelmult * 0.090;
+
+        struct analogtv_yiq_s *yiq=it->yiq;
+        analogtv_ntsc_to_yiq(it, lineno, signal,
+                             (scanstart_i>>16)-10, (scanend_i>>16)+10);
+        pixmultinc=pixrate;
+
+        x=0;
+        i=scanstart_i;
+        while (i<0 && x<it->usewidth) {
+          XPutPixel(it->image, x, y, it->colors[0]);
+          i+=pixmultinc;
+          x++;
+        }
+
+        while (i<scanend_i && x<it->usewidth) {
+          double pixfrac=(i&0xffff)/65536.0;
+          double invpixfrac=(1.0-pixfrac);
+          int pati=i>>16;
+          int yli,ili,qli,cmi;
+
+          double interpy=(yiq[pati].y*invpixfrac
+                          + yiq[pati+1].y*pixfrac) * levelmult_y;
+          double interpi=(yiq[pati].i*invpixfrac
+                          + yiq[pati+1].i*pixfrac) * levelmult_iq;
+          double interpq=(yiq[pati].q*invpixfrac
+                          + yiq[pati+1].q*pixfrac) * levelmult_iq;
+
+          yli = (int)(interpy * it->cmap_y_levels);
+          ili = (int)((interpi+0.5) * it->cmap_i_levels);
+          qli = (int)((interpq+0.5) * it->cmap_q_levels);
+          if (yli<0) yli=0;
+          if (yli>=it->cmap_y_levels) yli=it->cmap_y_levels-1;
+          if (ili<0) ili=0;
+          if (ili>=it->cmap_i_levels) ili=it->cmap_i_levels-1;
+          if (qli<0) qli=0;
+          if (qli>=it->cmap_q_levels) qli=it->cmap_q_levels-1;
+
+          cmi=qli + it->cmap_i_levels*(ili + it->cmap_q_levels*yli);
+
+#ifdef DEBUG
+          if ((random()%65536)==0) {
+            printf("%0.3f %0.3f %0.3f => %d %d %d => %d\n",
+                   interpy, interpi, interpq,
+                   yli, ili, qli,
+                   cmi);
+          }
+#endif
+
+          for (j=0; j<it->xrepl; j++) {
+            XPutPixel(it->image, x, y,
+                      it->colors[cmi]);
+            x++;
+          }
+          if (i >= squishright_i) {
+            pixmultinc += pixmultinc/squishdiv;
+          }
+          i+=pixmultinc;
+        }
+        while (x<it->usewidth) {
+          XPutPixel(it->image, x, y, it->colors[0]);
+          x++;
+        }
+      }
+    }
+    else {
+      struct analogtv_yiq_s *yiq=it->yiq;
+      analogtv_ntsc_to_yiq(it, lineno, signal,
+                           (scanstart_i>>16)-10, (scanend_i>>16)+10);
+
+      pixbright=it->contrast_control * puramp(it, 1.0, 0.0, 1.0)
+        / (0.5+0.5*puheight) * 1024.0/100.0;
+      pixmultinc=pixrate;
+      i=scanstart_i; rrp=rgb_start;
+      while (i<0 && rrp!=rgb_end) {
+        rrp[0]=rrp[1]=rrp[2]=0;
+        i+=pixmultinc;
+        rrp+=3;
+      }
+      while (i<scanend_i && rrp!=rgb_end) {
+        double pixfrac=(i&0xffff)/65536.0;
+        double invpixfrac=1.0-pixfrac;
+        int pati=i>>16;
+        double r,g,b;
+
+        double interpy=(yiq[pati].y*invpixfrac + yiq[pati+1].y*pixfrac);
+        double interpi=(yiq[pati].i*invpixfrac + yiq[pati+1].i*pixfrac);
+        double interpq=(yiq[pati].q*invpixfrac + yiq[pati+1].q*pixfrac);
+
+        /*
+          According to the NTSC spec, Y,I,Q are generated as:
+
+          y=0.30 r + 0.59 g + 0.11 b
+          i=0.60 r - 0.28 g - 0.32 b
+          q=0.21 r - 0.52 g + 0.31 b
+
+          So if you invert the implied 3x3 matrix you get what standard
+          televisions implement with a bunch of resistors (or directly in the
+          CRT -- don't ask):
+
+          r = y + 0.948 i + 0.624 q
+          g = y - 0.276 i - 0.639 q
+          b = y - 1.105 i + 1.729 q
+        */
+
+        r=(interpy + 0.948*interpi + 0.624*interpq) * pixbright;
+        g=(interpy - 0.276*interpi - 0.639*interpq) * pixbright;
+        b=(interpy - 1.105*interpi + 1.729*interpq) * pixbright;
+        if (r<0.0) r=0.0;
+        if (g<0.0) g=0.0;
+        if (b<0.0) b=0.0;
+        rrp[0]=r;
+        rrp[1]=g;
+        rrp[2]=b;
+
+        if (i>=squishright_i) {
+          pixmultinc += pixmultinc/squishdiv;
+          pixbright += pixbright/squishdiv/2;
+        }
+        i+=pixmultinc;
+        rrp+=3;
+      }
+      while (rrp != rgb_end) {
+        rrp[0]=rrp[1]=rrp[2]=0.0;
+        rrp+=3;
+      }
+
+      analogtv_blast_imagerow(it, raw_rgb_start, raw_rgb_end,
+                              ytop,ybot);
+    }
+  }
+  free(raw_rgb_start);
+
+#if 0
+  /* poor attempt at visible retrace */
+  for (i=0; i<15; i++) {
+    int ytop=(int)((i*it->useheight/15 -
+                    it->useheight/2)*puheight) + it->useheight/2;
+    int ybot=(int)(((i+1)*it->useheight/15 -
+                    it->useheight/2)*puheight) + it->useheight/2;
+    int div=it->usewidth*3/2;
+
+    for (x=0; x<it->usewidth; x++) {
+      y = ytop + (ybot-ytop)*x / div;
+      if (y<0 || y>=it->useheight) continue;
+      XPutPixel(it->image, x, y, 0xffffff);
+    }
+  }
+#endif
+
+  if (it->need_clear) {
+    XClearWindow(it->dpy, it->window);
+    it->need_clear=0;
+  }
+
+  if (overall_top>0) {
+    XClearArea(it->dpy, it->window,
+               it->screen_xo, it->screen_yo,
+               it->usewidth, overall_top, 0);
+  }
+  if (it->useheight > overall_bot) {
+    XClearArea(it->dpy, it->window,
+               it->screen_xo, it->screen_yo+overall_bot,
+               it->usewidth, it->useheight-overall_bot, 0);
+  }
+
+  if (overall_bot > overall_top) {
+    if (it->use_shm) {
+#ifdef HAVE_XSHM_EXTENSION
+      XShmPutImage(it->dpy, it->window, it->gc, it->image,
+                   0, overall_top,
+                   it->screen_xo, it->screen_yo+overall_top,
+                   it->usewidth, overall_bot - overall_top,
+                   False);
+#endif
+    } else {
+      XPutImage(it->dpy, it->window, it->gc, it->image,
+                0, overall_top,
+                it->screen_xo, it->screen_yo+overall_top,
+                it->usewidth, overall_bot - overall_top);
+    }
+  }
+
+#ifdef DEBUG
+  if (0) {
+    struct timeval tv;
+    double fps;
+    char buf[256];
+    gettimeofday(&tv,NULL);
+
+    fps=1.0/((tv.tv_sec - it->last_display_time.tv_sec)
+             + 0.000001*(tv.tv_usec - it->last_display_time.tv_usec));
+    sprintf(buf, "FPS=%0.1f",fps);
+    XDrawString(it->dpy, it->window, it->gc, 50, it->useheight*2/3,
+                buf, strlen(buf));
+
+    it->last_display_time=tv;
+  }
+#endif
+
+  XSync(it->dpy,0);
+}
+
+analogtv_input *
+analogtv_input_allocate()
+{
+  analogtv_input *ret=(analogtv_input *)calloc(1,sizeof(analogtv_input));
+
+  return ret;
+}
+
+/*
+  This takes a screen image and encodes it as a video camera would,
+  including all the bandlimiting and YIQ modulation.
+  This isn't especially tuned for speed.
+*/
+
+int
+analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im)
+{
+  int i,x,y;
+  int img_w,img_h;
+  int fyx[7],fyy[7];
+  int fix[4],fiy[4];
+  int fqx[4],fqy[4];
+  XColor col1[ANALOGTV_PIC_LEN];
+  XColor col2[ANALOGTV_PIC_LEN];
+  int multiq[ANALOGTV_PIC_LEN+4];
+
+  img_w=pic_im->width;
+  img_h=pic_im->height;
+
+  for (i=0; i<ANALOGTV_PIC_LEN+4; i++) {
+    double phase=90.0-90.0*i;
+    double ampl=1.0;
+    multiq[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 4096.0 * ampl);
+  }
+
+  for (y=0; y<ANALOGTV_VISLINES; y++) {
+    int picy1=(y*img_h)/ANALOGTV_VISLINES;
+    int picy2=(y*img_h+ANALOGTV_VISLINES/2)/ANALOGTV_VISLINES;
+
+    for (x=0; x<ANALOGTV_PIC_LEN; x++) {
+      int picx=(x*img_w)/ANALOGTV_PIC_LEN;
+      col1[x].pixel=XGetPixel(pic_im, picx, picy1);
+      col2[x].pixel=XGetPixel(pic_im, picx, picy2);
+    }
+    XQueryColors(it->dpy, it->colormap, col1, ANALOGTV_PIC_LEN);
+    XQueryColors(it->dpy, it->colormap, col2, ANALOGTV_PIC_LEN);
+
+    for (i=0; i<7; i++) fyx[i]=fyy[i]=0;
+    for (i=0; i<4; i++) fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
+
+    for (x=0; x<ANALOGTV_PIC_LEN; x++) {
+      int rawy,rawi,rawq;
+      int filty,filti,filtq;
+      int composite;
+      /* Compute YIQ as:
+           y=0.30 r + 0.59 g + 0.11 b
+           i=0.60 r - 0.28 g - 0.32 b
+           q=0.21 r - 0.52 g + 0.31 b
+          The coefficients below are in .4 format */
+
+      rawy=( 5*col1[x].red + 11*col1[x].green + 2*col1[x].blue +
+             5*col2[x].red + 11*col2[x].green + 2*col2[x].blue)>>7;
+      rawi=(10*col1[x].red -  4*col1[x].green - 5*col1[x].blue +
+            10*col2[x].red -  4*col2[x].green - 5*col2[x].blue)>>7;
+      rawq=( 3*col1[x].red -  8*col1[x].green + 5*col1[x].blue +
+             3*col2[x].red -  8*col2[x].green + 5*col2[x].blue)>>7;
+
+      /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz
+         with an extra zero at 3.5 MHz, from
+         mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l */
+
+      fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3];
+      fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6];
+      fyx[6] = (rawy * 1897) >> 16;
+      fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3];
+      fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6];
+      fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
+        + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
+      filty = fyy[6];
+
+      /* Filter I at 1.5 MHz. 3 pole Butterworth from
+         mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0 */
+
+      fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
+      fix[3] = (rawi * 1413) >> 16;
+      fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
+      fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
+        + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
+      filti = fiy[3];
+
+      /* Filter Q at 0.5 MHz. 3 pole Butterworth from
+         mkfilter -Bu -Lp -o 3 -a 3.5714285714e-02 0 -l */
+
+      fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
+      fqx[3] = (rawq * 75) >> 16;
+      fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
+      fqy[3] = (fqx[0]+fqx[3]) + 3 * (fqx[1]+fqx[2])
+        + ((2612*fqy[0] - 9007*fqy[1] + 10453 * fqy[2]) >> 12);
+      filtq = fqy[3];
+
+
+      composite = filty + ((multiq[x] * filti + multiq[x+3] * filtq)>>12);
+      composite = ((composite*100)>>14) + ANALOGTV_BLACK_LEVEL;
+      if (composite>125) composite=125;
+      if (composite<0) composite=0;
+      input->signal[y+ANALOGTV_TOP][x+ANALOGTV_PIC_START] = composite;
+    }
+  }
+
+  return 1;
+}
+
+#if 0
+void analogtv_channel_noise(analogtv_input *it, analogtv_input *s2)
+{
+  int x,y,newsig;
+  int change=random()%ANALOGTV_V;
+  unsigned int fastrnd=random();
+  double hso=(int)(random()%1000)-500;
+  int yofs=random()%ANALOGTV_V;
+  int noise;
+
+  for (y=change; y<ANALOGTV_V; y++) {
+    int s2y=(y+yofs)%ANALOGTV_V;
+    int filt=0;
+    int noiselevel=60000 / (y-change+100);
+
+    it->line_hsync[y] = s2->line_hsync[y] + (int)hso;
+    hso *= 0.9;
+    for (x=0; x<ANALOGTV_H; x++) {
+      FASTRND;
+      filt+= (-filt/16) + (int)(fastrnd&0xfff)-0x800;
+      noise=(filt*noiselevel)>>16;
+      newsig=s2->signal[s2y][x] + noise;
+      if (newsig>120) newsig=120;
+      if (newsig<0) newsig=0;
+      it->signal[y][x]=newsig;
+    }
+  }
+  s2->vsync=yofs;
+}
+#endif
+
+
+void analogtv_add_signal(analogtv *it, analogtv_reception *rec)
+{
+  analogtv_input *inp=rec->input;
+  double *ps=it->rx_signal;
+  double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
+  double *p=ps;
+  char *ss=&inp->signal[0][0];
+  char *se=&inp->signal[0][0] + ANALOGTV_SIGNAL_LEN;
+  char *s=ss + ((unsigned)rec->ofs % ANALOGTV_SIGNAL_LEN);
+  int i;
+  int ec=it->channel_change_cycles;
+  double level=rec->level;
+  double hfloss=rec->hfloss;
+  unsigned int fastrnd=random();
+  double dp[8];
+
+  /* assert((se-ss)%4==0 && (se-s)%4==0); */
+
+  /* duplicate the first line into the Nth line to ease wraparound computation */
+  memcpy(inp->signal[ANALOGTV_V], inp->signal[0],
+         ANALOGTV_H * sizeof(inp->signal[0][0]));
+
+  for (i=0; i<8; i++) dp[i]=0.0;
+
+  if (ec) {
+    double noise_ampl;
+
+    /* Do a big noisy transition. We can make the transition noise of
+       high constant strength regardless of signal strength.
+
+       There are two separate state machines. here, One is the noise
+       process and the other is the
+
+       We don't bother with the FIR filter here
+    */
+
+    noise_ampl = 1.3;
+
+    while (p!=pe && ec>0) {
+
+      double sig0=(double)s[0];
+      double noise = ((int)fastrnd-(int)0x7fffffff) * (50.0/(double)0x7fffffff);
+      fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
+
+      p[0] += sig0 * level * (1.0 - noise_ampl) + noise * noise_ampl;
+
+      noise_ampl *= 0.99995;
+
+      p++;
+      s++;
+      if (s>=se) s=ss;
+      ec--;
+    }
+
+  }
+
+  while (p != pe) {
+    double sig0,sig1,sig2,sig3,sigr;
+
+    sig0=(double)s[0];
+    sig1=(double)s[1];
+    sig2=(double)s[2];
+    sig3=(double)s[3];
+
+    dp[0]=sig0+sig1+sig2+sig3;
+
+    /* Get the video out signal, and add some ghosting, typical of RF
+       monitor cables. This corresponds to a pretty long cable, but
+       looks right to me.
+    */
+
+    sigr=(dp[1]*rec->ghostfir[0] + dp[2]*rec->ghostfir[1] +
+          dp[3]*rec->ghostfir[2] + dp[4]*rec->ghostfir[3]);
+    dp[4]=dp[3]; dp[3]=dp[2]; dp[2]=dp[1]; dp[1]=dp[0];
+
+    p[0] += (sig0+sigr + sig2*hfloss) * level;
+    p[1] += (sig1+sigr + sig3*hfloss) * level;
+    p[2] += (sig2+sigr + sig0*hfloss) * level;
+    p[3] += (sig3+sigr + sig1*hfloss) * level;
+
+    p += 4;
+    s += 4;
+    if (s>=se) s = ss + (s-se);
+  }
+
+  it->rx_signal_level =
+    sqrt(it->rx_signal_level * it->rx_signal_level +
+         (level * level * (1.0 + 4.0*(rec->ghostfir[0] + rec->ghostfir[1] +
+                                      rec->ghostfir[2] + rec->ghostfir[3]))));
+
+
+  it->channel_change_cycles=0;
+
+}
+
+#ifdef FIXME
+/* add hash */
+  if (it->hashnoise_times[lineno]) {
+    int hnt=it->hashnoise_times[lineno] - input->line_hsync[lineno];
+
+    if (hnt>=0 && hnt<ANALOGTV_PIC_LEN) {
+      double maxampl=1.0;
+      double cur=frand(150.0)-20.0;
+      int len=random()%15+3;
+      if (len > ANALOGTV_PIC_LEN-hnt) len=ANALOGTV_PIC_LEN-hnt;
+      for (i=0; i<len; i++) {
+        double sig=signal[hnt];
+
+        sig += cur*maxampl;
+        cur += frand(5.0)-5.0;
+        maxampl = maxampl*0.9;
+
+        signal[hnt]=sig;
+        hnt++;
+      }
+    }
+  }
+#endif
+
+
+void analogtv_init_signal(analogtv *it, double noiselevel)
+{
+  double *ps=it->rx_signal;
+  double *pe=it->rx_signal + ANALOGTV_SIGNAL_LEN;
+  double *p=ps;
+  unsigned int fastrnd=random();
+  double nm1=0.0,nm2=0.0;
+  double noisemul = sqrt(noiselevel*150)/(double)0x7fffffff;
+
+  while (p != pe) {
+    nm2=nm1;
+    nm1 = ((int)fastrnd-(int)0x7fffffff) * noisemul;
+    *p++ = nm1*nm2;
+    fastrnd = (fastrnd*1103515245+12345) & 0xffffffffu;
+  }
+
+  it->rx_signal_level = noiselevel;
+}
+
+void
+analogtv_reception_update(analogtv_reception *rec)
+{
+  int i;
+
+  if (rec->multipath > 0.0) {
+    for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
+      rec->ghostfir2[i] +=
+        -(rec->ghostfir2[i]/16.0) + rec->multipath * (frand(0.02)-0.01);
+    }
+    if (random()%20==0) {
+      rec->ghostfir2[random()%(ANALOGTV_GHOSTFIR_LEN)]
+        = rec->multipath * (frand(0.08)-0.04);
+    }
+    for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
+      rec->ghostfir[i] = 0.8*rec->ghostfir[i] + 0.2*rec->ghostfir2[i];
+    }
+
+    if (0) {
+      rec->hfloss2 += -(rec->hfloss2/16.0) + rec->multipath * (frand(0.08)-0.04);
+      rec->hfloss = 0.5*rec->hfloss + 0.5*rec->hfloss2;
+    }
+
+  } else {
+    for (i=0; i<ANALOGTV_GHOSTFIR_LEN; i++) {
+      rec->ghostfir[i] = (i>=ANALOGTV_GHOSTFIR_LEN/2) ? ((i&1) ? +0.04 : -0.08) /ANALOGTV_GHOSTFIR_LEN
+        : 0.0;
+    }
+  }
+}
+
+
+void
+analogtv_make_font(Display *dpy, Window window, analogtv_font *f,
+                   int w, int h, char *fontname)
+{
+  int i;
+  XFontStruct *font;
+  Pixmap text_pm;
+  GC gc;
+  XGCValues gcv;
+  XWindowAttributes xgwa;
+
+  f->char_w = w;
+  f->char_h = h;
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+
+  if (fontname) {
+
+    font = XLoadQueryFont (dpy, fontname);
+    if (!font) {
+      fprintf(stderr, "analogtv: can't load font %s\n", fontname);
+      abort();
+    }
+
+    text_pm=XCreatePixmap(dpy, window, 128*f->char_w, f->char_h, xgwa.depth);
+
+    memset(&gcv, 0, sizeof(gcv));
+    gcv.foreground=1;
+    gcv.background=0;
+    gcv.font=font->fid;
+    gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
+
+    XSetForeground(dpy, gc, 0);
+    XFillRectangle(dpy, text_pm, gc, 0, 0, 128*f->char_w, f->char_h);
+    XSetForeground(dpy, gc, 1);
+    /* Just ASCII */
+    for (i=0; i<128; i++) {
+      char c=i;
+      int x=f->char_w*i+1;
+      int y=f->char_h*8/10;
+      XDrawString(dpy, text_pm, gc, x, y, &c, 1);
+    }
+    f->text_im = XGetImage(dpy, text_pm, 0, 0, 128*f->char_w, f->char_h,
+                           ~0L, ZPixmap);
+    XFreeGC(dpy, gc);
+    XFreePixmap(dpy, text_pm);
+  } else {
+    f->text_im = XCreateImage(dpy, xgwa.visual, xgwa.depth,
+                              ZPixmap, 0, 0,
+                              128*f->char_w, f->char_h, 8, 0);
+    f->text_im->data = (char *)calloc(f->text_im->height,
+                                      f->text_im->bytes_per_line);
+
+  }
+  f->x_mult=4;
+  f->y_mult=2;
+}
+
+int
+analogtv_font_pixel(analogtv_font *f, int c, int x, int y)
+{
+  if (x<0 || x>=f->char_w) return 0;
+  if (y<0 || y>=f->char_h) return 0;
+  if (c<0 || c>=128) return 0;
+  return XGetPixel(f->text_im, c*f->char_w + x, y) ? 1 : 0;
+}
+
+void
+analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value)
+{
+  if (x<0 || x>=f->char_w) return;
+  if (y<0 || y>=f->char_h) return;
+  if (c<0 || c>=128) return;
+
+  XPutPixel(f->text_im, c*f->char_w + x, y, value);
+}
+
+void
+analogtv_font_set_char(analogtv_font *f, int c, char *s)
+{
+  int value,x,y;
+
+  if (c<0 || c>=128) return;
+
+  for (y=0; y<f->char_h; y++) {
+    for (x=0; x<f->char_w; x++) {
+      if (!*s) return;
+      value=(*s==' ') ? 0 : 1;
+      analogtv_font_set_pixel(f, c, x, y, value);
+      s++;
+    }
+  }
+}
+
+void
+analogtv_lcp_to_ntsc(double luma, double chroma, double phase, int ntsc[4])
+{
+  int i;
+  for (i=0; i<4; i++) {
+    double w=90.0*i + phase;
+    double val=luma + chroma * (cos(3.1415926/180.0*w));
+    if (val<0.0) val=0.0;
+    if (val>127.0) val=127.0;
+    ntsc[i]=(int)val;
+  }
+}
+
+void
+analogtv_draw_solid(analogtv_input *input,
+                    int left, int right, int top, int bot,
+                    int ntsc[4])
+{
+  int x,y;
+
+  if (right-left<4) right=left+4;
+  if (bot-top<1) bot=top+1;
+
+  for (y=top; y<bot; y++) {
+    for (x=left; x<right; x++) {
+      input->signal[y][x] = ntsc[x&3];
+    }
+  }
+}
+
+
+void
+analogtv_draw_solid_rel_lcp(analogtv_input *input,
+                            double left, double right, double top, double bot,
+                            double luma, double chroma, double phase)
+{
+  int ntsc[4];
+
+  int topi=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*top);
+  int boti=(int)(ANALOGTV_TOP + ANALOGTV_VISLINES*bot);
+  int lefti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*left);
+  int righti=(int)(ANALOGTV_VIS_START + ANALOGTV_VIS_LEN*right);
+
+  analogtv_lcp_to_ntsc(luma, chroma, phase, ntsc);
+  analogtv_draw_solid(input, lefti, righti, topi, boti, ntsc);
+}
+
+
+void
+analogtv_draw_char(analogtv_input *input, analogtv_font *f,
+                   int c, int x, int y, int ntsc[4])
+{
+  int yc,xc,ys,xs,pix;
+
+  for (yc=0; yc<f->char_h; yc++) {
+    for (ys=y + yc*f->y_mult; ys<y + (yc+1)*f->y_mult; ys++) {
+      if (ys<0 || ys>=ANALOGTV_V) continue;
+
+      for (xc=0; xc<f->char_w; xc++) {
+        pix=analogtv_font_pixel(f, c, xc, yc);
+
+        for (xs=x + xc*f->x_mult; xs<x + (xc+1)*f->x_mult; xs++) {
+          if (xs<0 || xs>=ANALOGTV_H) continue;
+          if (pix) {
+            input->signal[ys][xs] = ntsc[xs&3];
+          }
+        }
+      }
+    }
+  }
+}
+
+void
+analogtv_draw_string(analogtv_input *input, analogtv_font *f,
+                     char *s, int x, int y, int ntsc[4])
+{
+  while (*s) {
+    analogtv_draw_char(input, f, *s, x, y, ntsc);
+    x += f->char_w * 4;
+    s++;
+  }
+}
+
+void
+analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
+                              char *s, int x, int y, int ntsc[4])
+{
+  int width=strlen(s) * f->char_w * 4;
+  x -= width/2;
+
+  analogtv_draw_string(input, f, s, x, y, ntsc);
+}
+
+
+static const char hextonib[128] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+                                   0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+/*
+  Much of this function was adapted from logo.c
+ */
+void
+analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
+                  const char * const *xpm, int left, int top)
+{
+  int xpmw,xpmh;
+  int x,y,tvx,tvy,i;
+  int rawy,rawi,rawq;
+  int ncolors, nbytes;
+  char dummyc;
+  struct {
+    int r; int g; int b;
+  } cmap[256];
+
+
+  if (4 != sscanf ((const char *) *xpm,
+                   "%d %d %d %d %c",
+                   &xpmw, &xpmh, &ncolors, &nbytes, &dummyc))
+    abort();
+  if (ncolors < 1 || ncolors > 255)
+    abort();
+  if (nbytes != 1) /* a serious limitation */
+    abort();
+  xpm++;
+
+  for (i = 0; i < ncolors; i++) {
+    const char *line = *xpm;
+    int colori = ((unsigned char)*line++)&0xff;
+    while (*line)
+      {
+        int r, g, b;
+        char which;
+        while (*line == ' ' || *line == '\t')
+          line++;
+        which = *line++;
+        if (which != 'c' && which != 'm')
+          abort();
+        while (*line == ' ' || *line == '\t')
+          line++;
+        if (!strncasecmp(line, "None", 4))
+          {
+            r = g = b = -1;
+            line += 4;
+          }
+        else
+          {
+            if (*line == '#')
+              line++;
+            r = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
+            line += 2;
+            g = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
+            line += 2;
+            b = (hextonib[(int) line[0]] << 4) | hextonib[(int) line[1]];
+            line += 2;
+          }
+
+        if (which == 'c')
+          {
+            cmap[colori].r = r;
+            cmap[colori].g = g;
+            cmap[colori].b = b;
+          }
+      }
+
+    xpm++;
+  }
+
+  for (y=0; y<xpmh; y++) {
+    const char *line = *xpm++;
+    tvy=y+top;
+    if (tvy<ANALOGTV_TOP || tvy>=ANALOGTV_BOT) continue;
+
+    for (x=0; x<xpmw; x++) {
+      int cbyte=((unsigned char)line[x])&0xff;
+      int ntsc[4];
+      tvx=x*4+left;
+      if (tvx<ANALOGTV_PIC_START || tvx+4>ANALOGTV_PIC_END) continue;
+
+      rawy=( 5*cmap[cbyte].r + 11*cmap[cbyte].g + 2*cmap[cbyte].b) / 64;
+      rawi=(10*cmap[cbyte].r -  4*cmap[cbyte].g - 5*cmap[cbyte].b) / 64;
+      rawq=( 3*cmap[cbyte].r -  8*cmap[cbyte].g + 5*cmap[cbyte].b) / 64;
+
+      ntsc[0]=rawy+rawq;
+      ntsc[1]=rawy-rawi;
+      ntsc[2]=rawy-rawq;
+      ntsc[3]=rawy+rawi;
+
+      for (i=0; i<4; i++) {
+        if (ntsc[i]>ANALOGTV_WHITE_LEVEL) ntsc[i]=ANALOGTV_WHITE_LEVEL;
+        if (ntsc[i]<ANALOGTV_BLACK_LEVEL) ntsc[i]=ANALOGTV_BLACK_LEVEL;
+      }
+
+      input->signal[tvy][tvx+0]= ntsc[(tvx+0)&3];
+      input->signal[tvy][tvx+1]= ntsc[(tvx+1)&3];
+      input->signal[tvy][tvx+2]= ntsc[(tvx+2)&3];
+      input->signal[tvy][tvx+3]= ntsc[(tvx+3)&3];
+    }
+  }
+}
+
+extern XtAppContext app;
+
+int
+analogtv_handle_events (analogtv *it)
+{
+  XSync(it->dpy, False);
+  if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+    XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+
+  while (XPending (it->dpy))
+    {
+      XEvent event;
+      XNextEvent (it->dpy, &event);
+      switch (event.xany.type)
+        {
+        case ButtonPress:
+          return 1;
+
+        case KeyPress:
+          {
+            KeySym keysym;
+            char c = 0;
+            XLookupString (&event.xkey, &c, 1, &keysym, 0);
+            if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
+              return 1;
+          }
+          break;
+
+          /* I don't seem to get an event when clicking the "full
+             screen" window manager icon, at least when using
+             metacity. Thus, it doesn't change the video size. Is this
+             some separate WM_* message I have to deal with?
+          */
+        case ConfigureNotify:
+          if (event.xconfigure.width  != it->xgwa.width ||
+              event.xconfigure.height != it->xgwa.height)
+            analogtv_reconfigure(it);
+          break;
+
+        case Expose:
+        case GraphicsExpose:
+          it->need_clear=1;
+          break;
+
+        default:
+          break;
+        }
+      if (it->event_handler) {
+        (*it->event_handler) (it->dpy, &event);
+      }
+    }
+  return 0;
+}
+
diff --git a/hacks/analogtv.h b/hacks/analogtv.h
new file mode 100644 (file)
index 0000000..ff9e8db
--- /dev/null
@@ -0,0 +1,290 @@
+/* analogtv, Copyright (c) 2003 Trevor Blackwell <tlb@tlb.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef _XSCREENSAVER_ANALOGTV_H
+#define _XSCREENSAVER_ANALOGTV_H
+
+#include "xshm.h"
+
+/*
+  You'll need these to generate standard NTSC TV signals
+ */
+enum {
+  /* We don't handle interlace here */
+  ANALOGTV_V=262,
+  ANALOGTV_TOP=30,
+  ANALOGTV_VISLINES=200,
+  ANALOGTV_BOT=ANALOGTV_TOP + ANALOGTV_VISLINES,
+
+  /* This really defines our sampling rate, 4x the colorburst
+     frequency. Handily equal to the Apple II's dot clock.
+     You could also make a case for using 3x the colorburst freq,
+     but 4x isn't hard to deal with. */
+  ANALOGTV_H=912,
+
+  /* Each line is 63500 nS long. The sync pulse is 4700 nS long, etc.
+     Define sync, back porch, colorburst, picture, and front porch
+     positions */
+  ANALOGTV_SYNC_START=0,
+  ANALOGTV_BP_START=4700*ANALOGTV_H/63500,
+  ANALOGTV_CB_START=5800*ANALOGTV_H/63500,
+  /* signal[row][ANALOGTV_PIC_START] is the first displayed pixel */
+  ANALOGTV_PIC_START=9400*ANALOGTV_H/63500,
+  ANALOGTV_PIC_LEN=52600*ANALOGTV_H/63500,
+  ANALOGTV_FP_START=62000*ANALOGTV_H/63500,
+  ANALOGTV_PIC_END=ANALOGTV_FP_START,
+
+  /* TVs scan past the edges of the picture tube, so normally you only
+     want to use about the middle 3/4 of the nominal scan line.
+  */
+  ANALOGTV_VIS_START=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*1/8),
+  ANALOGTV_VIS_END=ANALOGTV_PIC_START + (ANALOGTV_PIC_LEN*7/8),
+  ANALOGTV_VIS_LEN=ANALOGTV_VIS_END-ANALOGTV_VIS_START,
+
+  ANALOGTV_HASHNOISE_LEN=6,
+
+  ANALOGTV_GHOSTFIR_LEN=4,
+
+  /* analogtv.signal is in IRE units, as defined below: */
+  ANALOGTV_WHITE_LEVEL=100,
+  ANALOGTV_GRAY50_LEVEL=55,
+  ANALOGTV_GRAY30_LEVEL=35,
+  ANALOGTV_BLACK_LEVEL=10,
+  ANALOGTV_BLANK_LEVEL=0,
+  ANALOGTV_SYNC_LEVEL=-40,
+  ANALOGTV_CB_LEVEL=20,
+
+  ANALOGTV_SIGNAL_LEN=ANALOGTV_V*ANALOGTV_H,
+
+  /* The number of intensity levels we deal with for gamma correction &c */
+  ANALOGTV_CV_MAX=1024
+};
+
+typedef struct analogtv_input_s {
+  char signal[ANALOGTV_V+1][ANALOGTV_H];
+
+  int do_teletext;
+
+  /* for client use */
+  void (*updater)(struct analogtv_input_s *inp);
+  void *client_data;
+  double next_update_time;
+
+} analogtv_input;
+
+typedef struct analogtv_font_s {
+  XImage *text_im;
+  int char_w, char_h;
+  int x_mult, y_mult;
+} analogtv_font;
+
+typedef struct analogtv_reception_s {
+
+  analogtv_input *input;
+  double ofs;
+  double level;
+  double multipath;
+  double freqerr;
+
+  double ghostfir[ANALOGTV_GHOSTFIR_LEN];
+  double ghostfir2[ANALOGTV_GHOSTFIR_LEN];
+
+  double hfloss;
+  double hfloss2;
+
+} analogtv_reception;
+
+/*
+  The rest of this should be considered mostly opaque to the analogtv module.
+ */
+
+typedef struct analogtv_s {
+
+  Display *dpy;
+  Window window;
+  Screen *screen;
+  XWindowAttributes xgwa;
+
+#if 0
+  unsigned int onscreen_signature[ANALOGTV_V];
+#endif
+
+  int n_colors;
+
+  int interlace;
+  int interlace_counter;
+
+  double agclevel;
+
+  /* If you change these, call analogtv_set_demod */
+  double tint_control,color_control,brightness_control,contrast_control;
+  double height_control, width_control, squish_control;
+  double horiz_desync;
+  double squeezebottom;
+  double powerup;
+
+  /* internal cache */
+  int blur_mult;
+
+  /* For fast display, set fakeit_top, fakeit_bot to
+     the scanlines (0..ANALOGTV_V) that can be preserved on screen.
+     fakeit_scroll is the number of scan lines to scroll it up,
+     or 0 to not scroll at all. It will DTRT if asked to scroll from
+     an offscreen region.
+  */
+  int fakeit_top;
+  int fakeit_bot;
+  int fakeit_scroll;
+  int redraw_all;
+
+  int use_shm,use_cmap,use_color;
+  int bilevel_signal;
+
+#ifdef HAVE_XSHM_EXTENSION
+  XShmSegmentInfo shm_info;
+#endif
+  int visdepth,visclass,visbits;
+  int red_invprec,red_shift,red_mask;
+  int green_invprec,green_shift,green_mask;
+  int blue_invprec,blue_shift,blue_mask;
+
+  Colormap colormap;
+  int usewidth,useheight,xrepl,subwidth;
+  XImage *image; /* usewidth * useheight */
+  GC gc;
+  int screen_xo,screen_yo; /* centers image in window */
+
+  void (*event_handler)(Display *dpy, XEvent *event);
+
+  int flutter_horiz_desync;
+  int flutter_tint;
+
+  struct timeval last_display_time;
+  int need_clear;
+
+
+  /* Add hash (in the radio sense, not the programming sense.) These
+     are the small white streaks that appear in quasi-regular patterns
+     all over the screen when someone is running the vacuum cleaner or
+     the blender. We also set shrinkpulse for one period which
+     squishes the image horizontally to simulate the temporary line
+     voltate drop when someone turns on a big motor */
+  double hashnoise_rpm;
+  int hashnoise_counter;
+  int hashnoise_times[ANALOGTV_V];
+  int hashnoise_signal[ANALOGTV_V];
+  int hashnoise_on;
+  int hashnoise_enable;
+  int shrinkpulse;
+
+  double crtload[ANALOGTV_V];
+
+  unsigned int red_values[ANALOGTV_CV_MAX];
+  unsigned int green_values[ANALOGTV_CV_MAX];
+  unsigned int blue_values[ANALOGTV_CV_MAX];
+
+  struct analogtv_yiq_s {
+    float y,i,q;
+  } yiq[ANALOGTV_PIC_LEN+10];
+
+  unsigned long colors[256];
+  int cmap_y_levels;
+  int cmap_i_levels;
+  int cmap_q_levels;
+
+  int cur_hsync;
+  int line_hsync[ANALOGTV_V];
+  int cur_vsync;
+  double cb_phase[4];
+  double line_cb_phase[ANALOGTV_V][4];
+
+  int channel_change_cycles;
+  double rx_signal_level;
+  double rx_signal[ANALOGTV_SIGNAL_LEN + 2*ANALOGTV_H];
+
+} analogtv;
+
+
+analogtv *analogtv_allocate(Display *dpy, Window window);
+analogtv_input *analogtv_input_allocate(void);
+
+/* call if window size changes */
+void analogtv_reconfigure(analogtv *it);
+
+void analogtv_set_defaults(analogtv *it, char *prefix);
+void analogtv_release(analogtv *it);
+int analogtv_set_demod(analogtv *it);
+void analogtv_setup_frame(analogtv *it);
+void analogtv_setup_sync(analogtv_input *input, int do_cb, int do_ssavi);
+void analogtv_draw(analogtv *it);
+
+int analogtv_load_ximage(analogtv *it, analogtv_input *input, XImage *pic_im);
+
+void analogtv_reception_update(analogtv_reception *inp);
+
+void analogtv_init_signal(analogtv *it, double noiselevel);
+void analogtv_add_signal(analogtv *it, analogtv_reception *rec);
+
+void analogtv_setup_teletext(analogtv_input *input);
+
+
+/* Functions for rendering content into an analogtv_input */
+
+void analogtv_make_font(Display *dpy, Window window,
+                        analogtv_font *f, int w, int h, char *fontname);
+int analogtv_font_pixel(analogtv_font *f, int c, int x, int y);
+void analogtv_font_set_pixel(analogtv_font *f, int c, int x, int y, int value);
+void analogtv_font_set_char(analogtv_font *f, int c, char *s);
+void analogtv_lcp_to_ntsc(double luma, double chroma, double phase,
+                          int ntsc[4]);
+
+
+void analogtv_draw_solid(analogtv_input *input,
+                         int left, int right, int top, int bot,
+                         int ntsc[4]);
+
+void analogtv_draw_solid_rel_lcp(analogtv_input *input,
+                                 double left, double right,
+                                 double top, double bot,
+                                 double luma, double chroma, double phase);
+
+void analogtv_draw_char(analogtv_input *input, analogtv_font *f,
+                        int c, int x, int y, int ntsc[4]);
+void analogtv_draw_string(analogtv_input *input, analogtv_font *f,
+                          char *s, int x, int y, int ntsc[4]);
+void analogtv_draw_string_centered(analogtv_input *input, analogtv_font *f,
+                                   char *s, int x, int y, int ntsc[4]);
+void analogtv_draw_xpm(analogtv *tv, analogtv_input *input,
+                       const char * const *xpm, int left, int top);
+
+int analogtv_handle_events (analogtv *it);
+
+#ifdef HAVE_XSHM_EXTENSION
+#define ANALOGTV_DEFAULTS_SHM "*useSHM:           True",
+#else
+#define ANALOGTV_DEFAULTS_SHM
+#endif
+
+#define ANALOGTV_DEFAULTS \
+  "*TVColor:         70", \
+  "*TVTint:          5",  \
+  "*TVBrightness:    2",  \
+  "*TVContrast:      150",\
+  "*Background:      Black", \
+  "*use_cmap:        0",  \
+  "*geometry:       800x600", \
+  ANALOGTV_DEFAULTS_SHM
+
+#define ANALOGTV_OPTIONS \
+  { "-use-cmap",        ".use_cmap",  XrmoptionSepArg, 0 },
+
+
+#endif /* _XSCREENSAVER_ANALOGTV_H */
index f41f19dee18561d93799b5d2d03aaee52a85f7b7..d7d07266594f114e5b25ca71c6f2b1021b4d112e 100644 (file)
@@ -4,9 +4,8 @@
  *         as Greg Turk's turmites) whose tape is the screen
  */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)ant.c        5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 4befef4e7de6b512ae92ae68fab3322ca02386e4..0c1888acd4c834d5d84ae6f3a16a3ceec040f1ef 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* apollonian --- Apollonian Circles */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)apollonian.c 5.02 2001/07/01 xlockmore";
 #endif
 
diff --git a/hacks/apple2-main.c b/hacks/apple2-main.c
new file mode 100644 (file)
index 0000000..ae38787
--- /dev/null
@@ -0,0 +1,1321 @@
+/* xscreensaver, Copyright (c) 1998-2003 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
+ * with additional work by Jamie Zawinski <jwz@jwz.org>
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include "apple2.h"
+#include <X11/Xutil.h>
+#include <X11/Intrinsic.h>
+#include <ctype.h>
+
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#define DEBUG
+
+extern XtAppContext app;
+
+Time subproc_relaunch_delay = 3000;
+
+
+/* Given a bitmask, returns the position and width of the field.
+ */
+static void
+decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
+{
+  int i;
+  for (i = 0; i < 32; i++)
+    if (mask & (1L << i))
+      {
+        int j = 0;
+        *pos_ret = i;
+        for (; i < 32; i++, j++)
+          if (! (mask & (1L << i)))
+            break;
+        *size_ret = j;
+        return;
+      }
+}
+
+
+/* Given a value and a field-width, expands the field to fill out 8 bits.
+ */
+static unsigned char
+spread_bits (unsigned char value, unsigned char width)
+{
+  switch (width)
+    {
+    case 8: return value;
+    case 7: return (value << 1) | (value >> 6);
+    case 6: return (value << 2) | (value >> 4);
+    case 5: return (value << 3) | (value >> 2);
+    case 4: return (value << 4) | (value);
+    case 3: return (value << 5) | (value << 2) | (value >> 2);
+    case 2: return (value << 6) | (value << 4) | (value);
+    default: abort(); break;
+    }
+}
+
+
+/* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
+   Scales it (without dithering) to WxH.
+ */
+static void
+scale_image (Display *dpy, Window window, XImage *in,
+             int fromx, int fromy, int fromw, int fromh,
+             unsigned int *out, int w, int h)
+{
+  float scale;
+  int x, y, i;
+  unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */
+  unsigned int rsiz=0, gsiz=0, bsiz=0;
+  unsigned int rmsk=0, gmsk=0, bmsk=0;
+  unsigned char spread_map[3][256];
+  XWindowAttributes xgwa;
+  XColor *colors = 0;
+
+  if (fromx + fromw > in->width ||
+      fromy + fromh > in->height)
+    abort();
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+
+  /* Compute the field offsets for RGB decoding in the XImage,
+     when in TrueColor mode.  Otherwise we use the colormap.
+   */
+  if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor ||
+      visual_class (xgwa.screen, xgwa.visual) == GrayScale)
+    {
+      int ncolors = visual_cells (xgwa.screen, xgwa.visual);
+      colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
+      for (i = 0; i < ncolors; i++)
+        colors[i].pixel = i;
+      XQueryColors (dpy, xgwa.colormap, colors, ncolors);
+    }
+  else
+    {
+      rmsk = xgwa.visual->red_mask;
+      gmsk = xgwa.visual->green_mask;
+      bmsk = xgwa.visual->blue_mask;
+      decode_mask (rmsk, &rpos, &rsiz);
+      decode_mask (gmsk, &gpos, &gsiz);
+      decode_mask (bmsk, &bpos, &bsiz);
+
+      for (i = 0; i < 256; i++)
+        {
+          spread_map[0][i] = spread_bits (i, rsiz);
+          spread_map[1][i] = spread_bits (i, gsiz);
+          spread_map[2][i] = spread_bits (i, bsiz);
+        }
+    }
+
+  scale = (fromw > fromh
+           ? (float) fromw / w
+           : (float) fromh / h);
+
+  /* Scale the pixmap from window size to Apple][ screen size (but 32bpp)
+   */
+  for (y = 0; y < h-1; y++)     /* iterate over dest pixels */
+    for (x = 0; x < w-1; x++)
+      {
+        int xx, yy;
+        unsigned int r=0, g=0, b=0;
+
+        int xx1 = x * scale + fromx;
+        int yy1 = y * scale + fromy;
+        int xx2 = (x+1) * scale + fromx;
+        int yy2 = (y+1) * scale + fromy;
+
+        /* Iterate over the source pixels contributing to this one, and sum. */
+        for (xx = xx1; xx < xx2; xx++)
+          for (yy = yy1; yy < yy2; yy++)
+            {
+              unsigned char rr, gg, bb;
+              unsigned long sp = ((xx > in->width || yy > in->height)
+                                  ? 0 : XGetPixel (in, xx, yy));
+              if (colors)
+                {
+                  rr = colors[sp].red   & 0xFF;
+                  gg = colors[sp].green & 0xFF;
+                  bb = colors[sp].blue  & 0xFF;
+                }
+              else
+                {
+                  rr = (sp & rmsk) >> rpos;
+                  gg = (sp & gmsk) >> gpos;
+                  bb = (sp & bmsk) >> bpos;
+                  rr = spread_map[0][rr];
+                  gg = spread_map[1][gg];
+                  bb = spread_map[2][bb];
+                }
+              r += rr;
+              g += gg;
+              b += bb;
+            }
+
+        /* Scale summed pixel values down to 8/8/8 range */
+        i = (xx2 - xx1) * (yy2 - yy1);
+        if (i < 1) i = 1;
+        r /= i;
+        g /= i;
+        b /= i;
+
+        out[y * w + x] = (r << 16) | (g << 8) | b;
+      }
+}
+
+
+/* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
+   Picks a random sub-image out of the source image, and scales it to WxH.
+ */
+static void
+pick_a2_subimage (Display *dpy, Window window, XImage *in,
+                  unsigned int *out, int w, int h)
+{
+  int fromx, fromy, fromw, fromh;
+  if (in->width <= w || in->height <= h)
+    {
+      fromx = 0;
+      fromy = 0;
+      fromw = in->width;
+      fromh = in->height;
+    }
+  else
+    {
+      int dw, dh;
+      do {
+        double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7));
+        fromw = w * scale;
+        fromh = h * scale;
+      } while (fromw > in->width ||
+               fromh > in->height);
+
+      dw = (in->width  - fromw) / 2;   /* near the center! */
+      dh = (in->height - fromh) / 2;
+
+      fromx = (random() % dw) + (dw/2);
+      fromy = (random() % dh) + (dh/2);
+    }
+
+  scale_image (dpy, window, in,
+               fromx, fromy, fromw, fromh,
+               out, w, h);
+}
+
+
+/* Floyd-Steinberg dither.  Derived from ppmquant.c,
+   Copyright (c) 1989, 1991 by Jef Poskanzer.
+ */
+static void
+a2_dither (unsigned int *in, unsigned char *out, int w, int h)
+{
+  /*
+    Apple ][ color map. Each pixel can only be 1 or 0, but what that
+    means depends on whether it's an odd or even pixel, and whether
+    the high bit in the byte is set or not. If it's 0, it's always
+    black.
+   */
+  static const int a2_cmap[2][2][3] = {
+    {
+      /* hibit=0 */
+      {/* odd pixels = blue */    0x00, 0x80, 0xff},
+      {/* even pixels = red */    0xff, 0x80, 0x00}
+    },
+    {
+      /* hibit=1 */
+      {/* even pixels = purple */ 0xa0, 0x40, 0xa0},
+      {/* odd pixels = green */   0x40, 0xff, 0x40}
+    }
+  };
+
+  int x, y;
+  unsigned int **pixels;
+  unsigned int *pP;
+  int maxval = 255;
+  long *this_rerr;
+  long *next_rerr;
+  long *this_gerr;
+  long *next_gerr;
+  long *this_berr;
+  long *next_berr;
+  long *temp_err;
+  int fs_scale = 1024;
+  int brightness = 75;
+  int fs_direction;
+
+#if 0
+  {
+    FILE *pipe = popen ("xv -", "w");
+    fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
+    for (y = 0; y < h; y++)
+      for (x = 0; x < w; x++)
+        {
+          unsigned int p = in[y * w + x];
+          unsigned int r = (p >> 16) & 0xFF;
+          unsigned int g = (p >>  8) & 0xFF;
+          unsigned int b = (p      ) & 0xFF;
+          fprintf(pipe, "%c%c%c", r, g, b);
+        }
+    fclose (pipe);
+  }
+#endif
+
+  /* Initialize Floyd-Steinberg error vectors. */
+  this_rerr = (long *) calloc (w + 2, sizeof(long));
+  next_rerr = (long *) calloc (w + 2, sizeof(long));
+  this_gerr = (long *) calloc (w + 2, sizeof(long));
+  next_gerr = (long *) calloc (w + 2, sizeof(long));
+  this_berr = (long *) calloc (w + 2, sizeof(long));
+  next_berr = (long *) calloc (w + 2, sizeof(long));
+
+
+  /* #### do we really need more than one element of "pixels" at once?
+   */
+  pixels = (unsigned int **) malloc (h * sizeof (unsigned int *));
+  for (y = 0; y < h; y++)
+    pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int));
+
+  for (x = 0; x < w + 2; ++x)
+    {
+      this_rerr[x] = random() % (fs_scale * 2) - fs_scale;
+      this_gerr[x] = random() % (fs_scale * 2) - fs_scale;
+      this_berr[x] = random() % (fs_scale * 2) - fs_scale;
+      /* (random errors in [-1 .. 1]) */
+    }
+  fs_direction = 1;
+
+  for (y = 0; y < h; y++)
+    for (x = 0; x < w; x++)
+      pixels[y][x] = in[y * w + x];
+
+  for (y = 0; y < h; y++)
+    {
+      int xbyte;
+      int err;
+      int prev_byte=0;
+
+      for (x = 0; x < w + 2; x++)
+        next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
+
+      /* It's too complicated to go back and forth on alternate rows,
+         so we always go left-right here. It doesn't change the result
+         very much.
+
+         For each group of 7 pixels, we have to try it both with the
+         high bit=0 and =1. For each high bit value, we add up the
+         total error and pick the best one.
+
+         Because we have to go through each group of bits twice, we
+         don't propagate the error values through this_[rgb]err since
+         it would add them twice. So we keep seperate local_[rgb]err
+         variables for propagating error within the 7-pixel group.
+      */
+
+      pP = pixels[y];
+      for (xbyte=0; xbyte<280; xbyte+=7)
+        {
+          int best_byte=0;
+          int best_error=2000000000;
+          int hibit;
+          int sr, sg, sb;
+          int r2, g2, b2;
+          int local_rerr=0, local_gerr=0, local_berr=0;
+
+          for (hibit=0; hibit<2; hibit++)
+            {
+              int byte = hibit<<7;
+              int tot_error=0;
+
+              for (x=xbyte; x<xbyte+7; x++)
+                {
+                  int dist0, dist1;
+
+                  /* Use Floyd-Steinberg errors to adjust actual color. */
+                  sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
+                  sg = ((pP[x] >>  8) & 0xFF) * brightness/256;
+                  sb = ((pP[x]      ) & 0xFF) * brightness/256;
+                  sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
+                  sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
+                  sb += (this_berr[x + 1] + local_berr) / fs_scale;
+
+                  if  (sr < 0) sr = 0;
+                  else if  (sr > maxval) sr = maxval;
+                  if  (sg < 0) sg = 0;
+                  else if  (sg > maxval) sg = maxval;
+                  if  (sb < 0) sb = 0;
+                  else if  (sb > maxval) sb = maxval;
+
+                  /* This is the color we'd get if we set the bit 1. For 0,
+                     we get black */
+                  r2=a2_cmap[hibit][x&1][0];
+                  g2=a2_cmap[hibit][x&1][1];
+                  b2=a2_cmap[hibit][x&1][2];
+
+                  /*
+                     dist0 and dist1 are the error (Minkowski 2-metric
+                     distances in the color space) for choosing 0 and
+                     1 respectively. 0 is black, 1 is the color r2,g2,b2.
+                  */
+                  dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
+                  dist0= sr*sr + sg*sg + sb*sb;
+
+                  if (dist1<dist0)
+                    {
+                      byte |= 1 << (x-xbyte);
+                      tot_error += dist1;
+
+                      /* Wanted sr but got r2, so propagate sr-r2 */
+                      local_rerr =  (sr - r2) * fs_scale * 7/16;
+                      local_gerr =  (sg - g2) * fs_scale * 7/16;
+                      local_berr =  (sb - b2) * fs_scale * 7/16;
+                    }
+                  else
+                    {
+                      tot_error += dist0;
+
+                      /* Wanted sr but got 0, so propagate sr */
+                      local_rerr =  sr * fs_scale * 7/16;
+                      local_gerr =  sg * fs_scale * 7/16;
+                      local_berr =  sb * fs_scale * 7/16;
+                    }
+                }
+
+              if (tot_error < best_error)
+                {
+                  best_byte = byte;
+                  best_error = tot_error;
+                }
+            }
+
+          /* Avoid alternating 7f and ff in all-white areas, because it makes
+             regular pink vertical lines */
+          if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
+            best_byte=prev_byte;
+          prev_byte=best_byte;
+
+        /*
+          Now that we've chosen values for all 8 bits of the byte, we
+          have to fill in the real pixel values into pP and propagate
+          all the error terms. We end up repeating a lot of the code
+          above.
+         */
+
+        for (x=xbyte; x<xbyte+7; x++)
+          {
+            int bit=(best_byte>>(x-xbyte))&1;
+            hibit=(best_byte>>7)&1;
+
+            sr = (pP[x] >> 16) & 0xFF;
+            sg = (pP[x] >>  8) & 0xFF;
+            sb = (pP[x]      ) & 0xFF;
+            sr += this_rerr[x + 1] / fs_scale;
+            sg += this_gerr[x + 1] / fs_scale;
+            sb += this_berr[x + 1] / fs_scale;
+
+            if  (sr < 0) sr = 0;
+            else if  (sr > maxval) sr = maxval;
+            if  (sg < 0) sg = 0;
+            else if  (sg > maxval) sg = maxval;
+            if  (sb < 0) sb = 0;
+            else if  (sb > maxval) sb = maxval;
+
+            r2=a2_cmap[hibit][x&1][0] * bit;
+            g2=a2_cmap[hibit][x&1][1] * bit;
+            b2=a2_cmap[hibit][x&1][2] * bit;
+
+            pP[x] = (r2<<16) | (g2<<8) | (b2);
+
+            /* Propagate Floyd-Steinberg error terms. */
+            err =  (sr - r2) * fs_scale;
+            this_rerr[x + 2] +=  (err * 7) / 16;
+            next_rerr[x    ] +=  (err * 3) / 16;
+            next_rerr[x + 1] +=  (err * 5) / 16;
+            next_rerr[x + 2] +=  (err    ) / 16;
+            err =  (sg - g2) * fs_scale;
+            this_gerr[x + 2] +=  (err * 7) / 16;
+            next_gerr[x    ] +=  (err * 3) / 16;
+            next_gerr[x + 1] +=  (err * 5) / 16;
+            next_gerr[x + 2] +=  (err    ) / 16;
+            err =  (sb - b2) * fs_scale;
+            this_berr[x + 2] +=  (err * 7) / 16;
+            next_berr[x    ] +=  (err * 3) / 16;
+            next_berr[x + 1] +=  (err * 5) / 16;
+            next_berr[x + 2] +=  (err    ) / 16;
+          }
+
+        /*
+          And put the actual byte into out.
+        */
+
+        out[y*(w/7) + xbyte/7] = best_byte;
+
+        }
+
+      temp_err  = this_rerr;
+      this_rerr = next_rerr;
+      next_rerr = temp_err;
+      temp_err  = this_gerr;
+      this_gerr = next_gerr;
+      next_gerr = temp_err;
+      temp_err  = this_berr;
+      this_berr = next_berr;
+      next_berr = temp_err;
+    }
+
+  free (this_rerr);
+  free (next_rerr);
+  free (this_gerr);
+  free (next_gerr);
+  free (this_berr);
+  free (next_berr);
+
+  for (y=0; y<h; y++)
+    free (pixels[y]);
+  free (pixels);
+
+#if 0
+  {
+    /* let's see what we got... */
+    FILE *pipe = popen ("xv -", "w");
+    fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
+    for (y = 0; y < h; y++)
+      for (x = 0; x < w; x++)
+        {
+          unsigned int r = (pixels[y][x]>>16)&0xff;
+          unsigned int g = (pixels[y][x]>>8)&0xff;
+          unsigned int b = (pixels[y][x]>>0)&0xff;
+          fprintf(pipe, "%c%c%c", r, g, b);
+        }
+    fclose (pipe);
+  }
+#endif
+}
+
+
+static unsigned char *
+load_image (Display *dpy, Window window, char **image_filename_r)
+{
+  XWindowAttributes xgwa;
+  Pixmap p;
+
+  int w = 280;
+  int h = 192;
+  XImage *image;
+  unsigned int  *buf32 = (unsigned int  *) calloc (w, h * 4);
+  unsigned char *buf8  = (unsigned char *) calloc (w/7, h);
+
+  if (!buf32 || !buf8)
+    {
+      fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
+      exit (1);
+    }
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+  p = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
+  load_random_image (xgwa.screen, window, p, image_filename_r);
+  image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
+  XFreePixmap (dpy, p);
+  p = 0;
+
+  /* Make sure the window's background is not set to None, and get the
+     grabbed bits (if any) off it as soon as possible. */
+  XSetWindowBackground (dpy, window,
+                        get_pixel_resource ("background", "Background",
+                                            dpy, xgwa.colormap));
+  XClearWindow (dpy, window);
+
+  /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
+     image (regardless of whether it started as TrueColor/PseudoColor.)
+   */
+  pick_a2_subimage (dpy, window, image, buf32, w, h);
+
+  /* Then dither the 32bpp image to a 6-color Apple][ colormap.
+   */
+  a2_dither (buf32, buf8, w, h);
+
+  free (buf32);
+  return buf8;
+}
+
+\f
+char *progclass = "Apple2";
+
+char *defaults [] = {
+  "*mode:                 random",
+  "*duration:             20",
+  ANALOGTV_DEFAULTS
+  0
+};
+
+XrmOptionDescRec options [] = {
+  { "-slideshow",      ".mode",                XrmoptionNoArg, "slideshow" },
+  { "-basic",          ".mode",                XrmoptionNoArg, "basic" },
+  { "-text",           ".mode",                XrmoptionNoArg, "text" },
+  { "-program",                ".program",             XrmoptionSepArg, 0 },
+  { "-duration",       ".duration",            XrmoptionSepArg, 0 },
+  ANALOGTV_OPTIONS
+  { 0, 0, 0, 0 }
+};
+
+/*
+  TODO: this should load 10 images at startup time, then cycle through them
+  to avoid the pause while it loads.
+ */
+
+void slideshow_controller(apple2_sim_t *sim, int *stepno,
+                          double *next_actiontime)
+{
+  apple2_state_t *st=sim->st;
+  int i;
+  struct mydata {
+    int slideno;
+    int render_img_lineno;
+    u_char *render_img;
+    char *img_filename;
+  } *mine;
+
+  if (!sim->controller_data)
+    sim->controller_data = calloc(sizeof(struct mydata),1);
+  mine=(struct mydata *) sim->controller_data;
+
+  switch(*stepno) {
+
+  case 0:
+    a2_invalidate(st);
+    a2_clear_hgr(st);
+    a2_cls(st);
+    sim->typing_rate = 0.3;
+    sim->dec->powerup=0.0;
+
+    a2_goto(st, 0, 16);
+    a2_prints(st, "APPLE ][");
+    a2_goto(st,23,0);
+    a2_printc(st,']');
+
+    *next_actiontime += 4.0;
+    *stepno=10;
+
+  case 10:
+    mine->render_img = load_image (sim->dpy, sim->window, &mine->img_filename);
+    if (st->gr_mode) {
+      *stepno=30;
+    } else {
+      *stepno=20;
+    }
+    *next_actiontime += 3.0;
+    break;
+
+  case 20:
+    sim->typing="HGR\n";
+    *stepno=29;
+    break;
+
+  case 29:
+    sim->printing="]";
+    *stepno=30;
+    break;
+
+  case 30:
+    st->gr_mode=A2_GR_HIRES;
+    if (mine->img_filename) {
+      char *basename, *tmp;
+      char *s;
+
+      basename = tmp = strdup (mine->img_filename);
+      while (1)
+        {
+          char *slash = strchr(basename, '/');
+          if (!slash || !slash[1]) break;
+          basename = slash+1;
+        }
+      {
+        char *dot=strchr(basename,'.');
+        if (dot) *dot=0;
+      }
+      if (strlen(basename)>20) basename[20]=0;
+      for (s=basename; *s; s++) *s = toupper (*s);
+      sprintf(sim->typing_buf, "BLOAD %s\n", basename);
+      sim->typing = sim->typing_buf;
+
+      free(tmp);
+    } else {
+      sim->typing = "BLOAD IMAGE\n";
+    }
+    mine->render_img_lineno=0;
+
+    *stepno=35;
+    break;
+
+  case 35:
+    *next_actiontime += 0.7;
+    *stepno=40;
+    break;
+
+  case 40:
+    if (mine->render_img_lineno>=192) {
+      sim->printing="]";
+      sim->typing="POKE 49234,0\n";
+      *stepno=50;
+      return;
+    }
+
+    for (i=0; i<6 && mine->render_img_lineno<192; i++) {
+      a2_display_image_loading(st, mine->render_img,
+                               mine->render_img_lineno++);
+    }
+
+    /* The disk would have to seek every 13 sectors == 78 lines.
+       (This ain't no newfangled 16-sector operating system) */
+    if ((mine->render_img_lineno%78)==0) {
+      *next_actiontime += 0.5;
+    } else {
+      *next_actiontime += 0.08;
+    }
+    break;
+
+  case 50:
+    st->gr_mode |= A2_GR_FULL;
+    *stepno=60;
+    *next_actiontime += sim->delay;
+    break;
+
+  case 60:
+    sim->printing="]";
+    sim->typing="POKE 49235,0\n";
+    *stepno=70;
+    break;
+
+  case 70:
+    sim->printing="]";
+    st->gr_mode &= ~A2_GR_FULL;
+    if (mine->render_img) {
+      free(mine->render_img);
+      mine->render_img=NULL;
+    }
+    if (mine->img_filename) {
+      free(mine->img_filename);
+      mine->img_filename=NULL;
+    }
+    *stepno=10;
+    break;
+
+  case A2CONTROLLER_FREE:
+    free(mine->render_img);
+    free(mine->img_filename);
+    free(mine);
+    return;
+
+  }
+}
+
+struct terminal_controller_data {
+  FILE *pipe;
+  int pipe_id;
+  int input_available_p;
+  XtIntervalId timeout_id;
+  char curword[256];
+  unsigned char lastc;
+  int fake_nl;
+  double last_emit_time;
+};
+
+static void
+subproc_cb (XtPointer closure, int *source, XtInputId *id)
+{
+  struct terminal_controller_data *mine =
+    (struct terminal_controller_data *) closure;
+  mine->input_available_p = True;
+}
+
+static void
+launch_text_generator (struct terminal_controller_data *mine)
+{
+  char *oprogram = get_string_resource ("program", "Program");
+  char *program;
+
+  if (!oprogram || !*oprogram)
+    oprogram = FORTUNE_PROGRAM;
+
+  program = (char *) malloc (strlen (oprogram) + 10);
+
+  strcpy (program, "( ");
+  strcat (program, oprogram);
+  strcat (program, " ) 2>&1");
+
+  if (mine->pipe) abort();
+  if ((mine->pipe = popen (program, "r")))
+    {
+      if (mine->pipe_id) abort();
+      mine->pipe_id =
+        XtAppAddInput (app, fileno (mine->pipe),
+                       (XtPointer) (XtInputReadMask | XtInputExceptMask),
+                       subproc_cb, (XtPointer) mine);
+    }
+  else
+    {
+      perror (program);
+    }
+}
+
+static void
+relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
+{
+  struct terminal_controller_data *mine =
+    (struct terminal_controller_data *) closure;
+  mine->timeout_id=0;
+  launch_text_generator (mine);
+}
+
+static void
+terminal_closegen(struct terminal_controller_data *mine)
+{
+  if (mine->pipe_id) {
+    XtRemoveInput (mine->pipe_id);
+    mine->pipe_id = 0;
+  }
+  if (mine->pipe) {
+    pclose (mine->pipe);
+    mine->pipe = 0;
+  }
+  if (mine->timeout_id) {
+    XtRemoveTimeOut(mine->timeout_id);
+    mine->timeout_id=0;
+  }
+}
+
+static int
+terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
+{
+  int rc;
+  if (mine->fake_nl) {
+    buf[0]='\n';
+    mine->fake_nl=0;
+    return 1;
+  }
+
+  if (!mine->input_available_p) return 0;
+
+  rc=read (fileno (mine->pipe), (void *) buf, n);
+  if (rc>0) mine->lastc=buf[rc-1];
+
+  if (rc<=0)
+    {
+      terminal_closegen(mine);
+
+      if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
+        mine->fake_nl=1;
+      }
+
+      /* Set up a timer to re-launch the subproc in a bit. */
+      mine->timeout_id =
+        XtAppAddTimeOut(app, subproc_relaunch_delay,
+                        relaunch_generator_timer,
+                        (XtPointer) mine);
+    }
+
+  mine->input_available_p = False;
+
+  return rc;
+}
+
+
+/*
+  It's fun to put things like "gdb" as the command. For one, it's
+  amusing how the standard mumble (version, no warranty, it's
+  GNU/Linux dammit) occupies an entire screen on the Apple ][.
+*/
+
+void
+terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
+{
+  apple2_state_t *st=sim->st;
+  int c;
+  int i;
+
+  struct terminal_controller_data *mine;
+  if (!sim->controller_data)
+    sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
+  mine=(struct terminal_controller_data *) sim->controller_data;
+
+  switch(*stepno) {
+
+  case 0:
+    if (random()%2)
+      st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
+                                    showing text */
+    a2_cls(st);
+    a2_goto(st,0,16);
+    a2_prints(st, "APPLE ][");
+    a2_goto(st,2,0);
+
+    if (! mine->pipe)
+      launch_text_generator(mine);
+
+    *next_actiontime += 4.0;
+    *stepno = 10;
+    break;
+
+  case 10:
+    {
+      unsigned char buf[5];
+      int nr,nwant;
+      double elapsed;
+
+      elapsed=sim->curtime - mine->last_emit_time;
+      mine->last_emit_time=sim->curtime;
+      nwant=elapsed*25.0;
+      if (elapsed>1.0) nwant=1;
+      if (nwant<1) nwant=1;
+      if (nwant>4) nwant=4;
+
+      nr=terminal_read(mine, buf, nwant);
+      for (i=0; i<nr; i++) {
+        c=buf[i];
+        if (c < 0)
+          ;
+        else if (c >= 'a' && c <= 'z')            /* upcase lower-case chars */
+          {
+            a2_printc(st, c&0xDF);
+          }
+        else if ((c >= 'A'+128) ||                    /* upcase and blink */
+                 (c < ' ' && c != 014 &&              /* high-bit & ctl chrs */
+                  c != '\r' && c != '\n' && c!='\t'))
+          {
+            a2_printc(st, (c & 0x1F) | 0x80);
+          }
+        else if (c >= 'A' && c <= 'Z')            /* invert upper-case chars */
+          {
+            a2_printc(st, c | 0x80);
+          }
+        else {
+          a2_printc(st, c);
+        }
+      }
+    }
+    break;
+
+  case A2CONTROLLER_FREE:
+    terminal_closegen(mine);
+    free(mine);
+    return;
+  }
+}
+
+struct basic_controller_data {
+  int prog_line;
+  int x,y,k;
+  char **progtext;
+  int progstep;
+  char *rep_str;
+  int rep_pos;
+  double prog_start_time;
+  char error_buf[256];
+};
+
+/*
+  Adding more programs is easy. Just add a listing here and to all_programs,
+  then add the state machine to actually execute it to basic_controller.
+ */
+static char *moire_program[]={
+  "10 HGR2\n",
+  "20 FOR Y = 0 TO 191 STEP 2\n",
+  "30 HCOLOR=4 : REM BLACK\n",
+  "40 HLINE 0,191-Y TO 279,Y\n",
+  "60 HCOLOR=7 : REM WHITE\n",
+  "80 HLINE 0,190-Y TO 279,Y+1\n",
+  "90 NEXT Y\n",
+  "100 FOR X = 0 TO 279 STEP 3\n",
+  "110 HCOLOR=4\n",
+  "120 HLINE 279-X,0 TO X,192\n",
+  "140 HCOLOR=7\n",
+  "150 HLINE 278-X,0 TO X+1,192\n",
+  "160 NEXT X\n",
+  NULL
+};
+
+static char *sinewave_program[] = {
+  "10 HGR\n",
+  "25 K=0\n",
+  "30 FOR X = 0 TO 279\n",
+  "32 HCOLOR= 0\n",
+  "35 HLINE X,0 TO X,159\n",
+  "38 HCOLOR= 3\n",
+  "40 Y = 80 + SIN(15*(X-K)/279)\n",
+  "50 HPLOT X,Y\n",
+  "60 NEXT X\n",
+  "70 K=K+4\n",
+  "80 GOTO 30\n",
+  NULL
+};
+
+#if 0
+static char *dumb_program[]={
+  "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
+  "20 GOTO 10\n",
+  NULL
+};
+#endif
+
+static char *random_lores_program[]={
+  "1 REM APPLE ][ SCREEN SAVER\n",
+  "10 GR\n",
+  "100 COLOR= RND(1)*16\n",
+
+  "110 X=RND(1)*40\n",
+  "120 Y1=RND(1)*48\n",
+  "130 Y2=RND(1)*48\n",
+  "140 FOR Y = Y1 TO Y2\n",
+  "150 PLOT X,Y\n",
+  "160 NEXT Y\n",
+
+  "210 Y=RND(1)*48\n",
+  "220 X1=RND(1)*40\n",
+  "230 X2=RND(1)*40\n",
+  "240 FOR X = X1 TO X2\n",
+  "250 PLOT X,Y\n",
+  "260 NEXT X\n",
+  "300 GOTO 100\n",
+
+  NULL
+};
+
+static char typo_map[256];
+
+int make_typo(char *out_buf, char *orig, char *err_buf)
+{
+  int i,j;
+  int errc;
+  int success=0;
+  err_buf[0]=0;
+
+  typo_map['A']='Q';
+  typo_map['S']='A';
+  typo_map['D']='S';
+  typo_map['F']='G';
+  typo_map['G']='H';
+  typo_map['H']='J';
+  typo_map['J']='H';
+  typo_map['K']='L';
+  typo_map['L']=';';
+
+  typo_map['Q']='1';
+  typo_map['W']='Q';
+  typo_map['E']='3';
+  typo_map['R']='T';
+  typo_map['T']='Y';
+  typo_map['Y']='U';
+  typo_map['U']='Y';
+  typo_map['I']='O';
+  typo_map['O']='P';
+  typo_map['P']='[';
+
+  typo_map['Z']='X';
+  typo_map['X']='C';
+  typo_map['C']='V';
+  typo_map['V']='C';
+  typo_map['B']='N';
+  typo_map['N']='B';
+  typo_map['M']='N';
+  typo_map[',']='.';
+  typo_map['.']=',';
+
+  typo_map['!']='1';
+  typo_map['@']='2';
+  typo_map['#']='3';
+  typo_map['$']='4';
+  typo_map['%']='5';
+  typo_map['^']='6';
+  typo_map['&']='7';
+  typo_map['*']='8';
+  typo_map['(']='9';
+  typo_map[')']='0';
+
+  typo_map['1']='Q';
+  typo_map['2']='W';
+  typo_map['3']='E';
+  typo_map['4']='R';
+  typo_map['5']='T';
+  typo_map['6']='Y';
+  typo_map['7']='U';
+  typo_map['8']='I';
+  typo_map['9']='O';
+  typo_map['0']='-';
+
+  strcpy(out_buf, orig);
+  for (i=0; out_buf[i]; i++) {
+    char *p = out_buf+i;
+
+    if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
+      break;
+
+    if (isalpha(p[0]) &&
+        isalpha(p[1]) &&
+        p[0] != p[1] &&
+        random()%15==0)
+      {
+        int tmp=p[1];
+        p[1]=p[0];
+        p[0]=tmp;
+        success=1;
+        sprintf(err_buf,"?SYNTAX ERROR\n");
+        break;
+      }
+
+    if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(u_char)p[0]])) {
+      int remain=strlen(p);
+      int past=random()%(remain-2)+1;
+      memmove(p+past+past, p, remain+1);
+      p[0]=errc;
+      for (j=0; j<past; j++) {
+        p[past+j]=010;
+      }
+      break;
+    }
+  }
+  return success;
+}
+
+struct {
+  char **progtext;
+  int progstep;
+} all_programs[]={
+  {moire_program, 100},
+  /*{dumb_program, 200}, */
+  {sinewave_program, 400},
+  {random_lores_program, 500},
+};
+
+void
+basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
+{
+  apple2_state_t *st=sim->st;
+  int i;
+
+  struct basic_controller_data *mine;
+  if (!sim->controller_data)
+    sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
+  mine=(struct basic_controller_data *) sim->controller_data;
+
+  switch (*stepno) {
+  case 0:
+    st->gr_mode=0;
+    a2_cls(st);
+    a2_goto(st,0,16);
+    a2_prints(st, "APPLE ][");
+    a2_goto(st,23,0);
+    a2_printc(st,']');
+    sim->typing_rate=0.2;
+
+    i=random()%countof(all_programs);
+    mine->progtext=all_programs[i].progtext;
+    mine->progstep=all_programs[i].progstep;
+    mine->prog_line=0;
+
+    *next_actiontime += 1.0;
+    *stepno=10;
+    break;
+
+  case 10:
+    if (st->cursx==0) a2_printc(st,']');
+    if (mine->progtext[mine->prog_line]) {
+      if (random()%4==0) {
+        int err=make_typo(sim->typing_buf,
+                          mine->progtext[mine->prog_line],
+                          mine->error_buf);
+        sim->typing=sim->typing_buf;
+        if (err) {
+          *stepno=11;
+        } else {
+          mine->prog_line++;
+        }
+      } else {
+        sim->typing=mine->progtext[mine->prog_line++];
+      }
+    } else {
+      *stepno=15;
+    }
+    break;
+
+  case 11:
+    sim->printing=mine->error_buf;
+    *stepno=12;
+    break;
+
+  case 12:
+    if (st->cursx==0) a2_printc(st,']');
+    *next_actiontime+=1.0;
+    *stepno=10;
+    break;
+
+  case 15:
+    sim->typing="RUN\n";
+    mine->y=0;
+    mine->x=0;
+    mine->k=0;
+    mine->prog_start_time=*next_actiontime;
+    *stepno=mine->progstep;
+    break;
+
+    /* moire_program */
+  case 100:
+    st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
+    for (i=0; i<24 && mine->y<192; i++)
+      {
+        a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
+        a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
+        mine->y += 2;
+      }
+    if (mine->y>=192) {
+      mine->x = 0;
+      *stepno = 110;
+    }
+    break;
+
+  case 110:
+    for (i=0; i<24 && mine->x<280; i++)
+      {
+        a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
+        a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
+        mine->x+=3;
+      }
+    if (mine->x >= 280) *stepno=120;
+    break;
+
+  case 120:
+    if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
+    break;
+
+    /* dumb_program */
+  case 200:
+    mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
+    for (i=0; i<30; i++) {
+      a2_prints(st, mine->rep_str);
+    }
+    *stepno=210;
+    break;
+
+  case 210:
+    i=random()%strlen(mine->rep_str);
+    while (mine->rep_pos != i) {
+      a2_printc(st, mine->rep_str[mine->rep_pos]);
+      mine->rep_pos++;
+      if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
+    }
+    if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
+    break;
+
+    /* sinewave_program */
+  case 400:
+    st->gr_mode=A2_GR_HIRES;
+    *stepno=410;
+    break;
+
+  case 410:
+    for (i=0; i<48; i++) {
+      int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
+      a2_hline(st, 0, mine->x, 0, mine->x, 159);
+      a2_hplot(st, 3, mine->x, y);
+      mine->x += 1;
+      if (mine->x>=279) {
+        mine->x=0;
+        mine->k+=4;
+      }
+    }
+    if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
+    break;
+
+  case 420:
+    a2_prints(st, "]");
+    *stepno=999;
+    break;
+
+    /* random_lores_program */
+  case 500:
+    st->gr_mode=A2_GR_LORES|A2_GR_FULL;
+    a2_clear_gr(st);
+    *stepno=510;
+
+  case 510:
+    for (i=0; i<10; i++) {
+      int color,x,y,x1,x2,y1,y2;
+
+      color=random()%15;
+      x=random()%40;
+      y1=random()%48;
+      y2=random()%48;
+      for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
+
+      x1=random()%40;
+      x2=random()%40;
+      y=random()%48;
+      for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
+    }
+    if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
+    break;
+
+  case 999:
+    *stepno=0;
+    break;
+
+  case A2CONTROLLER_FREE:
+    free(mine);
+    break;
+  }
+
+}
+
+void (*controllers[])(apple2_sim_t *sim, int *stepno,
+                      double *next_actiontime) = {
+  slideshow_controller,
+  terminal_controller,
+  basic_controller
+};
+
+void
+screenhack (Display *dpy, Window window)
+{
+  int duration = get_integer_resource ("duration", "Integer");
+  char *s;
+  void (*controller)(apple2_sim_t *sim, int *stepno, double *next_actiontime);
+
+  if (duration < 1) duration = 1;
+
+  s = get_string_resource ("mode", "Mode");
+  if (!s || !*s || !strcasecmp(s, "random"))
+    controller = controllers[random() % (countof(controllers))];
+  else if (!strcasecmp(s, "text"))
+     controller = terminal_controller;
+  else if (!strcasecmp(s, "slideshow"))
+     controller = slideshow_controller;
+  else if (!strcasecmp(s, "basic"))
+     controller = basic_controller;
+  else
+    {
+      fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
+               progname, s);
+      exit (1);
+    }
+
+  if (!get_boolean_resource ("root", "Boolean"))
+    {
+      XWindowAttributes xgwa;
+      XGetWindowAttributes (dpy, window, &xgwa);
+      XSelectInput (dpy, window,
+                    xgwa.your_event_mask |
+                    KeyPressMask | ButtonPressMask | ExposureMask);
+    }
+
+  apple2 (dpy, window, duration, controller);
+  XSync (dpy, False);
+}
diff --git a/hacks/apple2.c b/hacks/apple2.c
new file mode 100644 (file)
index 0000000..184ff68
--- /dev/null
@@ -0,0 +1,790 @@
+/* xscreensaver, Copyright (c) 1998-2003 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
+ * with additional work by Jamie Zawinski <jwz@jwz.org>
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include "apple2.h"
+#include <time.h>
+#include <sys/time.h>
+#include <X11/Intrinsic.h>
+
+#ifdef HAVE_XSHM_EXTENSION
+#include "xshm.h"
+#endif
+
+#define DEBUG
+
+extern XtAppContext app;
+
+/*
+ * Implementation notes
+ *
+ * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it
+ * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the
+ * same memory as the text screen. Each could be one of 16 colors. Hires gave
+ * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were
+ * orange or green depending on the setting of the high bit in each byte.
+ *
+ * The graphics modes could also have 4 lines of text at the bottom. This was
+ * fairly unreadable if you had a color monitor.
+ *
+ * Each mode had 2 different screens using different memory space. In hires
+ * mode this was sometimes used for double buffering, but more often the lower
+ * screen was full of code/data and the upper screen was used for display, so
+ * you got random garbage on the screen.
+ *
+ * The text font is based on X's standard 6x10 font, with a few tweaks like
+ * putting a slash across the zero.
+ *
+ * To use this, you'll call apple2(display, window, duration,
+ * controller) where the function controller defines what will happen.
+ * See bsod.c and apple2-main.c for example controllers. The
+ * controller function gets called whenever the machine ready to start
+ * something new. By setting sim->printing or sim->typing, it'll be
+ * busy for some time spitting characters out one at a time. By
+ * setting *next_actiontime+=X.X, it'll pause and just update the screen
+ * for that long before calling the controller function again.
+ *
+ * By setting stepno to A2CONTROLLER_DONE, the loop will end. It will also end
+ * after the time specified by the delay parameter. In either case, it calls
+ * the controller with stepno==A2CONTROLLER_FREE to allow it to release any
+ * memory.
+ *
+ * The void* apple2_sim_t::controller_data is for the use of the controller.
+ * It will be initialize to NULL, and the controller can store its own state
+ * there.
+ *
+ */
+
+void
+a2_scroll(apple2_state_t *st)
+{
+  int i;
+  for (i=0; i<23; i++) {
+    memcpy(st->textlines[i],st->textlines[i+1],40);
+  }
+  memset(st->textlines[23],0xe0,40);
+}
+
+void
+a2_printc(apple2_state_t *st, char c)
+{
+  st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
+
+  if (c == '\n')                      /* ^J == NL */
+    {
+      if (st->cursy==23)
+        {
+          a2_scroll(st);
+        }
+      else
+        {
+          st->cursy++;
+        }
+      st->cursx=0;
+    }
+  else if (c == 014)                  /* ^L == CLS, Home */
+    {
+      a2_cls(st);
+      a2_goto(st,0,0);
+    }
+  else if (c == '\t')                 /* ^I == tab */
+    {
+      a2_goto(st, st->cursy, (st->cursx+8)&~7);
+    }
+  else if (c == 010)                  /* ^H == backspace */
+    {
+      st->textlines[st->cursy][st->cursx]=0xe0;
+      a2_goto(st, st->cursy, st->cursx-1);
+    }
+  else if (c == '\r')                 /* ^M == CR */
+    {
+      st->cursx=0;
+    }
+  else
+    {
+      st->textlines[st->cursy][st->cursx]=c ^ 0xc0;
+      st->cursx++;
+      if (st->cursx==40) {
+        if (st->cursy==23) {
+          a2_scroll(st);
+        } else {
+          st->cursy++;
+        }
+        st->cursx=0;
+      }
+    }
+
+  st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
+}
+
+void
+a2_prints(apple2_state_t *st, char *s)
+{
+  while (*s) a2_printc(st, *s++);
+}
+
+void
+a2_goto(apple2_state_t *st, int r, int c)
+{
+  st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
+  st->cursy=r;
+  st->cursx=c;
+  st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
+}
+
+void
+a2_cls(apple2_state_t *st)
+{
+  int i;
+  for (i=0; i<24; i++) {
+    memset(st->textlines[i],0xe0,40);
+  }
+}
+
+void
+a2_clear_gr(apple2_state_t *st)
+{
+  int i;
+  for (i=0; i<24; i++) {
+    memset(st->textlines[i],0x00,40);
+  }
+}
+
+void
+a2_clear_hgr(apple2_state_t *st)
+{
+  int i;
+  for (i=0; i<192; i++) {
+    memset(st->hireslines[i],0,40);
+  }
+}
+
+void
+a2_invalidate(apple2_state_t *st)
+{
+}
+
+void
+a2_poke(apple2_state_t *st, int addr, int val)
+{
+
+  if (addr>=0x400 && addr<0x800) {
+    /* text memory */
+    int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8;
+    int col=(addr&0x7f)%0x28;
+    if (row<24 && col<40) {
+      st->textlines[row][col]=val;
+      if (!(st->gr_mode&(A2_GR_HIRES)) ||
+          (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) {
+      }
+    }
+  }
+  else if (addr>=0x2000 && addr<0x4000) {
+    int row=(((addr&0x1c00) / 0x400) * 1 +
+             ((addr&0x0380) / 0x80) * 8 +
+             ((addr&0x0078) / 0x28) * 64);
+    int col=((addr&0x07f)%0x28);
+    if (row<192 && col<40) {
+      st->hireslines[row][col]=val;
+      if (st->gr_mode&A2_GR_HIRES) {
+      }
+    }
+  }
+}
+
+void
+a2_hplot(apple2_state_t *st, int hcolor, int x, int y)
+{
+  int highbit,run;
+
+  highbit=((hcolor<<5)&0x80) ^ 0x80; /* capture bit 2 into bit 7 */
+
+  if (y<0 || y>=192 || x<0 || x>=280) return;
+
+  for (run=0; run<2 && x<280; run++) {
+    u_char *vidbyte = &st->hireslines[y][x/7];
+    u_char whichbit=1<<(x%7);
+    int masked_bit;
+
+    *vidbyte = (*vidbyte & 0x7f) | highbit;
+
+    /* use either bit 0 or 1 of hcolor for odd or even pixels */
+    masked_bit = (hcolor>>(1-(x&1)))&1;
+
+    /* Set whichbit to 1 or 0 depending on color */
+    *vidbyte = (*vidbyte & ~whichbit) | (masked_bit ? whichbit : 0);
+
+    x++;
+  }
+}
+
+void
+a2_hline(apple2_state_t *st, int hcolor, int x1, int y1, int x2, int y2)
+{
+  int dx,dy,incx,incy,x,y,balance;
+
+  /* Bresenham's line drawing algorithm */
+
+  if (x2>=x1) {
+    dx=x2-x1;
+    incx=1;
+  } else {
+    dx=x1-x2;
+    incx=-1;
+  }
+  if (y2>=y1) {
+    dy=y2-y1;
+    incy=1;
+  } else {
+    dy=y1-y2;
+    incy=-1;
+  }
+
+  x=x1; y=y1;
+
+  if (dx>=dy) {
+    dy*=2;
+    balance=dy-dx;
+    dx*=2;
+    while (x!=x2) {
+      a2_hplot(st, hcolor, x, y);
+      if (balance>=0) {
+        y+=incy;
+        balance-=dx;
+      }
+      balance+=dy;
+      x+=incx;
+    }
+    a2_hplot(st, hcolor, x, y);
+  } else {
+    dx*=2;
+    balance=dx-dy;
+    dy*=2;
+    while (y!=y2) {
+      a2_hplot(st, hcolor, x, y);
+      if (balance>=0) {
+        x+=incx;
+        balance-=dy;
+      }
+      balance+=dx;
+      y+=incy;
+    }
+    a2_hplot(st, hcolor, x, y);
+  }
+}
+
+void
+a2_plot(apple2_state_t *st, int color, int x, int y)
+{
+  int textrow=y/2;
+  u_char byte;
+
+  if (x<0 || x>=40 || y<0 || y>=48) return;
+
+  byte=st->textlines[textrow][x];
+  if (y&1) {
+    byte = (byte&0xf0) | (color&0x0f);
+  } else {
+    byte = (byte&0x0f) | ((color&0x0f)<<4);
+  }
+  st->textlines[textrow][x]=byte;
+}
+
+void
+a2_display_image_loading(apple2_state_t *st, unsigned char *image,
+                         int lineno)
+{
+  /*
+    When loading images,it would normally just load the big binary
+    dump into screen memory while you watched. Because of the way
+    screen memory was laid out, it wouldn't load from the top down,
+    but in a funny interleaved way. You should call this with lineno
+    increasing from 0 thru 191 over a period of a few seconds.
+  */
+
+  int row=(((lineno / 24) % 8) * 1 +
+           ((lineno / 3 ) % 8) * 8 +
+           ((lineno / 1 ) % 3) * 64);
+
+  memcpy (st->hireslines[row], &image[row * 40], 40);
+}
+
+/*
+  Simulate plausible initial memory contents for running a program.
+*/
+void
+a2_init_memory_active(apple2_sim_t *sim)
+{
+  int i,j,x,y,c;
+  int addr=0;
+  apple2_state_t *st=sim->st;
+
+  while (addr<0x4000) {
+    int n;
+
+    switch (random()%4) {
+    case 0:
+    case 1:
+      n=random()%500;
+      for (i=0; i<n && addr<0x4000; i++) {
+        u_char rb=((random()%6==0 ? 0 : random()%16) |
+                   ((random()%5==0 ? 0 : random()%16)<<4));
+        a2_poke(st, addr++, rb);
+      }
+      break;
+
+    case 2:
+      /* Simulate shapes stored in memory. We use the font since we have it.
+         Unreadable, since rows of each character are stored in consecutive
+         bytes. It was typical to store each of the 7 possible shifts of
+         bitmaps, for fastest blitting to the screen. */
+      x=random()%(sim->text_im->width);
+      for (i=0; i<100; i++) {
+        for (y=0; y<8; y++) {
+          c=0;
+          for (j=0; j<8; j++) {
+            c |= XGetPixel(sim->text_im, (x+j)%sim->text_im->width, y)<<j;
+          }
+          a2_poke(st, addr++, c);
+        }
+        x=(x+1)%(sim->text_im->width);
+      }
+      break;
+
+    case 3:
+      if (addr>0x2000) {
+        n=random()%200;
+        for (i=0; i<n && addr<0x4000; i++) {
+          a2_poke(st, addr++, 0);
+        }
+      }
+      break;
+
+    }
+  }
+}
+
+
+/* This table lists fixes for characters that differ from the standard 6x10
+   font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15)
+   where value is 0 for white and 1 for black. */
+static unsigned short a2_fixfont[] = {
+  /* Fix $ */  0x8421, 0x941d,
+  /* Fix % */  0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427,
+               0x1824, 0x9828,
+  /* Fix * */  0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049,
+               0x9449, 0x9849,
+  /* Fix , */  0x9057, 0x1458, 0x9856, 0x1857, 0x1c56,
+  /* Fix . */  0x1465, 0x1864, 0x1866, 0x1c65,
+  /* Fix / */  0x006e, 0x186a,
+  /* Fix 0 */  0x8874, 0x8c73, 0x9072,
+  /* Fix 1 */  0x0878, 0x1878, 0x187c,
+  /* Fix 5 */  0x8895, 0x0c94, 0x0c95,
+  /* Fix 6 */  0x809f, 0x8c9c, 0x109c,
+  /* Fix 7 */  0x8ca4, 0x0ca5, 0x90a3, 0x10a4,
+  /* Fix 9 */  0x08b3, 0x8cb3, 0x98b0,
+  /* Fix : */  0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9,
+               0x18ba, 0x1cb9,
+  /* Fix ; */  0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0,
+               0x1cbf,
+  /* Fix < */  0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6,
+               0x90c6, 0x10c7,
+               0x94c7, 0x14c8, 0x98c8, 0x18c9,
+  /* Fix > */  0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7,
+               0x90d5, 0x10d6,
+               0x94d4, 0x14d5, 0x98d3, 0x18d4,
+  /* Fix @ */  0x88e3, 0x08e4, 0x8ce4, 0x98e5,
+  /* Fix B */  0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef,
+               0x14f0,
+  /* Fix D */  0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe,
+               0x94fd, 0x14fe,
+  /* Fix G */  0x8116, 0x0516, 0x9916,
+  /* Fix J */  0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b,
+               0x112a, 0x912b,
+               0x152a, 0x952b, 0x992a,
+  /* Fix M */  0x853d, 0x853f, 0x093d, 0x893e, 0x093f,
+  /* Fix Q */  0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c,
+  /* Fix V */  0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f,
+  /* Fix [ */  0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e,
+               0x99a2,
+  /* Fix \ */  0x01a5, 0x19a9,
+  /* Fix ] */  0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac,
+               0x99b0,
+  /* Fix ^ */  0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6,
+               0x91b3, 0x91b7,
+  /* Fix _ */  0x9db9, 0x9dbf,
+  0,
+};
+
+static void
+a2_make_font(apple2_sim_t *sim)
+{
+  /*
+    Generate the font. It used a 5x7 font which looks a lot like the standard X
+    6x10 font, with a few differences. So we render up all the uppercase
+    letters of 6x10, and make a few tweaks (like putting a slash across the
+    zero) according to fixfont.
+   */
+
+  int i;
+  const char *def_font="6x10";
+  XFontStruct *font;
+  Pixmap text_pm;
+  GC gc;
+  XGCValues gcv;
+
+  font = XLoadQueryFont (sim->dpy, def_font);
+  if (!font) {
+    fprintf(stderr, "%s: can't load font %s\n", progname, def_font);
+    abort();
+  }
+
+  text_pm=XCreatePixmap(sim->dpy, sim->window, 64*7, 8, sim->dec->xgwa.depth);
+
+  memset(&gcv, 0, sizeof(gcv));
+  gcv.foreground=1;
+  gcv.background=0;
+  gcv.font=font->fid;
+  gc=XCreateGC(sim->dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
+
+  XSetForeground(sim->dpy, gc, 0);
+  XFillRectangle(sim->dpy, text_pm, gc, 0, 0, 64*7, 8);
+  XSetForeground(sim->dpy, gc, 1);
+  for (i=0; i<64; i++) {
+    char c=32+i;
+    int x=7*i+1;
+    int y=7;
+    if (c=='0') {
+      c='O';
+      XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1);
+    } else {
+      XDrawString(sim->dpy, text_pm, gc, x, y, &c, 1);
+    }
+  }
+  sim->text_im = XGetImage(sim->dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap);
+  XFreeGC(sim->dpy, gc);
+  XFreePixmap(sim->dpy, text_pm);
+
+  for (i=0; a2_fixfont[i]; i++) {
+    XPutPixel(sim->text_im, a2_fixfont[i]&0x3ff,
+              (a2_fixfont[i]>>10)&0xf,
+              (a2_fixfont[i]>>15)&1);
+  }
+}
+
+
+void
+apple2(Display *dpy, Window window, int delay,
+       void (*controller)(apple2_sim_t *sim,
+                          int *stepno,
+                          double *next_actiontime))
+{
+  int i,textrow,row,col,stepno;
+  int c;
+  double next_actiontime;
+  apple2_sim_t *sim;
+
+  sim=(apple2_sim_t *)calloc(1,sizeof(apple2_state_t));
+  sim->dpy = dpy;
+  sim->window = window;
+  sim->delay = delay;
+
+  sim->st = (apple2_state_t *)calloc(1,sizeof(apple2_state_t));
+  sim->dec = analogtv_allocate(dpy, window);
+  sim->dec->event_handler = screenhack_handle_event;
+  sim->inp = analogtv_input_allocate();
+
+  sim->reception.input = sim->inp;
+  sim->reception.level = 1.0;
+
+  sim->prompt=']';
+
+  if (random()%4==0 && !sim->dec->use_cmap && sim->dec->use_color && sim->dec->visbits>=8) {
+    sim->dec->flutter_tint=1;
+  }
+  else if (random()%3==0) {
+    sim->dec->flutter_horiz_desync=1;
+  }
+  sim->typing_rate = 1.0;
+
+  analogtv_set_defaults(sim->dec, "");
+  sim->dec->squish_control=0.05;
+  analogtv_setup_sync(sim->inp, 1, 0);
+
+
+  a2_make_font(sim);
+
+  stepno=0;
+  a2_goto(sim->st,23,0);
+
+  if (random()%2==0) sim->basetime_tv.tv_sec -= 1; /* random blink phase */
+  next_actiontime=0.0;
+
+  sim->curtime=0.0;
+  next_actiontime=sim->curtime;
+  (*controller)(sim, &stepno, &next_actiontime);
+
+# ifdef GETTIMEOFDAY_TWO_ARGS
+  gettimeofday(&sim->basetime_tv, NULL);
+# else
+  gettimeofday(&sim->basetime_tv);
+# endif
+
+  while (1) {
+    double blinkphase;
+
+    {
+      struct timeval curtime_tv;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+      struct timezone tzp;
+      gettimeofday(&curtime_tv, &tzp);
+# else
+      gettimeofday(&curtime_tv);
+# endif
+      sim->curtime=(curtime_tv.tv_sec - sim->basetime_tv.tv_sec) +
+        0.000001*(curtime_tv.tv_usec - sim->basetime_tv.tv_usec);
+      if (sim->curtime > sim->dec->powerup)
+        sim->dec->powerup=sim->curtime;
+    }
+
+    if (analogtv_handle_events(sim->dec)) {
+      sim->typing=NULL;
+      sim->printing=NULL;
+      stepno=A2CONTROLLER_FREE;
+      next_actiontime = sim->curtime;
+      (*controller)(sim, &stepno, &next_actiontime);
+      stepno=0;
+      sim->controller_data=NULL;
+      sim->st->gr_mode=0;
+      continue;
+    }
+
+    blinkphase=sim->curtime/0.8;
+
+    /* The blinking rate was controlled by 555 timer with a resistor/capacitor
+       time constant. Because the capacitor was electrolytic, the flash rate
+       varied somewhat between machines. I'm guessing 1.6 seconds/cycle was
+       reasonable. (I soldered a resistor in mine to make it blink faster.) */
+    i=sim->st->blink;
+    sim->st->blink=((int)blinkphase)&1;
+    if (sim->st->blink!=i && !(sim->st->gr_mode&A2_GR_FULL)) {
+      int downcounter=0;
+      /* For every row with blinking text, set the changed flag. This basically
+         works great except with random screen garbage in text mode, when we
+         end up redrawing the whole screen every second */
+      for (row=(sim->st->gr_mode ? 20 : 0); row<24; row++) {
+        for (col=0; col<40; col++) {
+          c=sim->st->textlines[row][col];
+          if ((c & 0xc0) == 0x40) {
+            downcounter=4;
+            break;
+          }
+        }
+        if (downcounter>0) {
+          downcounter--;
+        }
+      }
+    }
+
+    if (sim->printing) {
+      int nlcnt=0;
+      while (*sim->printing) {
+        if (*sim->printing=='\001') { /* pause */
+          sim->printing++;
+          break;
+        }
+        else if (*sim->printing=='\n') {
+          a2_printc(sim->st,*sim->printing);
+          sim->printing++;
+          nlcnt++;
+          if (nlcnt>=2) break;
+        }
+        else {
+          a2_printc(sim->st,*sim->printing);
+          sim->printing++;
+        }
+      }
+      if (!*sim->printing) sim->printing=NULL;
+    }
+    else if (sim->curtime >= next_actiontime) {
+      if (sim->typing) {
+
+        int c;
+        /* If we're in the midst of typing a string, emit a character with
+           random timing. */
+        c =*sim->typing++;
+        if (c==0) {
+          sim->typing=NULL;
+        }
+        else {
+          a2_printc(sim->st, c);
+          if (c=='\r' || c=='\n') {
+            next_actiontime = sim->curtime;
+          }
+          else if (c==010) {
+            next_actiontime = sim->curtime + 0.1;
+          }
+          else {
+            next_actiontime = (sim->curtime +
+                               (((random()%1000)*0.001 + 0.3) *
+                                sim->typing_rate));
+          }
+        }
+      } else {
+        next_actiontime=sim->curtime;
+
+        (*controller)(sim, &stepno, &next_actiontime);
+        if (stepno==A2CONTROLLER_DONE) goto finished;
+
+      }
+    }
+
+
+    analogtv_setup_sync(sim->inp, sim->st->gr_mode? 1 : 0, 0);
+    analogtv_setup_frame(sim->dec);
+
+    for (textrow=0; textrow<24; textrow++) {
+      for (row=textrow*8; row<textrow*8+8; row++) {
+
+        /* First we generate the pattern that the video circuitry shifts out
+           of memory. It has a 14.something MHz dot clock, equal to 4 times
+           the color burst frequency. So each group of 4 bits defines a color.
+           Each character position, or byte in hires, defines 14 dots, so odd
+           and even bytes have different color spaces. So, pattern[0..600]
+           gets the dots for one scan line. */
+
+        char *pp=&sim->inp->signal[row+ANALOGTV_TOP+4][ANALOGTV_PIC_START+100];
+
+        if ((sim->st->gr_mode&A2_GR_HIRES) &&
+            (row<160 || (sim->st->gr_mode&A2_GR_FULL))) {
+
+          /* Emulate the mysterious pink line, due to a bit getting
+             stuck in a shift register between the end of the last
+             row and the beginning of this one. */
+          if ((sim->st->hireslines[row][0] & 0x80) &&
+              (sim->st->hireslines[row][39]&0x40)) {
+            pp[-1]=ANALOGTV_WHITE_LEVEL;
+          }
+
+          for (col=0; col<40; col++) {
+            u_char b=sim->st->hireslines[row][col];
+            int shift=(b&0x80)?0:1;
+
+            /* Each of the low 7 bits in hires mode corresponded to 2 dot
+               clocks, shifted by one if the high bit was set. */
+            for (i=0; i<7; i++) {
+              pp[shift+1] = pp[shift] = (((b>>i)&1)
+                                         ?ANALOGTV_WHITE_LEVEL
+                                         :ANALOGTV_BLACK_LEVEL);
+              pp+=2;
+            }
+          }
+        }
+        else if ((sim->st->gr_mode&A2_GR_LORES) &&
+                 (row<160 || (sim->st->gr_mode&A2_GR_FULL))) {
+          for (col=0; col<40; col++) {
+            u_char nib=((sim->st->textlines[textrow][col] >> (((row/4)&1)*4))
+                        & 0xf);
+            /* The low or high nybble was shifted out one bit at a time. */
+            for (i=0; i<14; i++) {
+              *pp = (((nib>>((col*14+i)&3))&1)
+                     ?ANALOGTV_WHITE_LEVEL
+                     :ANALOGTV_BLACK_LEVEL);
+              pp++;
+            }
+          }
+        }
+        else {
+          for (col=0; col<40; col++) {
+            int rev;
+            c=sim->st->textlines[textrow][col]&0xff;
+            /* hi bits control inverse/blink as follows:
+               0x00: inverse
+               0x40: blink
+               0x80: normal
+               0xc0: normal */
+            rev=!(c&0x80) && (!(c&0x40) || sim->st->blink);
+
+            for (i=0; i<7; i++) {
+              unsigned long pix=XGetPixel(sim->text_im,
+                                          ((c&0x3f)^0x20)*7+i,
+                                          row%8);
+              pp[1] = pp[2] = ((pix^rev)
+                               ?ANALOGTV_WHITE_LEVEL
+                               :ANALOGTV_BLACK_LEVEL);
+              pp+=2;
+            }
+          }
+        }
+      }
+    }
+    analogtv_init_signal(sim->dec, 0.02);
+    analogtv_reception_update(&sim->reception);
+    analogtv_add_signal(sim->dec, &sim->reception);
+    analogtv_draw(sim->dec);
+  }
+
+ finished:
+
+  stepno=A2CONTROLLER_FREE;
+  (*controller)(sim, &stepno, &next_actiontime);
+
+  XSync(sim->dpy, False);
+  XClearWindow(sim->dpy, sim->window);
+}
+
+void
+a2controller_test(apple2_sim_t *sim, int *stepno, double *next_actiontime)
+{
+  int row,col;
+
+  switch(*stepno) {
+  case 0:
+    a2_invalidate(sim->st);
+    /*
+      For testing color rendering. The spec is:
+      red grn blu
+      0  black       0   0   0
+      1  red       227  30  96
+      2  dk blue    96  78 189
+      3  purple    255  68 253
+      4  dk green    0 163  96
+      5  gray      156 156 156
+      6  med blue   20 207 253
+      7  lt blue   208 195 255
+      8  brown      96 114   3
+      9  orange    255 106  60
+      10 grey      156 156 156
+      11 pink      255 160 208
+      12 lt green   20 245  60
+      13 yellow    208 221 141
+      14 aqua      114 255 208
+      15 white     255 255 255
+    */
+    sim->st->gr_mode=A2_GR_LORES;
+    for (row=0; row<24; row++) {
+      for (col=0; col<40; col++) {
+        sim->st->textlines[row][col]=(row&15)*17;
+      }
+    }
+    *next_actiontime+=0.4;
+    *stepno=99;
+    break;
+
+  case 99:
+    if (sim->curtime > 10) *stepno=-1;
+    break;
+  }
+}
diff --git a/hacks/apple2.h b/hacks/apple2.h
new file mode 100644 (file)
index 0000000..f445b3f
--- /dev/null
@@ -0,0 +1,111 @@
+/* xscreensaver, Copyright (c) 1998-2003 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
+ * with additional work by Jamie Zawinski <jwz@jwz.org>
+ */
+
+#ifndef __XSCREENSAVER_APPLE_II__
+#define __XSCREENSAVER_APPLE_II__
+
+#include "analogtv.h"
+
+
+typedef struct apple2_state {
+  u_char hireslines[192][40];
+  u_char textlines[24][40];
+  int gr_text;
+  enum {
+    A2_GR_FULL=1,
+    A2_GR_LORES=2,
+    A2_GR_HIRES=4
+  } gr_mode;
+  int cursx;
+  int cursy;
+  int blink;
+
+} apple2_state_t;
+
+
+typedef struct apple2_sim_s {
+  
+  void *controller_data;
+
+  apple2_state_t *st;
+
+  analogtv *dec;
+  analogtv_input *inp;
+  analogtv_reception reception;
+
+  char *typing;
+  char typing_buf[1024];
+  double typing_rate;
+
+  char *printing;
+  char printing_buf[1024];
+
+  char prompt;
+
+  Display *dpy;
+  Window window;
+  XWindowAttributes xgwa;
+  XImage *text_im;
+
+  struct timeval basetime_tv;
+  double curtime;
+  double delay;
+} apple2_sim_t;
+
+
+enum {
+  A2_HCOLOR_BLACK=0,
+  A2_HCOLOR_GREEN=1,
+  A2_HCOLOR_PURPLE=2,
+  A2_HCOLOR_WHITE=3,
+  A2_HCOLOR_ALTBLACK=4,
+  A2_HCOLOR_RED=5,
+  A2_HCOLOR_BLUE=6,
+  A2_HCOLOR_ALTWHITE=7
+ };
+
+enum {
+  A2CONTROLLER_DONE=-1,
+  A2CONTROLLER_FREE=-2
+};
+
+extern void
+apple2(Display *dpy, Window window, int delay,
+       void (*)(apple2_sim_t *sim, int *stepno, double *next_actiontime));
+
+
+void a2_poke(apple2_state_t *st, int addr, int val);
+void a2_goto(apple2_state_t *st, int r, int c);
+void a2_cls(apple2_state_t *st);
+void a2_invalidate(apple2_state_t *st);
+
+void a2_add_disk_item(apple2_state_t *st, char *name, u_char *data,
+                      int len, char type);
+void a2_scroll(apple2_state_t *st);
+void a2_printc(apple2_state_t *st, char c);
+void a2_prints(apple2_state_t *st, char *s);
+void a2_goto(apple2_state_t *st, int r, int c);
+void a2_cls(apple2_state_t *st);
+void a2_clear_hgr(apple2_state_t *st);
+void a2_clear_gr(apple2_state_t *st);
+void a2_invalidate(apple2_state_t *st);
+void a2_poke(apple2_state_t *st, int addr, int val);
+void a2_display_image_loading(apple2_state_t *st, unsigned char *image,
+                              int lineno);
+void a2_init_memory_active(apple2_sim_t *sim);
+void a2_hplot(apple2_state_t *st, int hcolor, int x, int y);
+void a2_hline(apple2_state_t *st, int hcolor, int x1, int y1, int x2, int y2);
+void a2_plot(apple2_state_t *st, int color, int x, int y);
+
+#endif /* __XSCREENSAVER_APPLE_II__ */
diff --git a/hacks/apple2.man b/hacks/apple2.man
new file mode 100644 (file)
index 0000000..a14400c
--- /dev/null
@@ -0,0 +1,149 @@
+.de EX         \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH XScreenSaver 1 "30-Oct-99" "X Version 11"
+.SH NAME
+apple2 - Apple ][ display emulator
+.SH SYNOPSIS
+.B apple2
+[\-display \fIhost:display.screen\fP] [\-foreground \fIcolor\fP]
+[\-background \fIcolor\fP] [\-window] [\-root] [\-mono] [\-install]
+[\-visual \fIvisual\fP] [\-delay \fIseconds\fP]
+[\-program \fIcommand to run\fP]
+[\-text] [\-slideshow] [\-basic]
+.SH DESCRIPTION
+The
+.I apple2 
+program simulates an original Apple ][ Plus computer in all its 1979
+glory. It also reproduces the appearance of display on a color
+television set of the period.
+.PP
+There are 3 modes: text, slideshow, and basic. Normally it chooses a
+mode randomly, but you can override with the \fI\-text\fP,
+\fI\-slideshow\fP, or \fI\-basic\fP options.
+.PP
+In text mode it displays the output of a command (by default your
+system's fortune program, but can be overridden with -program).
+.PP
+In slideshow mode it chooses a number of images from the image source
+you configured into XScreenSaver and displays them within the
+limitations of the Apple ][ display hardware. With only 6 available
+colors, you can only make out the general shape of the pictures.
+.PP
+In basic mode a simulated user types in a Basic program and runs it.
+
+.SH OPTIONS
+.I apple2
+accepts the following options:
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-mono 
+If on a color display, pretend we're on a monochrome display.
+.TP 8
+.B \-install
+Install a private colormap for the window.
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-delay \fIdelay\fP
+The delay between displaying one crash and another.
+.TP 8
+.B \-text
+Choose text mode
+.TP 8
+.B \-slideshow
+Choose slideshow mode
+.TP 8
+.B \-basic
+Choose basic mode
+.TP 8
+.B \-program \fIsh-command\fP
+In text mode, the command to run to generate the text to display. This
+option may be any string acceptable to /bin/sh. The program will be
+run at the end of a pipe, and any characters that it prints to
+\fIstdout\fP will be printed on the Apple ][ display. If the program
+exits, it will be launched again after 3 seconds.  Default:
+.BR fortune (1).
+
+Note that apple2 is \fInot\fP a terminal emulator: programs that try
+to directly address the screen will not do what you might expect. It
+merely draws the characters on the screen left to right, top to
+bottom. Lines wrap when they reach the right edge, and the screen
+scrolls when characters reach the bottom.
+
+In other words, programs like
+.BR fortune (1)
+will work, but programs like
+.BR top (1)
+won't.
+
+For example:
+.EX
+apple2 -text -program 'cat /usr/src/linux*/README | fold -sw40'
+apple2 -text -program 'ping apple.com'
+apple2 -text -program 'ps -e'
+apple2 -text -program 'od -txCz -w7 /dev/random'
+.EE
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH X RESOURCES
+Notable X resources supported include the following which correspond
+to standard TV controls:
+.BR analogTVTint ,
+.BR analogTVColor ,
+.BR analogTVBrightness , 
+and
+.BR analogTVContrast
+which correspond to standard TV controls. They range from 0 to
+100,except for tint which is an angle between -180 and +180.
+.SH TRADEMARKS
+Apple ][ and Applesoft are trademarks of Apple Computer.
+
+.SH SEE ALSO
+.BR xscreensaver (1),
+.BR bsod (1),
+.BR fortune (1),
+.BR phosphor (1),
+.BR starwars (1),
+.BR ljlatest (1),
+.BR dadadodo (1),
+.BR webcollage (1),
+.BR driftnet (1)
+.BR EtherPEG ,
+.BR EtherPeek
+.SH COPYRIGHT
+Copyright \(co 2002-2003 by Trevor Blackwell.  Permission to use, copy,
+modify, distribute, and sell this software and its documentation for
+any purpose is hereby granted without fee, provided that the above
+copyright notice appear in all copies and that both that copyright
+notice and this permission notice appear in supporting documentation.
+No representations are made about the suitability of this software for
+any purpose.  It is provided "as is" without express or implied
+warranty.
+.SH AUTHOR
+Television and Apple ][ emulation by Trevor Blackwell <tlb@tlb.org>.
+Slideshow and text mode by Jamie Zawinski <jwz@jwz.org>.
index a1eaf3138d5daf9cac9398ba4655aefe14ae56d2..1eebcb820f957747ce22215dfa2fda5e1e099b67 100644 (file)
@@ -94,7 +94,7 @@ read_screen (Display *dpy, Window window, int *widthP, int *heightP)
   gcv.function = GXcopy;
   gc = XCreateGC (dpy, window, GCFunction, &gcv);
 
-  load_random_image (xgwa.screen, window, p);
+  load_random_image (xgwa.screen, window, p, NULL);
 
   /* Reset the window's background color... */
   XSetWindowBackground (dpy, window,
index 7e77e464bb10120d67b91b9b951e953723f1ede4..29544f2da3bee277438f58d132dc444bc5e48d04 100644 (file)
@@ -19,9 +19,8 @@
     * other special, indirect and consequential damages.
  */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)bouboule.c   4.00 97/01/01 xlockmore";
-
 #endif
 
 /*-
index 55e4fa7ea77ba6d023b15bd4a4651318fdc8c7d4..efb424289b130a962f0dcb2809839e8ddfdea9fe 100644 (file)
@@ -4,9 +4,8 @@
  *           a rotational pattern
  */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)braid.c      5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 9e5c9da0bc3f87c2fb77c3e1df312168199f58be..84f5f312a1a1a47f4fa4039b0bcc361069febc34 100644 (file)
@@ -16,6 +16,7 @@
 #include <math.h>
 #include "screenhack.h"
 #include "xpm-pixmap.h"
+#include "apple2.h"
 #include <stdio.h>
 #include <ctype.h>
 #include <time.h>
@@ -293,12 +294,54 @@ windows (Display *dpy, Window window, int delay, int which)
   const char *wmeb =
     ("    System halted.");
 
+# ifdef __GNUC__
+  __extension__   /* don't warn about "string length is greater than the
+                     length ISO C89 compilers are required to support"
+                     in the following string constant... */
+# endif
+
+  const char *wxpa = /* From Wm. Rhodes <xscreensaver@27.org> */
+    ("A problem has been detected and windows has been shut down to prevent "
+      "damage\n"
+      "to your computer.\n"
+      "\n"
+      "If this is the first time you've seen this Stop error screen,\n"
+      "restart your computer. If this screen appears again, follow\n"
+      "these steps:\n"
+      "\n"
+      "Check to be sure you have adequate disk space. If a driver is\n"
+      "identified in the Stop message, disable the driver or check\n"
+      "with the manufacturer for driver updates. Try changing video\n"
+      "adapters.\n"
+      "\n"
+      "Check with you hardware vendor for any BIOS updates. Disable\n"
+      "BIOS memory options such as caching or shadowing. If you need\n"
+      "to use Safe Mode to remove or disable compinents, restart your\n"
+      "computer, press F8 to select Advanced Startup Options, and then\n"
+      "select Safe Mode.\n"
+      "\n"
+      "Technical information:\n"
+      "\n"
+      "*** STOP: 0x0000007E (0xC0000005,0xF88FF190,0x0xF8975BA0,0xF89758A0)\n"
+      "\n"
+      "\n"
+      "***  EPUSBDSK.sys - Address F88FF190 base at FF88FE000, datestamp "
+      "3b9f3248\n"
+      "\n"
+      "Beginning dump of physical memory\n");
+  const char *wxpb =
+    ("Physical memory dump complete.\n"
+     "Contact your system administrator or technical support group for "
+     "further\n"
+     "assitance.\n"
+     );
+
   if (which < 0 || which > 2) abort();
 
-  /* kludge to lump Win2K and WinME together; seems silly to add another
-     preference/command line option just for this little one. */
+  /* kludge to lump Win2K, WinME, and WinXP together; seems silly to add
+     another preference/command line option just for these little ones. */
   if (which == 2 && (random() % 2))
-    which = 3;
+    which = 3 + (random() % 2);
 
   XGetWindowAttributes (dpy, window, &xgwa);
 
@@ -354,6 +397,16 @@ windows (Display *dpy, Window window, int delay, int which)
       draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, w2kb, 750);
     }
   else if (which == 3)
+    {
+      int line_height = font->ascent + font->descent + 1;
+      int x = 4;
+      int y = 4;
+      draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, wxpa, 750);
+      y += line_height * 26;
+      bsod_sleep(dpy, 4);
+      draw_string(dpy, window, gc, &gcv, font, x, y, 10, 10, wxpb, 750);
+    }
+  else if (which == 4)
     {
       int line_height = font->ascent + font->descent;
       int x = 0;
@@ -1453,7 +1506,7 @@ blitdamage (Display *dpy, Window window, int delay)
   
   XGetWindowAttributes(dpy, window, &xwa);
 
-  load_random_image (xwa.screen, window, window);
+  load_random_image (xwa.screen, window, window, NULL);
 
   w = xwa.width;
   h = xwa.height;
@@ -1587,7 +1640,7 @@ make_scrolling_window (Display *dpy, Window window,
   if (!grab_screen_p) ts->sub_height += ts->sub_y, ts->sub_y = 0;
 
   if (grab_screen_p)
-    load_random_image (xgwa.screen, window, window);
+    load_random_image (xgwa.screen, window, window, NULL);
 
   sprintf (buf1, "%.50s.background", name);
   sprintf (buf2, "%.50s.Background", name);
@@ -2319,6 +2372,71 @@ vms (Display *dpy, Window window, int delay)
 }
 
 
+/* HVX (formerly GCOS6) and TPS6 crash
+   by Brian Garratt <brian-m.garratt@bull.co.uk>
+
+   GCOS6 is a Unix-like operating system developed by Honeywell in the
+   1970s in collaboration with MIT and AT&T (who called their version
+   UNIX).  Both are very much like MULTICS which Honeywell got from GE.
+
+   HVX ("High-performance Virtual System on Unix") is an AIX application
+   which emulates GCOS6 hardware on RS6000-like machines.
+ */
+static void
+hvx (Display *dpy, Window window, int delay)
+{
+  XWindowAttributes xgwa;
+  scrolling_window *ts;
+
+  int delay1 = 10000;
+  int delay2 = 100000;
+  const char *hvx_panic_1 =
+    ("(TP) Trap no E   Effective address 00000000   Instruction D7DE\n"
+     "(TP)  Registers :\n"
+     "(TP)  B1 -> B7  03801B02  00000000  03880D45  038BABDB  0388AFFD"
+     "  0389B3F8  03972317\n"
+     "(TP)  R1 -> R7  0001  0007  F10F  090F  0020  0106  0272\n"
+     "(TP)  P I Z M1  0388A18B  3232  0000 FF00\n"
+     "(TP) Program counter is at offset 0028 from string YTPAD\n"
+     "(TP) User id of task which trapped is LT 626\n"
+     "(TP)?\n"
+     );
+  const char *hvx_panic_2 =
+    ("\n"
+     "(TP)?\n"
+     "Core dumps initiated for selected HVX processes ...\n"
+     "Core dumps complete.\n"
+     "Fri Jul 19 15:53:09 2002\n"
+     "Live registers for cp 0:\n"
+     " P    =     7de3  IW=0000     I=32    CI=30000000   S=80006013"
+     "   IV=aa0      Level=13\n"
+     " R1-7 =       1f      913       13        4        8        0        0\n"
+     " B1-7 =   64e71b      a93      50e   64e73c     6c2c     7000      b54\n"
+     "Memory dump starting to file /var/hvx/dp01/diag/Level2 ...\n"
+     "Memory dump complete.\n"
+    );
+
+  XGetWindowAttributes (dpy, window, &xgwa);
+  ts = make_scrolling_window (dpy, window, "HVX", False);
+  XClearWindow(dpy, window);
+
+  scrolling_puts (ts, hvx_panic_1,         delay1);
+  if (bsod_sleep(dpy, 1)) goto DONE;
+  scrolling_puts (ts, " TP CLOSE ALL",     delay2);
+  scrolling_puts (ts, "\n(TP)?\n",         delay1);
+  if (bsod_sleep(dpy, 1)) goto DONE;
+  scrolling_puts (ts, " TP ABORT -LT ALL", delay2);
+  scrolling_puts (ts, "\n(TP)?\n",         delay1);
+  if (bsod_sleep(dpy, 1)) goto DONE;
+  scrolling_puts (ts, "  TP STOP KILL",    delay2);
+  scrolling_puts (ts, hvx_panic_2,         delay1);
+
+  bsod_sleep(dpy, delay);
+ DONE:
+  XClearWindow(dpy, window);
+}
+
+
 \f
 
 /* HPUX panic, by Tobias Klausmann <klausman@schwarzvogel.de>
@@ -2562,83 +2680,20 @@ DONE:
 
 \f
 /*
- * Simulate various Apple II crashes. The memory map encouraged many
- * programs to use the primary hi-res video page for various storage,
- * and the secondary hi-res page for active display. When it crashed
- * into Applesoft or the monitor, it would revert to the primary page
- * and you'd see memory garbage on the screen. Also, it was common for
- * copy-protected games to use the primary text page for important
- * code, because that made it really hard to reverse-engineer them. The
- * result often looked like what this generates.
- *
- * Sometimes an imaginary user types some of the standard commands to
- * recover from crashes.  You can turn off BSOD*apple2SimulateUser to
- * prevent this.
- *
- * It simulates the following characteristics of standard television
- * monitors:
+ * Simulate various Apple ][ crashes. The memory map encouraged many programs
+ * to use the primary hi-res video page for various storage, and the secondary
+ * hi-res page for active display. When it crashed into Applesoft or the
+ * monitor, it would revert to the primary page and you'd see memory garbage on
+ * the screen. Also, it was common for copy-protected games to use the primary
+ * text page for important code, because that made it really hard to
+ * reverse-engineer them. The result often looked like what this generates.
  *
- * - Realistic rendering of a composite video signal
- * - Compression & brightening on the right, as the scan gets truncated
- *   because of saturation in the flyback transformer
- * - Blooming of the picture dependent on brightness
- * - Overscan, cutting off a few pixels on the left side.
- * - Colored text in mixed graphics/text modes
- *
- * It's amazing how much it makes your high-end monitor look like at
- * large late-70s TV.  All you need is to put a big "Solid State" logo
- * in curly script on it and you'd be set.
+ * The Apple ][ logic and video hardware is in apple2.c. The TV is emulated by
+ * analogtv.c for maximum realism
  *
  * Trevor Blackwell <tlb@tlb.org> 
  */
 
-/*
- * Implementation notes:
- *
- * There are roughly 3 parts to this hack:
- *
- * - emulation of A2 Basic and Monitor. Not much more than printing random
- *   plausible messages. Here we work in the A2 memory space.
- *
- * - emulation of the A2's video output section, which shifted bits out of main
- *   memory at a 14 MHz dot clock rate, sort of. You could only read one byte
- *   per MHz, so there were various schemes for turning 8 bits into 14 screen
- *   pixels.
- *
- * - simulation of an NTSC television, which turned the bits into colored
- *   graphics and text.
- * 
- * The A2 had 3 display modes: text, lores, and hires. Text was 40x24, and it
- * disabled color in the TV. Lores gave you 40x48 graphics blocks, using the
- * same memory as the text screen. Each could be one of 16 colors. Hires gave
- * you 280x192 pixels. Odd pixels were blue or purple, and even pixels were
- * orange or green depending on the setting of the high bit in each byte.
- *
- * The graphics modes could also have 4 lines of text at the bottom. This was
- * fairly unreadable if you had a color monitor.
- *
- * Each mode had 2 different screens using different memory space. In hires
- * mode this was sometimes used for double buffering, but more often the lower
- * screen was full of code/data and the upper screen was used for display, so
- * you got random garbage on the screen.
- * 
- * In DirectColor or TrueColor modes, it generates pixel values directly from
- * RGB values it calculates across each scan line. In PseudoColor mode, it
- * consider each possible pattern of 5 preceding bit values in each possible
- * position modulo 4 and allocates a color for each. A few things, like the
- * brightening on the right side as the horizontal trace slows down, aren't
- * done in PseudoColor.
- *
- * The text font is based on X's standard 6x10 font, with a few tweaks like
- * putting a slash across the zero.
- *
- * I'd like to add a bit of visible retrace, but it conflicts with being able
- * to bitcopy the image when fast scrolling. After another couple of CPU
- * generations, we could probably regenerate the whole image from scratch every
- * time. On a P4 2 GHz it can manage this fine for blinking text, but scrolling
- * looks too slow.
- */
-
 static char * apple2_basic_errors[]={
   "BREAK",
   "NEXT WITHOUT FOR",
@@ -2653,6 +2708,12 @@ static char * apple2_basic_errors[]={
   "FORMULA TOO COMPLEX",
   "UNDEF'D FUNCTION",
   "OUT OF DATA"
+#if 0
+  ,
+  "DEFAULT ARGUMENTS ARE NOT ALLOWED IN DECLARATION OF FRIEND "
+  "TEMPLATE SPECIALIZATION"
+#endif
+
 };
 static char * apple2_dos_errors[]={
   "VOLUME MISMATCH",
@@ -2662,1403 +2723,223 @@ static char * apple2_dos_errors[]={
   "PROGRAM TOO LARGE",
 };
 
-struct apple2_state {
-  char hireslines[192][40];
-  char textlines[24][40];
-  int gr_text;
-  enum {
-    A2_GR_FULL=1,
-    A2_GR_LORES=2,
-    A2_GR_HIRES=4
-  } gr_mode;
-  int cursx;
-  int cursy;
-  int blink;
-  int rowimage[24];
-};
-
-enum {
-  A2_SP_ROWMASK=1023,
-  A2_SP_PUT=1024,
-  A2_SP_COPY=2048
-};
-
-static void
-a2_scroll(struct apple2_state *st)
+void a2controller_crash(apple2_sim_t *sim, int *stepno,
+                        double *next_actiontime)
 {
+  apple2_state_t *st=sim->st;
+  char *s;
   int i;
-  int top=(st->gr_mode&(A2_GR_LORES|A2_GR_HIRES)) ? 20 : 0;
-  if ((st->gr_mode&A2_GR_FULL) && (st->gr_mode&A2_GR_HIRES)) return;
-  if (st->gr_mode&A2_GR_FULL) top=0;
-  for (i=top; i<23; i++) {
-    if (memcmp(st->textlines[i],st->textlines[i+1],40)) {
-      memcpy(st->textlines[i],st->textlines[i+1],40);
-      st->rowimage[i]=st->rowimage[i+1];
-    }
-  }
-  memset(st->textlines[23],0xe0,40);
-  st->rowimage[23]=-1;
-}
 
-static void
-a2_printc(struct apple2_state *st, char c)
-{
-  st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
-  if (c=='\n') {
-    if (st->cursy==23) {
-      a2_scroll(st);
-    } else {
-      st->rowimage[st->cursy]=-1;
-      st->cursy++;
-      st->rowimage[st->cursy]=-1;
-    }
-    st->cursx=0;
-  } else {
-    st->textlines[st->cursy][st->cursx]=c ^ 0xc0;
-    st->rowimage[st->cursy]=-1;
-    st->cursx++;
-    if (st->cursx==40) {
-      if (st->cursy==23) {
-        a2_scroll(st);
-      } else {
-        st->rowimage[st->cursy]=-1;
-        st->cursy++;
-        st->rowimage[st->cursy]=-1;
-      }
-      st->cursx=0;
-    }
-  }
-  st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
-}
+  struct mydata {
+    int fillptr;
+    int fillbyte;
+  } *mine;
 
-static void
-a2_goto(struct apple2_state *st, int r, int c)
-{
-  st->textlines[st->cursy][st->cursx] |= 0xc0; /* turn off blink */
-  st->cursy=r;
-  st->cursx=c;
-  st->textlines[st->cursy][st->cursx] &= 0x7f; /* turn on blink */
-}
-
-static void
-a2_cls(struct apple2_state *st) 
-{
-  int i;
-  for (i=0; i<24; i++) {
-    memset(st->textlines[i],0xe0,40);
-    st->rowimage[i]=-1;
-  }
-}
-
-static void
-a2_invalidate(struct apple2_state *st) 
-{
-  int i;
-  for (i=0; i<24; i++) {
-    st->rowimage[i]=-1;
-  }
-}
-
-static void
-a2_poke(struct apple2_state *st, int addr, int val)
-{
+  if (!sim->controller_data)
+    sim->controller_data = calloc(sizeof(struct mydata),1);
+  mine=(struct mydata *) sim->controller_data;
   
-  if (addr>=0x400 && addr<0x800) {
-    /* text memory */
-    int row=((addr&0x380)/0x80) + ((addr&0x7f)/0x28)*8;
-    int col=(addr&0x7f)%0x28;
-    if (row<24 && col<40) {
-      st->textlines[row][col]=val;
-      if (!(st->gr_mode&(A2_GR_HIRES)) ||
-          (!(st->gr_mode&(A2_GR_FULL)) && row>=20)) {
-        st->rowimage[row]=-1;
-      }
-    }
-  }
-  else if (addr>=0x2000 && addr<0x4000) {
-    int row=(((addr&0x1c00) / 0x400) * 1 +
-             ((addr&0x0380) / 0x80) * 8 +
-             ((addr&0x0078) / 0x28) * 64);
-    int col=((addr&0x07f)%0x28);
-    if (row<192 && col<40) {
-      st->hireslines[row][col]=val;
-      if (st->gr_mode&A2_GR_HIRES) {
-        st->rowimage[row/8]=-1;
-      }
-    }
-  }
-}
-
-/* This table lists fixes for characters that differ from the standard 6x10
-   font. Each encodes a pixel, as (charindex*7 + x) + (y<<10) + (value<<15)
-   where value is 0 for white and 1 for black. */
-static unsigned short a2_fixfont[] = {
-  /* Fix $ */  0x8421, 0x941d,
-  /* Fix % */  0x8024, 0x0028, 0x8425, 0x0426, 0x0825, 0x1027, 0x1426, 0x9427,
-               0x1824, 0x9828,
-  /* Fix * */  0x8049, 0x8449, 0x8849, 0x0c47, 0x0c48, 0x0c4a, 0x0c4b, 0x9049,
-               0x9449, 0x9849,
-  /* Fix , */  0x9057, 0x1458, 0x9856, 0x1857, 0x1c56,
-  /* Fix . */  0x1465, 0x1864, 0x1866, 0x1c65,
-  /* Fix / */  0x006e, 0x186a,
-  /* Fix 0 */  0x8874, 0x8c73, 0x9072,
-  /* Fix 1 */  0x0878, 0x1878, 0x187c,
-  /* Fix 5 */  0x8895, 0x0c94, 0x0c95,
-  /* Fix 6 */  0x809f, 0x8c9c, 0x109c,
-  /* Fix 7 */  0x8ca4, 0x0ca5, 0x90a3, 0x10a4,
-  /* Fix 9 */  0x08b3, 0x8cb3, 0x98b0,
-  /* Fix : */  0x04b9, 0x08b8, 0x08ba, 0x0cb9, 0x90b9, 0x14b9, 0x18b8, 0x18b9,
-               0x18ba, 0x1cb9,
-  /* Fix ; */  0x04c0, 0x08bf, 0x08c1, 0x0cc0, 0x90c0, 0x14c1, 0x98bf, 0x18c0,
-               0x1cbf,
-  /* Fix < */  0x80c8, 0x00c9, 0x84c7, 0x04c8, 0x88c6, 0x08c7, 0x8cc5, 0x0cc6,
-               0x90c6, 0x10c7, 
-               0x94c7, 0x14c8, 0x98c8, 0x18c9,
-  /* Fix > */  0x80d3, 0x00d4, 0x84d4, 0x04d5, 0x88d5, 0x08d6, 0x8cd6, 0x0cd7,
-               0x90d5, 0x10d6, 
-               0x94d4, 0x14d5, 0x98d3, 0x18d4,
-  /* Fix @ */  0x88e3, 0x08e4, 0x8ce4, 0x98e5,
-  /* Fix B */  0x84ef, 0x04f0, 0x88ef, 0x08f0, 0x8cef, 0x90ef, 0x10f0, 0x94ef,
-               0x14f0,
-  /* Fix D */  0x84fd, 0x04fe, 0x88fd, 0x08fe, 0x8cfd, 0x0cfe, 0x90fd, 0x10fe,
-               0x94fd, 0x14fe,
-  /* Fix G */  0x8116, 0x0516, 0x9916,
-  /* Fix J */  0x0129, 0x012a, 0x052a, 0x852b, 0x092a, 0x892b, 0x0d2a, 0x8d2b,
-               0x112a, 0x912b, 
-               0x152a, 0x952b, 0x992a,
-  /* Fix M */  0x853d, 0x853f, 0x093d, 0x893e, 0x093f,
-  /* Fix Q */  0x915a, 0x155a, 0x955b, 0x155c, 0x195b, 0x995c, 0x1d5c,
-  /* Fix V */  0x8d7b, 0x0d7c, 0x0d7e, 0x8d7f, 0x917b, 0x117c, 0x117e, 0x917f,
-  /* Fix [ */  0x819e, 0x81a2, 0x859e, 0x899e, 0x8d9e, 0x919e, 0x959e, 0x999e,
-               0x99a2,
-  /* Fix \ */  0x01a5, 0x19a9,
-  /* Fix ] */  0x81ac, 0x81b0, 0x85b0, 0x89b0, 0x8db0, 0x91b0, 0x95b0, 0x99ac,
-               0x99b0,
-  /* Fix ^ */  0x01b5, 0x05b4, 0x05b6, 0x09b3, 0x89b5, 0x09b7, 0x8db4, 0x8db6,
-               0x91b3, 0x91b7,
-  /* Fix _ */  0x9db9, 0x9dbf,
-  0,
-};
-
-struct ntsc_dec {
-  char pattern[600];
-  int ntscy[600];
-  int ntsci[600];
-  int ntscq[600];
-  int multi[600];
-  int multq[600];
-  int brightness_control;
-};
-
-/*
-  First generate the I and Q reference signals, which we'll multiply by the
-  input signal to accomplish the demodulation. Normally they are shifted 33
-  degrees from the colorburst. I think this was convenient for
-  inductor-capacitor-vacuum tube implementation.
-               
-  The tint control, FWIW, just adds a phase shift to the chroma signal, and 
-  the color control controls the amplitude.
-               
-  In text modes (colormode==0) the system disabled the color burst, and no
-  color was detected by the monitor.
-
-  freq_error gives a mismatch between the built-in oscillator and the TV's
-  colorbust. Older II Plus machines seemed to occasionally get instability
-  problems -- the crystal oscillator was a single transistor if I remember
-  correctly -- and the frequency would vary enough that the tint would change
-  across the width of the screen.  The left side would be in correct tint
-  because it had just gotten resynchronized with the color burst.
-*/
-static void
-ntsc_set_demod(struct ntsc_dec *it, double tint_control, 
-               double color_control, double brightness_control,
-               double freq_error, 
-               int colormode)
-{
-  int i;
-
-  it->brightness_control=(int)(1024.0*brightness_control);
-
-  for (i=0; i<600; i++) {
-    double phase=90.0-90.0*i + freq_error*i/600.0 + tint_control;
-    it->multi[i]=(int)(-cos(3.1415926/180.0*(phase-303)) * 65536.0 * 
-                       color_control * colormode * 4);
-    it->multq[i]=(int)(cos(3.1415926/180.0*(phase-33)) * 65536.0 * 
-                       color_control * colormode * 4);
-  }
-}
-
-/* Here we model the analog circuitry of an NTSC television. Basically, it
-   splits the signal into 3 signals: Y, I and Q. Y corresponds to luminance,
-   and you get it by low-pass filtering the input signal to below 3.57 MHz.
-
-   I and Q are the in-phase and quadrature components of the 3.57 MHz
-   subcarrier. We get them by multiplying by cos(3.57 MHz*t) and sin(3.57
-   MHz*t), and low-pass filtering. Because the eye has less resolution in some
-   colors than others, the I component gets low-pass filtered at 1.5 MHz and
-   the Q at 0.5 MHz. The I component is approximately orange-blue, and Q is
-   roughly purple-green. See http://www.ntsc-tv.com for details.
- */
-static void
-ntsc_to_yiq(struct ntsc_dec *it) 
-{
-  int i;
-  int fyx[10],fyy[10];
-  int fix[10],fiy[10];
-  int fqx[10],fqy[10];
-  int pixghost;
-  int iny,ini,inq,pix,blank;
-  
-  for (i=0; i<10; i++) fyx[i]=fyy[i]=fix[i]=fiy[i]=fqx[i]=fqy[i]=0.0;
-  pixghost=0;
-  for (i=0; i<600; i++) {
-    /* Get the video out signal, and add a teeny bit of ghosting, typical of RF
-       monitor cables. This corresponds to a pretty long cable, but looks right
-       to me. */
-    pix=it->pattern[i]*1024;
-    if (i>=20) pixghost += it->pattern[i-20]*15;
-    if (i>=30) pixghost -= it->pattern[i-30]*15;
-    pix += pixghost;
-
-    /* Get Y, I, Q before filtering */
-    iny=pix;
-    ini=(pix*it->multi[i])>>16;
-    inq=(pix*it->multq[i])>>16;
-            
-    blank = (i>=7 && i<596 ? it->brightness_control : -200);
-
-    /* Now filter them. These are infinite impulse response filters calculated
-       by the script at http://www-users.cs.york.ac.uk/~fisher/mkfilter. This
-       is fixed-point integer DSP, son. No place for wimps. We do it in integer
-       because you can count on integer being faster on most CPUs. We care
-       about speed because we need to recalculate every time we blink text, and
-       when we spew random bytes into screen memory. This is roughly 16.16
-       fixed point arithmetic, but we scale some filter values up by a few bits
-       to avoid some nasty precision errors. */
-            
-    /* Filter y at with a 4-pole low-pass Butterworth filter at 3.5 MHz 
-       with an extra zero at 3.5 MHz, from
-       mkfilter -Bu -Lp -o 4 -a 2.1428571429e-01 0 -Z 2.5e-01 -l
-       Delay about 2 */
-
-    fyx[0] = fyx[1]; fyx[1] = fyx[2]; fyx[2] = fyx[3]; 
-    fyx[3] = fyx[4]; fyx[4] = fyx[5]; fyx[5] = fyx[6]; 
-    fyx[6] = (iny * 1897) >> 13;
-    fyy[0] = fyy[1]; fyy[1] = fyy[2]; fyy[2] = fyy[3]; 
-    fyy[3] = fyy[4]; fyy[4] = fyy[5]; fyy[5] = fyy[6]; 
-    fyy[6] = (fyx[0]+fyx[6]) + 4*(fyx[1]+fyx[5]) + 7*(fyx[2]+fyx[4]) + 8*fyx[3]
-      + ((-151*fyy[2] + 8115*fyy[3] - 38312*fyy[4] + 36586*fyy[5]) >> 16);
-    if (i>=2) it->ntscy[i-2] = blank + (fyy[6]>>3);
-
-    /* Filter I and Q at 1.5 MHz. 3 pole Butterworth from
-       mkfilter -Bu -Lp -o 3 -a 1.0714285714e-01 0
-       Delay about 3.
-
-       The NTSC spec says the Q value should be filtered at 0.5 MHz at the
-       transmit end, But the Apple's video circuitry doesn't any such thing.
-       AFAIK, oldish televisions (before comb filters) simply applied a 1.5 MHz
-       filter to both after the demodulator.
-    */
-
-    fix[0] = fix[1]; fix[1] = fix[2]; fix[2] = fix[3];
-    fix[3] = (ini * 1413) >> 14;
-    fiy[0] = fiy[1]; fiy[1] = fiy[2]; fiy[2] = fiy[3];
-    fiy[3] = (fix[0]+fix[3]) + 3*(fix[1]+fix[2])
-      + ((16559*fiy[0] - 72008*fiy[1] + 109682*fiy[2]) >> 16);
-    if (i>=3) it->ntsci[i-3] = fiy[3]>>2;
-
-    fqx[0] = fqx[1]; fqx[1] = fqx[2]; fqx[2] = fqx[3];
-    fqx[3] = (inq * 1413) >> 14;
-    fqy[0] = fqy[1]; fqy[1] = fqy[2]; fqy[2] = fqy[3];
-    fqy[3] = (fqx[0]+fqx[3]) + 3*(fqx[1]+fqx[2])
-      + ((16559*fqy[0] - 72008*fqy[1] + 109682*fqy[2]) >> 16);
-    if (i>=3) it->ntscq[i-3] = fqy[3]>>2;
-
-  }
-  for (; i<610; i++) {
-    if (i-2<600) it->ntscy[i-2]=0;
-    if (i-3<600) it->ntsci[i-3]=0;
-    if (i-9<600) it->ntscq[i-9]=0;
-  }
-}
-
-enum {
-  A2_CMAP_HISTBITS=5,
-  A2_CMAP_LEVELS=2,
-  A2_CMAP_OFFSETS=4
-};
-
-#define A2_CMAP_INDEX(COLORMODE, LEVEL, HIST, OFFSET) \
-((((COLORMODE)*A2_CMAP_LEVELS+(LEVEL))<<A2_CMAP_HISTBITS)+(HIST))* \
-A2_CMAP_OFFSETS+(OFFSET)
-
-static void
-apple2(Display *dpy, Window window, int delay)
-{
-  int w,h,i,j,x,y,textrow,row,col,stepno,colormode,imgrow;
-  char c,*s;
-  struct timeval basetime_tv;
-  double next_actiontime;
-  XWindowAttributes xgwa;
-  int visclass;
-  int screen_xo,screen_yo;
-  XImage *image=NULL;
-  XGCValues gcv;
-  GC gc=NULL;
-  XImage *text_im=NULL;
-  unsigned long colors[A2_CMAP_INDEX(1, A2_CMAP_LEVELS-1,
-                                     (1<<A2_CMAP_HISTBITS)-1,
-                                     A2_CMAP_OFFSETS-3)+1];
-  int n_colors=0;
-  int screen_plan[24];
-  struct ntsc_dec *dec=NULL;
-  short *raw_rgb=NULL, *rrp;
-  struct apple2_state *st=NULL;
-  char *typing=NULL,*printing=NULL;
-  char printbuf[1024];
-  char prompt=']';
-  int simulate_user;
-  double tint_control,color_control,brightness_control,contrast_control;
-  double freq_error=0.0,freq_error_inc=0.0;
-  double horiz_desync=5.0;
-  int flutter_horiz_desync=0;
-  int flutter_tint=0;
-  double crtload[192];
-  int red_invprec,red_shift,green_invprec,green_shift,blue_invprec,blue_shift;
-  int fillptr, fillbyte;
-  int use_shm,use_cmap,use_color;
-  /* localbyteorder is 1 if MSB first, 0 otherwise */
-  unsigned int localbyteorder_loc = MSBFirst<<24;
-  int localbyteorder=*(char *)&localbyteorder_loc;
-#ifdef HAVE_XSHM_EXTENSION
-  XShmSegmentInfo shm_info;
-#endif
-  
-#ifdef HAVE_XSHM_EXTENSION
-  use_shm=get_boolean_resource ("useSHM", "Boolean");
-#else
-  use_shm=0;
-#endif
-
-  /* Model the video controls on a standard television */
-  tint_control = get_float_resource("apple2TVTint","Apple2TVTint");
-  color_control = get_float_resource("apple2TVColor","Apple2TVColor")/100.0;
-  brightness_control = get_float_resource("apple2TVBrightness",
-                                          "Apple2TVBrightness") / 100.0;
-  contrast_control = get_float_resource("apple2TVContrast",
-                                        "Apple2TVContrast") / 100.0;
-  simulate_user = get_boolean_resource("apple2SimulateUser",
-                                       "Apple2SimulateUser");
-
-  XGetWindowAttributes (dpy, window, &xgwa);
-  visclass=xgwa.visual->class;
-  red_shift=red_invprec=green_shift=green_invprec=blue_shift=blue_invprec=-1;
-  if (visclass == TrueColor || xgwa.visual->class == DirectColor) {
-    use_cmap=0;
-    use_color=!mono_p;
-  }
-  else if (visclass == PseudoColor || visclass == StaticColor) {
-    use_cmap=1;
-    use_color=!mono_p;
-  }
-  else {
-    use_cmap=1;
-    use_color=0;
-  }
-
-  /* The Apple II screen was 280x192, sort of. We expand the width to 300
-     pixels to allow for overscan. We then pick a size within the window
-     that's an integer multiple of 300x192. The small case happens when
-     we're displaying in a subwindow. Then it ends up showing the center
-     of the screen, which is OK. */
-  w=xgwa.width;
-  h = (xgwa.height/192)*192;
-  if (w<300) w=300;
-  if (h==0) h=192;
-
-  dec=(struct ntsc_dec *)malloc(sizeof(struct ntsc_dec));
-  
-  if (use_cmap) {
-    int hist,offset,level;
-    int colorprec=8;
-
-  cmap_again:
-    n_colors=0;
-    /* Typically allocates 214 distinct colors, but will scale back its
-       ambitions pretty far if it can't get them */
-    for (colormode=0; colormode<=use_color; colormode++) {
-      ntsc_set_demod(dec, tint_control, color_control, brightness_control,
-                     0.0, colormode);
-      for (level=0; level<2; level++) {
-        for (hist=0; hist<(1<<A2_CMAP_HISTBITS); hist++) {
-          for (offset=0; offset<4; offset++) {
-            int interpy,interpi,interpq,r,g,b;
-            int levelmult=level ? 64 : 32;
-            int prec=colormode ? colorprec : (colorprec*2+2)/3;
-            int precmask=(0xffff<<(16-prec))&0xffff;
-            XColor col;
-
-            if (A2_CMAP_INDEX(colormode,level,hist,offset) != n_colors) {
-              fprintf(stderr, "apple2: internal colormap allocation error\n");
-              goto bailout;
-            }
+  switch(*stepno) {
+  case 0:
+    
+    a2_init_memory_active(sim);
+    sim->dec->powerup = 1000.0;
 
-            for (i=0; i<600; i++) dec->pattern[i]=0;
-            for (i=0; i<A2_CMAP_HISTBITS; i++) {
-              dec->pattern[64+offset-i]=(hist>>i)&1;
-            }
-        
-            ntsc_to_yiq(dec);
-            interpy=dec->ntscy[63+offset];
-            interpi=dec->ntsci[63+offset];
-            interpq=dec->ntscq[63+offset];
-
-            r=(interpy + ((+68128*interpi+40894*interpq)>>16))*levelmult;
-            g=(interpy + ((-18087*interpi-41877*interpq)>>16))*levelmult;
-            b=(interpy + ((-72417*interpi+113312*interpq)>>16))*levelmult;
-            if (r<0) r=0;
-            if (r>65535) r=65535;
-            if (g<0) g=0;
-            if (g>65535) g=65535;
-            if (b<0) b=0;
-            if (b>65535) b=65535;
-          
-            col.red=r & precmask;
-            col.green=g & precmask;
-            col.blue=b & precmask;
-            col.pixel=0;
-            if (!XAllocColor(dpy, xgwa.colormap, &col)) {
-              XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L);
-              n_colors=0;
-              colorprec--;
-              if (colorprec<3) {
-                goto bailout;
-              }
-              goto cmap_again;
-            }
-            colors[n_colors++]=col.pixel;
-          }
-        }
-      }
+    if (random()%3==0) {
+      st->gr_mode=0;
+      *next_actiontime+=0.4;
+      *stepno=100;
     }
-  } else {
-    /* Is there a standard way to do this? Does this handle all cases? */
-    int shift, prec;
-    for (shift=0; shift<32; shift++) {
-      for (prec=1; prec<16 && prec<32-shift; prec++) {
-        unsigned long mask=(0xffffUL>>(16-prec)) << shift;
-        if (red_shift<0 && mask==xgwa.visual->red_mask)
-          red_shift=shift, red_invprec=16-prec;
-        if (green_shift<0 && mask==xgwa.visual->green_mask)
-          green_shift=shift, green_invprec=16-prec;
-        if (blue_shift<0 && mask==xgwa.visual->blue_mask)
-          blue_shift=shift, blue_invprec=16-prec;
-      }
+    else if (random()%4==0) {
+      st->gr_mode=A2_GR_LORES;
+      if (random()%3==0) st->gr_mode |= A2_GR_FULL;
+      *next_actiontime+=0.4;
+      *stepno=100;
     }
-    if (red_shift<0 || green_shift<0 || blue_shift<0) {
-      if (0) fprintf(stderr,"Can't figure out color space\n");
-      goto bailout;
+    else if (random()%2==0) {
+      st->gr_mode=A2_GR_HIRES;
+      *stepno=300;
     }
-    raw_rgb=(short *)calloc(w*3, sizeof(short));
-  }
-
-  gcv.background=0;
-  gc = XCreateGC(dpy, window, GCBackground, &gcv);
-  XSetWindowBackground(dpy, window, gcv.background);
-  XClearWindow(dpy,window);
-
-  screen_xo=(xgwa.width-w)/2;
-  screen_yo=(xgwa.height-h)/2;
-
-  if (use_shm) {
-#ifdef HAVE_XSHM_EXTENSION
-    image = create_xshm_image (dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 
-                               &shm_info, w, h);
-#endif
-    if (!image) {
-      fprintf(stderr, "create_xshm_image failed\n");
-      use_shm=0;
+    else {
+      st->gr_mode=A2_GR_HIRES;
+      *next_actiontime+=0.4;
+      *stepno=100;
     }
-  }
-  if (!image) {
-    image = XCreateImage(dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 0,
-                         w, h, 8, 0);
-    image->data = (char *)calloc(image->height, image->bytes_per_line);
-  }
-
-  st=(struct apple2_state *)calloc(1,sizeof(struct apple2_state));
-
-  /*
-    Generate the font. It used a 5x7 font which looks a lot like the standard X
-    6x10 font, with a few differences. So we render up all the uppercase
-    letters of 6x10, and make a few tweaks (like putting a slash across the
-    zero) according to fixfont.
-   */
-  {
-    const char *def_font="6x10";
-    XFontStruct *font;
-    Pixmap text_pm;
-    GC gc;
-    
-    font = XLoadQueryFont (dpy, def_font);
-    if (!font) {
-      fprintf(stderr,"Can't load font %s\n",def_font);
-      goto bailout;
-    }
-    
-    text_pm=XCreatePixmap(dpy, window, 64*7, 8, xgwa.depth);
-    
-    gcv.foreground=1;
-    gcv.background=0;
-    gcv.font=font->fid;
-    gc=XCreateGC(dpy, text_pm, GCFont|GCBackground|GCForeground, &gcv);
-    
-    XSetForeground(dpy, gc, 0);
-    XFillRectangle(dpy, text_pm, gc, 0, 0, 64*7, 8);
-    XSetForeground(dpy, gc, 1);
-    for (i=0; i<64; i++) {
-      char c=32+i;
-      int x=7*i+1;
-      int y=7;
-      if (c=='0') {
-        c='O';
-        XDrawString(dpy, text_pm, gc, x, y, &c, 1);
+    break;
+
+  case 100:
+    /* An illegal instruction or a reset caused it to drop into the
+       assembly language monitor, where you could disassemble code & view
+       data in hex. */
+    if (random()%3==0) {
+      char ibytes[128];
+      char itext[128];
+      int addr=0xd000+random()%0x3000;
+      sprintf(ibytes,
+              "%02X",random()%0xff);
+      sprintf(itext,
+              "???");
+      sprintf(sim->printing_buf,
+              "\n\n"
+              "%04X: %-15s %s\n"
+              " A=%02X X=%02X Y=%02X S=%02X F=%02X\n"
+              "*",
+              addr,ibytes,itext,
+              random()%0xff, random()%0xff,
+              random()%0xff, random()%0xff,
+              random()%0xff);
+      sim->printing=sim->printing_buf;
+      a2_goto(st,23,1);
+      if (st->gr_mode) {
+        *stepno=180;
       } else {
-        XDrawString(dpy, text_pm, gc, x, y, &c, 1);
+        *stepno=200;
       }
+      sim->prompt='*';
+      *next_actiontime += 2.0 + (random()%1000)*0.0002;
     }
-    text_im = XGetImage(dpy, text_pm, 0, 0, 64*7, 8, ~0L, ZPixmap);
-    XFreeGC(dpy, gc);
-    XFreePixmap(dpy, text_pm);
-
-    for (i=0; a2_fixfont[i]; i++) {
-      XPutPixel(text_im, a2_fixfont[i]&0x3ff,
-                (a2_fixfont[i]>>10)&0xf,
-                (a2_fixfont[i]>>15)&1);
-    }
-  }
-
-  /*
-    Simulate plausible initial memory contents.
-   */
-  {
-    int addr=0;
-    while (addr<0x4000) {
-      int n;
-
-      switch (random()%4) {
-      case 0:
-      case 1:
-        n=random()%500;
-        for (i=0; i<n && addr<0x4000; i++) {
-          u_char rb=((random()%6==0 ? 0 : random()%16) |
-                     ((random()%5==0 ? 0 : random()%16)<<4));
-          a2_poke(st, addr++, rb);
-        }
-        break;
-      
-      case 2:
-        /* Simulate shapes stored in memory. We use the font since we have it.
-           Unreadable, since rows of each character are stored in consecutive
-           bytes. It was typical to store each of the 7 possible shifts of
-           bitmaps, for fastest blitting to the screen. */
-        x=random()%(text_im->width);
-        for (i=0; i<100; i++) {
-          for (y=0; y<8; y++) {
-            c=0;
-            for (j=0; j<8; j++) {
-              c |= XGetPixel(text_im, (x+j)%text_im->width, y)<<j;
-            }
-            a2_poke(st, addr++, c);
-          }
-          x=(x+1)%(text_im->width);
-        }
-        break;
-
-      case 3:
-        if (addr>0x2000) {
-          n=random()%200;
-          for (i=0; i<n && addr<0x4000; i++) {
-            a2_poke(st, addr++, 0);
-          }
-        }
-        break;
-
-      }
+    else {
+      /* Lots of programs had at least their main functionality in
+         Applesoft Basic, which had a lot of limits (memory, string
+         length, etc) and would sometimes crash unexpectedly. */
+      sprintf(sim->printing_buf,
+              "\n"
+              "\n"
+              "\n"
+              "?%s IN %d\n"
+              "\001]",
+              apple2_basic_errors[random() %
+                                  (sizeof(apple2_basic_errors)
+                                   /sizeof(char *))],
+              (1000*(random()%(random()%59+1)) +
+               100*(random()%(random()%9+1)) +
+               5*(random()%(random()%199+1)) +
+               1*(random()%(random()%(random()%2+1)+1))));
+      sim->printing=sim->printing_buf;
+      a2_goto(st,23,1);
+      *stepno=110;
+      sim->prompt=']';
+      *next_actiontime += 2.0 + (random()%1000)*0.0002;
     }
-  }
-  
-  if (random()%4==0 &&
-      !use_cmap && use_color &&
-      xgwa.visual->bits_per_rgb>=8) {
-    flutter_tint=1;
-  }
-  else if (random()%3==0) {
-    flutter_horiz_desync=1;
-  }
-
-  crtload[0]=0.0;
-  stepno=0;
-  a2_goto(st,23,0);
-  gettimeofday(&basetime_tv, NULL);
-  if (random()%2==0) basetime_tv.tv_sec -= 1; /* random blink phase */
-  next_actiontime=0.0;
-  fillptr=fillbyte=0;
-  while (1) {
-    double curtime,blinkphase;
-    int startdisplayrow=0;
-    int cheapdisplay=0;
-    int nodelay=0;
-    {
-      struct timeval curtime_tv;
-      gettimeofday(&curtime_tv, NULL);
-      curtime=(curtime_tv.tv_sec - basetime_tv.tv_sec) + 
-        0.000001*(curtime_tv.tv_usec - basetime_tv.tv_usec);
+    break;
+
+  case 110:
+    if (random()%3==0) {
+      /* This was how you reset the Basic interpreter. The sort of
+         incantation you'd have on a little piece of paper taped to the
+         side of your machine */
+      sim->typing="CALL -1370";
+      *stepno=120;
     }
-    if (curtime>delay) goto finished;
-
-    if (bsod_sleep(dpy,0)) goto finished;
-
-    if (flutter_tint && st->gr_mode && !printing) {
-      /* Oscillator instability. Look for freq_error below. We should only do
-         this with color depth>=8, since otherwise you see pixels changing. */
-      freq_error_inc += (-0.10*freq_error_inc
-                         + ((int)(random()&0xff)-0x80) * 0.01);
-      freq_error += freq_error_inc;
-      a2_invalidate(st);
-      nodelay=1;
+    else if (random()%2==0) {
+      sim->typing="CATALOG\n";
+      *stepno=170;
     }
-    else if (flutter_horiz_desync) {
-      /* Horizontal sync during vertical sync instability. */
-      horiz_desync += (-0.10*(horiz_desync-3.0) +
-                       ((int)(random()&0xff)-0x80) * 
-                       ((int)(random()&0xff)-0x80) *
-                       ((int)(random()&0xff)-0x80) * 0.0000003);
-      for (i=0; i<3; i++) st->rowimage[i]=-1;
-      nodelay=1;
-    } 
-
-    /* It's super-important to get the cursor/text flash out at exactly the
-       right time, or it looks wrong. So if we're almost due for a blink, wait
-       for it so we don't miss it in the middle of a screen update. */
-    blinkphase=curtime/0.8;
-    if (blinkphase-floor(blinkphase)>0.7 && !printing && !nodelay) {
-      /* We're about to blink */
-      int delay = ((1.0-(blinkphase-floor(blinkphase)))*0.8) * 1000000;
-      if (delay<1000) delay=1000;
-      usleep(delay);
-      continue;
+    else {
+      *next_actiontime+=1.0;
+      *stepno=999;
     }
-
-    /* The blinking rate was controlled by 555 timer with a resistor/capacitor
-       time constant. Because the capacitor was electrolytic, the flash rate
-       varied somewhat between machines. I'm guessing 1.6 seconds/cycle was
-       reasonable. (I soldered a resistor in mine to make it blink faster.) */
-    i=st->blink;
-    st->blink=((int)blinkphase)&1;
-    if (st->blink!=i && !(st->gr_mode&A2_GR_FULL)) {
-      int downcounter=0;
-      /* For every row with blinking text, set the changed flag. This basically
-         works great except with random screen garbage in text mode, when we
-         end up redrawing the whole screen every second */
-      for (row=(st->gr_mode ? 20 : 0); row<24; row++) {
-        for (col=0; col<40; col++) {
-          c=st->textlines[row][col];
-          if ((c & 0xc0) == 0x40) {
-            downcounter=4;
-            break;
-          }
-        }
-        if (downcounter>0) {
-          st->rowimage[row]=-1;
-          downcounter--;
-        }
-      }
-      st->rowimage[st->cursy]=-1;
-      startdisplayrow=random()%24;
-    } 
-    else if (next_actiontime > curtime && !printing && !nodelay) {
-      int delay = (next_actiontime-curtime)*1000000;
-
-      if (delay>100000) delay=100000;
-      if (delay<1000) delay=1000;
-      usleep(delay);
-      continue;
+    break;
+
+  case 120:
+    *stepno=130;
+    *next_actiontime += 0.5;
+    break;
+
+  case 130:
+    st->gr_mode=0;
+    a2_cls(st);
+    a2_goto(st,0,16);
+    for (s="APPLE ]["; *s; s++) a2_printc(st,*s);
+    a2_goto(st,23,0);
+    a2_printc(st,']');
+    *next_actiontime+=1.0;
+    *stepno=999;
+    break;
+
+  case 170:
+    if (random()%50==0) {
+      sprintf(sim->printing_buf,
+              "\nDISK VOLUME 254\n\n"
+              " A 002 HELLO\n"
+              "\n"
+              "]");
+      sim->printing=sim->printing_buf;
     }
-
-    if (printing) {
-      cheapdisplay=1;
-      while (*printing) {
-        if (*printing=='\001') { /* pause */
-          printing++;
-          for (i=20; i<24; i++) st->rowimage[i]=-1;
-          break;
-        } 
-        else if (*printing=='\n') {
-          a2_printc(st,*printing);
-          printing++;
-          break;
-        }
-        else {
-          a2_printc(st,*printing);
-          printing++;
-        }
-      }
-      if (!*printing) printing=NULL;
+    else {
+      sprintf(sim->printing_buf,"\n?%s\n]",
+              apple2_dos_errors[random()%
+                                (sizeof(apple2_dos_errors) /
+                                 sizeof(char *))]);
+      sim->printing=sim->printing_buf;
     }
-    else if (curtime >= next_actiontime) {
-      if (typing) {
-        /* If we're in the midst of typing a string, emit a character with
-           random timing. */
-        a2_printc(st, *typing);
-        if (*typing=='\n') {
-          next_actiontime = curtime;
-        } else {
-          next_actiontime = curtime + (random()%1000)*0.0003 + 0.3;
-        }
-        typing++;
-
-        if (!*typing) typing=NULL;
-
-      } 
-      else {
-        next_actiontime=curtime;
-
-        switch(stepno) {
-        case 0:
-          a2_invalidate(st);
-          if (0) {
-            /*
-              For testing color rendering. The spec is:
-                           red grn blu
-              0  black       0   0   0
-              1  red       227  30  96
-              2  dk blue    96  78 189
-              3  purple    255  68 253
-              4  dk green    0 163  96
-              5  gray      156 156 156
-              6  med blue   20 207 253
-              7  lt blue   208 195 255
-              8  brown      96 114   3
-              9  orange    255 106  60
-              10 grey      156 156 156
-              11 pink      255 160 208
-              12 lt green   20 245  60
-              13 yellow    208 221 141
-              14 aqua      114 255 208
-              15 white     255 255 255
-            */
-            st->gr_mode=A2_GR_LORES;
-            for (row=0; row<24; row++) {
-              for (col=0; col<40; col++) {
-                st->textlines[row][col]=(row&15)*17;
-              }
-            }
-            next_actiontime+=0.4;
-            stepno=88;
-          }
-          else if (random()%3==0) {
-            st->gr_mode=0;
-            next_actiontime+=0.4;
-            stepno=88;
-          }
-          else if (random()%4==0) {
-            st->gr_mode=A2_GR_LORES;
-            if (random()%3==0) st->gr_mode |= A2_GR_FULL;
-            next_actiontime+=0.4;
-            stepno=88;
-          } 
-          else if (random()%2==0) {
-            st->gr_mode=A2_GR_HIRES;
-            stepno=73;
-          }
-          else {
-            st->gr_mode=A2_GR_HIRES;
-            next_actiontime+=0.4;
-            stepno=88;
-          }
-          break;
-
-        case 88:
-          /* An illegal instruction or a reset caused it to drop into the
-             assembly language monitor, where you could disassemble code & view
-             data in hex. */
-          if (random()%3==0) {
-            char ibytes[128];
-            char itext[128];
-            int addr=0xd000+random()%0x3000;
-            sprintf(ibytes,
-                    "%02X",random()%0xff);
-            sprintf(itext,
-                    "???");
-            sprintf(printbuf,
-                    "\n\n"
-                    "%04X: %-15s %s\n"
-                    " A=%02X X=%02X Y=%02X S=%02X F=%02X\n"
-                    "*",
-                    addr,ibytes,itext,
-                    random()%0xff, random()%0xff,
-                    random()%0xff, random()%0xff,
-                    random()%0xff);
-            printing=printbuf;
-            a2_goto(st,23,1);
-            if (st->gr_mode) {
-              stepno=11;
-            } else {
-              stepno=13;
-            }
-            prompt='*';
-            next_actiontime += 2.0 + (random()%1000)*0.0002;
-          }
-          else {
-            /* Lots of programs had at least their main functionality in
-               Applesoft Basic, which had a lot of limits (memory, string
-               length, etc) and would sometimes crash unexpectedly. */
-            sprintf(printbuf,
-                    "\n"
-                    "\n"
-                    "\n"
-                    "?%s IN %d\n"
-                    "\001]",
-                    apple2_basic_errors[random() %
-                                        (sizeof(apple2_basic_errors)
-                                         /sizeof(char *))],
-                    (1000*(random()%(random()%59+1)) +
-                     100*(random()%(random()%9+1)) +
-                     5*(random()%(random()%199+1)) +
-                     1*(random()%(random()%(random()%2+1)+1))));
-            printing=printbuf;
-            a2_goto(st,23,1);
-            stepno=1;
-            prompt=']';
-            next_actiontime += 2.0 + (random()%1000)*0.0002;
-          }
-          break;
-      
-        case 1:
-          if (simulate_user && random()%3==0) {
-            /* This was how you reset the Basic interpreter. The sort of
-               incantation you'd have on a little piece of paper taped to the
-               side of your machine */
-            typing="CALL -1370";
-            stepno=2;
-          } 
-          else if (simulate_user && random()%2==0) {
-            typing="CATALOG\n";
-            stepno=22;
-          }
-          else {
-            next_actiontime+=1.0;
-            stepno=6;
-          }
-          break;
-
-        case 2:
-          stepno=3;
-          next_actiontime += 0.5;
-          break;
-      
-        case 3:
-          st->gr_mode=0;
-          a2_cls(st);
-          a2_goto(st,0,16);
-          for (s="APPLE ]["; *s; s++) a2_printc(st,*s);
-          a2_goto(st,23,0);
-          a2_printc(st,']');
-          next_actiontime+=1.0;
-          stepno=6;
-          break;
-
-        case 6:
-          if (simulate_user && random()%50==0 && 0) { /* disabled, too goofy */
-            typing="10 PRINT \"TRS-80S SUCK!!!\"\n"
-              "]20 GOTO 10\n"
-              "]RUN";
-            stepno=7;
-          }
-          else {
-            stepno=8;
-            next_actiontime += delay;
-          }
-          break;
-
-        case 7:
-          for (i=0; i<30; i++) {
-            for (s="\nTRS-80S SUCK"; *s; s++) a2_printc(st,*s);
-          }
-          stepno=8;
-          next_actiontime+=delay;
-
-        case 8:
-          break;
-
-        case 22:
-          if (random()%50==0) {
-            sprintf(printbuf,"\nDISK VOLUME 254\n\n"
-                    " A 002 HELLO\n"
-                    "\n"
-                    "]");
-            printing=printbuf;
-          }
-          else {
-            sprintf(printbuf,"\n?%s\n]",
-                    apple2_dos_errors[random()%
-                                      (sizeof(apple2_dos_errors) /
-                                       sizeof(char *))]);
-            printing=printbuf;
-          }
-          stepno=6;
-          next_actiontime+=1.0;
-          break;
-
-        case 11:
-          if (simulate_user && random()%2==0) {
-            /* This was how you went back to text mode in the monitor */
-            typing="FB4BG";
-            stepno=12;
-          } else {
-            next_actiontime+=1.0;
-            stepno=6;
-          }
-          break;
-
-        case 12:
-          st->gr_mode=0;
-          a2_invalidate(st);
-          a2_printc(st,'\n');
-          a2_printc(st,'*');
-          stepno=13;
-          next_actiontime+=2.0;
-          break;
-
-        case 13:
-          /* This reset things into Basic */
-          if (simulate_user && random()%2==0) {
-            typing="FAA6G";
-            stepno=2;
-          }
-          else {
-            stepno=8;
-            next_actiontime+=delay;
-          }
-          break;
-
-        case 73:
-          for (i=0; i<1500; i++) {
-            a2_poke(st, fillptr, fillbyte);
-            fillptr++;
-            fillbyte = (fillbyte+1)&0xff;
-          }
-          next_actiontime += 0.08;
-          /* When you hit c000, it changed video settings */
-          if (fillptr>=0xc000) {
-            a2_invalidate(st);
-            st->gr_mode=0;
-          }
-          /* And it seemed to reset around here, I dunno why */
-          if (fillptr>=0xcf00) stepno=3;
-          break;
-        }
-      }
+    *stepno=999;
+    *next_actiontime+=1.0;
+    break;
+
+  case 180:
+    if (random()%2==0) {
+      /* This was how you went back to text mode in the monitor */
+      sim->typing="FB4BG";
+      *stepno=190;
+    } else {
+      *next_actiontime+=1.0;
+      *stepno=999;
     }
-
-    /* Now, we turn the data in the Apple II video into a screen display. This
-       is interesting because of the interaction with the NTSC color decoding
-       in a color television. */
-
-    colormode=use_color && st->gr_mode!=0;
-    if (!use_cmap) {
-      ntsc_set_demod(dec, tint_control, color_control, brightness_control,
-                     freq_error, colormode);
+    break;
+
+  case 190:
+    st->gr_mode=0;
+    a2_invalidate(st);
+    a2_printc(st,'\n');
+    a2_printc(st,'*');
+    *stepno=200;
+    *next_actiontime+=2.0;
+    break;
+
+  case 200:
+    /* This reset things into Basic */
+    if (random()%2==0) {
+      sim->typing="FAA6G";
+      *stepno=120;
     }
-    imgrow=0;
-    for (textrow=0; textrow<24; textrow++) {
-      if (st->rowimage[textrow] == textrow) {
-        screen_plan[textrow]=0;
-      }
-      else if (cheapdisplay && st->rowimage[textrow]>=0 &&
-               textrow<21 && st->rowimage[textrow]<21 && 
-               st->rowimage[textrow]>=2 && textrow>=2 &&
-               (st->rowimage[textrow]+1)*h/24 + screen_xo <= xgwa.height) {
-        screen_plan[textrow]= A2_SP_COPY | st->rowimage[textrow];
-        for (i=0; i<8; i++) {
-          crtload[textrow*8+i]=crtload[st->rowimage[textrow]*8+i];
-        }
-        startdisplayrow=0;
-      }
-      else {
-        st->rowimage[textrow]=imgrow;
-        screen_plan[textrow]=imgrow | A2_SP_PUT;
-        
-        for (row=textrow*8; row<textrow*8+8; row++) {
-          char *pp;
-          int pixmultinc,pixbright;
-          int scanstart_i, scanend_i;
-          int squishright_i, squishdiv;
-          int pixrate;
-          double bloomthisrow,shiftthisrow;
-          int ytop=(imgrow*h/24) + ((row-textrow*8) * h/192);
-          int ybot=ytop+h/192;
-
-          /* First we generate the pattern that the video circuitry shifts out
-             of memory. It has a 14.something MHz dot clock, equal to 4 times
-             the color burst frequency. So each group of 4 bits defines a
-             color.  Each character position, or byte in hires, defines 14
-             dots, so odd and even bytes have different color spaces. So,
-             pattern[0..600] gets the dots for one scan line. */
-
-          memset(dec->pattern,0,sizeof(dec->pattern));
-          pp=dec->pattern+20;
-        
-          if ((st->gr_mode&A2_GR_HIRES) && (row<160 ||
-                                            (st->gr_mode&A2_GR_FULL))) {
-
-            /* Emulate the mysterious pink line, due to a bit getting
-               stuck in a shift register between the end of the last
-               row and the beginning of this one. */
-            if ((st->hireslines[row][0] & 0x80) &&
-                (st->hireslines[row][39]&0x40)) {
-              pp[-1]=1;
-            }
-
-            for (col=0; col<40; col++) {
-              u_char b=st->hireslines[row][col];
-              int shift=(b&0x80)?0:1;
-
-              /* Each of the low 7 bits in hires mode corresponded to 2 dot
-                 clocks, shifted by one if the high bit was set. */
-              for (i=0; i<7; i++) {
-                pp[shift+1] = pp[shift] =(b>>i)&1;
-                pp+=2;
-              }
-            }
-          } 
-          else if ((st->gr_mode&A2_GR_LORES) && (row<160 ||
-                                                 (st->gr_mode&A2_GR_FULL))) {
-            for (col=0; col<40; col++) {
-              u_char nib=(st->textlines[textrow][col] >> (((row/4)&1)*4))&0xf;
-              /* The low or high nybble was shifted out one bit at a time. */
-              for (i=0; i<14; i++) {
-                *pp = (nib>>((col*14+i)&3))&1;
-                pp++;
-              }
-            }
-          }
-          else {
-            for (col=0; col<40; col++) {
-              int rev;
-              c=st->textlines[textrow][col];
-              /* hi bits control inverse/blink as follows:
-                  0x00: inverse
-                  0x40: blink
-                  0x80: normal
-                  0xc0: normal */
-              rev=!(c&0x80) && (!(c&0x40) || st->blink);
-
-              for (i=0; i<7; i++) {
-                for (i=0; i<7; i++) {
-                  unsigned long pix=XGetPixel(text_im,
-                                              ((c&0x3f)^0x20)*7+i, row%8);
-                  pp[1] = pp[2] = pix^rev;
-                  pp+=2;
-                }
-              }
-            }
-          }
-
-          /*
-            Interpolate the 600-dotclock line into however many horizontal
-            screen pixels we're using, and convert to RGB. 
-
-            We add some 'bloom', variations in the horizontal scan width with
-            the amount of brightness, extremely common on period TV sets. They
-            had a single oscillator which generated both the horizontal scan
-            and (during the horizontal retrace interval) the high voltage for
-            the electron beam. More brightness meant more load on the
-            oscillator, which caused an decrease in horizontal deflection. Look
-            for (bloomthisrow).
-
-            Also, the A2 did a bad job of generating horizontal sync pulses
-            during the vertical blanking interval. This, and the fact that the
-            horizontal frequency was a bit off meant that TVs usually went a
-            bit out of sync during the vertical retrace, and the top of the
-            screen would be bent a bit to the left or right. Look for
-            (shiftthisrow).
-
-            We also add a teeny bit of left overscan, just enough to be
-            annoying, but you can still read the left column of text.
-            
-            We also simulate compression & brightening on the right side of the
-            screen. Most TVs do this, but you don't notice because they
-            overscan so it's off the right edge of the CRT. But the A2 video
-            system used so much of the horizontal scan line that you had to
-            crank the horizontal width down in order to not lose the right few
-            characters, and you'd see the compression on the right
-            edge. Associated with compression is brightening; since the
-            electron beam was scanning slower, the same drive signal hit the
-            phosphor harder. Look for (squishright_i) and (squishdiv).
-          */
-
-          for (i=j=0; i<600; i++) {
-            j += dec->pattern[i];
-          }
-          crtload[row] = (crtload[row>1 ? row-1 : 0]) * 0.98 + 0.02*(j/600.0) +
-            (row>180 ? (row-180)*(row-180)*0.0005 : 0.0);
-          bloomthisrow = -10.0 * crtload[row];
-          shiftthisrow=((row<18) ? ((18-row)*(18-row)* 0.002 + (18-row)*0.05)
-                        * horiz_desync : 0.0);
-
-          scanstart_i=(int)((bloomthisrow+shiftthisrow+18.0)*65536.0);
-          if (scanstart_i<0) scanstart_i=0;
-          if (scanstart_i>30*65536) scanstart_i=30*65536;
-          scanend_i=599*65536;
-          squishright_i=scanstart_i + 530*65536;
-          squishdiv=w/15;
-          pixrate=(int)((560.0-2.0*bloomthisrow)*65536.0/w);
-          
-          if (use_cmap) {
-            for (y=ytop; y<ybot; y++) {
-              int level=(!(y==ytop && ybot-ytop>=3) &&
-                         !(y==ybot-1 && ybot-ytop>=5));
-              int hist=0;
-              int histi=0;
-
-              pixmultinc=pixrate;
-              for (x=0, i=scanstart_i;
-                   x<w && i<scanend_i;
-                   x++, i+=pixmultinc) {
-                int pati=(i>>16);
-                int offset=pati&3;
-                while (pati>=histi) {
-                  hist=(((hist<<1) & ((1<<A2_CMAP_HISTBITS)-1)) |
-                        dec->pattern[histi]);
-                  histi++;
-                }
-                XPutPixel(image, x, y, 
-                          colors[A2_CMAP_INDEX(colormode,level,hist,offset)]);
-                if (i >= squishright_i) {
-                  pixmultinc += pixmultinc/squishdiv;
-                }
-              }
-              for ( ; x<w; x++) {
-                XPutPixel(image, x, y, colors[0]);
-              }
-            }
-          } else {
-
-            ntsc_to_yiq(dec);
-
-            pixbright=(int)(contrast_control*65536.0);
-            pixmultinc=pixrate;
-            for (x=0, i=scanstart_i, rrp=raw_rgb;
-                 x<w && i<scanend_i;
-                 x++, i+=pixmultinc, rrp+=3) {
-              int pixfrac=i&0xffff;
-              int invpixfrac=65536-pixfrac;
-              int pati=i>>16;
-              int r,g,b;
-
-              int interpy=((dec->ntscy[pati]*invpixfrac + 
-                            dec->ntscy[pati+1]*pixfrac)>>16);
-              int interpi=((dec->ntsci[pati]*invpixfrac + 
-                            dec->ntsci[pati+1]*pixfrac)>>16);
-              int interpq=((dec->ntscq[pati]*invpixfrac + 
-                            dec->ntscq[pati+1]*pixfrac)>>16);
-
-              /*
-                According to the NTSC spec, Y,I,Q are generated as:
-                
-                y=0.30 r + 0.59 g + 0.11 b
-                i=0.60 r - 0.28 g - 0.32 b
-                q=0.21 r - 0.52 g + 0.31 b
-                
-                So if you invert the implied 3x3 matrix you get what standard
-                televisions implement with a bunch of resistors (or directly in
-                the CRT -- don't ask):
-                
-                r = y + 0.948 i + 0.624 q
-                g = y - 0.276 i - 0.639 q
-                b = y - 1.105 i + 1.729 q
-                
-                These coefficients are below in 16.16 format.
-              */
-
-              r=((interpy + ((+68128*interpi+40894*interpq)>>16))*pixbright)
-                                                           >>16;
-              g=((interpy + ((-18087*interpi-41877*interpq)>>16))*pixbright)
-                                                           >>16;
-              b=((interpy + ((-72417*interpi+113312*interpq)>>16))*pixbright)
-                                                            >>16;
-              if (r<0) r=0;
-              if (g<0) g=0;
-              if (b<0) b=0;
-              rrp[0]=r;
-              rrp[1]=g;
-              rrp[2]=b;
-
-              if (i>=squishright_i) {
-                pixmultinc += pixmultinc/squishdiv;
-                pixbright += pixbright/squishdiv;
-              }
-            }
-            for ( ; x<w; x++, rrp+=3) {
-              rrp[0]=rrp[1]=rrp[2]=0;
-            }
-
-            for (y=ytop; y<ybot; y++) {
-              /* levelmult represents the vertical size of scan lines. Each
-                 line is brightest in the middle, and there's a dark band
-                 between them. */
-              int levelmult;
-              double levelmult_fp=(y + 0.5 - (ytop+ybot)*0.5) / (ybot-ytop);
-              levelmult_fp = 1.0-(levelmult_fp*levelmult_fp*levelmult_fp
-                                  *levelmult_fp)*16.0;
-              if (levelmult_fp<0.0) levelmult_fp=0.0;
-              levelmult = (int)(64.9*levelmult_fp);
-
-              /* Fast special cases to avoid the slow XPutPixel. Ugh. It goes
-                  to show why standard graphics sw has to be fast, or else
-                  people will have to work around it and risk incompatibility.
-                  The quickdraw folks understood this. The other answer would
-                  be for X11 to have fewer formats for bitm.. oh, never
-                  mind. If neither of these cases work (they probably cover 99%
-                  of setups) it falls back on the Xlib routines. */
-              if (image->format==ZPixmap && image->bits_per_pixel==32 && 
-                  sizeof(unsigned long)==4 &&
-                  image->byte_order==localbyteorder) {
-                unsigned long *pixelptr =
-                  (unsigned long *) (image->data + y * image->bytes_per_line);
-                for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
-                  unsigned long ntscri, ntscgi, ntscbi;
-                  ntscri=((unsigned long)rrp[0])*levelmult;
-                  ntscgi=((unsigned long)rrp[1])*levelmult;
-                  ntscbi=((unsigned long)rrp[2])*levelmult;
-                  if (ntscri>65535) ntscri=65535;
-                  if (ntscgi>65535) ntscgi=65535;
-                  if (ntscbi>65535) ntscbi=65535;
-                  *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
-                    ((ntscgi>>green_invprec)<<green_shift) |
-                    ((ntscbi>>blue_invprec)<<blue_shift);
-                }
-              }
-              else if (image->format==ZPixmap && image->bits_per_pixel==16 && 
-                       sizeof(unsigned short)==2 &&
-                       image->byte_order==localbyteorder) {
-                unsigned short *pixelptr =
-                (unsigned short *)(image->data + y*image->bytes_per_line);
-                for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
-                  unsigned long ntscri, ntscgi, ntscbi;
-                  ntscri=((unsigned long)rrp[0])*levelmult;
-                  ntscgi=((unsigned long)rrp[1])*levelmult;
-                  ntscbi=((unsigned long)rrp[2])*levelmult;
-                  if (ntscri>65535) ntscri=65535;
-                  if (ntscgi>65535) ntscgi=65535;
-                  if (ntscbi>65535) ntscbi=65535;
-                  *pixelptr++ = ((ntscri>>red_invprec)<<red_shift) |
-                    ((ntscgi>>green_invprec)<<green_shift) |
-                    ((ntscbi>>blue_invprec)<<blue_shift);
-                }
-                
-              }
-              else {
-                for (x=0, rrp=raw_rgb; x<w; x++, rrp+=3) {
-                  unsigned long pixel, ntscri, ntscgi, ntscbi;
-                  /* Convert to 16-bit color values, with saturation. The ntscr
-                     values are 22.10 fixed point, and levelmult is 24.6, so we
-                     get 16 bits out*/
-                  ntscri=((unsigned long)rrp[0])*levelmult;
-                  ntscgi=((unsigned long)rrp[1])*levelmult;
-                  ntscbi=((unsigned long)rrp[2])*levelmult;
-                  if (ntscri>65535) ntscri=65535;
-                  if (ntscgi>65535) ntscgi=65535;
-                  if (ntscbi>65535) ntscbi=65535;
-                  pixel = ((ntscri>>red_invprec)<<red_shift) |
-                    ((ntscgi>>green_invprec)<<green_shift) |
-                    ((ntscbi>>blue_invprec)<<blue_shift);
-                  XPutPixel(image, x, y, pixel);
-                }
-              }
-            }
-          }
-        }
-        imgrow++;
-      }
+    else {
+      *stepno=999;
+      *next_actiontime+=sim->delay;
     }
+    break;
 
-    /* For just the the rows which changed, blit the image to the screen. */
-    for (textrow=0; textrow<24; ) {
-      int top,bot,srcrow,srctop,nrows;
-      
-      nrows=1;
-      while (textrow+nrows < 24 &&
-             screen_plan[textrow+nrows] == screen_plan[textrow]+nrows)
-        nrows++;
-
-      top=h*textrow/24;
-      bot=h*(textrow+nrows)/24;
-      srcrow=screen_plan[textrow]&A2_SP_ROWMASK;
-      srctop=srcrow*h/24;
-
-      if (screen_plan[textrow] & A2_SP_COPY) {
-        if (0) printf("Copy %d screenrows %d to %d\n", nrows, srcrow, textrow);
-        XCopyArea(dpy, window, window, gc,
-                  screen_xo, screen_yo + srctop,
-                  w, bot-top,
-                  screen_xo, screen_yo + top);
-      }
-      else if (screen_plan[textrow] & A2_SP_PUT) {
-        if (0) printf("Draw %d imgrows %d to %d\n", nrows, srcrow, textrow);
-        if (use_shm) {
-#ifdef HAVE_XSHM_EXTENSION
-          XShmPutImage(dpy, window, gc, image, 
-                       0, srctop, screen_xo, screen_yo + top,
-                       w, bot-top, False);
-#endif
-        } else {
-          XPutImage(dpy, window, gc, image, 
-                    0, srctop,
-                    screen_xo, screen_yo + top,
-                    w, bot-top);
-        }
-      }
-      textrow += nrows;
+  case 300:
+    for (i=0; i<1500; i++) {
+      a2_poke(st, mine->fillptr, mine->fillbyte);
+      mine->fillptr++;
+      mine->fillbyte = (mine->fillbyte+1)&0xff;
     }
-    XSync(dpy,0);
-
-    for (textrow=0; textrow<24; textrow++) {
-      st->rowimage[textrow]=textrow;
+    *next_actiontime += 0.08;
+    /* When you hit c000, it changed video settings */
+    if (mine->fillptr>=0xc000) {
+      a2_invalidate(st);
+      st->gr_mode=0;
     }
-  }
+    /* And it seemed to reset around here, I dunno why */
+    if (mine->fillptr>=0xcf00) *stepno=130;
+    break;
 
- finished:
-  XSync(dpy,False);
-  XClearWindow(dpy, window);
-  goto cleanup;
-
- bailout:
-  ;
+  case 999:
+    break;
 
- cleanup:
-  if (image) {
-    if (use_shm) {
-#ifdef HAVE_XSHM_EXTENSION
-      destroy_xshm_image(dpy, image, &shm_info);
-#endif
-    } else {
-      XDestroyImage(image);
-    }
-    image=NULL;
+  case A2CONTROLLER_FREE:
+    free(mine);
+    break;
   }
-  if (text_im) XDestroyImage(text_im);
-  if (gc) XFreeGC(dpy, gc);
-  if (st) free(st);
-  if (raw_rgb) free(raw_rgb);
-  if (dec) free(dec);
-  if (n_colors) XFreeColors(dpy, xgwa.colormap, colors, n_colors, 0L);
 }
 
+static void
+apple2crash (Display* dpy, Window window, int delay)
+{
+  apple2 (dpy, window, delay, a2controller_crash);
+}
 
-\f
 char *progclass = "BSOD";
 
 char *defaults [] = {
@@ -4084,6 +2965,7 @@ char *defaults [] = {
   "*doApple2:              True",
   "*doOS390:               True",
   "*doVMS:                True",
+  "*doHVX:                True",
 
   ".Windows.font:         -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
   ".Windows.font2:        -*-courier-bold-r-*-*-*-180-*-*-m-*-*-*",
@@ -4124,6 +3006,11 @@ char *defaults [] = {
   ".SCO.foreground:       White",
   ".SCO.background:       Black",
 
+  ".HVX.font:             -*-courier-bold-r-*-*-*-120-*-*-m-*-*-*",
+  ".HVX.font2:            -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
+  ".HVX.foreground:       White",
+  ".HVX.background:       Black",
+
   ".Linux.font:                   9x15bold",
   ".Linux.font2:          -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*",
   ".Linux.foreground:      White",
@@ -4168,6 +3055,8 @@ char *defaults [] = {
   ".VMS.foreground:       White",
   ".VMS.background:       Black",
 
+  ANALOGTV_DEFAULTS
+
 #ifdef HAVE_XSHM_EXTENSION
   "*useSHM:                True",
 #endif
@@ -4199,6 +3088,8 @@ XrmOptionDescRec options [] = {
   { "-no-apple2",      ".doApple2",            XrmoptionNoArg,  "False" },
   { "-sco",            ".doSCO",               XrmoptionNoArg,  "True"  },
   { "-no-sco",         ".doSCO",               XrmoptionNoArg,  "False" },
+  { "-hvx",            ".doHVX",               XrmoptionNoArg,  "True"  },
+  { "-no-hvx",         ".doHVX",               XrmoptionNoArg,  "False" },
   { "-bsd",            ".doBSD",               XrmoptionNoArg,  "True"  },
   { "-no-bsd",         ".doBSD",               XrmoptionNoArg,  "False" },
   { "-linux",          ".doLinux",             XrmoptionNoArg,  "True"  },
@@ -4215,6 +3106,7 @@ XrmOptionDescRec options [] = {
   { "-no-os390",       ".doOS390",             XrmoptionNoArg,  "False" },
   { "-vms",            ".doVMS",               XrmoptionNoArg,  "True"  },
   { "-no-vms",         ".doVMS",               XrmoptionNoArg,  "False" },
+  ANALOGTV_OPTIONS
   { 0, 0, 0, 0 }
 };
 
@@ -4224,14 +3116,15 @@ static struct {
   void (*fn) (Display *, Window, int delay);
 } all_modes[] = {
   { "Windows",         windows_31 },
-  { "Nt",              windows_nt },
-  { "2k",              windows_2k },
+  { "NT",              windows_nt },
+  { "Win2K",           windows_2k },
   { "Amiga",           amiga },
   { "Mac",             mac },
   { "MacsBug",         macsbug },
   { "Mac1",            mac1 },
   { "MacX",            macx },
   { "SCO",             sco },
+  { "HVX",             hvx },
   { "SparcLinux",      sparc_linux },
   { "BSD",             bsd },
   { "Atari",           atari },
@@ -4240,7 +3133,7 @@ static struct {
   { "Linux",           linux_fsck },
   { "HPUX",            hpux },
   { "OS390",           os390 },
-  { "Apple2",          apple2 },
+  { "Apple2",          apple2crash },
   { "VMS",             vms },
 };
 
@@ -4277,7 +3170,8 @@ screenhack (Display *dpy, Window window)
       XWindowAttributes xgwa;
       XGetWindowAttributes (dpy, window, &xgwa);
       XSelectInput (dpy, window,
-                    xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
+                    xgwa.your_event_mask |
+                    KeyPressMask | ButtonPressMask | ExposureMask);
     }
 
   while (1)
index b3ddb902c29612de353a6b4b7e3f8a4a0eddd91e..b581d91b16216fdf19e08abaf744b8c8d0cc9ff5 100644 (file)
@@ -84,24 +84,21 @@ hacks are displayed and which aren't.
 .BR doBlitDamage ,
 .BR doSolaris ,
 .BR doHPUX ,
+.BR doApple2 ,
 .BR doOS390 ,
+.BR doVMS ,
 and
-.BR doApple2 .
+.BR doHVX .
 Each of these is a Boolean resource, they all default to true, except
 for doAtari, doBSD, and doSparcLinux, which are turned off by default,
 because they're really not all that interesting looking unless you're a
 fan of those systems.  
 
 There are command-line options for all of these:
-e.g., \fI\-bsd\fP, \fI\-no-bsd\fP.
+e.g., \fI\-bsd\fP, \fI\-no-bsd\fP.  (Also note the \fI\-only\fP option.)
 .SH BUGS
-Unlike the systems that the images are borrowed from,
-.I bsod
-does not require a reboot after running.
-.PP
-.I bsod
-should also emulate more systems, but systems with interesting crash
-graphics are not as common as one might hope.
+Unlike the systems being simulated, \fIbsod\fP does not require a
+reboot after running.
 .SH SEE ALSO
 .BR X (1),
 .BR xscreensaver (1),
index 1500bcc8b114f9034688403bf5550e65776a0055..5b73e7c569002079a56035bae2f705590c9021c2 100644 (file)
@@ -313,7 +313,7 @@ void InitBumpMap( SBumps *pBumps, XWindowAttributes *pXWinAttribs )
     p = XCreatePixmap(pBumps->pDisplay, pBumps->Win,
                       pXWinAttribs->width, pXWinAttribs->height,
                       pXWinAttribs->depth);
-    load_random_image (pXWinAttribs->screen, pBumps->Win, p);
+    load_random_image (pXWinAttribs->screen, pBumps->Win, p, NULL);
 
        pScreenImage = XGetImage( pBumps->pDisplay, p, 0, 0, pBumps->iWinWidth, pBumps->iWinHeight, ~0L, ZPixmap );
     XFreePixmap (pBumps->pDisplay, p);
index 0612b7b1c4e0a620bfa6ee5a256cc3335ea56de2..7ae7c0a5b7b38a4fd55e8a38eb42cc357903c31b 100644 (file)
@@ -1,6 +1,9 @@
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANALOGTV.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANEMONE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANT.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APOLLONIAN.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2-MAIN.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ATTRACTION.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BARCODE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BLASTER.C
@@ -33,6 +36,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLAME.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLOW.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLUIDBALLS.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FONTGLIDE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FOREST.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GALAXY.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GOOP.C
@@ -73,6 +77,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PHOSPHOR.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PIECEWISE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POLYOMINOES.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PONG.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POPSQUARES.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PYRO.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) QIX.C
@@ -108,6 +113,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLWINDWARP.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLYGIG.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WORM.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XANALOGTV.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XFLAME.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XJACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XLOCKMORE.C
index 0612b7b1c4e0a620bfa6ee5a256cc3335ea56de2..7ae7c0a5b7b38a4fd55e8a38eb42cc357903c31b 100644 (file)
@@ -1,6 +1,9 @@
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANALOGTV.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANEMONE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ANT.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APOLLONIAN.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) APPLE2-MAIN.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) ATTRACTION.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BARCODE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) BLASTER.C
@@ -33,6 +36,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLAME.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLOW.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FLUIDBALLS.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FONTGLIDE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) FOREST.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GALAXY.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) GOOP.C
@@ -73,6 +77,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PHOSPHOR.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PIECEWISE.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POLYOMINOES.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PONG.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) POPSQUARES.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) PYRO.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) QIX.C
@@ -108,6 +113,7 @@ $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLWINDWARP.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WHIRLYGIG.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) WORM.C
+$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XANALOGTV.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XFLAME.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XJACK.C
 $ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H,STANDALONE)/INCL=([],[-],[-.UTILS]) XLOCKMORE.C
index 69aa2028c0730b603f5500b94f3a871a97e93abe..1975f672c3a9f2ca1441df80c77a6ecee0b93fb0 100644 (file)
@@ -4,8 +4,8 @@
             a screen saver and locker for the X window system
                             by Jamie Zawinski
 
-                              version 4.13
-                               07-Sep-2003
+                              version 4.14
+                               25-Oct-2003
 
                      http://www.jwz.org/xscreensaver/
 
diff --git a/hacks/config/apple2.xml b/hacks/config/apple2.xml
new file mode 100644 (file)
index 0000000..db1388d
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="apple2" _label="Apple ][">
+
+ <command arg="-root"/>
+
+ <select id="mode">
+  <option id="random"    _label="Random Mode"/>
+  <option id="text"      _label="Text Mode"              arg-set="-text"/>
+  <option id="slideshow" _label="Slideshow Mode"         arg-set="-slideshow"/>
+  <option id="basic"     _label="Basic Programming Mode" arg-set="-basic"/>
+ </select>
+
+ <file id="program" _label="Text Program" arg="-program %"/>
+
+ <_description>
+
+Simulates an original Apple ][ Plus computer in all its 1979 glory.  It
+also reproduces the appearance of display on a color television set of
+the period.
+
+In "Text Mode", it displays the output of a command (e.g., "fortune".)
+
+In "Slideshow Mode", it chooses a number of images from the image
+source you configured into XScreenSaver and displays them within the
+limitations of the Apple ][ display hardware.  (Six available colors
+in hi-res mode!)
+
+In "Basic Programming Mode", a simulated user types in a BASIC program
+and runs it.
+
+By Trevor Blackwell.
+  </_description>
+</screensaver>
diff --git a/hacks/config/blinkbox.xml b/hacks/config/blinkbox.xml
new file mode 100644 (file)
index 0000000..0c25f39
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="blinkbox" _label="BlinkBox">
+
+  <command arg="-root"/>
+
+  <number id="Speed" type="slider" arg="-delay %"
+          _label="Speed" _low-label="Fast" _high-label="Slow"
+          low="0" high="50000" default="30000"/>
+
+  <_description>
+Shows a ball contained inside of a bounding box.
+Colored blocks blink in when the ball hits the edges.
+Written by Jeremy English.
+  </_description>
+</screensaver>
index d7042e19b6612fd96ed8f078fe4a1ed2d30fdb6d..35ecf6a862e42a2ec52a5d259bed11a5cfaf5ddf 100644 (file)
@@ -27,6 +27,7 @@
      <boolean id="sparclinux" _label="Sparc Linux"  arg-set="-sparclinux"/>
      <boolean id="solaris"    _label="Solaris"      arg-unset="-no-solaris"/>
      <boolean id="sco"        _label="SCO"          arg-unset="-no-sco"/>
+     <boolean id="hvx"        _label="HVX/GCOS6"    arg-unset="-no-hvx"/>
      <boolean id="hpux"       _label="HPUX"         arg-unset="-no-hpux"/>
      <boolean id="amiga"      _label="AmigaDOS"     arg-unset="-no-amiga"/>
      <boolean id="atari"      _label="Atari"        arg-set="-atari"/>
diff --git a/hacks/config/fontglide.xml b/hacks/config/fontglide.xml
new file mode 100644 (file)
index 0000000..5ac9483
--- /dev/null
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="fontglide" _label="FontGlide">
+
+  <command arg="-root"/>
+
+  <number id="delay" type="slider" arg="-delay %"
+          _label="Animation Speed" _low-label="Slow" _high-label="Fast"
+          low="0" high="80000" default="10000"
+          convert="invert"/>
+
+  <number id="speed" type="slider" arg="-speed %"
+          _label="Motion Speed" _low-label="Slow" _high-label="Fast"
+          low="0.1" high="10" default="1"/>
+
+  <number id="linger" type="slider" arg="-linger %"
+          _label="Text Linger" _low-label="Brief" _high-label="Long"
+          low="0.1" high="10" default="1"/>
+
+  <number id="bw" type="spinbutton" arg="-bw %"
+           _label="Font Border Thickness" low="0" high="8" default="2"/>
+
+ <hgroup>
+  <select id="mode">
+   <option id="random" _label="Random"/>
+   <option id="page"   _label="Pages of text"  arg-set="-page"/>
+   <option id="scroll" _label="Horizontally scrolling text" arg-set="-scroll"/>
+  </select>
+
+   <boolean id="trails" _label="Vapor Trails"    arg-set="-trails"/>
+ </hgroup>
+
+  <boolean id="db"     _label="Double Buffer" arg-unset="-no-dbuf"/>
+
+  <file id="program" _label="Text Program" arg="-program %"/>
+
+  <_description>
+Puts text on the screen using large characters that glide in from the
+edges, assemble, then disperse.  Alternately, it can simply scroll whole 
+sentences from right to left.  By Jamie Zawinski.
+  </_description>
+</screensaver>
diff --git a/hacks/config/gleidescope.xml b/hacks/config/gleidescope.xml
new file mode 100644 (file)
index 0000000..ec8c1f6
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="gleidescope" _label="Gleidescope">
+
+  <command arg="-root"/>
+
+  <boolean id="move"   _label="Move" arg-unset="-no-move"/>
+  <boolean id="rotate" _label="Rotate" arg-unset="-no-rotate"/>
+  <boolean id="zoom"   _label="Zoom" arg-set="-zoom"/>
+  <file id="image"             _label="Image file" arg="-image %"/>
+  <number id="size"            _label="Size of tube" arg="-size %"
+         type="slider" _low-label="Small" _high-label="Large"
+         low="1" high="10" default="7" />
+  <number id="duration" type="slider" arg="-duration %"
+          _label="Image Duration"
+          _low-label="10 Seconds" _high-label="5 Minutes"
+          low="10" high="300" default="30"/>
+  <boolean id="showfps"        _label="Show Frames-per-Second" arg-set="-fps"/>
+
+  <_description>
+An OpenGL Kaleidescope.
+Written by andrew dean.
+  </_description>
+</screensaver>
index 4d07efb62b47b3e0c2600f486ad36f044d1c919b..479b2991f659cb15d546ccc58dc33a205d850db9 100644 (file)
@@ -29,6 +29,8 @@
           low="0" high="100000" default="20000"
           convert="invert"/>
 
+  <boolean id="titles" _label="Show Image Titles" arg-set="-titles"/>
+
   <boolean id="showfps" _label="Show Frames-per-Second" arg-set="-fps"/>
 
   <_description>
diff --git a/hacks/config/mirrorblob.xml b/hacks/config/mirrorblob.xml
new file mode 100644 (file)
index 0000000..7601266
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="mirrorblob" _label="MirrorBlob">
+
+  <command arg="-root"/>
+
+  <hgroup>
+    <select id="render">
+      <option id="wire"  _label="Wireframe" arg-set="-wire"/>
+      <option id="solid" _label="Solid Surface"/>
+    </select>
+  </hgroup>
+
+  <number id="speed" type="slider" arg="-delay %"
+          _label="Speed" _low-label="Slow" _high-label="Fast"
+          low="0" high="20000" default="10000"
+          convert="invert"/>
+
+  <number id="x_res" type="slider" arg="-x_res %"
+          _label="X Resolution" low="5" high="500" default="60"/>
+
+  <number id="y_res" type="slider" arg="-y_res %"
+          _label="Y Resolution" low="5" high="500" default="32"/>
+
+  <number id="field_points" type="slider" arg="-field_points %"
+          _label="Field Points" low="0" high="50" default="10"/>
+
+  <number id="hold_frames" type="slider" arg="-hold_frames %"
+          _label="Frames Between Images" low="1" high="65536" default="10240"/>
+
+  <number id="fade_speed" type="slider" arg="-fade_speed %"
+          _label="Fade Speed" low="1" high="256" default="1"/>
+
+  <hgroup>
+    <select id="Calculation Method">
+      <option id="wire"  _label="Blobby"/>
+      <option id="calm"  _label="Calm"           arg-set="-incremental 1"/>
+      <option id="freaky"  _label="Freaky"       arg-set="-incremental 3"/>
+      <option id="vfreaky"  _label="Very Freaky" arg-set="-incremental 2"/>
+    </select>
+  </hgroup>
+
+  <hgroup>
+   <vgroup>
+     <boolean id="walls" _label="Enable Walls"/>
+     <boolean id="colour" _label="Enable Colouring" arg-set="-colour"/>
+     <boolean id="texture" _label="Enable Reflected Image" arg-unset="-no-texture"/>
+     <boolean id="background" _label="Enable Background Image" arg-unset="-no-bgimage"/>
+     <boolean id="offset_texture" _label="Offset Texture Coordinates" arg-set="-offset_texture"/>
+     <boolean id="showfps" _label="Show Frames-per-Second" arg-set="-fps"/>
+   </vgroup>
+  </hgroup>
+
+  <_description>
+Draws a wobbly blob that distorts the image behind it.
+Requires OpenGL hardware acceleration for nice animation.
+Written by Jon Dowdall.
+  </_description>
+</screensaver>
diff --git a/hacks/config/pong.xml b/hacks/config/pong.xml
new file mode 100644 (file)
index 0000000..7ed8ee7
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="pong" _label="Pong">
+
+  <command arg="-root"/>
+
+  <number id="speed" type="slider" arg="-speed %"
+          _label="Speed" _low-label="Slow" _high-label="Fast"
+          low="1" high="20" default="7"/>
+
+  <number id="percent" type="slider" arg="-percent %"
+          _label="Size" _low-label="Thin" _high-label="Thick"
+          low="0.01" high="0.075" default="0.0375"/>
+
+  <_description>
+Displays a game of pong. It doesn't keep score
+because they are not suppose to miss.
+Written by Jeremy English.
+  </_description>
+</screensaver>
index 7d805cd994dc61ce464e80deaf2b38dd7532c2d3..2464c184c71e504412f67c92b57b22e6a38a8bb7 100644 (file)
@@ -9,10 +9,6 @@
           low="0" high="20000" default="2000"
           convert="invert"/>
 
-  <number id="curve" type="slider" arg="-curve %"
-            _label="Curviness" _low-label="Low" _high-label="High"
-            low="1" high="50" default="10"/>
-
   <number id="ncolors" type="slider" arg="-ncolors %"
             _label="Number of Colors" _low-label="Two" _high-label="Many"
             low="1" high="255" default="100"/>
diff --git a/hacks/config/xanalogtv.xml b/hacks/config/xanalogtv.xml
new file mode 100644 (file)
index 0000000..e476537
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<screensaver name="xanalogtv" _label="XAnalogTV">
+
+  <command arg="-root"/>
+
+
+  <_description>
+
+XAnalogTV shows a detailed simulation of an old TV set showing various
+test patters, with various picture artifacts like snow, bloom,
+distortion, ghosting, and hash noise. It also simulates the TV warming
+up. It will cycle through 12 channels, some with images you give it,
+and some with color bars or nothing but static.
+
+By Trevor Blackwell.
+  </_description>
+</screensaver>
index 3ce26cdd38541a1f049256b36f520d9d99e1a30e..80bcca5b8ebb3de4841e459f80dc7894d3cef5be 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* crystal --- polygons moving according to plane group rules */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)crystal.c    4.12 98/09/10 xlockmore";
-
 #endif
 
 /*-
index 7127bc983b4f2b89a1f3a97de04ec876ec0a4752..fa6561c1dae1a2e160ab87edc466193a9b972e27 100644 (file)
@@ -110,7 +110,7 @@ init_decay (Display *dpy, Window window)
   sizex = xgwa.width;
   sizey = xgwa.height;
 
-  load_random_image (xgwa.screen, window, window);
+  load_random_image (xgwa.screen, window, window, NULL);
   
   if (mode == MELT || mode == STRETCH) {
     /* make sure screen eventually turns background color */
index 522e01352947c3f4ddec21c0372c93c43f0b9ffb..75b9832900956d7d6369d2fbbe3bb87e997e3840 100644 (file)
@@ -335,9 +335,9 @@ screenhack (Display *dpy, Window window)
 
   while (1)
     {
-      if (!dbeclear_p ||
+      if (!dbeclear_p
 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
-          !backb
+          || !backb
 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
           )
         XFillRectangle (dpy, b, erase_gc, 0, 0, xgwa.width, xgwa.height);
index df7ea3e8a501ad1a487a9c09e350c2f86f53b0d8..ba6c2b5d62905b676d7d34ae031142982ca73b76 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* demon --- David Griffeath's cellular automata */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)demon.c      5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index bf251f579d3f8f1d18e02819554107e2f7b4bf57..66c0a3ed89463eac409c30a9431dc458b8a239b7 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* discrete --- chaotic mappings */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)discrete.c   5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index b9f6cee75ecdb9a6b3b4715a536792944627dc4b..54a12fa9d3e6623a10ff7bf91f914f31b47c4605 100644 (file)
@@ -248,7 +248,7 @@ static void init_distort(Display *dpy, Window window)
                gcflags |= GCSubwindowMode;
        gc = XCreateGC (dpy, window, gcflags, &gcv);
 
-    load_random_image (xgwa.screen, window, window);
+    load_random_image (xgwa.screen, window, window, NULL);
 
        buffer_map = 0;
        orig_map = XGetImage(dpy, window, 0, 0, xgwa.width, xgwa.height,
index 8b4aa63ca5df6836e7a26234177c1b7d97b118d9..38cdf7b70d8a70b809c946237a2153c852fd02aa 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* drift --- drifting recursive fractal cosmic flames */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)drift.c      5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index c73b64ad62a75f26f94c250e0d91d7ab1680a8cc..7b694e545a606a918db9130625e3bb7d0946fd05 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* euler2d --- 2 Dimensional Incompressible Inviscid Fluid Flow */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)euler2d.c    5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*
index ad48b7100f2e10892aacd52b759273c6317c9191..1f8ada11d8d5258dac692302b3feefb1b79481f3 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* fadeplot --- a fading plot of sine squared */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)fadeplot.c   5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 41311f4a92a26bb30d38167765aab2e4f6bda9c3..8d1ab15503ed094bb375cf00d0cab4700773a970 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*-
  * flag --- a waving flag
  */
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)flag.c       4.02 97/04/01 xlockmore";
 #endif
 
index 607994d0387b1494e8de74aee346fc5906640367..08bd2b046ed7cc723f2865d2d5d109c46f9c5ca7 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* flow --- flow of strange bees */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)flow.c 4.10 98/04/24 xlockmore";
-
 #endif
 
 /*-
index 3413a945d510145763c9bbe59f1f15f51bbcf8b8..0ce65db3f590bdfd0d44adebed3614ef435e6792 100644 (file)
@@ -370,6 +370,9 @@ init_balls (Display *dpy, Window window)
       state->m[i] = pow(state->r[i],3) * M_PI * 1.3333;
     }
 
+  memcpy (state->opx, state->px, sizeof (*state->opx) * (state->count + 1));
+  memcpy (state->opy, state->py, sizeof (*state->opx) * (state->count + 1));
+
   return state;
 }
 
@@ -423,7 +426,7 @@ check_wall_clock (b_state *state, float max_d)
   if (tick++ > 20)  /* don't call gettimeofday() too often -- it's slow. */
     {
       struct timeval now;
-      static struct timeval last = {0, };
+      static struct timeval last = { 0, 0 };
 # ifdef GETTIMEOFDAY_TWO_ARGS
       struct timezone tzp;
       gettimeofday(&now, &tzp);
@@ -483,9 +486,9 @@ repaint_balls (b_state *state)
       x2b = (state->px[a] + state->r[a] - state->xmin);
       y2b = (state->py[a] + state->r[a] - state->ymin);
 
-      if (!state->dbeclear_p ||
+      if (!state->dbeclear_p
 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
-          !state->backb
+          || !state->backb
 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
          )
        {
diff --git a/hacks/fontglide.c b/hacks/fontglide.c
new file mode 100644 (file)
index 0000000..02493c8
--- /dev/null
@@ -0,0 +1,1589 @@
+/* xscreensaver, Copyright (c) 2003 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ * fontglide -- reads text from a subprocess and puts it on the screen using
+ * large characters that glide in from the edges, assemble, then disperse.
+ * Requires a system with scalable fonts.  (X's font handing sucks.  A lot.)
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include <X11/Intrinsic.h>
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+#include "xdbe.h"
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+extern XtAppContext app;
+
+
+typedef struct {
+  char *text;
+  int x, y, width, height;
+  int ascent, lbearing, rbearing;
+
+  int nticks, tick;
+  int start_x,  start_y;
+  int target_x, target_y;
+  Pixmap pixmap, mask;
+} word;
+
+
+typedef struct {
+  int id;
+  XColor fg;
+  XColor bg;
+  Bool dark_p;
+  Bool move_chars_p;
+  int width;
+
+  char *font_name;
+  XFontStruct *font;
+
+  GC fg_gc;
+
+  int nwords;
+  word **words;
+
+  enum { IN, PAUSE, OUT } anim_state;
+  enum { LEFT, CENTER, RIGHT } alignment;
+  int pause_tick;
+
+} sentence;
+
+
+typedef struct {
+  Display *dpy;
+  Window window;
+  XWindowAttributes xgwa;
+
+  Pixmap b, ba;        /* double-buffer to reduce flicker */
+  GC bg_gc;
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+  XdbeBackBuffer backb;
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+  Bool dbuf;            /* Whether we're using double buffering. */
+  Bool dbeclear_p;      /* ? */
+
+  int border_width;     /* size of the font outline */
+  char *charset;        /* registry and encoding for font lookups */
+  double speed;                /* frame rate multiplier */
+  double linger;       /* multiplier for how long to leave words on screen */
+  Bool trails_p;
+  Bool debug_p;
+  enum { PAGE, SCROLL } mode;
+
+  char *font_override;  /* if -font was specified on the cmd line */
+
+  FILE *pipe;
+  XtInputId pipe_id;
+  Time subproc_relaunch_delay;
+  Bool input_available_p;
+
+  char buf [40];       /* this only needs to be as big as one "word". */
+  int buf_tail;
+
+  int nsentences;
+  sentence **sentences;
+  Bool spawn_p;                /* whether it is time to create a new sentence */
+  int latest_sentence;
+
+} state;
+
+
+static void launch_text_generator (state *);
+static void drain_input (state *s);
+
+
+/* Finds the set of scalable fonts on the system; picks one;
+   and loads that font in a random pixel size.
+   Returns False if something went wrong.
+ */
+static Bool
+pick_font_1 (state *s, sentence *se)
+{
+  char pattern[1024];
+  char **names = 0;
+  char **names2 = 0;
+  XFontStruct *info = 0;
+  int count = 0, count2 = 0;
+  int i;
+  Bool ok = False;
+
+  if (se->font)
+    {
+      XFreeFont (s->dpy, se->font);
+      free (se->font_name);
+      se->font = 0;
+      se->font_name = 0;
+    }
+
+  if (s->font_override)
+    sprintf (pattern, "%.200s", s->font_override);
+  else
+    sprintf (pattern, "-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s",
+             "*",         /* foundry */
+             "*",         /* family */
+             "*",         /* weight */
+             "*",         /* slant */
+             "*",         /* swidth */
+             "*",         /* adstyle */
+             "0",         /* pixel size */
+             "0",         /* point size */
+             "0",         /* resolution x */
+             "0",         /* resolution y */
+             "p",         /* spacing */
+             "0",         /* avg width */
+             s->charset); /* registry + encoding */
+
+  names = XListFonts (s->dpy, pattern, 1000, &count);
+
+  if (count <= 0)
+    {
+      if (s->font_override)
+        fprintf (stderr, "%s: -font option bogus: %s\n", progname, pattern);
+      else
+        fprintf (stderr, "%s: no scalable fonts found!  (pattern: %s)\n",
+                 progname, pattern);
+      exit (1);
+    }
+
+  i = random() % count;
+
+  names2 = XListFontsWithInfo (s->dpy, names[i], 1000, &count2, &info);
+  if (count2 <= 0)
+    {
+      fprintf (stderr, "%s: pattern %s\n"
+                "     gave unusable %s\n\n",
+               progname, pattern, names[i]);
+      goto FAIL;
+    }
+
+  {
+    XFontStruct *font = &info[0];
+    unsigned long value = 0;
+    char *foundry=0, *family=0, *weight=0, *slant=0, *setwidth=0, *add_style=0;
+    unsigned long pixel=0, point=0, res_x=0, res_y=0;
+    char *spacing=0;
+    unsigned long avg_width=0;
+    char *registry=0, *encoding=0;
+    Atom a;
+    char *bogus = "\"?\"";
+
+# define STR(ATOM,VAR)                                 \
+  bogus = (ATOM);                                      \
+  a = XInternAtom (s->dpy, (ATOM), False);             \
+  if (XGetFontProperty (font, a, &value))              \
+    VAR = XGetAtomName (s->dpy, value);                        \
+  else                                                 \
+    goto FAIL2
+
+# define INT(ATOM,VAR)                                 \
+  bogus = (ATOM);                                      \
+  a = XInternAtom (s->dpy, (ATOM), False);             \
+  if (!XGetFontProperty (font, a, &VAR) ||             \
+      VAR > 9999)                                      \
+    goto FAIL2
+
+    STR ("FOUNDRY",          foundry);
+    STR ("FAMILY_NAME",      family);
+    STR ("WEIGHT_NAME",      weight);
+    STR ("SLANT",            slant);
+    STR ("SETWIDTH_NAME",    setwidth);
+    STR ("ADD_STYLE_NAME",   add_style);
+    INT ("PIXEL_SIZE",       pixel);
+    INT ("POINT_SIZE",       point);
+    INT ("RESOLUTION_X",     res_x);
+    INT ("RESOLUTION_Y",     res_y);
+    STR ("SPACING",          spacing);
+    INT ("AVERAGE_WIDTH",    avg_width);
+    STR ("CHARSET_REGISTRY", registry);
+    STR ("CHARSET_ENCODING", encoding);
+
+#undef INT
+#undef STR
+
+    {
+      double scale = s->xgwa.height / 1024.0;  /* shrink for small windows */
+      int min, max, r;
+
+      min = scale * 24;
+      max = scale * 260;
+
+      if (min < 10) min = 10;
+      if (max < 30) max = 30;
+
+      r = ((max-min)/3)+1;
+
+      pixel = min + ((random() % r) + (random() % r) + (random() % r));
+
+      if (s->mode == SCROLL)  /* scroll mode likes bigger fonts */
+        pixel *= 1.5;
+    }
+
+#if 0
+    /* Occasionally change the aspect ratio of the font, by increasing
+       either the X or Y resolution (while leaving the other alone.)
+
+       #### Looks like this trick doesn't really work that well: the
+            metrics of the individual characters are ok, but the
+            overall font ascent comes out wrong (unscaled.)
+     */
+    if (! (random() % 8))
+      {
+        double n = 2.5 / 3;
+        double scale = 1 + (frand(n) + frand(n) + frand(n));
+        if (random() % 2)
+          res_x *= scale;
+        else
+          res_y *= scale;
+      }
+# endif
+
+    sprintf (pattern,
+             "-%s-%s-%s-%s-%s-%s-%ld-%s-%ld-%ld-%s-%s-%s-%s",
+             foundry, family, weight, slant, setwidth, add_style,
+             pixel, "*", /* point, */
+             res_x, res_y, spacing,
+             "*", /* avg_width */
+             registry, encoding);
+    ok = True;
+
+  FAIL2:
+    if (!ok)
+      fprintf (stderr, "%s: font has bogus %s property: %s\n",
+               progname, bogus, names[i]);
+
+    if (foundry)   XFree (foundry);
+    if (family)    XFree (family);
+    if (weight)    XFree (weight);
+    if (slant)     XFree (slant);
+    if (setwidth)  XFree (setwidth);
+    if (add_style) XFree (add_style);
+    if (spacing)   XFree (spacing);
+    if (registry)  XFree (registry);
+    if (encoding)  XFree (encoding);
+  }
+
+ FAIL: 
+
+  XFreeFontInfo (names2, info, count2);
+  XFreeFontNames (names);
+
+  if (! ok) return False;
+
+  se->font = XLoadQueryFont (s->dpy, pattern);
+  if (! se->font)
+    {
+      fprintf (stderr, "%s: unable to load font %s\n",
+               progname, pattern);
+      return False;
+    }
+
+  if (se->font->min_bounds.width == se->font->max_bounds.width &&
+      !s->font_override)
+    {
+      /* This is to weed out
+         "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and
+         "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1".
+         We asked for only proportional fonts, but this fixed-width font
+         shows up anyway -- but it has goofy metrics (see below) so it
+         looks terrible anyway.
+       */
+      if (s->debug_p)
+        fprintf (stderr,
+                 "%s: skipping bogus monospace non-charcell font: %s\n",
+                 progname, pattern);
+      return False;
+    }
+
+  if (s->debug_p) 
+    fprintf(stderr, "%s: %s\n", progname, pattern);
+
+  se->font_name = strdup (pattern);
+  XSetFont (s->dpy, se->fg_gc, se->font->fid);
+  return True;
+}
+
+
+/* Finds the set of scalable fonts on the system; picks one;
+   and loads that font in a random pixel size.
+ */
+static void
+pick_font (state *s, sentence *se)
+{
+  int i;
+  for (i = 0; i < 20; i++)
+    if (pick_font_1 (s, se))
+      return;
+  fprintf (stderr, "%s: too many failures: giving up!\n", progname);
+  exit (1);
+}
+
+
+static char *unread_word_text = 0;
+
+/* Returns a newly-allocated string with one word in it, or NULL if there
+   is no complete word available.
+ */
+static char *
+get_word_text (state *s)
+{
+  char *start = s->buf;
+  char *end;
+  char *result = 0;
+  int lfs = 0;
+
+  if (unread_word_text)
+    {
+      char *s = unread_word_text;
+      unread_word_text = 0;
+      return s;
+    }
+
+  /* Skip over whitespace at the beginning of the buffer,
+     and count up how many linebreaks we see while doing so.
+   */
+  while (*start &&
+         (*start == ' ' ||
+          *start == '\t' ||
+          *start == '\r' ||
+          *start == '\n'))
+    {
+      if (*start == '\n' || (*start == '\r' && start[1] != '\n'))
+        lfs++;
+      start++;
+    }
+
+  end = start;
+
+  /* If we saw a blank line, then return NULL (treat it as a temporary "eof",
+     to trigger a sentence break here.) */
+  if (lfs >= 2)
+    goto DONE;
+
+  /* Skip forward to the end of this word (find next whitespace.) */
+  while (*end &&
+         (! (*end == ' ' ||
+             *end == '\t' ||
+             *end == '\r' ||
+             *end == '\n')))
+    end++;
+
+  /* If we have a word, allocate a string for it */
+  if (end > start)
+    {
+      result = malloc ((end - start) + 1);
+      strncpy (result, start, (end-start));
+      result [end-start] = 0;
+    }
+
+ DONE:
+
+  /* Make room in the buffer by compressing out any bytes we've processed.
+   */
+  if (end > s->buf)
+    {
+      int n = end - s->buf;
+      memmove (s->buf, end, sizeof(s->buf) - n);
+      s->buf_tail -= n;
+    }
+
+  /* See if there is more to be read, now that there's room in the buffer. */
+  drain_input (s);
+
+  return result;
+}
+
+
+/* Gets some random text, and creates a "word" object from it.
+ */
+static word *
+new_word (state *s, sentence *se, char *txt, Bool alloc_p)
+{
+  word *w;
+  XCharStruct overall;
+  int dir, ascent, descent;
+  int bw = s->border_width;
+
+  if (!txt)
+    return 0;
+
+  w = (word *) calloc (1, sizeof(*w));
+  XTextExtents (se->font, txt, strlen(txt), &dir, &ascent, &descent, &overall);
+
+  w->width    = overall.rbearing - overall.lbearing + bw + bw;
+  w->height   = overall.ascent   + overall.descent  + bw + bw;
+  w->ascent   = overall.ascent   + bw;
+  w->lbearing = overall.lbearing - bw;
+  w->rbearing = overall.width    + bw;
+
+# if 0
+  /* The metrics on some fonts are strange -- e.g.,
+     "-urw-nimbus mono l-medium-o-normal--*-*-*-*-p-*-iso8859-1" and
+     "-urw-courier-medium-r-normal--*-*-*-*-p-*-iso8859-1" both have
+     an rbearing so wide that it looks like there are two spaces after
+     each letter.  If this character says it has an rbearing that is to
+     the right of its ink, ignore that.
+
+     #### Of course, this hack only helps when we're in `move_chars_p' mode
+          and drawing a char at a time -- when we draw the whole word at once,
+          XDrawString believes the bogus metrics and spaces the font out
+          crazily anyway.
+
+     Sigh, this causes some text to mis-render in, e.g.,
+     "-adobe-utopia-medium-i-normal--114-*-100-100-p-*-iso8859-1"
+     (in "ux", we need the rbearing on "r" or we get too much overlap.)
+   */
+  if (w->rbearing > w->width)
+    w->rbearing = w->width;
+# endif /* 0 */
+
+  if (s->mode == SCROLL && !alloc_p) abort();
+
+  if (alloc_p)
+    {
+      int i, j;
+      XGCValues gcv;
+      GC gc0, gc1;
+
+      w->pixmap = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
+      w->mask   = XCreatePixmap (s->dpy, s->b, w->width, w->height, 1L);
+
+      gcv.font = se->font->fid;
+      gcv.foreground = 0L;
+      gcv.background = 1L;
+      gc0 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground,
+                       &gcv);
+      gcv.foreground = 1L;
+      gcv.background = 0L;
+      gc1 = XCreateGC (s->dpy, w->pixmap, GCFont|GCForeground|GCBackground,
+                       &gcv);
+
+      XFillRectangle (s->dpy, w->mask,   gc0, 0, 0, w->width, w->height);
+      XFillRectangle (s->dpy, w->pixmap, gc0, 0, 0, w->width, w->height);
+
+      if (s->debug_p)
+        {
+          /* bounding box (behind the characters) */
+          XDrawRectangle (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
+                          0, 0, w->width-1, w->height-1);
+          XDrawRectangle (s->dpy, w->mask,   gc1,
+                          0, 0, w->width-1, w->height-1);
+        }
+
+      /* Draw foreground text */
+      XDrawString (s->dpy, w->pixmap, gc1, -w->lbearing, w->ascent,
+                   txt, strlen(txt));
+
+      /* Cheesy hack to draw a border */
+      /* (I should be able to do this in i*2 time instead of i*i time,
+         but I can't get it right, so fuck it.) */
+      XSetFunction (s->dpy, gc1, GXor);
+      for (i = -bw; i <= bw; i++)
+        for (j = -bw; j <= bw; j++)
+          XCopyArea (s->dpy, w->pixmap, w->mask, gc1,
+                     0, 0, w->width, w->height,
+                     i, j);
+
+      if (s->debug_p)
+        {
+          XSetFunction (s->dpy, gc1, GXset);
+          if (w->ascent != w->height)
+            {
+              /* baseline (on top of the characters) */
+              XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
+                         0, w->ascent, w->width-1, w->ascent);
+              XDrawLine (s->dpy, w->mask,   gc1,
+                         0, w->ascent, w->width-1, w->ascent);
+            }
+
+          if (w->lbearing != 0)
+            {
+              /* left edge of charcell */
+              XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
+                         w->lbearing, 0, w->lbearing, w->height-1);
+              XDrawLine (s->dpy, w->mask,   gc1,
+                         w->lbearing, 0, w->lbearing, w->height-1);
+            }
+
+          if (w->rbearing != w->width)
+            {
+              /* right edge of charcell */
+              XDrawLine (s->dpy, w->pixmap, (se->dark_p ? gc0 : gc1),
+                         w->rbearing, 0, w->rbearing, w->height-1);
+              XDrawLine (s->dpy, w->mask,   gc1,
+                         w->rbearing, 0, w->rbearing, w->height-1);
+            }
+        }
+
+      XFreeGC (s->dpy, gc0);
+      XFreeGC (s->dpy, gc1);
+    }
+
+  w->text = txt;
+  return w;
+}
+
+
+static void
+free_word (state *s, word *w)
+{
+  if (w->text)   free (w->text);
+  if (w->pixmap) XFreePixmap (s->dpy, w->pixmap);
+  if (w->mask)   XFreePixmap (s->dpy, w->mask);
+}
+
+
+static sentence *
+new_sentence (state *s)
+{
+  static int id = 0;
+  XGCValues gcv;
+  sentence *se = (sentence *) calloc (1, sizeof (*se));
+  se->fg_gc = XCreateGC (s->dpy, s->b, 0, &gcv);
+  se->anim_state = IN;
+  se->id = ++id;
+  return se;
+}
+
+
+static void
+free_sentence (state *s, sentence *se)
+{
+  int i;
+  for (i = 0; i < se->nwords; i++)
+    free_word (s, se->words[i]);
+  if (se->words) free (se->words);
+
+  if (se->fg.flags)
+    XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0);
+  if (se->bg.flags)
+    XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0);
+
+  if (se->font_name) free (se->font_name);
+  if (se->font) XFreeFont (s->dpy, se->font);
+  if (se->fg_gc) XFreeGC (s->dpy, se->fg_gc);
+
+  free (se);
+}
+
+
+/* free the word, and put its text back at the front of the input queue,
+   to be read next time. */
+static void
+unread_word (state *s, word *w)
+{
+  if (unread_word_text)
+    abort();
+  unread_word_text = w->text;
+  w->text = 0;
+  free_word (s, w);
+}
+
+
+/* Divide each of the words in the sentence into one character words,
+   without changing the positions of those characters.
+ */
+static void
+split_words (state *s, sentence *se)
+{
+  word **words2;
+  int nwords2 = 0;
+  int i, j;
+  for (i = 0; i < se->nwords; i++)
+    nwords2 += strlen (se->words[i]->text);
+
+  words2 = (word **) calloc (nwords2, sizeof(*words2));
+
+  for (i = 0, j = 0; i < se->nwords; i++)
+    {
+      word *ow = se->words[i];
+      int L = strlen (ow->text);
+      int k;
+
+      int x  = ow->x;
+      int y  = ow->y;
+      int sx = ow->start_x;
+      int sy = ow->start_y;
+      int tx = ow->target_x;
+      int ty = ow->target_y;
+
+      for (k = 0; k < L; k++)
+        {
+          char *t2 = malloc (2);
+          word *w2;
+          int xoff, yoff;
+
+          t2[0] = ow->text[k];
+          t2[1] = 0;
+          w2 = new_word (s, se, t2, True);
+          words2[j++] = w2;
+
+          xoff = (w2->lbearing - ow->lbearing);
+          yoff = (ow->ascent - w2->ascent);
+
+          w2->x        = x  + xoff;
+          w2->y        = y  + yoff;
+          w2->start_x  = sx + xoff;
+          w2->start_y  = sy + yoff;
+          w2->target_x = tx + xoff;
+          w2->target_y = ty + yoff;
+
+          x  += w2->rbearing;
+          sx += w2->rbearing;
+          tx += w2->rbearing;
+        }
+
+      free_word (s, ow);
+      se->words[i] = 0;
+    }
+  free (se->words);
+
+  se->words = words2;
+  se->nwords = nwords2;
+}
+
+
+/* Set the source or destination position of the words to be somewhere
+   off screen.
+ */
+static void
+scatter_sentence (state *s, sentence *se)
+{
+  int i = 0;
+  int off = 100;
+
+  int flock_p = ((random() % 4) == 0);
+  int mode = (flock_p ? (random() % 12) : 0);
+
+  for (i = 0; i < se->nwords; i++)
+    {
+      word *w = se->words[i];
+      int x, y;
+      int r = (flock_p ? mode : (random() % 4));
+      switch (r)
+        {
+          /* random positions on the edges */
+
+        case 0:
+          x = -off - w->width;
+          y = random() % s->xgwa.height;
+          break;
+        case 1:
+          x = off + s->xgwa.width;
+          y = random() % s->xgwa.height;
+          break;
+        case 2:
+          x = random() % s->xgwa.width;
+          y = -off - w->height;
+          break;
+        case 3:
+          x = random() % s->xgwa.width;
+          y = off + s->xgwa.height;
+          break;
+
+          /* straight towards the edges */
+
+        case 4:
+          x = -off - w->width;
+          y = w->target_y;
+          break;
+        case 5:
+          x = off + s->xgwa.width;
+          y = w->target_y;
+          break;
+        case 6:
+          x = w->target_x;
+          y = -off - w->height;
+          break;
+        case 7:
+          x = w->target_x;
+          y = off + s->xgwa.height;
+          break;
+
+          /* corners */
+
+        case 8:
+          x = -off - w->width;
+          y = -off - w->height;
+          break;
+        case 9:
+          x = -off - w->width;
+          y =  off + s->xgwa.height;
+          break;
+        case 10:
+          x =  off + s->xgwa.width;
+          y =  off + s->xgwa.height;
+          break;
+        case 11:
+          x =  off + s->xgwa.width;
+          y = -off - w->height;
+          break;
+
+        default:
+          abort();
+          break;
+        }
+
+      if (se->anim_state == IN)
+        {
+          w->start_x = x;
+          w->start_y = y;
+        }
+      else
+        {
+          w->start_x = w->x;
+          w->start_y = w->y;
+          w->target_x = x;
+          w->target_y = y;
+        }
+
+      w->nticks = ((100 + ((random() % 140) +
+                           (random() % 140) +
+                           (random() % 140)))
+                   / s->speed);
+      if (w->nticks < 2)
+        w->nticks = 2;
+      w->tick = 0;
+    }
+}
+
+
+/* Set the source position of the words to be off the right side,
+   and the destination to be off the left side.
+ */
+static void
+aim_sentence (state *s, sentence *se)
+{
+  int i = 0;
+  int nticks;
+  int yoff = 0;
+
+  if (se->nwords <= 0) abort();
+
+  /* Have the sentence shift up or down a little bit; not too far, and
+     never let it fall off the top or bottom of the screen before its
+     last character has reached the left edge.
+   */
+  for (i = 0; i < 10; i++)
+    {
+      int ty = random() % (s->xgwa.height - se->words[0]->ascent);
+      yoff = ty - se->words[0]->target_y;
+      if (yoff < s->xgwa.height/3)  /* this one is ok */
+        break;
+    }
+
+  for (i = 0; i < se->nwords; i++)
+    {
+      word *w = se->words[i];
+      w->start_x   = w->target_x + s->xgwa.width;
+      w->target_x -= se->width;
+      w->start_y   = w->target_y;
+      w->target_y += yoff;
+    }
+
+  nticks = ((se->words[0]->start_x - se->words[0]->target_x)
+            / (s->speed * 10));
+  nticks *= (frand(0.9) + frand(0.9) + frand(0.9));
+
+  if (nticks < 2)
+    nticks = 2;
+
+  for (i = 0; i < se->nwords; i++)
+    {
+      word *w = se->words[i];
+      w->nticks = nticks;
+      w->tick = 0;
+    }
+}
+
+
+/* Randomize the order of the words in the list (since that changes
+   which ones are "on top".)
+ */
+static void
+shuffle_words (state *s, sentence *se)
+{
+  int i;
+  for (i = 0; i < se->nwords-1; i++)
+    {
+      int j = i + (random() % (se->nwords - i));
+      word *swap = se->words[i];
+      se->words[i] = se->words[j];
+      se->words[j] = swap;
+    }
+}
+
+
+/* qsort comparitor */
+static int
+cmp_sentences (const void *aa, const void *bb)
+{
+  const sentence *a = *(sentence **) aa;
+  const sentence *b = *(sentence **) bb;
+  return ((a ? a->id : 999999) - (b ? b->id : 999999));
+}
+
+
+/* Sort the sentences by id, so that sentences added later are on top.
+ */
+static void
+sort_sentences (state *s)
+{
+  qsort (s->sentences, s->nsentences, sizeof(*s->sentences), cmp_sentences);
+}
+
+
+/* Re-pick the colors of the text and border
+ */
+static void
+recolor (state *s, sentence *se)
+{
+  if (se->fg.flags)
+    XFreeColors (s->dpy, s->xgwa.colormap, &se->fg.pixel, 1, 0);
+  if (se->bg.flags)
+    XFreeColors (s->dpy, s->xgwa.colormap, &se->bg.pixel, 1, 0);
+
+  se->fg.flags  = DoRed|DoGreen|DoBlue;
+  se->bg.flags  = DoRed|DoGreen|DoBlue;
+
+  switch (random() % 2)
+    {
+    case 0:   /* bright fg, dim bg */
+      se->fg.red    = (random() % 0x8888) + 0x8888;
+      se->fg.green  = (random() % 0x8888) + 0x8888;
+      se->fg.blue   = (random() % 0x8888) + 0x8888;
+      se->bg.red    = (random() % 0x5555);
+      se->bg.green  = (random() % 0x5555);
+      se->bg.blue   = (random() % 0x5555);
+      break;
+
+    case 1:   /* bright bg, dim fg */
+      se->fg.red    = (random() % 0x4444);
+      se->fg.green  = (random() % 0x4444);
+      se->fg.blue   = (random() % 0x4444);
+      se->bg.red    = (random() % 0x4444) + 0xCCCC;
+      se->bg.green  = (random() % 0x4444) + 0xCCCC;
+      se->bg.blue   = (random() % 0x4444) + 0xCCCC;
+      break;
+
+    default:
+      abort();
+      break;
+    }
+
+  if (s->debug_p)
+    se->dark_p = (se->fg.red*2 + se->fg.green*3 + se->fg.blue <
+                  se->bg.red*2 + se->bg.green*3 + se->bg.blue);
+
+  if (XAllocColor (s->dpy, s->xgwa.colormap, &se->fg))
+    XSetForeground (s->dpy, se->fg_gc, se->fg.pixel);
+  if (XAllocColor (s->dpy, s->xgwa.colormap, &se->bg))
+    XSetBackground (s->dpy, se->fg_gc, se->bg.pixel);
+}
+
+
+static void
+align_line (state *s, sentence *se, int line_start, int x, int right)
+{
+  int off, j;
+  switch (se->alignment)
+    {
+    case LEFT:   off = 0;               break;
+    case CENTER: off = (right - x) / 2; break;
+    case RIGHT:  off = (right - x);     break;
+    default:     abort();               break;
+    }
+
+  if (off != 0)
+    for (j = line_start; j < se->nwords; j++)
+      se->words[j]->target_x += off;
+}
+
+
+/* Fill the sentence with new words: in "page" mode, fills the page
+   with text; in "scroll" mode, just makes one long horizontal sentence.
+   The sentence might have *no* words in it, if no text is currently
+   available.
+ */
+static void
+populate_sentence (state *s, sentence *se)
+{
+  int i = 0;
+  int left, right, top, x, y;
+  int space = 0;
+  int line_start = 0;
+  Bool done = False;
+
+  int array_size = 100;
+
+  se->move_chars_p = (s->mode == SCROLL ? False :
+                      (random() % 3) ? False : True);
+  se->alignment = (random() % 3);
+
+  recolor (s, se);
+
+  if (se->words)
+    {
+      for (i = 0; i < se->nwords; i++)
+        free_word (s, se->words[i]);
+      free (se->words);
+    }
+
+  se->words = (word **) calloc (array_size, sizeof(*se->words));
+  se->nwords = 0;
+
+  switch (s->mode)
+    {
+    case PAGE:
+      left  = random() % (s->xgwa.width / 3);
+      right = s->xgwa.width - (random() % (s->xgwa.width / 3));
+      top = random() % (s->xgwa.height * 2 / 3);
+      break;
+    case SCROLL:
+      left = 0;
+      right = s->xgwa.width;
+      top = random() % s->xgwa.height;
+      break;
+    default:
+      abort();
+      break;
+    }
+
+  x = left;
+  y = top;
+
+  while (!done)
+    {
+      char *txt = get_word_text (s);
+      word *w;
+      if (!txt)
+        {
+          if (se->nwords == 0)
+            return;            /* If the stream is empty, bail. */
+          else
+            break;             /* If EOF after some words, end of sentence. */
+        }
+
+      if (! se->font)           /* Got a word: need a font now */
+        {
+          pick_font (s, se);
+          if (y < se->font->ascent)
+            y += se->font->ascent;
+          space = XTextWidth (se->font, " ", 1);
+        }
+
+      w = new_word (s, se, txt, !se->move_chars_p);
+
+      /* If we have a few words, let punctuation terminate the sentence:
+         stop gathering more words if the last word ends in a period, etc. */
+      if (se->nwords >= 4)
+        {
+          char c = w->text[strlen(w->text)-1];
+          if (c == '.' || c == '?' || c == '!')
+            done = True;
+        }
+
+      /* If the sentence is kind of long already, terminate at commas, etc. */
+      if (se->nwords >= 12)
+        {
+          char c = w->text[strlen(w->text)-1];
+          if (c == ',' || c == ';' || c == ':' || c == '-' ||
+              c == ')' || c == ']' || c == '}')
+            done = True;
+        }
+
+      if (se->nwords >= 25)  /* ok that's just about enough out of you */
+        done = True;
+
+      if (s->mode == PAGE &&
+          x + w->rbearing > right)                     /* wrap line */
+        {
+          align_line (s, se, line_start, x, right);
+          line_start = se->nwords;
+
+          x = left;
+          y += se->font->ascent;
+
+          /* If we're close to the bottom of the screen, stop, and 
+             unread the current word.  (But not if this is the first
+             word, otherwise we might just get stuck on it.)
+           */
+          if (se->nwords > 0 &&
+              y + se->font->ascent > s->xgwa.height)
+            {
+              unread_word (s, w);
+              done = True;
+              break;
+            }
+        }
+
+      w->target_x = x + w->lbearing;
+      w->target_y = y - w->ascent;
+
+      x += w->rbearing + space;
+      se->width = x;
+
+      if (se->nwords >= (array_size - 1))
+        {
+          array_size += 100;
+          se->words = (word **) realloc (se->words,
+                                         array_size * sizeof(*se->words));
+          if (!se->words)
+            {
+              fprintf (stderr, "%s: out of memory (%d words)\n",
+                       progname, array_size);
+              exit (1);
+            }
+        }
+
+      se->words[se->nwords++] = w;
+    }
+
+  se->width -= space;
+
+  switch (s->mode)
+    {
+    case PAGE:
+      align_line (s, se, line_start, x, right);
+      if (se->move_chars_p)
+        split_words (s, se);
+      scatter_sentence (s, se);
+      shuffle_words (s, se);
+      break;
+    case SCROLL:
+      aim_sentence (s, se);
+      break;
+    default:
+      abort();
+      break;
+    }
+
+# ifdef DEBUG
+  if (s->debug_p)
+    {
+      fprintf (stderr, "%s: sentence %d:", progname, se->id);
+      for (i = 0; i < se->nwords; i++)
+        fprintf (stderr, " %s", se->words[i]->text);
+      fprintf (stderr, "\n");
+    }
+# endif
+}
+
+
+/* Render a single word object to the screen.
+ */
+static void
+draw_word (state *s, sentence *se, word *w)
+{
+  if (! w->pixmap) return;
+
+  if (w->x + w->width < 0 ||
+      w->y + w->height < 0 ||
+      w->x > s->xgwa.width ||
+      w->y > s->xgwa.height)
+    return;
+
+  XSetClipMask (s->dpy, se->fg_gc, w->mask);
+  XSetClipOrigin (s->dpy, se->fg_gc, w->x, w->y);
+  XCopyPlane (s->dpy, w->pixmap, s->b, se->fg_gc,
+              0, 0, w->width, w->height,
+              w->x, w->y,
+              1L);
+}
+
+
+/* If there is room for more sentences, add one.
+ */
+static void
+more_sentences (state *s)
+{
+  int i;
+  Bool any = False;
+  for (i = 0; i < s->nsentences; i++)
+    {
+      sentence *se = s->sentences[i];
+      if (! se)
+        {
+          se = new_sentence (s);
+          populate_sentence (s, se);
+          if (se->nwords > 0)
+            s->spawn_p = False, any = True;
+          else
+            {
+              free_sentence (s, se);
+              se = 0;
+            }
+          s->sentences[i] = se;
+          if (se)
+            s->latest_sentence = se->id;
+          break;
+        }
+    }
+
+  if (any) sort_sentences (s);
+}
+
+
+/* Render all the words to the screen, and run the animation one step.
+ */
+static void
+draw_sentence (state *s, sentence *se)
+{
+  int i;
+  Bool moved = False;
+
+  if (! se) return;
+
+  for (i = 0; i < se->nwords; i++)
+    {
+      word *w = se->words[i];
+
+      switch (s->mode)
+        {
+        case PAGE:
+          if (se->anim_state != PAUSE &&
+              w->tick <= w->nticks)
+            {
+              int dx = w->target_x - w->start_x;
+              int dy = w->target_y - w->start_y;
+              double r = sin (w->tick * M_PI / (2 * w->nticks));
+              w->x = w->start_x + (dx * r);
+              w->y = w->start_y + (dy * r);
+
+              w->tick++;
+              if (se->anim_state == OUT && s->mode == PAGE)
+                w->tick++;  /* go out faster */
+              moved = True;
+            }
+          break;
+        case SCROLL:
+          {
+            int dx = w->target_x - w->start_x;
+            int dy = w->target_y - w->start_y;
+            double r = (double) w->tick / w->nticks;
+            w->x = w->start_x + (dx * r);
+            w->y = w->start_y + (dy * r);
+            w->tick++;
+            moved = (w->tick <= w->nticks);
+
+            /* Launch a new sentence when:
+               - the front of this sentence is almost off the left edge;
+               - the end of this sentence is almost on screen.
+               - or, randomly
+             */
+            if (se->anim_state != OUT &&
+                i == 0 &&
+                se->id == s->latest_sentence)
+              {
+                Bool new_p = (w->x < (s->xgwa.width * 0.4) &&
+                              w->x + se->width < (s->xgwa.width * 2.1));
+                Bool rand_p = (new_p ? 0 : !(random() % 2000));
+
+                if (new_p || rand_p)
+                  {
+                    se->anim_state = OUT;
+                    s->spawn_p = True;
+# ifdef DEBUG
+                    if (s->debug_p)
+                      fprintf (stderr, "%s: OUT   %d (x2 = %d%s)\n",
+                               progname, se->id,
+                               se->words[0]->x + se->width,
+                               rand_p ? " randomly" : "");
+# endif
+                  }
+              }
+          }
+          break;
+        default:
+          abort();
+          break;
+        }
+
+      draw_word (s, se, w);
+    }
+
+  if (moved && se->anim_state == PAUSE)
+    abort();
+
+  if (! moved)
+    {
+      switch (se->anim_state)
+        {
+        case IN:
+          se->anim_state = PAUSE;
+          se->pause_tick = (se->nwords * 7 * s->linger);
+          if (se->move_chars_p)
+            se->pause_tick /= 5;
+          scatter_sentence (s, se);
+          shuffle_words (s, se);
+# ifdef DEBUG
+          if (s->debug_p)
+            fprintf (stderr, "%s: PAUSE %d\n", progname, se->id);
+# endif
+          break;
+        case PAUSE:
+          if (--se->pause_tick <= 0)
+            {
+              se->anim_state = OUT;
+              s->spawn_p = True;
+# ifdef DEBUG
+              if (s->debug_p)
+                fprintf (stderr, "%s: OUT   %d\n", progname, se->id);
+# endif
+            }
+          break;
+        case OUT:
+# ifdef DEBUG
+          if (s->debug_p)
+            fprintf (stderr, "%s: DEAD  %d\n", progname, se->id);
+# endif
+          {
+            int j;
+            for (j = 0; j < s->nsentences; j++)
+              if (s->sentences[j] == se)
+                s->sentences[j] = 0;
+            free_sentence (s, se);
+          }
+          break;
+        default:
+          abort();
+          break;
+        }
+    }
+}
+
+
+/* Render all the words to the screen, and run the animation one step.
+   Clear screen first, swap buffers after.
+ */
+static void
+draw_fontglide (state *s)
+{
+  int i;
+
+  if (s->spawn_p)
+    more_sentences (s);
+
+  if (!s->trails_p)
+    XFillRectangle (s->dpy, s->b, s->bg_gc,
+                    0, 0, s->xgwa.width, s->xgwa.height);
+
+  for (i = 0; i < s->nsentences; i++)
+    draw_sentence (s, s->sentences[i]);
+
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+  if (s->backb)
+    {
+      XdbeSwapInfo info[1];
+      info[0].swap_window = s->window;
+      info[0].swap_action = (s->dbeclear_p ? XdbeBackground : XdbeUndefined);
+      XdbeSwapBuffers (s->dpy, info, 1);
+    }
+  else
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+  if (s->dbuf)
+    {
+      XCopyArea (s->dpy, s->b, s->window, s->bg_gc,
+                0, 0, s->xgwa.width, s->xgwa.height, 0, 0);
+    }
+}
+
+
+static void
+handle_events (state *s)
+{
+  while (XPending (s->dpy))
+    {
+      XEvent event;
+      XNextEvent (s->dpy, &event);
+
+      if (event.xany.type == ConfigureNotify)
+        {
+          XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
+
+          if (s->dbuf && (s->ba))
+            {
+              XFreePixmap (s->dpy, s->ba);
+              s->ba = XCreatePixmap (s->dpy, s->window, 
+                                    s->xgwa.width, s->xgwa.height,
+                                    s->xgwa.depth);
+              XFillRectangle (s->dpy, s->ba, s->bg_gc, 0, 0, 
+                              s->xgwa.width, s->xgwa.height);
+              s->b = s->ba;
+            }
+        }
+
+      screenhack_handle_event (s->dpy, &event);
+    }
+}
+
+
+\f
+/* Subprocess.
+   (This bit mostly cribbed from phosphor.c)
+ */
+
+static void
+subproc_cb (XtPointer closure, int *source, XtInputId *id)
+{
+  state *s = (state *) closure;
+  s->input_available_p = True;
+}
+
+
+static void
+launch_text_generator (state *s)
+{
+  char *oprogram = get_string_resource ("program", "Program");
+  char *program;
+
+  program = (char *) malloc (strlen (oprogram) + 10);
+  strcpy (program, "( ");
+  strcat (program, oprogram);
+  strcat (program, " ) 2>&1");
+
+  if (s->debug_p)
+    fprintf (stderr, "%s: forking: %s\n", progname, program);
+
+  if ((s->pipe = popen (program, "r")))
+    {
+      s->pipe_id =
+        XtAppAddInput (app, fileno (s->pipe),
+                       (XtPointer) (XtInputReadMask | XtInputExceptMask),
+                       subproc_cb, (XtPointer) s);
+    }
+  else
+    {
+      perror (program);
+    }
+}
+
+
+static void
+relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
+{
+  state *s = (state *) closure;
+  launch_text_generator (s);
+}
+
+
+/* When the subprocess has generated some output, this reads as much as it
+   can into s->buf at s->buf_tail.
+ */
+static void
+drain_input (state *s)
+{
+  /* allow subproc_cb() to run, if the select() down in Xt says that
+     input is available.  This tells us whether we can read() without
+     blocking. */
+  if (! s->input_available_p)
+    if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+      XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+
+  if (! s->pipe) return;
+  if (! s->input_available_p) return;
+  s->input_available_p = False;
+
+  if (s->buf_tail < sizeof(s->buf) - 2)
+    {
+      int target = sizeof(s->buf) - s->buf_tail - 2;
+      int n;
+      n = read (fileno (s->pipe),
+                (void *) (s->buf + s->buf_tail),
+                target);
+      if (n > 0)
+        {
+          s->buf_tail += n;
+          s->buf[s->buf_tail] = 0;
+        }
+      else
+        {
+          XtRemoveInput (s->pipe_id);
+          s->pipe_id = 0;
+          pclose (s->pipe);
+          s->pipe = 0;
+
+          /* If the process didn't print a terminating newline, add one. */
+          if (s->buf_tail > 1 &&
+              s->buf[s->buf_tail-1] != '\n')
+            {
+              s->buf[s->buf_tail++] = '\n';
+              s->buf[s->buf_tail] = 0;
+            }
+
+          /* Then add one more, to make sure there's a sentence break at EOF.
+           */
+          s->buf[s->buf_tail++] = '\n';
+          s->buf[s->buf_tail] = 0;
+
+          /* Set up a timer to re-launch the subproc in a bit. */
+          XtAppAddTimeOut (app, s->subproc_relaunch_delay,
+                           relaunch_generator_timer,
+                           (XtPointer) s);
+        }
+    }
+}
+
+\f
+/* Window setup and resource loading */
+
+static state *
+init_fontglide (Display *dpy, Window window)
+{
+  XGCValues gcv;
+  state *s = (state *) calloc (1, sizeof(*s));
+  s->dpy = dpy;
+  s->window = window;
+
+  XGetWindowAttributes (s->dpy, s->window, &s->xgwa);
+
+  s->font_override = get_string_resource ("font", "Font");
+  if (s->font_override && (!*s->font_override || *s->font_override == '('))
+    s->font_override = 0;
+
+  s->charset = get_string_resource ("fontCharset", "FontCharset");
+  s->border_width = get_integer_resource ("fontBorderWidth", "Integer");
+  if (s->border_width < 0 || s->border_width > 20)
+    s->border_width = 1;
+
+  s->speed = get_float_resource ("speed", "Float");
+  if (s->speed <= 0 || s->speed > 200)
+    s->speed = 1;
+
+  s->linger = get_float_resource ("linger", "Float");
+  if (s->linger <= 0 || s->linger > 200)
+    s->linger = 1;
+
+  s->debug_p = get_boolean_resource ("debug", "Debug");
+  s->trails_p = get_boolean_resource ("trails", "Trails");
+
+  s->dbuf = get_boolean_resource ("doubleBuffer", "Boolean");
+  s->dbeclear_p = get_boolean_resource ("useDBEClear", "Boolean");
+
+  if (s->trails_p) s->dbuf = False;  /* don't need it in this case */
+
+  {
+    char *ss = get_string_resource ("mode", "Mode");
+    if (!ss || !*ss || !strcasecmp (ss, "random"))
+      s->mode = ((random() % 2) ? SCROLL : PAGE);
+    else if (!strcasecmp (ss, "scroll"))
+      s->mode = SCROLL;
+    else if (!strcasecmp (ss, "page"))
+      s->mode = PAGE;
+    else
+      {
+        fprintf (stderr,
+                "%s: `mode' must be `scroll', `page', or `random', not `%s'\n",
+                 progname, ss);
+      }
+  }
+
+  if (s->dbuf)
+    {
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+      if (s->dbeclear_p)
+        s->b = xdbe_get_backbuffer (dpy, window, XdbeBackground);
+      else
+        s->b = xdbe_get_backbuffer (dpy, window, XdbeUndefined);
+      s->backb = s->b;
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+
+      if (!s->b)
+        {
+          s->ba = XCreatePixmap (s->dpy, s->window, 
+                                 s->xgwa.width, s->xgwa.height,
+                                 s->xgwa.depth);
+          s->b = s->ba;
+        }
+    }
+  else
+    {
+      s->b = s->window;
+    }
+
+  gcv.foreground = get_pixel_resource ("background", "Background",
+                                       s->dpy, s->xgwa.colormap);
+  s->bg_gc = XCreateGC (s->dpy, s->b, GCForeground, &gcv);
+
+  s->subproc_relaunch_delay = 2 * 1000;
+
+  launch_text_generator (s);
+
+  s->nsentences = 5; /* #### */
+  s->sentences = (sentence **) calloc (s->nsentences, sizeof (sentence *));
+  s->spawn_p = True;
+
+  return s;
+}
+
+
+\f
+char *progclass = "FontGlide";
+
+char *defaults [] = {
+  ".background:                #000000",
+  ".foreground:                #DDDDDD",
+  ".borderColor:       #555555",
+  "*delay:             10000",
+  "*program:         " FORTUNE_PROGRAM,
+  "*mode:               random",
+  ".font:               (default)",
+  "*fontCharset:        iso8859-1",
+  "*fontBorderWidth:    2",
+  "*speed:              1.0",
+  "*linger:             1.0",
+  "*trails:             False",
+  "*debug:              False",
+  "*doubleBuffer:      True",
+#ifdef HAVE_DOUBLE_BUFFER_EXTENSION
+  "*useDBE:            True",
+  "*useDBEClear:       True",
+#endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
+  0
+};
+
+XrmOptionDescRec options [] = {
+  { "-scroll",         ".mode",            XrmoptionNoArg, "scroll" },
+  { "-page",           ".mode",            XrmoptionNoArg, "page"   },
+  { "-random",         ".mode",            XrmoptionNoArg, "random" },
+  { "-delay",          ".delay",           XrmoptionSepArg, 0 },
+  { "-speed",          ".speed",           XrmoptionSepArg, 0 },
+  { "-linger",         ".linger",          XrmoptionSepArg, 0 },
+  { "-program",                ".program",         XrmoptionSepArg, 0 },
+  { "-font",           ".font",            XrmoptionSepArg, 0 },
+  { "-fn",             ".font",            XrmoptionSepArg, 0 },
+  { "-bw",             ".fontBorderWidth", XrmoptionSepArg, 0 },
+  { "-trails",         ".trails",          XrmoptionNoArg,  "True"  },
+  { "-no-trails",      ".trails",          XrmoptionNoArg,  "False" },
+  { "-db",             ".doubleBuffer",    XrmoptionNoArg,  "True"  },
+  { "-no-db",          ".doubleBuffer",    XrmoptionNoArg,  "False" },
+  { "-debug",          ".debug",           XrmoptionNoArg,  "True"  },
+  { 0, 0, 0, 0 }
+};
+
+
+void
+screenhack (Display *dpy, Window window)
+{
+  state *s = init_fontglide (dpy, window);
+  int delay = get_integer_resource ("delay", "Integer");
+
+  while (1)
+    {
+      handle_events (s);
+      draw_fontglide (s);
+      XSync (dpy, False);
+      if (delay) usleep (delay);
+    }
+}
diff --git a/hacks/fontglide.man b/hacks/fontglide.man
new file mode 100644 (file)
index 0000000..bf036d5
--- /dev/null
@@ -0,0 +1,119 @@
+.TH XScreenSaver 1 "30-Oct-99" "X Version 11"
+.SH NAME
+fontglide - characters float onto the screen to form words
+.SH SYNOPSIS
+.B fontglide
+[\-display \fIhost:display.screen\fP] [\-window] [\-root] [\-install]
+[\-visual \fIvisual\fP] 
+[\-delay \fIusecs\fP] 
+[\-scroll\fP] 
+[\-page\fP] 
+[\-random\fP] 
+[\-speed \fIfloat\fP] 
+[\-linger \fIfloat\fP] 
+[\-program \fIsh-command\fP]
+[\-font \fIfont-name\fP]
+[\-bw \fIint\fP]
+[\-trails]
+[\-db]
+[\-debug]
+.SH DESCRIPTION
+The \fIfontglide\fP program reads text from a subprocess and puts it on
+the screen using large characters that glide in from the edges,
+assemble, then disperse.  Alternately, it can simply scroll whole 
+sentences from right to left.
+.SH OPTIONS
+.I fontglide
+accepts the following options:
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-install
+Install a private colormap for the window.
+.TP 8
+.B \-visual \fIvisual\fP\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-delay \fIusecs\fP
+The delay between steps of the animation, in microseconds: default 10000.
+.TP 8
+.B \-page
+With this option, a page full of text will glide in, and disperse.
+.TP 8
+.B \-scroll
+With this option, sentences will scroll by from right to left.
+.TP 8
+.B \-random
+The default is to pick randomly between \fI\-page\fP and  \fI\-scroll\fP.
+.TP 8
+.B \-speed \fIfloat\fP
+How fast to animate; 2 means twice as fast, 0.5 means half as fast.
+Default 1.0.
+.TP 8
+.B \-linger \fIfloat\fP
+How long to leave the assembled text on the screen in \fI\-page\fP mode;
+2 means twice as long, 0.5 means half as long.  Default 1.0.  (The more
+words there are on the screen, the longer it lingers.)
+.TP 8
+.B \-program \fIsh-command\fP
+The command to run to generate the text to display.  This option may be
+any string acceptable to /bin/sh.  The program will be run at the end of
+a pipe, and any words that it prints to \fIstdout\fP will end up on
+the window.  (Whitespace and line breaks are ignored.)  If the program 
+exits, it will be launched again after we have processed all the text
+it produced.  Default:
+.BR fortune (1).
+.TP 8
+.B \-font\fP \fIstring\fP
+The base font pattern to use when loading fonts.  The default is to search
+for any Latin1 scalable proportional fonts on the system.  Once a base font
+is selected, it will be loaded in a random size.
+.TP 8
+.B \-bw \fIint\fP
+How thick an outline to draw around the characters.  Default 2 pixels.
+.TP 8
+.B \-trails\fP
+Leave "vapor trails" behind the moving text.  Default off.
+.TP 8
+.B \-no-db\fP
+Turn off double-buffering.  It may be faster, but will flicker.
+.TP 8
+.B \-debug\fP
+Draw some boxes showing character metrics, and print the name of the
+current font to stderr.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH SEE ALSO
+.BR xscreensaver (1),
+.BR fortune (1),
+.BR phosphor (1),
+.BR apple2 (1),
+.BR starwars (1),
+.BR ljlatest (1),
+.BR dadadodo (1),
+.BR webcollage (1),
+.BR driftnet (1)
+.BR EtherPEG ,
+.BR EtherPeek
+.SH COPYRIGHT
+Copyright \(co 2003 by Jamie Zawinski.  Permission to use, copy, modify, 
+distribute, and sell this software and its documentation for any purpose is 
+hereby granted without fee, provided that the above copyright notice appear 
+in all copies and that both that copyright notice and this permission notice
+appear in supporting documentation.  No representations are made about the 
+suitability of this software for any purpose.  It is provided "as is" without
+express or implied warranty.
+.SH AUTHOR
+Jamie Zawinski <jwz@jwz.org>, 15-Sep-2003.
index 08d870bb6e2a3b312dc748041ecab880465aa73e..87e698b156538903f48037251df9ccff40db09fa 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* galaxy --- spinning galaxies */
 /* #include<math.h>*/
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)galaxy.c 4.04 97/07/28 xlockmore";
 #endif
 
index 6e6834127621a12e3341c063940b0ae76aee4fa5..84609e08bc35f7bd99e83abc38bac376b6d144e9 100644 (file)
@@ -94,7 +94,8 @@ SRCS          = xscreensaver-gl-helper.c \
                  bouncingcow.c cow_face.c cow_hide.c cow_hoofs.c cow_horns.c \
                  cow_tail.c cow_udder.c glslideshow.c jigglypuff.c klein.c \
                  hypertorus.c glmatrix.c cubestorm.c glknots.c blocktube.c \
-                 flipflop.c antspotlight.c polytopes.c
+                 flipflop.c antspotlight.c polytopes.c gleidescope.c \
+                 mirrorblob.c blinkbox.c
 
 OBJS           = xscreensaver-gl-helper.o \
                  atlantis.o b_draw.o b_lockglue.o b_sphere.o bubble3d.o \
@@ -121,7 +122,8 @@ OBJS                = xscreensaver-gl-helper.o \
                  bouncingcow.o cow_face.o cow_hide.o cow_hoofs.o cow_horns.o \
                  cow_tail.o cow_udder.o glslideshow.o jigglypuff.o klein.o \
                  hypertorus.o glmatrix.o cubestorm.o glknots.o blocktube.o \
-                 flipflop.o antspotlight.o polytopes.o
+                 flipflop.o antspotlight.o polytopes.o gleidescope.o \
+                 mirrorblob.o blinkbox.o
 
 GL_EXES                = cage gears moebius pipes sproingies stairs superquadrics \
                  morph3d rubik atlantis lament bubble3d glplanet pulsar \
@@ -130,7 +132,8 @@ GL_EXES             = cage gears moebius pipes sproingies stairs superquadrics \
                  glforestfire sballs cubenetic spheremonics lavalite queens \
                  endgame glblur flurry atunnel flyingtoasters bouncingcow \
                  glslideshow jigglypuff klein hypertorus glmatrix cubestorm \
-                 glknots blocktube flipflop antspotlight polytopes
+                 glknots blocktube flipflop antspotlight polytopes \
+                 gleidescope mirrorblob blinkbox
 GLE_EXES       = extrusion
 GL_UTIL_EXES   = xscreensaver-gl-helper
 HACK_EXES      = @GL_EXES@ @GLE_EXES@
@@ -161,7 +164,8 @@ GL_MEN              = atlantis.man boxed.man bubble3d.man cage.man circuit.man \
                  flyingtoasters.man bouncingcow.man glslideshow.man \
                  jigglypuff.man klein.man hypertorus.man glmatrix.man \
                  cubestorm.man glknots.man blocktube.man flipflop.man \
-                 antspotlight.man polytopes.man
+                 antspotlight.man polytopes.man gleidescope.man \
+                 mirrorblob.man blinkbox.man
 MEN            = @GL_MEN@
 EXTRAS         = README Makefile.in dxf2gl.pl
 
@@ -378,11 +382,11 @@ TRACK_OBJS=rotator.o trackball.o gltrackball.o
 
 ATLANTIS_OBJS = $(HACK_OBJS) dolphin.o shark.o swim.o whale.o xpm-ximage.o
 atlantis:      atlantis.o      $(ATLANTIS_OBJS)
-       $(CC_HACK) -o $@ $@.o   $(ATLANTIS_OBJS) $(HACK_LIBS) $(XPM_LIBS)
+       $(CC_HACK) -o $@ $@.o   $(ATLANTIS_OBJS) $(XPM_LIBS)
 
 ATUNNEL_OBJS = $(HACK_OBJS) tunnel_draw.o xpm-ximage.o
 atunnel:       atunnel.o $(ATUNNEL_OBJS)
-       $(CC_HACK) -o $@ $@.o   $(ATUNNEL_OBJS) $(HACK_LIBS) $(XPM_LIBS)
+       $(CC_HACK) -o $@ $@.o   $(ATUNNEL_OBJS) $(XPM_LIBS)
 
 cage:          cage.o          $(HACK_OBJS)
        $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(HACK_LIBS)
@@ -432,7 +436,7 @@ glplanet:   glplanet.o      $(PLANETHACKS) $(HACK_OBJS)
        $(CC_HACK) -o $@ $@.o   $(PLANETHACKS) $(HACK_OBJS) $(XPM_LIBS)
 
 pulsar:                pulsar.o        $(HACK_OBJS) xpm-ximage.o
-       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) xpm-ximage.o $(HACK_LIBS) $(XPM_LIBS)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) xpm-ximage.o $(XPM_LIBS)
 
 EXTRUSION_OBJS=extrusion.o extrusion-helix2.o extrusion-helix3.o    \
        extrusion-helix4.o extrusion-joinoffset.o extrusion-screw.o \
@@ -538,7 +542,7 @@ glknots:    glknots.o       tube.o $(TRACK_OBJS) $(HACK_OBJS)
        $(CC_HACK) -o $@ $@.o   tube.o $(TRACK_OBJS) $(HACK_OBJS) $(HACK_LIBS)
 
 blocktube:     blocktube.o     $(HACK_OBJS) xpm-ximage.o
-       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) xpm-ximage.o $(HACK_LIBS) $(XPM_LIBS)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) xpm-ximage.o $(XPM_LIBS)
 
 flipflop:      flipflop.o      $(TRACK_OBJS) $(HACK_OBJS)
        $(CC_HACK) -o $@ $@.o   $(TRACK_OBJS) $(HACK_OBJS) $(HACK_LIBS)
@@ -552,7 +556,7 @@ polytopes:  polytopes.o     $(HACK_OBJS)
 # This one works differently (it's not xlock-like.)
 #
 STONER_OBJS=stonerview.o stonerview-move.o stonerview-osc.o stonerview-view.o \
-           $(UTILS_BIN)/yarandom.o
+           $(UTILS_BIN)/yarandom.o $(UTILS_BIN)/usleep.o
 stonerview:    $(STONER_OBJS)
        $(CC_HACK) -o $@ $(STONER_OBJS) $(HACK_LIBS)
 
@@ -571,8 +575,17 @@ MOLECULEOBJS=sphere.o tube.o $(TRACK_OBJS)
 molecule:      molecule.o      $(MOLECULEOBJS) $(HACK_OBJS)
        $(CC_HACK) -o $@ $@.o   $(MOLECULEOBJS) $(HACK_OBJS) $(HACK_LIBS)
 
-dnalogo:       dnalogo.o       tube.o $(HACK_OBJS)
-       $(CC_HACK) -o $@ $@.o   tube.o $(HACK_OBJS) $(HACK_LIBS)
+gleidescope:   gleidescope.o xpm-ximage.o $(HACK_OBJS) $(GRAB_OBJS)
+       $(CC_HACK) -o $@ $@.o xpm-ximage.o $(HACK_OBJS) $(GRAB_OBJS) $(XPM_LIBS)
+
+mirrorblob:    mirrorblob.o    $(HACK_OBJS) $(GRAB_OBJS)
+       $(CC_HACK) -o $@ $@.o   $(HACK_OBJS) $(GRAB_OBJS) $(XPM_LIBS)
+
+blinkbox:      blinkbox.o      sphere.o $(HACK_OBJS)
+       $(CC_HACK) -o $@ $@.o   sphere.o $(HACK_OBJS) $(HACK_LIBS)
+
+dnalogo:       dnalogo.o       tube.o $(TRACK_OBJS) $(HACK_OBJS)
+       $(CC_HACK) -o $@ $@.o   tube.o $(TRACK_OBJS) $(HACK_OBJS) $(HACK_LIBS)
 
 ##############################################################################
 #
@@ -595,6 +608,8 @@ atunnel.o: $(srcdir)/tunnel_draw.h
 atunnel.o: $(srcdir)/xpm-ximage.h
 b_draw.o: $(srcdir)/bubble3d.h
 b_draw.o: ../../config.h
+blinkbox.o: ../../config.h
+blinkbox.o: $(srcdir)/sphere.h
 b_lockglue.o: $(srcdir)/bubble3d.h
 b_lockglue.o: ../../config.h
 blocktube.o: ../../config.h
@@ -694,6 +709,9 @@ gflux.o: $(srcdir)/grab-ximage.h
 glblur.o: ../../config.h
 glblur.o: $(srcdir)/gltrackball.h
 glblur.o: $(srcdir)/rotator.h
+gleidescope.o: ../../config.h
+gleidescope.o: $(srcdir)/grab-ximage.h
+gleidescope.o: $(srcdir)/xpm-ximage.h
 glforestfire.o: ../../config.h
 glforestfire.o: $(HACK_SRC)/images/ground.xpm
 glforestfire.o: $(HACK_SRC)/images/tree.xpm
@@ -753,6 +771,8 @@ marching.o: $(srcdir)/marching.h
 menger.o: ../../config.h
 menger.o: $(srcdir)/gltrackball.h
 menger.o: $(srcdir)/rotator.h
+mirrorblob.o: ../../config.h
+mirrorblob.o: $(srcdir)/grab-ximage.h
 moebius.o: ../../config.h
 moebius.o: $(srcdir)/e_textures.h
 moebius.o: $(srcdir)/gltrackball.h
index d84982a244718a8fec2d1c49307fe4e00d3c22d8..a129b7994dc15cfe178dfd958f71572ccc495056 100644 (file)
@@ -357,6 +357,7 @@ void build_ant(void) {
   ant->direction = 0.0;
   ant->velocity = 0.02;
   ant->material = MaterialGray5;
+  ant->step = 0;
   find_goal();
 }
 
@@ -479,6 +480,8 @@ void draw_antspotlight_strip(ModeInfo *mi) {
   ant->position[0] += ant->velocity * cos(ant->direction);
   ant->position[2] += ant->velocity * sin(-ant->direction);
   ant->step += 10*ant->velocity;
+  while(ant->step > 2*Pi)
+    ant->step -= 2*Pi;
 }
 
 void reshape_antspotlight(ModeInfo * mi, int width, int height) {
@@ -625,7 +628,7 @@ void get_snapshot(ModeInfo *modeinfo) {
   if(MI_IS_WIREFRAME(modeinfo))
     return;
 
-  ximage = screen_to_ximage(modeinfo->xgwa.screen, modeinfo->window);
+  ximage = screen_to_ximage(modeinfo->xgwa.screen, modeinfo->window, NULL);
 
   qw = QW; qh = QH;
   tw = modeinfo->xgwa.width;
index e7fc50f1d7d00f00c1e7e05f2aeba865168a26d2..e0a90ae897fe630734516ced4e9addf4668d8aa1 100644 (file)
@@ -1,8 +1,7 @@
 /* atlantis --- Shows moving 3D sea animals */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)atlantis.c   5.08 2003/04/09 xlockmore";
-
 #endif
 
 /* Copyright (c) E. Lassauge, 1998. */
index 7f73a7dc24fbda33e1e3c7cda070467bad840c1e..6d3b8b621f9073b23c1c422687986be9865acfc8 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* atunnels --- OpenGL Advanced Tunnel Screensaver */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)atunnels.c   5.07 2003/02/12 xlockmore";
 #endif
 
index 18c89d9a49a8a65d35d4d16d6b3ef0e4106b59f2..0986156df04b722164eb54ac4b8c60951a520932 100644 (file)
@@ -1,6 +1,5 @@
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)b_draw.c  4.11 98/06/16 xlockmore";
-
 #endif
 
 /*-
index fba029e441725c2f7c1adb326b8bf695cda43944..d0bc7229fc93f488c56a2497419bc2dafdd65ad6 100644 (file)
@@ -1,6 +1,5 @@
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)b_lockglue.c  4.11 98/06/16 xlockmore";
-
 #endif
 
 /*-
index 2f8b25f644581666ca8e255d1d0e82489e2fef55..9177b3515cdfefc3f48edf7e8da68282aabb5b35 100644 (file)
@@ -1,6 +1,5 @@
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)b_sphere.c  4.11 98/06/16 xlockmore";
-
 #endif
 
 /*-
diff --git a/hacks/glx/blinkbox.c b/hacks/glx/blinkbox.c
new file mode 100644 (file)
index 0000000..bbd1e59
--- /dev/null
@@ -0,0 +1,351 @@
+/* blinkbox, Copyright (c) 2003 Jeremy English <jenglish@myself.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ */
+
+#include <X11/Intrinsic.h>
+
+extern XtAppContext app;
+
+#define PROGCLASS        "BlinkBox"
+#define HACK_INIT        init_ball
+#define HACK_DRAW        draw_ball
+#define HACK_RESHAPE  reshape_ball
+#define sws_opts         xlockmore_opts
+
+#define MAX_COUNT 20
+
+#define DEFAULTS       "*delay:        30000       \n" \
+
+#include "xlockmore.h"
+#include "sphere.h"
+#include <ctype.h>
+
+#ifdef USE_GL /* whole file */
+
+
+#include <GL/glu.h>
+
+typedef struct{
+  GLfloat x,y,z;
+} Tdpos;
+
+typedef struct{
+  int hit;
+  Tdpos pos;
+  int counter;
+  GLfloat color[3];
+  GLfloat rot[4];
+}Side;
+
+struct Bounding_box {
+  Tdpos top;
+  Tdpos bottom;
+} bbox = {{7,7,10},{-7,-7,-10}};
+
+struct Ball {
+  GLfloat x;
+  GLfloat y;
+  GLfloat z;
+  int d;
+} ball = {0,0,0,1};
+
+static GLuint ballList;
+static GLuint boxList;
+Tdpos mo = { 1.0, 1.0, 1.0};  /*motion*/
+Tdpos moh= {-1.0,-1.5,-1.5}; /*hold motion value*/
+
+GLfloat bscale[3] = {1,1,0.25};
+GLfloat bpos[3];
+
+/*sides*/
+static Side lside = {0, {0, 0, 0,}, MAX_COUNT,  {1.0, 0.0, 0.0},{90,0,1,0}};/*Red*/
+static Side rside = {0, {0, 0, 0,}, MAX_COUNT,  {0.0, 1.0, 0.0},{90,0,1,0}};/*Green*/
+static Side tside = {0, {0, 0, 0,}, MAX_COUNT,  {0.0, 0.0, 1.0},{90,1,0,0}};/*Blue*/
+static Side bside = {0, {0, 0, 0,}, MAX_COUNT,  {1.0, 0.5, 0.0},{90,1,0,0}};/*Orange*/
+static Side fside = {0, {0, 0, 0,}, MAX_COUNT,  {1.0, 1.0, 0.0},{90,0,0,1}};/*Yellow*/
+static Side aside = {0, {0, 0, 0,}, MAX_COUNT,  {0.5, 0.0, 1.0},{90,0,0,1}};/*Purple*/
+Side *sp;
+
+/* lights */
+static float LightDiffuse[]=   { 1.0f, 1.0f, 1.0f, 1.0f };
+static float LightPosition[]=  { 20.0f, 100.0f, 20.0f, 1.0f };
+
+ModeSpecOpt sws_opts = {0,NULL,0,NULL,NULL};
+
+void
+swap(GLfloat *a, GLfloat *b)
+{
+  GLfloat t = *a;
+  *a = *b;
+  *b = t;
+}
+
+float 
+get_rand(void)
+{
+  GLfloat j = 1+(random() % 2);
+  return (j);
+}
+
+void
+swap_mov(GLfloat *a, GLfloat *b)
+{
+  int j;
+  swap(a,b);
+  j = get_rand();
+  if (*a < 0)
+    *a = (j *= -1);
+  else
+    *a = j;
+}
+
+void 
+cp_b_pos(Tdpos *s_pos){
+  s_pos->x = ball.x;
+  s_pos->y = ball.y;
+  s_pos->z = ball.z;
+}
+
+void
+hit_side(void)
+{
+  if ((ball.x - ball.d) <= bbox.bottom.x){ 
+    lside.hit = 1;
+    lside.counter = MAX_COUNT;
+    cp_b_pos(&lside.pos);
+    swap_mov(&mo.x,&moh.x);
+  }else
+  if ((ball.x + ball.d) >= bbox.top.x){ 
+    rside.hit = 1;
+    rside.counter = MAX_COUNT;
+    cp_b_pos(&rside.pos);
+    swap_mov(&mo.x,&moh.x);
+  }
+}
+
+void
+hit_top_bottom(void)
+{
+  if ((ball.y - ball.d) <= bbox.bottom.y){
+    bside.hit = 1;
+    bside.counter = MAX_COUNT;    
+    cp_b_pos(&bside.pos);
+    swap_mov(&mo.y,&moh.y);
+  }else
+  if ((ball.y + ball.d) >= bbox.top.y){
+    tside.hit = 1;
+    tside.counter = MAX_COUNT;
+    cp_b_pos(&tside.pos);
+    swap_mov(&mo.y,&moh.y);
+  }
+}
+
+void
+hit_front_back(void)
+{    
+  if ((ball.z - ball.d) <= bbox.bottom.z){
+    aside.hit = 1;
+    aside.counter = MAX_COUNT;
+    cp_b_pos(&aside.pos);
+    swap_mov(&mo.z,&moh.z);
+  }else
+  if((ball.z + ball.d) >= bbox.top.z){
+    fside.hit = 1;
+    fside.counter = MAX_COUNT;
+    cp_b_pos(&fside.pos);
+    swap_mov(&mo.z,&moh.z);
+  }
+}
+
+void
+reshape_ball (ModeInfo *mi, int width, int height)
+{
+  GLfloat h = (GLfloat) height / (GLfloat) width;
+
+  glViewport (0, 0, (GLint) width, (GLint) height);
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  gluPerspective (30.0, 1/h, 1.0, 100.0);
+
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  gluLookAt( 0.0, 0.0, 40.0,
+             0.0, 0.0, 0.0,
+             0.0, 2.0,  10.0);
+
+}
+
+void
+unit_cube(void)
+{
+  glBegin(GL_QUADS);           
+  glNormal3f( 0.0f, -1.0f, 0.0f);
+  glVertex3f(-1.0f, -1.0f, -1.0f);     
+  glVertex3f( 1.0f, -1.0f, -1.0f);
+  glVertex3f( 1.0f, -1.0f,  1.0f);
+  glVertex3f(-1.0f, -1.0f,  1.0f);
+  glNormal3f( 0.0f,  0.0f,  1.0f);
+  glVertex3f(-1.0f, -1.0f,  1.0f);
+  glVertex3f( 1.0f, -1.0f,  1.0f);
+  glVertex3f( 1.0f,  1.0f,  1.0f);
+  glVertex3f(-1.0f,  1.0f,  1.0f);
+  glNormal3f( 0.0f,  0.0f, -1.0f);
+  glVertex3f(-1.0f, -1.0f, -1.0f);
+  glVertex3f(-1.0f,  1.0f, -1.0f);
+  glVertex3f( 1.0f,  1.0f, -1.0f);
+  glVertex3f( 1.0f, -1.0f, -1.0f);     
+  glNormal3f( 1.0f,  0.0f,  0.0f);
+  glVertex3f( 1.0f, -1.0f, -1.0f);
+  glVertex3f( 1.0f,  1.0f, -1.0f);
+  glVertex3f( 1.0f,  1.0f,  1.0f);
+  glVertex3f( 1.0f, -1.0f,  1.0f);
+  glNormal3f( -1.0f, 0.0f,  0.0f);
+  glVertex3f(-1.0f, -1.0f, -1.0f);
+  glVertex3f(-1.0f, -1.0f,  1.0f);
+  glVertex3f(-1.0f,  1.0f,  1.0f);
+  glVertex3f(-1.0f,  1.0f, -1.0f);
+  glNormal3f( 1.0f,  1.0f,  0.0f);
+  glVertex3f(-1.0f,  1.0f, -1.0f);
+  glVertex3f(-1.0f,  1.0f,  1.0f);
+  glVertex3f( 1.0f,  1.0f,  1.0f);
+  glVertex3f( 1.0f,  1.0f, -1.0f);
+  glEnd();                                             
+}
+
+void 
+init_ball (ModeInfo *mi)
+{ 
+  #define SPHERE_SLICES 32  /* how densely to render spheres */
+  #define SPHERE_STACKS 16
+  sp = malloc(sizeof(*sp));
+  if(sp == NULL){
+    fprintf(stderr,"Could not allocate memory\n");
+    exit(1);
+  }
+  init_GL(mi);
+  reshape_ball(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+  ballList = glGenLists(1);
+  glNewList(ballList, GL_COMPILE);
+  unit_sphere (SPHERE_STACKS, SPHERE_SLICES, False);
+  glEndList ();
+
+  boxList = glGenLists(1);
+  glNewList(boxList, GL_COMPILE);
+  unit_cube();
+  glEndList();
+
+  glEnable(GL_COLOR_MATERIAL);
+  glShadeModel(GL_SMOOTH);                             
+  glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
+  glClearDepth(1.0f);                          
+  glEnable(GL_DEPTH_TEST);             
+  glDepthFunc(GL_LEQUAL);
+  glEnable(GL_LIGHTING);
+  glClearDepth(1);
+  glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
+  glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);
+  glEnable(GL_LIGHT1);
+}
+
+void
+draw_ball (ModeInfo *mi)
+{  
+   Display *dpy = MI_DISPLAY(mi);
+   Window window = MI_WINDOW(mi);
+   /*int dem = 1;*/
+   int i = 0;
+   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+   hit_top_bottom();
+   hit_front_back();
+   hit_side();
+
+   glRotated(0.25,0,0,1);
+   glRotated(0.25,0,1,0);
+   glRotated(0.25,1,0,0);
+
+   glColor3f(1,1,1);
+   glPushMatrix();
+   glTranslatef(ball.x += mo.x,
+                ball.y += mo.y,
+                ball.z += mo.z);
+
+   glCallList(ballList);
+   glPopMatrix();
+
+   while(i < 6){
+    switch(i){
+      case 0:{
+               sp = &lside;
+               bpos[0] = lside.pos.z*-1;
+               bpos[1] = lside.pos.y;
+               bpos[2] = bbox.bottom.x - bscale[3];
+               break;
+             }
+      case 1:{
+               sp = &rside;
+               bpos[0] = rside.pos.z*-1;
+               bpos[1] = rside.pos.y;
+               bpos[2] = bbox.top.x + bscale[3];
+               break;
+             }
+      case 2:{
+               sp = &tside;
+               bpos[0] = tside.pos.x;
+               bpos[1] = tside.pos.z;
+               bpos[2] = bbox.bottom.y - bscale[3];
+               break;
+             }
+      case 3:{
+               sp = &bside;
+               bpos[0] = bside.pos.x;
+               bpos[1] = bside.pos.z;
+               bpos[2] = bbox.top.y + bscale[3];
+               break;
+             }
+      case 4:{
+               sp = &fside;
+               bpos[0] = fside.pos.y;
+               bpos[1] = fside.pos.x*-1;
+               bpos[2] = bbox.top.z + bscale[3];
+               break;
+             }
+      case 5:{
+               sp = &aside;
+               bpos[0] = aside.pos.y;
+               bpos[1] = aside.pos.x*-1;
+               bpos[2] = bbox.bottom.z + bscale[3];
+               break;
+             }
+    }
+    if(sp->hit){
+     glColor3fv(sp->color);
+     glPushMatrix();
+     glRotatef(sp->rot[0],sp->rot[1],sp->rot[2],sp->rot[3]);
+     glTranslatef(bpos[0],bpos[1],bpos[2]);
+     /*dem = (MAX_COUNT-(sp->counter+1));*/
+     /*glScalef(bscale[0]/dem,bscale[1]/dem,bscale[2]);*/
+     glScalef(bscale[0],bscale[1],bscale[2]);
+     glCallList(boxList);
+     glPopMatrix();
+     sp->counter--;
+     if(!sp->counter)
+       sp->hit = 0;
+    }
+    i++;
+  }
+
+
+   glFinish();
+   glXSwapBuffers(dpy, window);
+
+}
+
+#endif /* USE_GL */
diff --git a/hacks/glx/blinkbox.man b/hacks/glx/blinkbox.man
new file mode 100644 (file)
index 0000000..e8815d7
--- /dev/null
@@ -0,0 +1,49 @@
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+blinkbox 
+.SH SYNOPSIS
+.B boundingbox
+[\-display \fIhost:display.screen\fP]
+[\-visual \fIvisual\fP]
+[\-window]
+[\-root]
+[\-delay \fInumber\fP]
+.SH DESCRIPTION
+Shows a ball contained inside of a bounding box. Colored blocks blink in
+when the ball hits the edges.
+.SH OPTIONS
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-delay \fInumber\fP
+Per-frame delay, in microseconds.  Default: 30000 (0.03 seconds.).
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1)
+.SH COPYRIGHT
+Copyright \(co 2003 by Jeremy English.  Permission to use, copy, modify, 
+distribute, and sell this software and its documentation for any purpose is 
+hereby granted without fee, provided that the above copyright notice appear 
+in all copies and that both that copyright notice and this permission notice
+appear in supporting documentation.  No representations are made about the 
+suitability of this software for any purpose.  It is provided "as is" without
+express or implied warranty.
+.SH AUTHOR
+Jeremy English.
index 3159a2438a60364c2280a565330b548f154e727a..238cb3c8f8dcba2d4b7323f4371054c61a5047e2 100644 (file)
@@ -1,8 +1,7 @@
 /* thebox --- 3D bouncing balls that explode */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)boxed.c      0.9 01/09/26 xlockmore";
-
 #endif
 
 /*-
index dd5b68e4af1425be28aa990305fff72a5f7e44e9..52dbc33e03b29843084a62de9c331ee99141f839 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* bubble3d.c - 3D bubbles  */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)bubble3d.c  4.11 98/06/16 xlockmore";
-
 #endif
 
 /*-
index 1aa86edaaf2cb719e0e8ce80b2de24b98003de58..7ae9e5f575495c63eab6ec1eec432fec3bf36349 100644 (file)
@@ -1,7 +1,6 @@
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)buildlwo.c   4.02 97/04/20 xlockmore";
-
 #endif
 
 /*-
index 18c2bda5381be14e269b2d72c60ae0d262340710..85bd17fbd8b09042b0289ebf098b76fbddb00e9e 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* cage --- the Impossible Cage, an Escher like scene. */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)cage.c       5.01 2001/03/01 xlockmore";
-
 #endif
 
 /*-
index 1d61eaa36aafd2be8395f270a342ac9ba58144eb..a752d4f75de06af0c2e52e659caf2123f172a2ef 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * endgame -- plays through a chess game ending.  enjoy.
  *
- * Copyright (C) 2002 Blair Tennessy (tennessb@unbc.ca)
+ * Copyright (C) 2002 Blair Tennessy (tennessy@cs.ubc.ca)
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -35,15 +35,13 @@ typedef struct {
       in case the move promotes a pawn, we assume a queening.
       (see drawMovingPiece())
 
-      whats lacking? 
-      castling, en passant, under-promotions.  hack at will.
-      more games!  feel free to encode favorites!
-      and this moves[40][4] junk.  i love c!
+      what's lacking? 
+      castling, en passant, under-promotions.
   */
   int moves[40][4];
 } ChessGame;
 
-#define GAMES 3
+#define GAMES 7
 ChessGame games[GAMES] = {
   
   /** 
@@ -170,7 +168,176 @@ ChessGame games[GAMES] = {
       {0, 0, 0, 0}, /* mull it over... */
       {0, 0, 3, 1}
     }    
-  }
+  },
+
+  /** 
+      game 4: 
+
+      M.B. Newman
+      White to play and win.
+      
+      "Chess Amateur"
+      1913
+  */
+  {
+    {
+      {      0,      0,      0,      0,  BQUEEN,      0,      0,      0},
+      {BKNIGHT,      0,      0,      0,       0,      0,      0,      0},
+      {      0,      0,      0,      0,       0,      0,      0,      0},
+      {      0,      0,      0,      0,       0,      0,      0,   PAWN},
+      {  BKING,      0, BISHOP,      0,  KNIGHT,      0,      0,      0},
+      {  PAWN,       0,      0,      0,  KNIGHT,      0,      0,      0},
+      {     0,       0,      0,      0,       0,      0,      0,      0},
+      {  KING,       0,      0,      0,       0,      0,      0,      0},
+    },
+    
+    15,
+
+    { 
+      {4, 2, 3, 1},
+      {0, 4, 3, 1}, /* queen wins bishop */
+      {4, 4, 5, 2},
+      {4, 0, 5, 0}, /* king takes pawn */
+      {5, 2, 3, 1}, /* knight takes queen, check */
+      {1, 0, 3, 1}, /* knight takes knight */
+      {3, 7, 2, 7}, /* pawn advances */
+      {3, 1, 2, 3},
+      {5, 4, 4, 2},
+      {2, 3, 4, 2},
+      {2, 7, 1, 7}, /* pawn advances */
+      {4, 2, 2, 3},
+      {1, 7, 0, 7},
+      {0, 0, 0, 0},
+      {0, 0, 5, 0}
+    }    
+  },
+
+  /** 
+      game 5:
+
+      V.A. Korolikov
+      White to play and win
+      
+      First Prize - "Truda"
+      1935
+  */
+  {
+    {
+      {      0,      0, BISHOP,      0,       0,      0,      0,       0},
+      {  BPAWN,   ROOK,      0,      0,       0,      0,      0,       0},
+      {      0,      0,  BPAWN,   PAWN,       0,  BKING,      0,       0},
+      {      0,      0,      0,      0,       0,      0,      0,       0},
+      {      0,      0,      0,      0,       0,      0,   KING, BBISHOP},
+      {      0,      0,      0,      0,   BPAWN,      0,   PAWN,      0},
+      {      0,      0,      0,      0,       0,  BPAWN,      0,      0},
+      {      0,      0,      0,      0,       0,      0,      0,      0},
+    },
+    
+    21,
+
+    { 
+      {2, 3, 1, 3}, /* pawn to q7 */
+      {2, 5, 1, 4}, /* cover with king */
+      {1, 1, 0, 1},
+      {4, 7, 5, 6}, /* bishop takes pawn */
+      {0, 1, 0, 0}, /* r - r8 */
+      {6, 5, 7, 5}, /* queened */
+      {1, 3, 0, 3}, /* white pawn promoted */
+      {1, 4, 0, 3}, /* king takes queen */
+      {0, 2, 2, 0}, /* discovered check */
+      {5, 6, 0, 1}, /* pull back bishop */
+      {2, 0, 7, 5}, /* bishop takes queen */
+      {0, 3, 1, 2},
+      {7, 5, 2, 0}, /* save rook */
+      {5, 4, 6, 4},
+      {2, 0, 6, 4}, /* bishop takes pawn */
+      {1, 2, 1, 1}, /* king moves in */
+      {6, 4, 5, 5},
+      {1, 1, 0, 0},
+      {5, 5, 2, 2},
+      {0, 0, 0, 0},
+      {0, 0, 0, 0}
+    }    
+  },
+
+  /** 
+      game 6:
+
+      T.B. Gorgiev
+      White to play and win
+      
+      First Prize - "64"
+      1929
+  */
+  {
+    {
+      {      0,      0,      0,      0,       0,      0, KNIGHT,       0},
+      {  BKNIGHT,    0,      0,      0,       0,      0,      0,       0},
+      {      0,      0,      0,  BKING, BKNIGHT,      0,      0,       0},
+      {   KING,      0,      0,      0,       0,      0,      0,       0},
+      {      0,      0,      0,      0,       0,      0, KNIGHT,       0},
+      {      0,      0,      0,      0,       0,      0,      0,      0},
+      {      0,      0,      0,      0,       0,      0,      0,      0},
+      {      0,      0,      0,      0,  BISHOP,      0,      0,      0},
+    },
+    
+    13,
+
+    { 
+      {3, 0, 2, 1}, /* king on move */
+      {1, 0, 0, 2}, /* check */
+      {2, 1, 1, 1},
+      {0, 2, 1, 4}, /* knight moves on */
+      {7, 4, 5, 6}, /* bishop puts king in check */
+      {2, 3, 1, 3}, /* king moves back */
+      {0, 6, 2, 5}, /* knight moves in, check */
+      {1, 3, 0, 3}, /* king moves back queen */
+      {5, 6, 1, 2}, /* bishop - b7 ch!! */
+      {2, 4, 1, 2}, /* black knight takes bishop */
+      {4, 6, 3, 4}, /* knight to k5 */
+      {0, 0, 0, 0}, /* mate */
+      {0, 0, 0, 0}
+    }    
+  },
+
+  /** 
+      game 7:
+
+      K. A. L. Kubbel
+      White to play and win
+      
+      "Schachmatny Listok"
+      1922
+  */
+  {
+    {
+      {      0, KNIGHT,      0,      0,       0,      0,       0,      0},
+      {      0,      0,      0,      0,       0,      0,      0,      0},
+      {   KING,      0,      0,      0,       0,      0,      0,      0},
+      {      0,      0,      0,  BKING,       0,      0,      0,      0},
+      {      0,      0,      0,  BPAWN,       0,      0,      0, BISHOP},
+      {  BPAWN,      0,      0,      0,       0,      0,      0,      0},
+      {      0,      0,   PAWN,   PAWN,       0,      0,      0,      0},
+      {      0,      0,      0,      0,       0,      0,      0,      0},
+    },
+    
+    12,
+
+    { 
+      {0, 1, 2, 2}, /* kt-b6 */
+      {3, 3, 2, 2}, /* k x kt */
+      {4, 7, 2, 5}, /* b-b6 */
+      {2, 2, 3, 3}, /* king back to original position */
+      {6, 3, 5, 3}, /* p-q3! */
+      {5, 0, 6, 0}, /* p-r7 */
+      {6, 2, 4, 2}, /* p-b4ch */
+      {3, 3, 3, 2}, /* king moves, black cannot capture in passing */
+      {2, 0, 1, 1}, /* k-kt7! */
+      {6, 0, 7, 0}, /* promo */
+      {2, 5, 1, 4}, /* mate */
+      {0, 0, 3, 2},
+    }    
+  },
 };
 
 #endif /* __CHESSGAMES_H__ */
index 67406d2047e8c01b2e41ab0aea916be1b26f813e..91d0db979807142ee3a5be82d136b9fbdca2ca62 100644 (file)
@@ -1,8 +1,7 @@
 /* atlantis --- Shows moving 3D sea animals */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)dolphin.c    1.2 98/06/16 xlockmore";
-
 #endif
 
 /* Copyright (c) E. Lassauge, 1998. */
index 83ce4e412559d880035f1a2d3c7f034c3073e121..5bac990a231bb2bcaec707c1c22f039598667cac 100644 (file)
@@ -29,6 +29,7 @@
 #define DEFAULTS       "*delay:       20000       \n" \
                        "*showFPS:       False       \n" \
                       "*wireframe:     False     \n"   \
+                      "*reflections:   True     \n"    \
 
 # include "xlockmore.h"
 
 static XrmOptionDescRec opts[] = {
   {"+rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "false" },
   {"-rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "true" },
+  {"+reflections", ".chess.reflections", XrmoptionNoArg, (caddr_t) "false" },
+  {"-reflections", ".chess.reflections", XrmoptionNoArg, (caddr_t) "true" },
+  {"+smooth", ".chess.smooth", XrmoptionNoArg, (caddr_t) "false" },
+  {"-smooth", ".chess.smooth", XrmoptionNoArg, (caddr_t) "true" },
 };
 
-int rotate;
+int rotate, reflections, smooth;
 
 static argtype vars[] = {
   {(caddr_t *) &rotate, "rotate", "Rotate", "True", t_Bool},
+  {(caddr_t *) &reflections, "reflections", "Reflections", "True", t_Bool},
+  {(caddr_t *) &smooth, "smooth", "Smooth", "True", t_Bool},
 };
 
 ModeSpecOpt chess_opts = {countof(opts), opts, countof(vars), vars, NULL};
@@ -94,71 +101,35 @@ GLfloat colors[2][3] =
     {0.5, 0.5, 0.5},
   };
 
-/* well, i prefer silver tip */
-GLfloat whites[3][3] = 
+#define WHITES 5
+
+/* i prefer silvertip */
+GLfloat whites[WHITES][3] = 
   {
     {1.0, 0.5, 0.0},
     {0.8, 0.45, 1.0},
-    {0.37, 0.56, 0.87},   
+    {0.43, 0.54, 0.76},
+    {0.8, 0.8, 0.8},
+    {0.15, 0.77, 0.54},
   };
 
-/* int board[BOARDSIZE][BOARDSIZE]; */
-
 #include "chessgames.h"
 
 ChessGame game;
-
-/* void buildBoard(void) { */
-/*   board[0][5] = BKING; */
-/*   board[1][4] = BPAWN; */
-/*   board[1][2] = BPAWN; */
-/*   board[1][0] = BPAWN; */
-/*   board[2][2] = BPAWN; */
-/*   board[2][4] = BPAWN; */
-/*   board[2][7] = KNIGHT; */
-/*   board[3][0] = PAWN; */
-/*   board[3][2] = ROOK; */
-/*   board[4][0] = PAWN; */
-/*   board[4][4] = KING; */
-/*   board[4][5] = PAWN; */
-/*   board[6][0] = BPAWN; */
-/*   board[6][7] = PAWN; */
-/*   board[7][0] = BBISHOP; */
-/* } */
+int oldwhite = -1;
 
 void build_colors(void) {
-  int white = random()%3;
-  colors[0][0] = whites[white][0];
-  colors[0][1] = whites[white][1];
-  colors[0][2] = whites[white][2];
-}
 
-/* int moves[MOVES][4] =  */
-/*   { {3, 2, 6, 2}, */
-/*     {7, 0, 6, 1}, */
-/*     {6, 2, 6, 6}, */
-/*     {0, 5, 0, 4}, */
-/*     {6, 6, 0, 6}, */
-/*     {0, 4, 1, 3}, */
-/*     {2, 7, 1, 5}, */
-/*     {2, 2, 3, 2}, */
-/*     {0, 6, 0, 3},  */
-/*     {1, 3, 2, 2}, */
-/*     {0, 3, 6, 3}, */
-/*     {3, 2, 4, 2}, /\* pawn to bishop 5 *\/ */
-/*     {1, 5, 0, 3}, /\* check *\/ */
-/*     {2, 2, 3, 2}, */
-/*     {0, 3, 2, 4}, /\* takes pawn *\/ */
-/*     {3, 2, 2, 2}, */
-/*     {2, 4, 0, 3}, */
-/*     {2, 2, 3, 2}, */
-/*     {6, 3, 6, 1}, /\* rook takes bishop *\/ */
-/*     {6, 0, 7, 0}, */
-/*     {6, 1, 3, 1}, */
-/*     {3, 2, 2, 3}, */
-/*     {3, 1, 3, 3}, */
-/*     {0, 0, 2, 3}, */
-/*   }; */
+  /* find new white */
+  int newwhite = oldwhite;
+  while(newwhite == oldwhite)
+    newwhite = random()%WHITES;
+  oldwhite = newwhite;
+
+  colors[0][0] = whites[oldwhite][0];
+  colors[0][1] = whites[oldwhite][1];
+  colors[0][2] = whites[oldwhite][2];
+}
 
 /* yay its c */
 int mpiece = 0, tpiece, steps = 0, done = 1;
@@ -192,13 +163,23 @@ Bool chess_handle_event (ModeInfo *mi, XEvent *event) {
   return False;
 }
 
-GLfloat position[] = { 3.0, 7.0, 3.0, 1.0 };
+GLfloat position[] = { 0.0, 5.0, 5.0, 1.0 };
+GLfloat position2[] = { 5.0, 5.0, 5.0, 1.0 };
+GLfloat diffuse2[] = {1.0, 1.0, 1.0, 1.0};
+GLfloat ambient2[] = {0.6, 0.6, 0.6, 1.0};
 
 /* configure lighting */
 void setup_lights(void) {
   glEnable(GL_LIGHTING);
   glLightfv(GL_LIGHT0, GL_POSITION, position);
+  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse2);
   glEnable(GL_LIGHT0);
+
+/*   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient2); */
+
+  glLightfv(GL_LIGHT1, GL_SPECULAR, diffuse2);
+  glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
+  glEnable(GL_LIGHT1);
 }
 
 /** draw pieces */
@@ -377,14 +358,32 @@ void display(Chesscreen *c) {
   glRotatef(theta*100, 0.0, 1.0, 0.0);
   glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
 
+  position[0] = 4.0 + 1.0*-sin(theta*100*M_PI/180.0);
+  position[2] = 4.0 + 1.0*cos(theta*100*M_PI/180.0);
+  position[1] = 8.0;
+
+  position2[0] = 4.0 + 8.0*-sin(theta*100*M_PI/180.0);
+  position2[2] = 4.0 + 8.0*cos(theta*100*M_PI/180.0);
+
+  glEnable(GL_LIGHTING);
+  glLightfv(GL_LIGHT0, GL_POSITION, position);
+  glLightfv(GL_LIGHT1, GL_POSITION, position2);
+  glEnable(GL_LIGHT0);
+
   /** draw board, pieces */
   if(!wire) {
     glEnable(GL_LIGHTING);
     glEnable(GL_COLOR_MATERIAL);
-    draw_reflections();
-    glEnable(GL_BLEND);
+
+    if(reflections) {
+      draw_reflections();
+      glEnable(GL_BLEND);
+    }
+
     drawBoard();
-    glDisable(GL_BLEND);
+
+    if(reflections)
+      glDisable(GL_BLEND);
   }
   else
     drawBoard();
@@ -442,7 +441,7 @@ void init_chess(ModeInfo *mi) {
   if(!wire) {
     setup_lights();
     glColorMaterial(GL_FRONT, GL_DIFFUSE);
-    glShadeModel(GL_SMOOTH);
+    glShadeModel(smooth ? GL_SMOOTH : GL_FLAT);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glEnable(GL_DEPTH_TEST);
   }
@@ -450,6 +449,8 @@ void init_chess(ModeInfo *mi) {
     glPolygonMode(GL_FRONT, GL_LINE);
 }
 
+int oldgame = -1;
+
 /** does dirty work drawing scene, moving pieces */
 void draw_chess(ModeInfo *mi) {
   Chesscreen *c = &qs[MI_SCREEN(mi)];
@@ -494,8 +495,13 @@ void draw_chess(ModeInfo *mi) {
       moving = 1;
     }
     else if(done == 1) {
-      /* copy over new game */
-      game = games[random()%GAMES];
+      int newgame = oldgame;
+      while(newgame == oldgame)
+       newgame = random()%GAMES;
+
+      /* same old game */
+      oldgame = newgame;
+      game = games[oldgame];
       build_colors();
       done = 2;
       count = 0;
@@ -507,9 +513,14 @@ void draw_chess(ModeInfo *mi) {
   }
 
   /* set lighting */
-  if(done)
+  if(done) {
     glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 
             done == 1 ? 1.0+0.1*count : 100.0/count);
+    glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 
+            done == 1 ? 1.0+0.1*count : 100.0/count);
+    glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.15);
+    glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.15);
+  }
 
   display(c);
 
index 2febb1d42bd31857c8938de2eddce8c058830813..f2a956f5438341f0dc1642b5b0e00776bad74a88 100644 (file)
@@ -253,7 +253,7 @@ engine_type engines[] = {
 /* given a number of cylinders and an included angle, finds matching engine */
 int find_engine(const char *name)
 {
-  int i;
+  unsigned int i;
 
   if (!name || !*name || !strcasecmp (name, "(none)"))
     return (random() % countof(engines));
@@ -762,8 +762,8 @@ void makeshaft (void)
 {
   int i;
   int j;
-  const static float crankThick = 0.2;
-  const static float crankDiam = 0.3;
+  static const float crankThick = 0.2;
+  static const float crankDiam = 0.3;
 
   i = glGenLists(1);
   glNewList(i, GL_COMPILE);
@@ -866,7 +866,7 @@ print_title_string (ModeInfo *mi, const char *string, GLfloat x, GLfloat y)
       glMatrixMode(GL_MODELVIEW);
       glPushMatrix();
       {
-        int i;
+        unsigned int i;
         int x2 = x;
         glLoadIdentity();
 
index e72d8dce8725f7890ceb739345591ad41d35f17b..735c8d2c0729b6ab5a1e022a10be4f02bc209a5c 100644 (file)
@@ -405,7 +405,7 @@ void getSnapshot (ModeInfo *modeinfo)
   if (MI_IS_WIREFRAME(modeinfo))
     return;
 
- ximage = screen_to_ximage (modeinfo->xgwa.screen, modeinfo->window);
+ ximage = screen_to_ximage (modeinfo->xgwa.screen, modeinfo->window, NULL);
 
   qw = QW; qh = QH;
   tw = modeinfo->xgwa.width;
index 63b89f666a83b42b49d62fa14a66e14e4a44325d..3571bece03dad6ec4fe4f22127e5787a966fddeb 100644 (file)
@@ -37,9 +37,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 /* flurry */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)flurry.c     4.07 97/11/24 xlockmore";
-
 #endif
 
 /*-
@@ -107,8 +106,12 @@ static double gTimeCounter = 0.0;
 
 double currentTime(void) {
   struct timeval tv;
-
-  gettimeofday(&tv, NULL);
+# ifdef GETTIMEOFDAY_TWO_ARGS
+  struct timezone tzp;
+  gettimeofday(&tv, &tzp);
+# else
+  gettimeofday(&tv);
+# endif
 
   return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
 }
index 33d4250a26df3bba98ab59e6bbf1677ece45888e..237ef1fef6daf9bab1a6d5d79e6e9af977727e7d 100644 (file)
@@ -186,8 +186,8 @@ fps_1 (ModeInfo *mi)
   static int last_ifps = -1;
   static GLfloat last_fps = -1;
   static int frame_count = 0;
-  static struct timeval prev = { 0, };
-  static struct timeval now  = { 0, };
+  static struct timeval prev = { 0, };
+  static struct timeval now  = { 0, };
 
   if (!initted_p)
     {
index 52bdab820adf0e387b4674bacbc9487f0e9c59b3..e1c08bdc35b39d64b243dbd4b66fd4011b170a06 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* gears --- 3D gear wheels */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)gears.c      4.07 97/11/24 xlockmore";
-
 #endif
 
 /*-
index f9b1cb2ec6193761e82fc0a96d331017c7e8d84d..27a6bd4588f1afa993f0d8e4478940218d6f05e8 100644 (file)
@@ -621,7 +621,8 @@ grabTexture(void)
   int real_width  = gflux->modeinfo->xgwa.width;
   int real_height = gflux->modeinfo->xgwa.height;
   XImage *ximage = screen_to_ximage (gflux->modeinfo->xgwa.screen,
-                                     gflux->window);
+                                     gflux->window,
+                                     NULL);
   Bool bigimage = False;
   int size = 0;
 
diff --git a/hacks/glx/gleidescope.c b/hacks/glx/gleidescope.c
new file mode 100644 (file)
index 0000000..7470d3f
--- /dev/null
@@ -0,0 +1,1088 @@
+/* -*- Mode: C; tab-width: 4 -*- */
+
+#if !defined( lint ) && !defined( SABER )
+static const char sccsid[] = "@(#)gleidescope.c        1.0 03/06/27 xlockmore";
+#endif
+
+/* enable -grab switch */
+/*#define      GRAB*/
+
+/*-
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind.  The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof.  In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ *
+ *     Revision History:
+ *
+ *     20030627        1.0             acd             First Release.
+ *                                                             Texture loading code from 'glplanet'
+ *                                                                     by Jamie Zawinski <jwz@jwz.org>
+ *     20030810        1.1             acd             Added size flag.
+ *                                                             Now grabs screen / video / picture
+ *                                                                     (uses code from 'glslideshow' by
+ *                                                                     Mike Oliphant, Ben Buxton, Jamie Zawinski).
+ *                                                             Added -duration.
+ *                                                             Added mouse code.
+ *                                                             Added fade code (also from glslideshow).
+ *     20031013        1.2             acd             Migrated to compile without warnings under
+ *                                                                     xscreensaver 4.13.
+ *     20031023        1.3             acd             Better code to limit twisting speeds.
+ *                                                             Tweaked initial rotation values.
+ *                                                             Move, Rotate, Zoom now chosen at random if
+ *                                                                     no preference is given.
+ *                                                             Made grid slightly bigger so you can't see
+ *                                                                     the edge when zooming and moving.
+ */
+
+#include <X11/Intrinsic.h>
+#include "colors.h"
+
+#include "xpm-ximage.h"
+
+/*
+**----------------------------------------------------------------------------
+** Defines
+**----------------------------------------------------------------------------
+*/
+
+#ifdef STANDALONE
+# define PROGCLASS                             "gleidescope"
+# define HACK_INIT                             init_gleidescope
+# define HACK_DRAW                             draw_gleidescope
+# define HACK_RESHAPE                  reshape_gleidescope
+# define HACK_HANDLE_EVENT             gleidescope_handle_event
+# define EVENT_MASK                            PointerMotionMask
+# define gleidescope_opts              xlockmore_opts
+# define DEFAULTS \
+               "*delay:                20000           \n"     \
+               "*showFPS:              False           \n"     \
+               "*move:                 False           \n"     \
+               "*rotate:               False           \n"     \
+               "*zoom:                 False           \n"     \
+               "*image:                DEFAULT         \n"     \
+               "*size:                 -1                      \n"     \
+               "*duration:             30                      \n" \
+
+# include "xlockmore.h"                                /* from the xscreensaver distribution */
+#else  /* !STANDALONE */
+# include "xlock.h"                                    /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL
+
+#include <GL/glu.h>
+
+/* acd TODO should all these be in gleidestruct? */
+#ifdef GRAB
+static Bool            grab;                   /* grab images */
+#endif
+static Bool            move;                   /* moving camera */
+static Bool            nomove;                 /* no moving camera */
+static Bool            rotate;                 /* rotate in place */
+static Bool            norotate;               /* no rotate in place */
+static int             size = -1;              /* size */
+static Bool            zoom;                   /* zooming camera */
+static Bool            nozoom;                 /* no zooming camera */
+static char            *image;                 /* name of texture to load */
+static int             duration;               /* length of time to display grabbed image */
+
+#define        MAX_TANGLE_VEL  2.0
+
+static float   tangle = 0;                     /* texture angle */
+static float   tangle_vel = 0.0;       /* texture velocity */
+static float   tangle_acc = 0.0;       /* texture acceleration */
+
+#define        MAX_RANGLE_VEL  1.5
+
+static float   rangle = 0;                     /* rotate angle */
+static float   rangle_vel = 0.0;       /* rotate velocity */
+static float   rangle_acc = 0.0;       /* rotate acceleration */
+
+static XrmOptionDescRec opts[] =
+{
+#ifdef GRAB
+       {"-grab",               (char *) ".gleidescope.grab",           XrmoptionNoArg,         "true"},
+#endif
+       {"-move",               (char *) ".gleidescope.move",           XrmoptionNoArg,         "true"},
+       {"-no-move",    (char *) ".gleidescope.nomove",         XrmoptionNoArg,         "true"},
+       {"-rotate",             (char *) ".gleidescope.rotate",         XrmoptionNoArg,         "true"},
+       {"-no-rotate",  (char *) ".gleidescope.norotate",       XrmoptionNoArg,         "true"},
+       /*{"-size",             (char *) ".gleidescope.size",           XrmoptionNoArg,         "-1"},*/
+       {"-zoom",               (char *) ".gleidescope.zoom",           XrmoptionNoArg,         "true"},
+       {"-no-zoom",    (char *) ".gleidescope.nozoom",         XrmoptionNoArg,         "true"},
+       {"-image",              (char *) ".gleidescope.image",          XrmoptionSepArg,        "DEFAULT"},
+       {"-duration",   (char *) ".gleidescope.duration",       XrmoptionSepArg,        "30"},
+};
+
+
+static argtype vars[] = {
+#ifdef GRAB
+       {(caddr_t *) &grab,                     "grab",         "Grab",         "False",        t_Bool},
+#endif
+       {(caddr_t *) &move,                     "move",         "Move",         "False",        t_Bool},
+       {(caddr_t *) &nomove,           "nomove",       "noMove",       "False",        t_Bool},
+       {(caddr_t *) &rotate,           "rotate",       "Rotate",       "False",        t_Bool},
+       {(caddr_t *) &norotate,         "norotate",     "noRotate",     "False",        t_Bool},
+       /*{(caddr_t *) &size,                   "size",         "Size",         "-1",           t_Int},*/
+       {(caddr_t *) &zoom,                     "zoom",         "Zoom",         "False",        t_Bool},
+       {(caddr_t *) &nozoom,           "nozoom",       "noZoom",       "False",        t_Bool},
+       {(caddr_t *) &image,            "image",        "Image",        "DEFAULT",      t_String},
+       {(caddr_t *) &duration,         "duration",     "Duration",     "30",           t_Int},
+};
+
+static OptionStruct desc[] = {
+#ifdef GRAB
+       {"-grab",               "grab images to create animation"},
+#endif
+       {"-move",               "camera will move"},
+       {"-no-move",    "camera won't move"},
+       {"-rotate",             "camera will rotate"},
+       {"-no-rotate",  "camera won't rotate"},
+       /*{"-size",             "size of the hexagons (1-10)"},*/
+       {"-zoom",               "camera will zoom"},
+       {"-no-zoom",    "camera won't zoom"},
+       {"-image",              "xpm / xbm image file to use for texture"},
+       {"-duration",   "length of time texture will be used"},
+};
+
+ModeSpecOpt gleidescope_opts = {
+       sizeof opts / sizeof opts[0], opts,
+       sizeof vars / sizeof vars[0], vars,
+       desc
+};
+
+#ifdef USE_MODULES
+ModStruct   gleidescope_description = { 
+     "gleidescope", "init_gleidescope", "draw_gleidescope", "release_gleidescope",
+     "draw_gleidescope", "init_gleidescope", NULL, &gleidescope_opts,
+     1000, 1, 2, 1, 4, 1.0, "",
+     "GL Kaleidescope", 0, NULL};
+#endif
+
+/*
+**-----------------------------------------------------------------------------
+**     Typedefs
+**-----------------------------------------------------------------------------
+*/
+
+typedef struct hex_s {
+       GLfloat x, y, z;                /* position */
+} hex_t;
+
+typedef struct {
+       GLfloat x;
+       GLfloat y;
+       GLfloat z;
+} vectorf;
+
+#define        MAX_FADE        500     /* number of fade cycles */
+
+typedef struct {
+       float                   cam_x_speed, cam_z_speed, cam_y_speed;
+       int                             cam_x_phase, cam_z_phase, cam_y_phase;
+       float                   tic;
+       GLXContext              *glx_context;
+       Window                  window;
+       GLfloat                 max_tx, max_ty; /* maximum texture sizes */
+       GLuint                  textures[2];    /* texture handles */
+       GLuint                  visible;                /* texture handle for new texture */
+       GLint                   fade;
+       time_t                  start_time;
+       Bool                    button_down_p;
+} gleidestruct;
+
+#define        XOFFSET (0.8660254f)    /* sin 60' */
+#define        YOFFSET (1.5000000f)    /* cos 60' + 1 */
+
+#if 0
+
+#define        SIZE    3
+
+/* generates a grid with edges of given size */
+/* acd TODO - replace hex[] with this and allow size and distance as parameters */
+
+int
+generate_grid(int size)
+
+       int     i, x, y;
+
+       size--;
+
+       i = size;
+       for (y = -size ; y <= size ; y++) {
+               for (x = -i ; x <= i ; x += 2) {
+                       printf("{XOFFSET * %d, YOFFSET * %d, 0},\n", x, y);
+               }
+               printf("\n");
+               if (y < 0) {
+                       i++;
+               } else {
+                       i--;
+               }
+       }
+       return 0;
+}
+#endif
+
+hex_t hex[] = {
+       /* edges of size 7 */
+       /* number of hexagons required to cover screen depends on camera distance */
+       /* at a distance of 10 this is just about enough. */
+       {XOFFSET * -6, YOFFSET * -6, 0},
+       {XOFFSET * -4, YOFFSET * -6, 0},
+       {XOFFSET * -2, YOFFSET * -6, 0},
+       {XOFFSET * 0, YOFFSET * -6, 0},
+       {XOFFSET * 2, YOFFSET * -6, 0},
+       {XOFFSET * 4, YOFFSET * -6, 0},
+       {XOFFSET * 6, YOFFSET * -6, 0},
+
+       {XOFFSET * -7, YOFFSET * -5, 0},
+       {XOFFSET * -5, YOFFSET * -5, 0},
+       {XOFFSET * -3, YOFFSET * -5, 0},
+       {XOFFSET * -1, YOFFSET * -5, 0},
+       {XOFFSET * 1, YOFFSET * -5, 0},
+       {XOFFSET * 3, YOFFSET * -5, 0},
+       {XOFFSET * 5, YOFFSET * -5, 0},
+       {XOFFSET * 7, YOFFSET * -5, 0},
+
+       {XOFFSET * -8, YOFFSET * -4, 0},
+       {XOFFSET * -6, YOFFSET * -4, 0},
+       {XOFFSET * -4, YOFFSET * -4, 0},
+       {XOFFSET * -2, YOFFSET * -4, 0},
+       {XOFFSET * 0, YOFFSET * -4, 0},
+       {XOFFSET * 2, YOFFSET * -4, 0},
+       {XOFFSET * 4, YOFFSET * -4, 0},
+       {XOFFSET * 6, YOFFSET * -4, 0},
+       {XOFFSET * 8, YOFFSET * -4, 0},
+
+       {XOFFSET * -9, YOFFSET * -3, 0},
+       {XOFFSET * -7, YOFFSET * -3, 0},
+       {XOFFSET * -5, YOFFSET * -3, 0},
+       {XOFFSET * -3, YOFFSET * -3, 0},
+       {XOFFSET * -1, YOFFSET * -3, 0},
+       {XOFFSET * 1, YOFFSET * -3, 0},
+       {XOFFSET * 3, YOFFSET * -3, 0},
+       {XOFFSET * 5, YOFFSET * -3, 0},
+       {XOFFSET * 7, YOFFSET * -3, 0},
+       {XOFFSET * 9, YOFFSET * -3, 0},
+
+       {XOFFSET * -10, YOFFSET * -2, 0},
+       {XOFFSET * -8, YOFFSET * -2, 0},
+       {XOFFSET * -6, YOFFSET * -2, 0},
+       {XOFFSET * -4, YOFFSET * -2, 0},
+       {XOFFSET * -2, YOFFSET * -2, 0},
+       {XOFFSET * 0, YOFFSET * -2, 0},
+       {XOFFSET * 2, YOFFSET * -2, 0},
+       {XOFFSET * 4, YOFFSET * -2, 0},
+       {XOFFSET * 6, YOFFSET * -2, 0},
+       {XOFFSET * 8, YOFFSET * -2, 0},
+       {XOFFSET * 10, YOFFSET * -2, 0},
+
+       {XOFFSET * -11, YOFFSET * -1, 0},
+       {XOFFSET * -9, YOFFSET * -1, 0},
+       {XOFFSET * -7, YOFFSET * -1, 0},
+       {XOFFSET * -5, YOFFSET * -1, 0},
+       {XOFFSET * -3, YOFFSET * -1, 0},
+       {XOFFSET * -1, YOFFSET * -1, 0},
+       {XOFFSET * 1, YOFFSET * -1, 0},
+       {XOFFSET * 3, YOFFSET * -1, 0},
+       {XOFFSET * 5, YOFFSET * -1, 0},
+       {XOFFSET * 7, YOFFSET * -1, 0},
+       {XOFFSET * 9, YOFFSET * -1, 0},
+       {XOFFSET * 11, YOFFSET * -1, 0},
+
+       {XOFFSET * -12, YOFFSET * 0, 0},
+       {XOFFSET * -10, YOFFSET * 0, 0},
+       {XOFFSET * -8, YOFFSET * 0, 0},
+       {XOFFSET * -6, YOFFSET * 0, 0},
+       {XOFFSET * -4, YOFFSET * 0, 0},
+       {XOFFSET * -2, YOFFSET * 0, 0},
+       {XOFFSET * 0, YOFFSET * 0, 0},
+       {XOFFSET * 2, YOFFSET * 0, 0},
+       {XOFFSET * 4, YOFFSET * 0, 0},
+       {XOFFSET * 6, YOFFSET * 0, 0},
+       {XOFFSET * 8, YOFFSET * 0, 0},
+       {XOFFSET * 10, YOFFSET * 0, 0},
+       {XOFFSET * 12, YOFFSET * 0, 0},
+
+       {XOFFSET * -11, YOFFSET * 1, 0},
+       {XOFFSET * -9, YOFFSET * 1, 0},
+       {XOFFSET * -7, YOFFSET * 1, 0},
+       {XOFFSET * -5, YOFFSET * 1, 0},
+       {XOFFSET * -3, YOFFSET * 1, 0},
+       {XOFFSET * -1, YOFFSET * 1, 0},
+       {XOFFSET * 1, YOFFSET * 1, 0},
+       {XOFFSET * 3, YOFFSET * 1, 0},
+       {XOFFSET * 5, YOFFSET * 1, 0},
+       {XOFFSET * 7, YOFFSET * 1, 0},
+       {XOFFSET * 9, YOFFSET * 1, 0},
+       {XOFFSET * 11, YOFFSET * 1, 0},
+
+       {XOFFSET * -10, YOFFSET * 2, 0},
+       {XOFFSET * -8, YOFFSET * 2, 0},
+       {XOFFSET * -6, YOFFSET * 2, 0},
+       {XOFFSET * -4, YOFFSET * 2, 0},
+       {XOFFSET * -2, YOFFSET * 2, 0},
+       {XOFFSET * 0, YOFFSET * 2, 0},
+       {XOFFSET * 2, YOFFSET * 2, 0},
+       {XOFFSET * 4, YOFFSET * 2, 0},
+       {XOFFSET * 6, YOFFSET * 2, 0},
+       {XOFFSET * 8, YOFFSET * 2, 0},
+       {XOFFSET * 10, YOFFSET * 2, 0},
+
+       {XOFFSET * -9, YOFFSET * 3, 0},
+       {XOFFSET * -7, YOFFSET * 3, 0},
+       {XOFFSET * -5, YOFFSET * 3, 0},
+       {XOFFSET * -3, YOFFSET * 3, 0},
+       {XOFFSET * -1, YOFFSET * 3, 0},
+       {XOFFSET * 1, YOFFSET * 3, 0},
+       {XOFFSET * 3, YOFFSET * 3, 0},
+       {XOFFSET * 5, YOFFSET * 3, 0},
+       {XOFFSET * 7, YOFFSET * 3, 0},
+       {XOFFSET * 9, YOFFSET * 3, 0},
+
+       {XOFFSET * -8, YOFFSET * 4, 0},
+       {XOFFSET * -6, YOFFSET * 4, 0},
+       {XOFFSET * -4, YOFFSET * 4, 0},
+       {XOFFSET * -2, YOFFSET * 4, 0},
+       {XOFFSET * 0, YOFFSET * 4, 0},
+       {XOFFSET * 2, YOFFSET * 4, 0},
+       {XOFFSET * 4, YOFFSET * 4, 0},
+       {XOFFSET * 6, YOFFSET * 4, 0},
+       {XOFFSET * 8, YOFFSET * 4, 0},
+
+       {XOFFSET * -7, YOFFSET * 5, 0},
+       {XOFFSET * -5, YOFFSET * 5, 0},
+       {XOFFSET * -3, YOFFSET * 5, 0},
+       {XOFFSET * -1, YOFFSET * 5, 0},
+       {XOFFSET * 1, YOFFSET * 5, 0},
+       {XOFFSET * 3, YOFFSET * 5, 0},
+       {XOFFSET * 5, YOFFSET * 5, 0},
+       {XOFFSET * 7, YOFFSET * 5, 0},
+
+       {XOFFSET * -6, YOFFSET * 6, 0},
+       {XOFFSET * -4, YOFFSET * 6, 0},
+       {XOFFSET * -2, YOFFSET * 6, 0},
+       {XOFFSET * 0, YOFFSET * 6, 0},
+       {XOFFSET * 2, YOFFSET * 6, 0},
+       {XOFFSET * 4, YOFFSET * 6, 0},
+       {XOFFSET * 6, YOFFSET * 6, 0},
+};
+
+/*
+**----------------------------------------------------------------------------
+** Local Variables
+**----------------------------------------------------------------------------
+*/
+
+static gleidestruct *gleidescope = NULL;
+
+/*
+ *load defaults in config structure
+ */
+void setdefaultconfig(void)
+{
+#ifdef GRAB
+       grab = False;
+#endif
+       move = False;
+       rotate = False;
+       zoom = False;
+       image = NULL;
+}
+
+static int xstart;
+static int ystart;
+static double xmouse = 0.0;
+static double ymouse = 0.0;
+
+Bool
+gleidescope_handle_event(ModeInfo *mi, XEvent *event)
+{
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+
+       /*
+       printf("event:%d\n", event->xany.type);
+       printf("button:%d\n", event->xbutton.button);
+       */
+       switch(event->xany.type)
+       {
+               case ButtonPress:
+
+                       if (event->xbutton.button == Button1 || event->xbutton.button == Button3)
+                       {
+                               /* store initial values of mouse */
+                               xstart = event->xbutton.x;
+                               ystart = event->xbutton.y;
+
+                               /* button is down */
+                               gp->button_down_p = True;
+                               return True;
+                       }
+#if 0  /* TODO */
+                       else if (event->xbutton.button == Button4)
+                       {
+                               /* zoom in */
+                               return True;
+                       }
+                       else if (event->xbutton.button == Button5)
+                       {
+                               /* zoom out */
+                               return True;
+                       }
+#endif
+                       break;
+
+               case ButtonRelease:
+
+                       if (event->xbutton.button == Button1 || event->xbutton.button == Button3)
+                       {
+                               /* button is up */
+                               gp->button_down_p = False;
+                               return True;
+                       }
+                       break;
+
+               case MotionNotify:
+
+                       if (gp->button_down_p)
+                       {
+                               /* update mouse position */
+                               xmouse += (double)(event->xmotion.x - xstart) / MI_WIDTH(mi);
+                               ymouse += (double)(event->xmotion.y - ystart) / MI_HEIGHT(mi);
+                               xstart = event->xmotion.x;
+                               ystart = event->xmotion.y;
+
+                               return True;
+                       }
+                       break;
+       }
+
+       return False;
+}
+
+#include "grab-ximage.h"
+
+static void
+getSnapshot(ModeInfo *mi, GLuint name)
+{
+       XImage  *ximage;
+       int     status;
+       int     tw, th;
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+
+       if (MI_IS_WIREFRAME(mi))
+               return;
+
+       ximage = screen_to_ximage(mi->xgwa.screen, mi->window, 0);
+
+       tw = mi->xgwa.width;
+       th = mi->xgwa.height;
+
+       gp->max_tx = (GLfloat) tw / (GLfloat) ximage->width;
+       gp->max_ty = (GLfloat) th / (GLfloat) ximage->height;
+
+       glBindTexture (GL_TEXTURE_2D, name);
+
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+                       GL_LINEAR_MIPMAP_LINEAR);
+
+       clear_gl_error();
+       status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
+                       ximage->width, ximage->height,
+                       GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
+
+       if (!status && glGetError())
+               /* Some implementations of gluBuild2DMipmaps(), but set a GL error anyway.
+               **            We could just call check_gl_error(), but that would exit. */
+               status = -1;
+
+       if (status)
+       {
+               const GLubyte *s = gluErrorString (status);
+               if (s)
+               {
+                       fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
+                                       progname, ximage->width, ximage->height, s);
+               }
+               else
+               {
+                       fprintf (stderr, "%s: error mipmapping %dx%d texture: (unknown)\n",
+                                       progname, ximage->width, ximage->height);
+               }
+               clear_gl_error();
+       }
+       check_gl_error("mipmapping");  /* should get a return code instead of a
+                                                                         GL error, but just in case... */
+
+       free(ximage->data);
+       ximage->data = 0;
+       XDestroyImage (ximage);
+
+       /* remember time of last image change */
+       gp->start_time = time ((time_t *) 0);
+}
+
+static void
+setup_file_texture (ModeInfo *mi, char *filename, GLuint name)
+{
+       Display *dpy = mi->dpy;
+       Visual *visual = mi->xgwa.visual;
+       char buf[1024];
+
+       Colormap cmap = mi->xgwa.colormap;
+       XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
+
+       /* use this texture */
+       glBindTexture(GL_TEXTURE_2D, name);
+
+       clear_gl_error();
+       glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+                       image->width, image->height, 0,
+                       GL_RGBA, GL_UNSIGNED_BYTE, image->data);
+       sprintf (buf, "texture: %.100s (%dx%d)",
+                       filename, image->width, image->height);
+       check_gl_error(buf);
+
+       /* setup parameters for texturing */
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+       glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+}
+
+static void
+setup_texture(ModeInfo * mi, GLuint id)
+{
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+
+       if (!image || !*image || !strcmp(image, "DEFAULT")) {
+               /* no image specified - grab screen */
+               getSnapshot(mi, id);
+               /* max_tx and max_ty set in getSnapshot() */
+       } else {
+               /* use supplied image file */
+               setup_file_texture(mi, image, id);
+
+               /* set tx params to use whole image */
+               gp->max_tx = 1.0f;
+               gp->max_ty = 1.0f;
+       }
+
+       check_gl_error("texture initialization");
+
+       /* Need to flip the texture top for bottom for some reason. */
+       glMatrixMode (GL_TEXTURE);
+       glScalef (1, -1, 1);
+       glMatrixMode (GL_MODELVIEW);
+}
+
+#define        VERTEX0 glVertex3f( 0.0000f,  0.000f, 0.0f);
+#define        VERTEX1 glVertex3f( 0.0000f,  1.000f, 0.0f);
+#define        VERTEX2 glVertex3f( XOFFSET,  0.500f, 0.0f);
+#define        VERTEX3 glVertex3f( XOFFSET, -0.500f, 0.0f);
+#define        VERTEX4 glVertex3f( 0.0000f, -1.000f, 0.0f);
+#define        VERTEX5 glVertex3f(-XOFFSET, -0.500f, 0.0f);
+#define        VERTEX6 glVertex3f(-XOFFSET,  0.500f, 0.0f);
+
+static void
+draw_hexagons(ModeInfo *mi, int translucency, GLuint texture)
+{
+       int             i;
+       GLfloat col[4];
+       GLfloat t1x, t1y, t2x, t2y, t3x, t3y;
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+       GLfloat tangle2;
+
+       col[0] = 1.0;
+       col[1] = 1.0;
+       col[2] = 1.0;
+       col[3] = (float)translucency / MAX_FADE;
+
+       /* calculate vertices of equilateral triangle within image. */
+       /* t1 is always in centre */
+       t1x = gp->max_tx / 2;
+       t1y = gp->max_ty / 2;
+       /* t2 rotates */
+       t2x = (gp->max_tx / 2) * (1 + cos((ymouse * 2 * M_PI) + (tangle * M_PI / 180)));
+       t2y = (gp->max_ty / 2) * (1 + sin((ymouse * 2 * M_PI) + (tangle * M_PI / 180)));
+       /* t3 is always 60' further around than t2 */
+       tangle2 = (ymouse * 2 * M_PI) + (tangle * M_PI / 180) + (M_PI * 2 / 6);
+       t3x = (gp->max_tx / 2) * (1 + (cos(tangle2)));
+       t3y = (gp->max_ty / 2) * (1 + (sin(tangle2)));
+       /* NB image is flipped vertically hence: */
+       t1y = 1 - t1y;
+       t2y = 1 - t2y;
+       t3y = 1 - t3y;
+       /*printf("texcoords:[%f,%f]->[%f,%f](%f,%f)\n", t1x, t1y, t2x, t2y, gp->max_tx, gp->max_ty);*/
+
+       glColor4f(1.0, 1.0, 1.0, (float)translucency / MAX_FADE);
+       glEnable(GL_TEXTURE_2D);
+       glEnable(GL_BLEND);
+       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       glDepthMask(GL_FALSE);
+       glBindTexture(GL_TEXTURE_2D, gp->textures[texture]);
+
+       for (i = 0 ; i < sizeof(hex) / sizeof(hex[0]) ; i++) {
+
+               glPushMatrix();
+
+               glTranslatef(hex[i].x, hex[i].y, 0.0);
+
+               glBegin(GL_TRIANGLES);
+
+               /*
+               ** six triangles to each hexagon
+               */
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t2x, t2y);
+               VERTEX1;
+               glTexCoord2f(t3x, t3y);
+               VERTEX6;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t3x, t3y);
+               VERTEX6;
+               glTexCoord2f(t2x, t2y);
+               VERTEX5;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t2x, t2y);
+               VERTEX5;
+               glTexCoord2f(t3x, t3y);
+               VERTEX4;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t3x, t3y);
+               VERTEX4;
+               glTexCoord2f(t2x, t2y);
+               VERTEX3;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t2x, t2y);
+               VERTEX3;
+               glTexCoord2f(t3x, t3y);
+               VERTEX2;
+
+               glTexCoord2f(t1x, t1y);
+               VERTEX0;
+               glTexCoord2f(t3x, t3y);
+               VERTEX2;
+               glTexCoord2f(t2x, t2y);
+               VERTEX1;
+
+               glEnd();
+
+               glPopMatrix();
+       }
+       
+#ifdef DISPLAY_TEXTURE
+       glPushMatrix();
+       /* acd debug - display (bigger, centred) texture */
+       glScalef(2.0, 2.0, 2.0);
+       glTranslatef(-0.5, -0.5, 0.0);
+       glBegin(GL_QUADS);
+       glTexCoord2f(0.0, 0.0);
+       glVertex3f(0.0, 0.0, -0.1);
+       glTexCoord2f(1.0, 0.0);
+       glVertex3f(1.0, 0.0, -0.1);
+       glTexCoord2f(1.0, 1.0);
+       glVertex3f(1.0, 1.0, -0.1);
+       glTexCoord2f(0.0, 1.0);
+       glVertex3f(0.0, 1.0, -0.1);
+       glEnd();
+       /* acd debug - display texture triangle */
+       glColor4f(1.0, 1.0, 1.0, 1.0);
+       glBegin(GL_LINE_LOOP);
+       glVertex3f(t1x, t1y, -0.11);
+       glVertex3f(t2x, t2y, -0.11);
+       glVertex3f(t3x, t3y, -0.11);
+       glEnd();
+       glPopMatrix();
+#endif
+
+       glDisable(GL_TEXTURE_2D);
+       glDepthMask(GL_TRUE);
+       glDisable(GL_BLEND);
+}
+
+/*
+ * main rendering loop
+ */
+static void
+draw(ModeInfo * mi)
+{
+       GLfloat x_angle, y_angle, z_angle;
+       gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
+       vectorf v1;
+       GLfloat pos[4];
+
+       glClearColor(0.5, 0.5, 0.5, 1.0);
+       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+       glLoadIdentity();
+
+       gp->tic += 0.005f;
+
+       x_angle = gp->cam_x_phase + gp->tic * gp->cam_x_speed;
+       y_angle = gp->cam_y_phase + gp->tic * gp->cam_y_speed;
+       z_angle = gp->cam_z_phase + gp->tic * gp->cam_z_speed;
+
+       if (move) {
+               v1.x = 2 * sin(x_angle);
+               v1.y = 2 * sin(y_angle);
+       } else {
+               v1.x = 0;
+               v1.y = 0;
+       }
+
+       /* size is changed in pinit() to be distance from plane */
+       size = MI_SIZE(mi);
+       if (size > 10) {
+               size = 10;
+       }
+       if (size < -1) {
+               size = -1;
+       }
+       if (size != -1) {
+               /* user defined size */
+               v1.z = size;
+       } else if (zoom) {
+               /* max distance given by adding the constant and the multiplier */
+               v1.z = 5.0 + 4.0 * sin(z_angle);
+       } else {
+               /* default */
+               v1.z = 7.0;
+       }
+
+       /* update rotation angle (but not if mouse button down) */
+       if (rotate && !gp->button_down_p)
+       {
+               float   new_rangle_vel = 0.0;
+
+               /* update camera rotation angle and velocity */
+               rangle += rangle_vel;
+               new_rangle_vel = rangle_vel + rangle_acc;
+               if (new_rangle_vel > -MAX_RANGLE_VEL && new_rangle_vel < MAX_RANGLE_VEL)
+               {
+                       /* new velocity is within limits */
+                       rangle_vel = new_rangle_vel;
+               }
+
+               /* randomly change twisting speed */
+               if ((random() % 1000) < 1)
+               {
+                       rangle_acc = frand(0.002) - 0.001;
+               }
+       }
+
+#ifdef WOBBLE
+       /* this makes the image wobble - requires -move and a larger grid */
+       gluLookAt(0, 0, v1.z, v1.x, v1.y, 0.0, 0.0, 1.0, 0.0);
+#else
+       /* no wobble - camera always perpendicular to grid */
+
+       /* rotating camera rather than entire space - smoother */
+       gluLookAt(
+                       v1.x, v1.y, v1.z,
+                       v1.x, v1.y, 0.0,
+                       sin((xmouse * M_PI * 2) + rangle * M_PI / 180),
+                       cos((xmouse * M_PI * 2) + rangle * M_PI / 180),
+                       0.0);
+#endif
+
+       /* light position same as camera */
+       pos[0] = v1.x;
+       pos[1] = v1.y;
+       pos[2] = v1.z;
+       pos[3] = 0;
+
+       if (gp->fade == 0)
+       {
+               /* not fading */
+               draw_hexagons(mi, MAX_FADE, gp->visible);
+       }
+       else
+       {
+               /* fading - show both textures with alpha */
+               draw_hexagons(mi, MAX_FADE - gp->fade, gp->visible);
+               draw_hexagons(mi, gp->fade, 1 - gp->visible);
+
+               /* fade some more */
+               gp->fade++;
+
+               /* have we faded enough? */
+               if (gp->fade > MAX_FADE)
+               {
+                       /* stop fading */
+                       gp->fade = 0;
+                       gp->visible = 1 - gp->visible;
+               }
+       }
+
+       /* increment texture angle based on time, velocity etc */
+       /* but only if button is not down */
+       if (!gp->button_down_p)
+       {
+               float           new_tangle_vel = 0.0;
+
+               tangle += tangle_vel;
+
+               /* work out new texture angle velocity */
+               new_tangle_vel = tangle_vel + tangle_acc;
+               if (new_tangle_vel > -MAX_TANGLE_VEL && new_tangle_vel < MAX_TANGLE_VEL)
+               {
+                       /* new velocity is inside limits */
+                       tangle_vel = new_tangle_vel;
+               }
+
+               /* randomly change texture angle acceleration */
+               if ((random() % 1000) < 1)
+               {
+                       tangle_acc = frand(0.002) - 0.001;
+               }
+       }
+
+       glFlush();
+}
+
+/* 
+ * new window size or exposure 
+ */
+void reshape_gleidescope(ModeInfo *mi, int width, int height)
+{
+       GLfloat         h = (GLfloat) height / (GLfloat) width;
+
+       glViewport(0, 0, (GLint) width, (GLint) height);
+       glMatrixMode(GL_PROJECTION);
+       glLoadIdentity();
+       gluPerspective(50.0, 1/h, 0.1, 2000.0);
+       glMatrixMode (GL_MODELVIEW);
+
+       glLineWidth(1);
+       glPointSize(1);   
+}
+
+static void
+pinit(ModeInfo * mi)
+{
+       gleidestruct    *gp = &gleidescope[MI_SCREEN(mi)];
+
+       /* set start time - star_time = 0 implies non-dynamic texture */
+       gp->start_time = (time_t)0;
+
+       /* set the texture size to default */
+       gp->max_tx = 1.0;
+       gp->max_ty = 1.0;
+
+       /* no fading */
+       gp->fade = 0;
+
+       glShadeModel(GL_SMOOTH);
+       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+       glEnable(GL_DEPTH_TEST);
+       glEnable(GL_CULL_FACE);
+       glDisable(GL_LIGHTING);
+
+       /* space for textures */
+       glGenTextures(1, &gp->textures[0]);
+       glGenTextures(1, &gp->textures[1]);
+       gp->visible = 0;
+
+       setup_texture(mi, gp->textures[gp->visible]);
+
+       /*
+       **      want to choose a value for arg randomly if neither -arg nor -no-arg
+       **      is specified. xscreensaver libraries don't seem to let you do this -
+       **      if something isn't true then it is false (pesky two-state boolean values).
+       **      so, i've defined both -arg and -no-arg to arguments and added the 
+       **      following logic.
+       **      (btw if both -arg and -no-arg are defined then arg is set to False)
+       */
+       if (zoom == False && nozoom == False)
+       {
+               /* no zoom preference - randomise */
+               zoom = (((random() & 0x1) == 0x1) ? True : False);
+       }
+       else if (nozoom == True)
+       {
+               /* definately no zoom */
+               zoom = False;
+       }
+
+       if (move == False && nomove == False)
+       {
+               /* no move preference - randomise */
+               move = (((random() & 0x1) == 0x1) ? True : False);
+       }
+       else if (nomove == True)
+       {
+               /* definately no move */
+               move = False;
+       }
+
+       if (rotate == False && norotate == False)
+       {
+               /* no rotate preference - randomise */
+               rotate = (((random() & 0x1) == 0x1) ? True : False);
+       }
+       else if (norotate == True)
+       {
+               /* definately no rotate */
+               rotate = False;
+       }
+
+       /* define cam variables */
+       gp->cam_x_speed = frand(3.0) - 1.5;
+       gp->cam_x_phase = random() % 360;
+       gp->cam_y_speed = frand(3.0) - 1.5;
+       gp->cam_y_phase = random() % 360;
+       gp->cam_z_speed = frand(3.0) - 1.5;
+       gp->cam_z_phase = random() % 360;
+
+       /* initial angular speeds */
+       rangle_vel = frand(0.2) - 0.1;
+       tangle_vel = frand(0.2) - 0.1;
+       rangle_acc = frand(0.002) - 0.001;
+       tangle_acc = frand(0.002) - 0.001;
+
+    /* jwz */
+    {
+      GLfloat speed = 15;
+      rangle_vel *= speed;
+      tangle_vel *= speed;
+      rangle_acc *= speed;
+      tangle_acc *= speed;
+    }
+
+       /* distance is 11 - size */
+       if (size != -1) {
+               if (zoom) {
+                       fprintf(stderr, "-size given. ignoring -zoom.\n");
+                       zoom = False;
+               }
+               if (size < 1) {
+                       size = 1;
+               } else if (size >= 10) {
+                       size = 10;
+               }
+               size = 11 - size;
+       }
+
+#ifdef DEBUG
+printf("phases [%d, %d, %d]\n", gp->cam_x_phase, gp->cam_y_phase, gp->cam_z_phase);
+#endif
+}
+
+void
+init_gleidescope(ModeInfo * mi)
+{
+       gleidestruct *gp;
+       int screen = MI_SCREEN(mi);
+
+
+       if (gleidescope == NULL) {
+               gleidescope = (gleidestruct *) calloc(MI_NUM_SCREENS(mi), sizeof (gleidestruct));
+               if (gleidescope == NULL) {
+                       return;
+               }
+       }
+       gp = &gleidescope[screen];
+       gp->window = MI_WINDOW(mi);
+
+       if ((gp->glx_context = init_GL(mi)) != NULL) {
+
+               reshape_gleidescope(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+               glDrawBuffer(GL_BACK);
+
+               /* do initialisation */
+               pinit(mi);
+
+       } else {
+               MI_CLEARWINDOW(mi);
+       }
+}
+
+void
+draw_gleidescope(ModeInfo * mi)
+{
+       gleidestruct    *gp = &gleidescope[MI_SCREEN(mi)];
+       Display         *display = MI_DISPLAY(mi);
+       Window          window = MI_WINDOW(mi);
+
+
+       if (!gp->glx_context)
+               return;
+
+       glDrawBuffer(GL_BACK);
+
+       glXMakeCurrent(display, window, *(gp->glx_context));
+       draw(mi);
+
+       if (mi->fps_p) {
+               do_fps (mi);
+       }
+
+       glFinish();
+       glXSwapBuffers(display, window);
+
+#ifdef GRAB
+       if (grab) {
+               grab_frame(mi);
+       }
+#endif
+
+       /* need to change texture? */
+       if ((gp->start_time != 0) && (duration != -1) && gp->fade == 0) {
+               if (gp->start_time + duration <= time((time_t *)0)) {
+                       /* get new snapshot (into back buffer) and start fade count */
+                       getSnapshot(mi, gp->textures[1 - gp->visible]);
+                       gp->fade = 1;
+               }
+       }
+}
+
+void
+release_gleidescope(ModeInfo * mi)
+{
+       if (gleidescope != NULL) {
+               int screen;
+
+               for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
+                       gleidestruct *gp = &gleidescope[screen];
+
+                       /* acd -  is this needed? */
+                       if (gp->glx_context) {
+                               /* Display lists MUST be freed while their glXContext is current. */
+                               glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
+
+                               /* acd - was code here for freeing things that are no longer in struct */
+                       }
+               }
+               (void) free((void *) gleidescope);
+               gleidescope = NULL;
+       }
+       
+       FreeAllGL(mi);
+}
+
+#endif
diff --git a/hacks/glx/gleidescope.man b/hacks/glx/gleidescope.man
new file mode 100644 (file)
index 0000000..97ffacf
--- /dev/null
@@ -0,0 +1,77 @@
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+gleidescope - a tiled OpenGL kaleidescope
+.SH SYNOPSIS
+.B gleidescope
+[\-display \fIhost:display.screen\fP]
+[\-visual \fIvisual\fP]
+[\-window]
+[\-root]
+[-delay \fInumber\fP]
+[-move]
+[-rotate]
+[-zoom]
+[-image \fIfile\fP]
+[-fps]
+[-size \fInumber\fP]
+[-duration \fInumber\fP]
+.SH DESCRIPTION
+A tiled kaleidescope using OpenGL.
+.SH OPTIONS
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-delay \fInumber\fP
+Per-frame delay, in microseconds.  Default: 20000 (0.02 seconds.).
+.TP 8
+.B \-move
+Move the camera.
+.TP 8
+.B \-rotate
+Rotate the camera.
+.TP 8
+.B \-zoom
+Zoom the camera in and out.
+.TP 8
+.B \-image \fIfile\fP
+The texture map to use at the end of the kaleidescope.
+.TP 8
+.B \-fps | \-no-fps
+Whether to show a frames-per-second display at the bottom of the screen.
+.TP 8
+.B \-size \fInumber\fP
+The size of the hexagons being displayed [1(small)-10(large)]
+.TP 8
+.B \-duration \fInumber\fP
+The time in seconds before another image is chosen.
+.TP 8
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1)
+.SH COPYRIGHT
+Copyright \(co 2003 by Andrew Dean  Permission to use, copy, modify, 
+distribute, and sell this software and its documentation for any purpose is 
+hereby granted without fee, provided that the above copyright notice appear 
+in all copies and that both that copyright notice and this permission notice
+appear in supporting documentation.  No representations are made about the 
+suitability of this software for any purpose.  It is provided "as is" without
+express or implied warranty.
+.SH AUTHOR
+Andrew Dean.
index 2f4846d04e6b5c7e447d4e6096aa90525a35d440..2a9507318c4acf05727a583e4855af5a3aa0d1e1 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* fire --- 3D fire or rain landscape */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)fire.c       5.02 2001/09/26 xlockmore";
 #endif
 
index ab514646646c18378f8c045dd0557fb903a3306f..4ce911eed2d149e1456ed77b65fd13a660a51315 100644 (file)
@@ -80,7 +80,7 @@ static char *do_spin;
 static GLfloat speed;
 static Bool do_wander;
 static GLfloat thickness;
-static int segments;
+static unsigned int segments;
 static int duration;
 
 static XrmOptionDescRec opts[] = {
@@ -114,7 +114,7 @@ make_knot (ModeInfo *mi)
   GLfloat diam = (4 * thickness);
   int faces = (wire ? 3 : 6);
 
-  int i;
+  unsigned int i;
   double x, y, z, ox=0, oy=0, oz=0;
   double mu;
 
index 5a88a4e5c39436b2573215bee823ec6860c3980d..fa150bbd3e1c52cc6e660b18d1d78f5f1848f2aa 100644 (file)
@@ -33,6 +33,7 @@
 # define DEF_IMAGE_DURATION "30"
 # define DEF_ZOOM           "75"
 # define DEF_FPS_CUTOFF     "5"
+# define DEF_TITLES         "False"
 # define DEF_DEBUG          "False"
 
 #define DEFAULTS  "*delay:           20000                \n" \
@@ -45,6 +46,8 @@
                  "*wireframe:       False                \n" \
                   "*showFPS:         False                \n" \
                  "*fpsSolid:        True                 \n" \
+                 "*titles:        " DEF_TITLES  "\n" \
+                 "*titleFont:       -*-times-bold-r-normal-*-180-*\n" \
                   "*desktopGrabber:  xscreensaver-getimage -no-desktop %s\n"
 
 # include "xlockmore.h"
@@ -69,6 +72,7 @@ typedef struct {
   GLuint texid;                           /* which texture to draw */
   enum { IN, OUT, DEAD } state;    /* how to draw it */
   rect from, to;                  /* the journey this quad is taking */
+  char *title;
 } gls_quad;
 
 
@@ -100,6 +104,9 @@ typedef struct {
   Bool low_fps_p;              /* Whether we have compensated for a low
                                    frame rate. */
 
+  XFontStruct *xfont;
+  GLuint font_dlist;
+
 } slideshow_state;
 
 static slideshow_state *sss = NULL;
@@ -107,15 +114,18 @@ static slideshow_state *sss = NULL;
 
 /* Command-line arguments
  */
-int fade_seconds;    /* Duration in seconds of fade transitions.
-                        If 0, jump-cut instead of fading. */
-int pan_seconds;     /* Duration of each pan through an image. */
-int image_seconds;   /* How many seconds until loading a new image. */
-int zoom;            /* How far in to zoom when panning, in percent of image
-                        size: that is, 75 means "when zoomed all the way in,
-                        75% of the image will be on screen."  */
-int fps_cutoff;      /* If the frame-rate falls below this, turn off zooming.*/
-Bool debug_p;       /* Be loud and do weird things. */
+static int fade_seconds;    /* Duration in seconds of fade transitions.
+                               If 0, jump-cut instead of fading. */
+static int pan_seconds;     /* Duration of each pan through an image. */
+static int image_seconds;   /* How many seconds until loading a new image. */
+static int zoom;            /* How far in to zoom when panning, in percent of
+                               image size: that is, 75 means "when zoomed all
+                               the way in, 75% of the image will be visible."
+                             */
+static int fps_cutoff;      /* If the frame-rate falls below this, turn off
+                               zooming.*/
+static Bool do_titles;     /* Display image titles. */
+static Bool debug_p;       /* Be loud and do weird things. */
 
 
 static XrmOptionDescRec opts[] = {
@@ -124,6 +134,8 @@ static XrmOptionDescRec opts[] = {
   {"-duration", ".slideshow.imageDuration", XrmoptionSepArg, 0     },
   {"-zoom",     ".slideshow.zoom",          XrmoptionSepArg, 0     },
   {"-cutoff",   ".slideshow.FPScutoff",     XrmoptionSepArg, 0     },
+  {"-titles",   ".slideshow.titles",        XrmoptionNoArg, "True" },
+  {"+titles",   ".slideshow.titles",        XrmoptionNoArg, "True" },
   {"-debug",    ".slideshow.debug",         XrmoptionNoArg, "True" },
 };
 
@@ -134,6 +146,7 @@ static argtype vars[] = {
   { &zoom,          "zoom",         "Zoom",         DEF_ZOOM,           t_Int},
   { &fps_cutoff,    "FPScutoff",    "FPSCutoff",    DEF_FPS_CUTOFF,     t_Int},
   { &debug_p,       "debug",        "Debug",        DEF_DEBUG,         t_Bool},
+  { &do_titles,     "titles",       "Titles",       DEF_TITLES,        t_Bool},
 };
 
 ModeSpecOpt slideshow_opts = {countof(opts), opts, countof(vars), vars, NULL};
@@ -156,6 +169,90 @@ double_time (void)
 }
 
 
+static void
+load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP)
+{
+  const char *font = get_string_resource (res, "Font");
+  XFontStruct *f;
+  Font id;
+  int first, last;
+
+  if (!font) font = "-*-times-bold-r-normal-*-180-*";
+
+  f = XLoadQueryFont(mi->dpy, font);
+  if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
+
+  id = f->fid;
+  first = f->min_char_or_byte2;
+  last = f->max_char_or_byte2;
+  
+  clear_gl_error ();
+  *dlistP = glGenLists ((GLuint) last+1);
+  check_gl_error ("glGenLists");
+  glXUseXFont(id, first, last-first+1, *dlistP + first);
+  check_gl_error ("glXUseXFont");
+
+  *fontP = f;
+}
+
+
+static void
+print_title_string (ModeInfo *mi, const char *string, GLfloat x, GLfloat y)
+{
+  slideshow_state *ss = &sss[MI_SCREEN(mi)];
+  XFontStruct *font = ss->xfont;
+  GLfloat line_height = font->ascent + font->descent;
+
+  y -= line_height;
+
+  glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
+                GL_ENABLE_BIT);     /* for various glDisable calls */
+  glDisable (GL_LIGHTING);
+  glDisable (GL_DEPTH_TEST);
+  {
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    {
+      glLoadIdentity();
+
+      glMatrixMode(GL_MODELVIEW);
+      glPushMatrix();
+      {
+        unsigned int i;
+        int x2 = x;
+        glLoadIdentity();
+
+        gluOrtho2D (0, mi->xgwa.width, 0, mi->xgwa.height);
+
+        glRasterPos2f (x, y);
+        for (i = 0; i < strlen(string); i++)
+          {
+            char c = string[i];
+            if (c == '\n')
+              {
+                glRasterPos2f (x, (y -= line_height));
+                x2 = x;
+              }
+            else
+              {
+                glCallList (ss->font_dlist + (int)(c));
+                x2 += (font->per_char
+                       ? font->per_char[c - font->min_char_or_byte2].width
+                       : font->min_bounds.width);
+              }
+          }
+      }
+      glPopMatrix();
+    }
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+  }
+  glPopAttrib();
+
+  glMatrixMode(GL_MODELVIEW);
+}
+
+
 static void
 draw_quad (ModeInfo *mi, gls_quad *q)
 {
@@ -252,6 +349,20 @@ draw_quad (ModeInfo *mi, gls_quad *q)
       glEnd();
     }
 
+  if (do_titles &&
+      q->state != DEAD &&
+      q->title && *q->title)
+    {
+      /* #### this is wrong -- I really want to draw this with
+         "1,1,1,opacity", so that the text gets laid down on top
+         of the image with alpha, but that doesn't work, and I
+         don't know why...
+       */
+      glColor4f (opacity, opacity, opacity, 1);
+      print_title_string (mi, q->title,
+                          10, mi->xgwa.height - 10);
+    }
+
   glPopMatrix();
 
   if (debug_p)
@@ -441,7 +552,19 @@ load_quad (ModeInfo *mi, gls_quad *q)
   if (wire)
     goto DONE;
 
-  ximage = screen_to_ximage (mi->xgwa.screen, mi->window);
+  if (q->title) free (q->title);
+  q->title = 0;
+  ximage = screen_to_ximage (mi->xgwa.screen, mi->window, &q->title);
+
+  if (q->title)   /* strip filename to part after last /. */
+    {
+      char *s = strrchr (q->title, '/');
+      if (s) strcpy (q->title, s+1);
+    }
+
+  if (debug_p)
+    fprintf (stderr, "%s: debug: loaded image %d (%s)\n",
+             progname, q->texid, (q->title ? q->title : "(null)"));
 
   glBindTexture (GL_TEXTURE_2D, q->texid);
   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
@@ -521,7 +644,6 @@ load_quad (ModeInfo *mi, gls_quad *q)
 }
 
 
-
 void
 reshape_slideshow (ModeInfo *mi, int width, int height)
 {
@@ -642,6 +764,8 @@ init_slideshow (ModeInfo *mi)
   ss->pan_start_time   = ss->now;
   ss->image_start_time = ss->now;
 
+  load_font (mi, "titleFont", &ss->xfont, &ss->font_dlist);
+
   for (i = 0; i < countof(ss->texids); i++)
     glGenTextures (1, &ss->texids[i]);
   ss->current_texid = 0;
index b5208dc85e868c1b311f9b47ec2aacda34c24058..53c15249ebd5bd14839b9989eadf84eb8e541b8f 100644 (file)
@@ -12,6 +12,7 @@ glslideshow - slideshow of images using smooth zooming and fades
 [\-zoom \fIpercent\fP]
 [\-delay \fIusecs\fP]
 [\-cutoff \fIint\fP]
+[\-titles]
 [\-fps]
 [\-debug]
 [\-wireframe]
@@ -67,6 +68,9 @@ The idea here is that if your machine can't maintain a decent frame
 rate, then it must not have fast 3D hardware, so we might as well
 behave in a simpler manner.  Set this to 0 to disable this check.
 .TP 8
+.B \-titles
+Whether to print the file name of the current image in the upper left corner.
+.TP 8
 .B \-fps
 Whether to show a frames-per-second display at the bottom of the screen.
 .TP 8
index 90a28ced3f1fa847d29d53e57bd12ac0ce29b85d..dd533539f41e0b06c4142465aa6badb9025dd794 100644 (file)
@@ -1,10 +1,9 @@
-/* glsnake, Copyright (c) 2001,2002
- * Jamie Wilkinson,    Andrew Bennetts,     Peter Aylett
- * jaq@spacepants.org, andrew@puzzling.org, peter@ylett.com
- *
- * ported to xscreensaver 15th Jan 2002 by Jamie Wilkinson
- * http://spacepants.org/src/glsnake/
- *
+/* glsnake.c - OpenGL imitation of Rubik's Snake
+ * 
+ * (c) 2001-2003 Jamie Wilkinson <jaq@spacepants.org>
+ * (c) 2001-2003 Andrew Bennetts <andrew@puzzling.org> 
+ * (c) 2001-2003 Peter Aylett <peter@ylett.com>
+ * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <X11/Intrinsic.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
-extern XtAppContext app;
+/* HAVE_GLUT defined if we're building a standalone glsnake,
+ * and not defined if we're building as an xscreensaver hack */
+#ifdef HAVE_GLUT
+# include <GL/glut.h>
+#else
+# include <GL/gl.h>
+# include <GL/glu.h>
+#endif
 
-#define PROGCLASS     "glsnake"
-#define HACK_INIT     glsnake_init
-#define HACK_DRAW     glsnake_draw
-#define HACK_RESHAPE  glsnake_reshape
-#define sws_opts      xlockmore_opts
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+/* angles */
+#define ZERO     0.0
+#define LEFT    90.0
+#define PIN            180.0
+#define RIGHT   270.0
+
+#ifdef HAVE_GETTIMEOFDAY
+#ifdef GETTIMEOFDAY_TWO_ARGS
+# include <sys/time.h>
+# include <time.h>
+  typedef struct timeval snaketime;
+# define GETSECS(t) ((t).tv_sec)
+# define GETMSECS(t) ((t).tv_usec/1000)
+#else /* GETTIMEOFDAY_TWO_ARGS */
+# include <sys/time.h>
+# include <time.h>
+  typedef struct timeval snaketime;
+# define GETSECS(t) ((t).tv_sec)
+# define GETMSECS(t) ((t).tv_usec/1000)
+#endif
+#else /* HAVE_GETTIMEOFDAY */
+#ifdef HAVE_FTIME
+# include <sys/timeb.h>
+  typedef struct timeb snaketime;
+# define GETSECS(t) ((long)(t).time)
+# define GETMSECS(t) ((t).millitm/1000)
+#endif /* HAVE_FTIME */
+#endif /* HAVE_GETTIMEOFDAY */
+
+#include <math.h>
+
+#ifndef M_SQRT1_2      /* Win32 doesn't have this constant  */
+#define M_SQRT1_2 0.70710678118654752440084436210485
+#endif
 
-#define DEF_SPEED       "0.05"
+#define NODE_COUNT 24
+
+#ifdef HAVE_GLUT
+#define DEF_YANGVEL     0.10
+#define DEF_ZANGVEL     0.14
+#define DEF_EXPLODE     0.03
+#define DEF_ANGVEL      1.0
+#define DEF_ACCEL       0.1
+#define DEF_STATICTIME  5000
+#define DEF_ALTCOLOUR   0
+#define DEF_TITLES      1
+#define DEF_INTERACTIVE 0
+#define DEF_ZOOM        25.0
+#define DEF_WIREFRAME   0
+#else
+/* xscreensaver options doobies prefer strings */
+#define DEF_YANGVEL     "0.10"
+#define DEF_ZANGVEL     "0.14"
 #define DEF_EXPLODE     "0.03"
-#define DEF_VELOCITY    "1.0"
+#define DEF_ANGVEL      "1.0"
 #define DEF_ACCEL       "0.1"
 #define DEF_STATICTIME  "5000"
-#define DEF_YSPIN       "0.10"
-#define DEF_ZSPIN       "0.14"
-#define DEF_SCARYCOLOUR "False"
-#define DEF_LABELS     "True"
-#define DEF_BBOX        "False"
-
-#define DEFAULTS       "*delay:        30000        \n" \
-                       "*count:        30            \n" \
-                       "*showFPS:      False         \n" \
-                       "*wireframe:    False         \n" \
-                       "*speed:      " DEF_SPEED "   \n" \
-                       "*explode:    " DEF_EXPLODE " \n" \
-                       "*velocity:   " DEF_VELOCITY " \n" \
-/*                     "*accel:      " DEF_ACCEL "   \n" */ \
-                       "*statictime: " DEF_STATICTIME " \n" \
-                       "*yspin:      " DEF_YSPIN "   \n" \
-                       "*zspin:      " DEF_ZSPIN "   \n" \
-                       "*scarycolour:" DEF_SCARYCOLOUR " \n" \
-                       "*labels:     " DEF_LABELS "  \n" \
-                       "*labelfont:  -*-times-bold-r-normal-*-180-*\n" \
-                       "*bbox:       " DEF_BBOX     "\n" \
+#define DEF_ALTCOLOUR   "False"
+#define DEF_TITLES      "True"
+#define DEF_INTERACTIVE "False"
+#define DEF_ZOOM        "25.0"
+#define DEF_WIREFRAME   "False"
+#endif
+
+/* static variables */
+#ifndef HAVE_GLUT
+# include <X11/Intrinsic.h>
+#else
+/* xscreensaver boolean type */
+# define Bool int
+#endif
+
+static GLfloat explode;
+static GLfloat accel;
+static long statictime;
+static GLfloat yspin = 0;
+static GLfloat zspin = 0;
+static GLfloat yangvel;
+static GLfloat zangvel;
+static Bool altcolour;
+static Bool titles;
+static Bool interactive;
+static Bool wireframe;
+static GLfloat zoom;
+static GLfloat angvel;
+
+#ifndef HAVE_GLUT
+/* xscreensaver setup */
+extern XtAppContext app;
+
+#define PROGCLASS "glsnake"
+#define HACK_INIT glsnake_init
+#define HACK_DRAW glsnake_display
+#define HACK_RESHAPE glsnake_reshape
+#define sws_opts xlockmore_opts
+
+
+/* xscreensaver defaults */
+#define DEFAULTS "*delay:          30000                      \n" \
+                 "*count:          30                         \n" \
+                 "*showFPS:        False                      \n" \
+                 "*wireframe:      False                      \n" \
+                 "*explode:      " DEF_EXPLODE              " \n" \
+                 "*angvel:       " DEF_ANGVEL               " \n" \
+                 "*accel:        " DEF_ACCEL                " \n" \
+                 "*statictime:   " DEF_STATICTIME           " \n" \
+                 "*yangvel:      " DEF_YANGVEL              " \n" \
+                 "*zangvel:      " DEF_ZANGVEL              " \n" \
+                 "*altcolour:    " DEF_ALTCOLOUR            " \n" \
+                 "*titles:         True                       \n" \
+                "*labelfont:   -*-times-bold-r-normal-*-180-*\n" \
+                 "*zoom:         " DEF_ZOOM                 " \n" \
 
 
 
@@ -64,688 +158,1334 @@ extern XtAppContext app;
 
 #include "xlockmore.h"
 
-#ifdef USE_GL /* whole file */
-
-#include <GL/glu.h>
-#include <sys/time.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#define ZERO  0
-#define LEFT  1
-#define PIN   2
-#define RIGHT 3
-
-typedef struct model_s {
-  char * name;
-  float node[24];
-} model_t;
-
-typedef struct nodeang_s {
-  float cur_angle;
-  float dest_angle;
-} nodeang_t;
-
-typedef struct {
-  GLXContext * glx_context;
-
-  int node_list; /* name of the display list */
-  int is_cyclic;
-  int is_legal;
-  int last_turn;
-  int selected;
-  struct timeval last_iteration;
-  struct timeval last_morph;
-  int morphing;
-  nodeang_t node[24];
-  GLfloat roty;
-  GLfloat rotz;
-  int paused;
-  int dragging;
-  int m_count;
-  int m;
-  int cur_model;
-  int interactive;
-  GLfloat colour_t[3];
-  GLfloat colour_i[3];
-  GLfloat colour[3];
-  model_t * models;
-
-  XFontStruct * font;
-  GLuint font_list;
-} glsnake_configuration;
-
-static glsnake_configuration *glc = NULL;
-
-static GLfloat speed;
-static GLfloat explode;
-static GLfloat velocity;
-/* static GLfloat accel; */
-static long statictime;
-static GLfloat yspin;
-static GLfloat zspin;
-static Bool scarycolour;
-static Bool labels;
-static Bool do_bbox;
-
 static XrmOptionDescRec opts[] = {
-  { "-speed", ".speed", XrmoptionSepArg, 0 },
-  { "-explode", ".explode", XrmoptionSepArg, 0 },
-  { "-velocity", ".velocity", XrmoptionSepArg, 0 },
-/*  { "-accel", ".accel", XrmoptionSepArg, 0 }, */
-  { "-statictime", ".statictime", XrmoptionSepArg, 0 },
-  { "-yspin", ".yspin", XrmoptionSepArg, 0 },
-  { "-zspin", ".zspin", XrmoptionSepArg, 0 },
-  { "-scarycolour", ".scarycolour", XrmoptionNoArg, "True" },
-  { "+scarycolour", ".scarycolour", XrmoptionNoArg, "False" },
-  { "-labels", ".labels", XrmoptionNoArg, "True" },
-  { "+labels", ".labels", XrmoptionNoArg, "False" },
-#if 0
-  { "-bbox",   ".bbox",  XrmoptionNoArg, "True" },
-  { "+bbox",   ".bbox",  XrmoptionNoArg, "False" },
-#endif
+    { "-explode", ".explode", XrmoptionSepArg, DEF_EXPLODE },
+    { "-angvel", ".angvel", XrmoptionSepArg, DEF_ANGVEL },
+    { "-accel", ".accel", XrmoptionSepArg, DEF_ACCEL },
+    { "-statictime", ".statictime", XrmoptionSepArg, DEF_STATICTIME },
+    { "-yangvel", ".yangvel", XrmoptionSepArg, DEF_YANGVEL },
+    { "-zangvel", ".zangvel", XrmoptionSepArg, DEF_ZANGVEL },
+    { "-altcolour", ".altcolour", XrmoptionNoArg, (caddr_t) "True" },
+    { "-no-altcolour", ".altcolour", XrmoptionNoArg, (caddr_t) "False" },
+    { "-titles", ".titles", XrmoptionNoArg, (caddr_t) "True" },
+    { "-no-titles", ".titles", XrmoptionNoArg, (caddr_t) "False" },
+    { "-zoom", ".zoom", XrmoptionSepArg, DEF_ZOOM },
+    { "-wireframe", ".wireframe", XrmoptionNoArg, (caddr_t) "true" },
+    { "-no-wireframe", ".wireframe", XrmoptionNoArg, (caddr_t) "false" },
 };
 
 static argtype vars[] = {
-  {(caddr_t *) &speed, "speed", "Speed", DEF_SPEED, t_Float},
-  {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float},
-  {(caddr_t *) &velocity, "velocity", "Velocity", DEF_VELOCITY, t_Float},
-/*  {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float}, */
-  {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int},
-  {(caddr_t *) &yspin, "yspin", "Y Spin", DEF_YSPIN, t_Float},
-  {(caddr_t *) &zspin, "zspin", "Z Spin", DEF_ZSPIN, t_Float},
-/*  {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool}, */
-  {(caddr_t *) &scarycolour, "scarycolour", "Scary Colour", DEF_SCARYCOLOUR, t_Bool},
-  {(caddr_t *) &labels, "labels", "Labels", DEF_LABELS, t_Bool},
-  {(caddr_t *) &do_bbox, "bbox",  "BBox",   DEF_BBOX,   t_Bool},
+    {(caddr_t *) &explode, "explode", "Explode", DEF_EXPLODE, t_Float},
+    {(caddr_t *) &angvel, "angvel", "Angular Velocity", DEF_ANGVEL, t_Float},
+    {(caddr_t *) &accel, "accel", "Acceleration", DEF_ACCEL, t_Float},
+    {(caddr_t *) &statictime, "statictime", "Static Time", DEF_STATICTIME, t_Int},
+    {(caddr_t *) &yangvel, "yangvel", "Angular Velocity about Y axis", DEF_YANGVEL, t_Float},
+    {(caddr_t *) &zangvel, "zangvel", "Angular Velocity about X axis", DEF_ZANGVEL, t_Float},
+    {(caddr_t *) &interactive, "interactive", "Interactive", DEF_INTERACTIVE, t_Bool},
+    {(caddr_t *) &altcolour, "altcolour", "Alternate Colour Scheme", DEF_ALTCOLOUR, t_Bool},
+    {(caddr_t *) &titles, "titles", "Titles", DEF_TITLES, t_Bool},
+    {(caddr_t *) &zoom, "zoom", "Zoom", DEF_ZOOM, t_Float},
+    {(caddr_t *) &wireframe, "wireframe", "Wireframe", DEF_WIREFRAME, t_Bool},
 };
 
 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
+#endif
 
-/* prism models */
-#define VOFFSET 0.045
-float solid_prism_v[][3] = {
-  /* first corner, bottom left front */
-  { VOFFSET, VOFFSET, 1.0 },
-  { VOFFSET, 0.0, 1.0 - VOFFSET },
-  { 0.0, VOFFSET, 1.0 - VOFFSET },
-  /* second corner, rear */
-  { VOFFSET, VOFFSET, 0.00 },
-  { VOFFSET, 0.0, VOFFSET },
-  { 0.0, VOFFSET, VOFFSET },
-  /* third, right front */
-  { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 },
-  { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET },
-  { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET },
-  /* fourth, right rear */
-  { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 },
-  { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET },
-  { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET },
-  /* fifth, upper front */
-  { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 },
-  { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET },
-  { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET},
-  /* sixth, upper rear */
-  { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 },
-  { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET },
-  { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET }
+struct model_s {
+    char * name;
+    float node[NODE_COUNT];
 };
 
-/* normals */
-float solid_prism_n[][3] = {
-  /* corners */
-  { -VOFFSET, -VOFFSET, VOFFSET },
-  { VOFFSET, -VOFFSET, VOFFSET },
-  { -VOFFSET, VOFFSET, VOFFSET },
-  { -VOFFSET, -VOFFSET, -VOFFSET },
-  { VOFFSET, -VOFFSET, -VOFFSET },
-  { -VOFFSET, VOFFSET, -VOFFSET },
-  /* edges */
-  { -VOFFSET, 0.0, VOFFSET },
-  { 0.0, -VOFFSET, VOFFSET },
-  { VOFFSET, VOFFSET, VOFFSET },
-  { -VOFFSET, 0.0, -VOFFSET },
-  { 0.0, -VOFFSET, -VOFFSET },
-  { VOFFSET, VOFFSET, -VOFFSET },
-  { -VOFFSET, -VOFFSET, 0.0 },
-  { VOFFSET, -VOFFSET, 0.0 },
-  { -VOFFSET, VOFFSET, 0.0 },
-  /* faces */
-  { 0.0, 0.0, 1.0 },
-  { 0.0, -1.0, 0.0 },
-  { M_SQRT1_2, M_SQRT1_2, 0.0 },
-  { -1.0, 0.0, 0.0 },
-  { 0.0, 0.0, -1.0 }
-};
+struct glsnake_cfg {
+#ifndef HAVE_GLUT
+    GLXContext * glx_context;
+    XFontStruct * font;
+    GLuint font_list;
+#else
+    /* font list number */
+    int font;
+#endif
+
+    /* window id */
+    int window;
+
+    /* is a morph in progress? */
+    int morphing;
+
+    /* has the model been paused? */
+    int paused;
+
+    /* snake metrics */
+    int is_cyclic;
+    int is_legal;
+    int last_turn;
+    int debug;
+
+    /* the shape of the model */
+    float node[NODE_COUNT];
+
+    /* currently selected node for interactive mode */
+    int selected;
 
-/* vertices */
-float wire_prism_v[][3] = {
-  { 0.0, 0.0, 1.0 },
-  { 1.0, 0.0, 1.0 },
-  { 0.0, 1.0, 1.0 },
-  { 0.0, 0.0, 0.0 },
-  { 1.0, 0.0, 0.0 },
-  { 0.0, 1.0, 0.0 }
+    /* models */
+    int prev_model;
+    int next_model;
+
+    /* model morphing */
+    int new_morph;
+
+    /* colours */
+    float colour[2][3];
+    int next_colour;
+    int prev_colour;
+
+    /* timing variables */
+    snaketime last_iteration;
+    snaketime last_morph;
+
+    /* window size */
+    int width, height;
+    int old_width, old_height;
+
+    /* the id of the display lists for drawing a node */
+    int node_solid, node_wire;
+
+    /* is the window fullscreen? */
+    int fullscreen;
 };
 
-/* normals */
-float wire_prism_n[][3] = {
-  { 0.0, 0.0, 1.0},
-  { 0.0,-1.0, 0.0},
-  { M_SQRT1_2, M_SQRT1_2, 0.0},
-  {-1.0, 0.0, 0.0},
-  { 0.0, 0.0,-1.0}
+#define COLOUR_CYCLIC 0
+#define COLOUR_ACYCLIC 1
+#define COLOUR_INVALID 2
+#define COLOUR_AUTHENTIC 3
+
+float colour[][2][3] = {
+    /* cyclic - green */
+    { { 0.4, 0.8, 0.2 },
+      { 1.0, 1.0, 1.0 } },
+    /* acyclic - blue */
+    { { 0.3, 0.1, 0.9 },
+      { 1.0, 1.0, 1.0 } },
+    /* invalid - grey */
+    { { 0.3, 0.1, 0.9 },
+      { 1.0, 1.0, 1.0 } },
+    /* authentic - purple and green */
+    { { 0.38, 0.0, 0.55 },
+      { 0.0,  0.5, 0.34 } }
 };
 
-/* default models */
-#define Z   0.0
-#define L  90.0
-#define P 180.0
-#define R 270.0
-
-static model_t default_models[] = { 
-  { "Ball", 
-    { R, R, L, L, R, L, R, R, L, R, L, L, R, R, L, L, R, L, R, R, L, R, L }
-  },
-  { "Snow",
-    { R, R, R, R, L, L, L, L, R, R, R, R, L, L, L, L, R, R, R, R, L, L, L }
-  },
-  { "Propellor",
-    { Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z, L, Z, Z, Z, R, L, R, Z }
-  },
-  { "Flamingo",
-    { Z, P, Z, Z, Z, Z, Z, P, R, R, P, R, L, P, L, R, P, R, R, Z, Z, Z, P }
-  },
-  { "Cat",
-    { Z, P, P, Z, P, P, Z, L, Z, P, P, Z, P, P, Z, P, P, Z, Z, Z, Z, Z, Z }
-  },
-  { "Rooster",
-    { Z, Z, P, P, Z, L, Z, L, R, P, R, Z, P, P, Z, R, P, R, L, Z, L, Z, P }
-  }
+struct model_s model[] = {
+#define STRAIGHT_MODEL 0
+    { "straight",
+      { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO }
+    },
+    /* the models in the Rubik's snake manual */
+#define START_MODEL 1
+    { "ball",
+      { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT,
+       RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT,
+       RIGHT, LEFT, RIGHT, LEFT }
+    },
+    { "snow",
+      { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT,
+       RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT,
+       RIGHT, LEFT, LEFT, LEFT }
+    },
+    { "propellor",
+      { ZERO, ZERO, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, ZERO, ZERO,
+       ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, ZERO, ZERO, ZERO, RIGHT,
+       LEFT, RIGHT, ZERO, LEFT }
+    },
+    { "flamingo",
+      { ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, RIGHT, PIN,
+       RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, ZERO, ZERO,
+       ZERO, PIN }
+    },
+    { "cat",
+      { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN,
+       ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO }
+    },
+    { "rooster",
+      { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, LEFT, RIGHT, PIN, RIGHT,
+       ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, LEFT, ZERO,
+       PIN }
+    },
+    /* These models were taken from Andrew and Peter's original snake.c
+     * as well as some newer ones made up by Jamie, Andrew and Peter. */
+    { "half balls",
+      { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT,
+       LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT,
+       RIGHT, LEFT, LEFT, LEFT }
+    },
+    { "zigzag1",
+      { RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT,
+       LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT,
+       RIGHT, RIGHT, LEFT, LEFT }
+    },
+    { "zigzag2",
+      { PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN,
+       ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN }
+    },
+    { "zigzag3",
+      { PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN,
+       LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN }
+    },
+    { "caterpillar",
+      { RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT,
+       LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN,
+       LEFT, LEFT }
+    },
+    { "bow",
+      { RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT,
+       LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT,
+       RIGHT, RIGHT, LEFT, LEFT }
+    },
+    { "turtle",
+      { ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT,
+       LEFT, RIGHT, LEFT, LEFT, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT,
+       RIGHT, RIGHT, RIGHT }
+    },
+    { "basket",
+      { RIGHT, PIN, ZERO, ZERO, PIN, LEFT, ZERO, LEFT, LEFT, ZERO,
+       LEFT, PIN, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO,
+       PIN, LEFT }
+    },
+    { "thing",
+      { PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT,
+       LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT,
+       RIGHT, LEFT, LEFT }
+    },
+    { "hexagon",
+      { ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO,
+       ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO,
+       LEFT, ZERO, ZERO, RIGHT }
+    },
+    { "tri1",
+      { ZERO, ZERO, LEFT, RIGHT, ZERO, LEFT, ZERO, RIGHT, ZERO, ZERO,
+       LEFT, RIGHT, ZERO, LEFT, ZERO, RIGHT, ZERO, ZERO, LEFT, RIGHT,
+       ZERO, LEFT, ZERO, RIGHT }
+    },
+    { "triangle",
+      { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO,
+       ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO, LEFT, RIGHT }
+    },
+    { "flower",
+      { ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT,
+       RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN,
+       RIGHT, RIGHT, PIN }
+    },
+    { "crucifix",
+      { ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN,
+       PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN }
+    },
+    { "kayak",
+      { PIN, RIGHT, LEFT, PIN, LEFT, PIN, ZERO, ZERO, RIGHT, PIN, LEFT,
+       ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+       PIN, RIGHT }
+    },
+    { "bird",
+      { ZERO, ZERO, ZERO, ZERO, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT,
+       ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT,
+       LEFT, ZERO, PIN }
+    },
+    { "seal",
+      { RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO,
+       LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT,
+       RIGHT, LEFT }
+    },
+    { "dog",
+      { ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN,
+       ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN }
+    },
+    { "frog",
+      { RIGHT, RIGHT, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN,
+       RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT,
+       RIGHT, LEFT, LEFT }
+    },
+    { "quavers",
+      { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, ZERO, ZERO,
+       RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, ZERO, LEFT, LEFT,
+       RIGHT, LEFT, RIGHT, RIGHT }
+    },
+    { "fly",
+      { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, ZERO,
+       LEFT, PIN, RIGHT, ZERO, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, LEFT,
+       RIGHT, RIGHT }
+    },
+    { "puppy",
+      { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO,
+       RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT,
+       LEFT }
+    },
+    { "stars",
+      { LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT,
+       ZERO, ZERO, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN,
+       RIGHT, LEFT }
+    },
+    { "mountains",
+      { RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN,
+       LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT,
+       PIN, LEFT, PIN }
+    },
+    { "quad1",
+      { RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN,
+       LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT,
+       LEFT, PIN, LEFT, PIN }
+    },
+    { "quad2",
+      { ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN,
+       ZERO, PIN, ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT,
+       PIN, ZERO, PIN }
+    },
+    { "glasses",
+      { ZERO, PIN, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, PIN,
+       ZERO, PIN, ZERO, PIN, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO,
+       PIN, ZERO, PIN }
+    },
+    { "em",
+      { ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN,
+       ZERO, PIN, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+       PIN, ZERO, PIN }
+    },
+    { "quad3",
+      { ZERO, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT,
+       ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+       LEFT, ZERO, PIN }
+    },
+    { "vee",
+      { ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO,
+       ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO,
+       ZERO, ZERO, PIN }
+    },
+    { "square",
+      { ZERO, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, ZERO,
+       ZERO, PIN, ZERO, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO,
+       ZERO, ZERO, PIN }
+    },
+    { "eagle",
+      { RIGHT, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, ZERO,
+       LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT,
+       ZERO, ZERO, LEFT, PIN }
+    },
+    { "volcano",
+      { RIGHT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT,
+       ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT,
+       LEFT, RIGHT, ZERO, LEFT, PIN }
+    },
+    { "saddle",
+      { RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO,
+       LEFT, PIN, RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT,
+       ZERO, LEFT, PIN }
+    },
+    { "c3d",
+      { ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, ZERO,
+       ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT,
+       ZERO, ZERO, PIN }
+    },
+    { "block",
+      { ZERO, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN,
+       RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT,
+       PIN, RIGHT }
+    },
+    { "duck",
+      { LEFT, PIN, LEFT, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, LEFT,
+       PIN, RIGHT, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN,
+       LEFT }
+    },
+    { "prayer",
+      { RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, ZERO,
+       ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, RIGHT, RIGHT, LEFT,
+       RIGHT, LEFT, LEFT, LEFT, PIN }
+    },
+    { "giraffe",
+      { ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, RIGHT,
+       RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT,
+       PIN, LEFT, LEFT, LEFT }
+    },
+    { "tie fighter",
+      { PIN, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, LEFT,
+       ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, LEFT, ZERO,
+       LEFT, RIGHT, ZERO }
+    },
+    { "Strong Arms",
+      { PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, RIGHT,
+       RIGHT, PIN, RIGHT, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO,
+       ZERO, PIN, PIN, ZERO }
+    },
+
+    /* the following modesl were created during the slug/compsoc codefest
+     * febrray 2003 */
+    { "cool gegl",
+      { PIN, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT,
+       ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, ZERO,
+       ZERO, ZERO }
+    },
+    { "knuckledusters",
+      { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN,
+       PIN, ZERO, RIGHT, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO,
+       RIGHT, ZERO }
+    },
+    { "k's turd",
+      { RIGHT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT,
+       RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN,
+       RIGHT, LEFT, RIGHT, PIN, ZERO }
+    },
+    { "lightsabre",
+      { ZERO, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO }
+    },
+    { "not a stairway",
+      { LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO,
+       RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT,
+       RIGHT, ZERO, LEFT, ZERO }
+    },
+    { "arse gegl",
+      { ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO,
+       PIN, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO, PIN, PIN, ZERO,
+       PIN, ZERO }
+    },
+    { "box",
+      { ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO, ZERO }
+    },
+    { "kissy box",
+      { PIN, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO,
+       ZERO, PIN, ZERO }
+    },
+    { "erect penis",     /* thanks benno */
+      { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, PIN,
+       PIN, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO }
+    },
+    { "flaccid penis",
+      { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, PIN,
+       PIN, ZERO, ZERO, ZERO, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO,
+       ZERO, ZERO }
+    },
+    { "vagina",
+      { RIGHT, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO,
+       LEFT, ZERO, ZERO, ZERO, LEFT, ZERO, LEFT, PIN, LEFT, PIN, RIGHT,
+       PIN, RIGHT, ZERO }
+    },
+    { "mask",
+      { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN,
+       ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO,
+       ZERO, ZERO, ZERO }
+    },
+    { "poles or columns or something",
+      { LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+       ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+       ZERO, LEFT, ZERO }
+    },
+    { "crooked v",
+      { ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+       ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+       ZERO, ZERO, ZERO }
+    },
+    { "dog leg",
+      { ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO,
+       LEFT, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO,
+       ZERO, ZERO }
+    },
+    { "scrubby",
+      { ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO,
+       LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO,
+       LEFT, PIN, ZERO, ZERO }
+    },
+    { "voltron's eyes",
+      { ZERO, ZERO, PIN, RIGHT, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO,
+       LEFT, PIN, ZERO, ZERO, PIN, ZERO, LEFT, ZERO, RIGHT, LEFT, ZERO,
+       RIGHT, ZERO, ZERO }
+    },
+    { "flying toaster",
+      { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+       RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+       PIN, ZERO }
+    },
+    { "dubbya",
+      { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+       ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+       PIN, ZERO }
+    },
+    { "tap handle",
+      { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+       LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+       PIN, ZERO }
+    },
+    { "wingnut",
+      { PIN, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO,
+       PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO,
+       PIN, ZERO }
+    },
+    { "tight twist",
+      { RIGHT, ZERO, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT,
+       RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT,
+       ZERO, ZERO, RIGHT, ZERO }
+    },
+    { "double helix",
+      { RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT,
+       ZERO, RIGHT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, ZERO,
+       RIGHT, ZERO, RIGHT, ZERO, ZERO }
+    },
+
+    /* These models come from the website at 
+     * http://www.geocities.com/stigeide/snake */
+    { "Abstract",
+        { RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO }
+    },
+    { "AlanH1",
+        { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN }
+    },
+    { "AlanH2",
+        { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT }
+    },
+    { "AlanH3",
+        { LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN }
+    },
+    { "AlanH4",
+        { ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, ZERO }
+    },
+    { "Alien",
+        { RIGHT, LEFT, RIGHT, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO, PIN, PIN }
+    },
+    { "Angel",
+        { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT }
+    },
+    { "AnotherFigure",
+        { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, ZERO }
+    },
+    { "Ball",
+        { LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT }
+    },
+    { "Basket",
+        { ZERO, RIGHT, RIGHT, ZERO, RIGHT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, ZERO, LEFT }
+    },
+    { "Beetle",
+        { PIN, LEFT, RIGHT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, ZERO, LEFT, RIGHT, PIN, RIGHT }
+    },
+    { "Bone",
+        { PIN, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, PIN }
+    },
+    { "Bow",
+        { LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT }
+    },
+    { "Bra",
+        { RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT }
+    },
+    { "BronchoSaurian",
+        { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, PIN }
+    },
+    { "Cactus",
+        { PIN, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, ZERO }
+    },
+    { "Camel",
+        { RIGHT, ZERO, PIN, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, ZERO, ZERO, LEFT }
+    },
+    { "Candlestick",
+        { LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT }
+    },
+    { "Cat",
+        { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO }
+    },
+    { "Cave",
+        { RIGHT, ZERO, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO }
+    },
+    { "Chains",
+        { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO }
+    },
+    { "Chair",
+        { RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, LEFT, RIGHT, LEFT, LEFT }
+    },
+    { "Chick",
+        { RIGHT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, LEFT, LEFT }
+    },
+    { "Clockwise",
+        { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT }
+    },
+    { "Cobra",
+        { ZERO, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT }
+    },
+    { "Cobra2",
+        { LEFT, ZERO, PIN, ZERO, PIN, LEFT, ZERO, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, PIN, ZERO, RIGHT }
+    },
+    { "Cobra3",
+        { ZERO, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, LEFT }
+    },
+    { "Compact1",
+        { ZERO, ZERO, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN }
+    },
+    { "Compact2",
+        { LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO }
+    },
+    { "Compact3",
+        { ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN }
+    },
+    { "Compact4",
+        { PIN, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO }
+    },
+    { "Compact5",
+        { LEFT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT }
+    },
+    { "Contact",
+        { PIN, ZERO, ZERO, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, ZERO, ZERO, PIN, RIGHT, PIN }
+    },
+    { "Contact2",
+        { RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, PIN, LEFT }
+    },
+    { "Cook",
+        { ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, PIN }
+    },
+    { "Counterclockwise",
+        { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT }
+    },
+    { "Cradle",
+        { LEFT, LEFT, ZERO, PIN, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, RIGHT, LEFT, LEFT, ZERO, ZERO, RIGHT }
+    },
+    { "Crankshaft",
+        { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT }
+    },
+    { "Cross",
+        { ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN }
+    },
+    { "Cross2",
+        { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO }
+    },
+    { "Cross3",
+        { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, ZERO, PIN, PIN, ZERO }
+    },
+    { "CrossVersion1",
+        { PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN }
+    },
+    { "CrossVersion2",
+        { RIGHT, LEFT, PIN, LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, LEFT, PIN, LEFT, RIGHT }
+    },
+    { "Crown",
+        { LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, PIN, ZERO }
+    },
+    { "DNAStrand",
+        { RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT }
+    },
+    { "Diamond",
+        { ZERO, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT }
+    },
+    { "Dog",
+        { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT }
+    },
+    { "DogFace",
+        { ZERO, ZERO, PIN, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN }
+    },
+    { "DoublePeak",
+        { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, LEFT, ZERO, PIN, ZERO, RIGHT, RIGHT, LEFT, PIN, LEFT, RIGHT }
+    },
+    { "DoubleRoof",
+        { ZERO, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT }
+    },
+    { "DoubleToboggan",
+        { ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN }
+    },
+    { "Doubled",
+        { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT }
+    },
+    { "Doubled1",
+        { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT }
+    },
+    { "Doubled2",
+        { LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT, LEFT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT }
+    },
+    { "DumblingSpoon",
+        { PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO }
+    },
+    { "Embrace",
+        { PIN, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO }
+    },
+    { "EndlessBelt",
+        { ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT }
+    },
+    { "Entrance",
+        { LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT }
+    },
+    { "Esthetic",
+        { LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT }
+    },
+    { "Explotion",
+        { RIGHT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT }
+    },
+    { "F-ZeroXCar",
+        { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT }
+    },
+    { "Face",
+        { ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT }
+    },
+    { "Fantasy",
+        { LEFT, LEFT, RIGHT, PIN, ZERO, RIGHT, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, LEFT, ZERO, PIN, LEFT, RIGHT, RIGHT, RIGHT, PIN }
+    },
+    { "Fantasy1",
+        { PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN }
+    },
+    { "FaserGun",
+        { ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, RIGHT, ZERO, PIN }
+    },
+    { "FelixW",
+        { ZERO, RIGHT, ZERO, PIN, LEFT, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, RIGHT, PIN, ZERO, LEFT, ZERO }
+    },
+    { "Flamingo",
+        { ZERO, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, ZERO, ZERO, ZERO, PIN }
+    },
+    { "FlatOnTheTop",
+        { ZERO, PIN, PIN, ZERO, PIN, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, PIN }
+    },
+    { "Fly",
+        { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT }
+    },
+    { "Fountain",
+        { LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN }
+    },
+    { "Frog",
+        { LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, RIGHT, RIGHT }
+    },
+    { "Frog2",
+        { LEFT, ZERO, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, ZERO, RIGHT }
+    },
+    { "Furby",
+        { PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN }
+    },
+    { "Gate",
+        { ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, ZERO }
+    },
+    { "Ghost",
+        { LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT }
+    },
+    { "Globus",
+        { RIGHT, LEFT, ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, PIN, ZERO, RIGHT, LEFT, ZERO }
+    },
+    { "Grotto",
+        { PIN, PIN, ZERO, LEFT, RIGHT, LEFT, ZERO, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, RIGHT, LEFT, RIGHT }
+    },
+    { "H",
+        { PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, PIN, PIN, ZERO }
+    },
+    { "HeadOfDevil",
+        { PIN, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO }
+    },
+    { "Heart",
+        { RIGHT, ZERO, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, RIGHT, ZERO, PIN, ZERO, LEFT, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, ZERO, LEFT }
+    },
+    { "Heart2",
+        { ZERO, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, PIN, ZERO }
+    },
+    { "Hexagon",
+        { ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO }
+    },
+    { "HoleInTheMiddle1",
+        { ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT }
+    },
+    { "HoleInTheMiddle2",
+        { ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT }
+    },
+    { "HouseBoat",
+        { RIGHT, RIGHT, PIN, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT, PIN }
+    },
+    { "HouseByHouse",
+        { LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, RIGHT }
+    },
+    { "Infinity",
+        { LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT }
+    },
+    { "Integral",
+        { RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT }
+    },
+    { "Iron",
+        { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT }
+    },
+    { "JustSquares",
+        { RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, LEFT }
+    },
+    { "Kink",
+        { ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO }
+    },
+    { "Knot",
+        { LEFT, LEFT, PIN, LEFT, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, RIGHT, PIN, RIGHT, RIGHT, LEFT }
+    },
+    { "Leaf",
+        { ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO }
+    },
+    { "LeftAsRight",
+        { RIGHT, PIN, LEFT, RIGHT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, RIGHT, LEFT, RIGHT, PIN, LEFT }
+    },
+    { "Long-necked",
+        { PIN, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO }
+    },
+    { "LunaModule",
+        { PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT }
+    },
+    { "MagnifyingGlass",
+        { ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO }
+    },
+    { "Mask",
+        { ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT }
+    },
+    { "Microscope",
+        { PIN, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN }
+    },
+    { "Mirror",
+        { PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, PIN, ZERO, ZERO, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, PIN, ZERO }
+    },
+    { "MissPiggy",
+        { ZERO, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, ZERO, LEFT, PIN, RIGHT, RIGHT, ZERO, RIGHT }
+    },
+    { "Mole",
+        { ZERO, RIGHT, ZERO, RIGHT, LEFT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, RIGHT, PIN, LEFT }
+    },
+    { "Monk",
+        { LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT }
+    },
+    { "Mountain",
+        { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, LEFT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO }
+    },
+    { "Mountains",
+        { ZERO, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO }
+    },
+    { "MouseWithoutTail",
+        { ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO }
+    },
+    { "Mushroom",
+        { PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN }
+    },
+    { "Necklace",
+        { ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, LEFT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO, ZERO, RIGHT, ZERO, ZERO }
+    },
+    { "NestledAgainst",
+        { LEFT, ZERO, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT }
+    },
+    { "NoClue",
+        { ZERO, RIGHT, PIN, LEFT, LEFT, LEFT, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, RIGHT, RIGHT, RIGHT, PIN, LEFT, ZERO }
+    },
+    { "Noname",
+        { LEFT, PIN, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT }
+    },
+    { "Obelisk",
+        { PIN, ZERO, ZERO, ZERO, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, ZERO }
+    },
+    { "Ostrich",
+        { ZERO, ZERO, PIN, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN }
+    },
+    { "Ostrich2",
+        { PIN, PIN, ZERO, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO }
+    },
+    { "PairOfGlasses",
+        { ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, LEFT, ZERO, PIN, ZERO, RIGHT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO }
+    },
+    { "Parrot",
+        { ZERO, ZERO, ZERO, ZERO, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, ZERO, PIN }
+    },
+    { "Penis",
+        { PIN, PIN, RIGHT, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, LEFT, PIN, PIN }
+    },
+    { "PictureCommingSoon",
+        { LEFT, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, RIGHT }
+    },
+    { "Pitti",
+        { LEFT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT }
+    },
+    { "Plait",
+        { LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT }
+    },
+    { "Platform",
+        { RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, LEFT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT }
+    },
+    { "PodRacer",
+        { ZERO, PIN, ZERO, PIN, RIGHT, PIN, ZERO, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, ZERO, PIN, LEFT }
+    },
+    { "Pokemon",
+        { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT }
+    },
+    { "Prawn",
+        { RIGHT, PIN, ZERO, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, PIN, ZERO, LEFT, PIN, ZERO, PIN, LEFT }
+    },
+    { "Propeller",
+        { ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, RIGHT, ZERO, LEFT, RIGHT }
+    },
+    { "Pyramid",
+        { ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, LEFT }
+    },
+    { "QuarterbackTiltedAndReadyToHut",
+        { PIN, ZERO, RIGHT, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, LEFT, ZERO, PIN }
+    },
+    { "Ra",
+        { PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT }
+    },
+    { "Rattlesnake",
+        { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, PIN, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT }
+    },
+    { "Revelation",
+        { ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN }
+    },
+    { "Revolution1",
+        { LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT }
+    },
+    { "Ribbon",
+        { RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT }
+    },
+    { "Rocket",
+        { RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT }
+    },
+    { "Roofed",
+        { ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, LEFT, ZERO, PIN, ZERO, RIGHT }
+    },
+    { "Roofs",
+        { PIN, PIN, RIGHT, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, LEFT, PIN, PIN }
+    },
+    { "RowHouses",
+        { RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT }
+    },
+    { "Sculpture",
+        { RIGHT, LEFT, PIN, ZERO, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT, ZERO, ZERO, ZERO, PIN, LEFT, RIGHT, LEFT }
+    },
+    { "Seal",
+        { LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT }
+    },
+    { "Seal2",
+        { RIGHT, PIN, ZERO, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO }
+    },
+    { "Sheep",
+        { RIGHT, LEFT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, LEFT, LEFT, RIGHT, LEFT }
+    },
+    { "Shelter",
+        { LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, ZERO, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, RIGHT }
+    },
+    { "Ship",
+        { PIN, RIGHT, LEFT, LEFT, LEFT, LEFT, PIN, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, LEFT, ZERO, PIN, PIN }
+    },
+    { "Shpongle",
+        { LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO }
+    },
+    { "Slide",
+        { LEFT, RIGHT, LEFT, RIGHT, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, RIGHT, LEFT }
+    },
+    { "SmallShip",
+        { ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, RIGHT, ZERO, RIGHT, LEFT, ZERO, LEFT, RIGHT, ZERO, LEFT }
+    },
+    { "SnakeReadyToStrike",
+        { LEFT, ZERO, LEFT, ZERO, LEFT, ZERO, LEFT, RIGHT, ZERO, RIGHT, ZERO, RIGHT, ZERO, LEFT, ZERO, ZERO, ZERO, PIN, ZERO, ZERO, ZERO, ZERO, LEFT }
+    },
+    { "Snakes14",
+        { RIGHT, RIGHT, PIN, ZERO, RIGHT, LEFT, RIGHT, ZERO, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, RIGHT }
+    },
+    { "Snakes15",
+        { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, ZERO, PIN, RIGHT }
+    },
+    { "Snakes18",
+        { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, RIGHT, PIN, RIGHT, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, ZERO, PIN }
+    },
+    { "Snowflake",
+        { LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, RIGHT }
+    },
+    { "Snowman",
+        { ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO }
+    },
+    { "Source",
+        { PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN }
+    },
+    { "Spaceship",
+        { PIN, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, RIGHT, PIN, PIN }
+    },
+    { "Spaceship2",
+        { PIN, PIN, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, LEFT, LEFT, PIN, PIN }
+    },
+    { "Speedboat",
+        { LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, LEFT, ZERO, ZERO, PIN, ZERO, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT }
+    },
+    { "Speedboat2",
+        { PIN, RIGHT, LEFT, LEFT, RIGHT, RIGHT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, LEFT, LEFT, RIGHT, RIGHT, LEFT, PIN, ZERO, RIGHT, PIN, LEFT }
+    },
+    { "Spider",
+        { RIGHT, RIGHT, ZERO, ZERO, LEFT, RIGHT, LEFT, PIN, ZERO, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, ZERO, PIN, RIGHT, LEFT, RIGHT, ZERO, ZERO, LEFT }
+    },
+    { "Spitzbergen",
+        { PIN, LEFT, ZERO, RIGHT, RIGHT, LEFT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, RIGHT, LEFT, LEFT, ZERO }
+    },
+    { "Square",
+        { ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO, LEFT, LEFT, PIN, RIGHT, RIGHT, ZERO, ZERO }
+    },
+    { "SquareHole",
+        { PIN, ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN }
+    },
+    { "Stage",
+        { RIGHT, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, LEFT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO }
+    },
+    { "Stairs",
+        { ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO }
+    },
+    { "Stairs2",
+        { ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO }
+    },
+    { "Straight",
+        { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO }
+    },
+    { "Swan",
+        { ZERO, PIN, ZERO, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, LEFT, PIN, LEFT, RIGHT }
+    },
+    { "Swan2",
+        { PIN, ZERO, PIN, RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO, ZERO, PIN, PIN }
+    },
+    { "Swan3",
+        { PIN, PIN, ZERO, ZERO, ZERO, RIGHT, ZERO, RIGHT, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, ZERO, RIGHT }
+    },
+    { "Symbol",
+        { RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, ZERO, PIN, PIN, ZERO, PIN, LEFT, LEFT, RIGHT }
+    },
+    { "Symmetry",
+        { RIGHT, ZERO, LEFT, RIGHT, LEFT, ZERO, LEFT, RIGHT, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, RIGHT, ZERO, RIGHT, LEFT, RIGHT, ZERO, LEFT }
+    },
+    { "Symmetry2",
+        { ZERO, PIN, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, PIN, LEFT }
+    },
+    { "TableFireworks",
+        { ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, ZERO, RIGHT, PIN }
+    },
+    { "Tapering",
+        { ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, PIN, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO }
+    },
+    { "TaperingTurned",
+        { ZERO, ZERO, RIGHT, LEFT, PIN, LEFT, ZERO, PIN, PIN, ZERO, LEFT, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, ZERO, ZERO }
+    },
+    { "TeaLightStick",
+        { RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN }
+    },
+    { "Tent",
+        { RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, LEFT, RIGHT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO }
+    },
+    { "Terraces",
+        { RIGHT, LEFT, ZERO, RIGHT, LEFT, PIN, LEFT, LEFT, PIN, LEFT, RIGHT, RIGHT, RIGHT, LEFT, LEFT, LEFT, RIGHT, PIN, RIGHT, RIGHT, PIN, RIGHT, LEFT }
+    },
+    { "Terrier",
+        { PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO }
+    },
+    { "Three-Legged",
+        { RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT, PIN, RIGHT, ZERO, RIGHT, ZERO, PIN, ZERO, LEFT, ZERO, LEFT, PIN, RIGHT, ZERO, LEFT, RIGHT, ZERO, LEFT }
+    },
+    { "ThreePeaks",
+        { RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT }
+    },
+    { "ToTheFront",
+        { ZERO, PIN, RIGHT, LEFT, LEFT, LEFT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, LEFT, LEFT, PIN, ZERO, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT }
+    },
+    { "Top",
+        { PIN, LEFT, LEFT, PIN, LEFT, ZERO, ZERO, RIGHT, LEFT, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, ZERO }
+    },
+    { "Transport",
+        { PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, ZERO, ZERO }
+    },
+    { "Triangle",
+        { ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT, LEFT, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, RIGHT }
+    },
+    { "Tripple",
+        { PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, PIN, LEFT, PIN, LEFT, PIN, RIGHT, PIN }
+    },
+    { "Turtle",
+        { RIGHT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, PIN, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, ZERO, LEFT, RIGHT, ZERO }
+    },
+    { "Twins",
+        { ZERO, PIN, ZERO, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, PIN, ZERO, ZERO, PIN, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, ZERO, PIN, ZERO, ZERO }
+    },
+    { "TwoSlants",
+        { ZERO, PIN, ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO, ZERO, RIGHT, PIN }
+    },
+    { "TwoWings",
+        { PIN, LEFT, ZERO, RIGHT, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, PIN, PIN, ZERO, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, LEFT, ZERO }
+    },
+    { "UFO",
+        { LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, LEFT, PIN, LEFT, LEFT, LEFT, RIGHT, LEFT, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT }
+    },
+    { "USSEnterprice",
+        { LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, ZERO, PIN, PIN, ZERO, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, ZERO }
+    },
+    { "UpAndDown",
+        { ZERO, PIN, ZERO, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO }
+    },
+    { "Upright",
+        { ZERO, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, PIN, ZERO, ZERO, LEFT, PIN, RIGHT, ZERO, ZERO, PIN, RIGHT, RIGHT, LEFT, RIGHT, LEFT, LEFT, ZERO }
+    },
+    { "Upside-down",
+        { PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, RIGHT, RIGHT, LEFT, LEFT, PIN, RIGHT, RIGHT, LEFT, LEFT, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN }
+    },
+    { "Valley",
+        { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, RIGHT, ZERO, PIN, ZERO, LEFT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, PIN, LEFT, ZERO }
+    },
+    { "Viaduct",
+        { PIN, RIGHT, PIN, LEFT, PIN, ZERO, ZERO, PIN, RIGHT, ZERO, RIGHT, RIGHT, ZERO, RIGHT, PIN, ZERO, ZERO, PIN, LEFT, PIN, RIGHT, PIN, ZERO }
+    },
+    { "View",
+        { ZERO, RIGHT, PIN, LEFT, PIN, RIGHT, ZERO, ZERO, RIGHT, PIN, LEFT, LEFT, RIGHT, RIGHT, PIN, LEFT, ZERO, ZERO, LEFT, PIN, RIGHT, PIN, LEFT }
+    },
+    { "Waterfall",
+        { LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT, PIN, LEFT, ZERO, RIGHT }
+    },
+    { "WindWheel",
+        { PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, ZERO }
+    },
+    { "Window",
+        { PIN, ZERO, PIN, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, ZERO, PIN, PIN, ZERO, ZERO, ZERO, ZERO }
+    },
+    { "WindowToTheWorld",
+        { PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO, RIGHT, PIN, LEFT, ZERO, PIN, ZERO, ZERO, PIN, ZERO, ZERO, PIN, ZERO }
+    },
+    { "Windshield",
+        { PIN, PIN, ZERO, RIGHT, PIN, LEFT, LEFT, PIN, RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, RIGHT, PIN, LEFT, ZERO, PIN, PIN, ZERO, PIN }
+    },
+    { "WingNut",
+        { ZERO, ZERO, ZERO, ZERO, PIN, RIGHT, RIGHT, RIGHT, PIN, RIGHT, LEFT, PIN, LEFT, RIGHT, PIN, RIGHT, RIGHT, RIGHT, PIN, ZERO, ZERO, ZERO, ZERO }
+    },
+    { "Wings2",
+        { RIGHT, ZERO, PIN, ZERO, LEFT, PIN, RIGHT, PIN, RIGHT, LEFT, RIGHT, RIGHT, LEFT, LEFT, RIGHT, LEFT, PIN, LEFT, PIN, RIGHT, ZERO, PIN, ZERO }
+    },
+    { "WithoutName",
+        { PIN, RIGHT, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN, ZERO, PIN, RIGHT, PIN, LEFT, PIN, ZERO, PIN, RIGHT, RIGHT, PIN, LEFT, LEFT, PIN }
+    },
+    { "Wolf",
+        { ZERO, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, PIN, ZERO, PIN, PIN, ZERO, PIN, ZERO, ZERO, ZERO, PIN, PIN, ZERO, ZERO, ZERO, PIN }
+    },
+    { "X",
+        { LEFT, ZERO, ZERO, PIN, LEFT, RIGHT, RIGHT, PIN, LEFT, RIGHT, ZERO, PIN, PIN, ZERO, LEFT, RIGHT, PIN, LEFT, LEFT, RIGHT, PIN, ZERO, ZERO }
+    },
 };
 
-/* add a model to the model list */
-model_t * add_model(model_t * models, char * name, int * rotations, int * count)
-{
-  int i;
-  
-  (*count)++;
-  models = realloc(models, sizeof(model_t) * (*count));
-  models[(*count)-1].name = strdup(name);
-#ifdef DEBUG
-  fprintf(stderr, "resized models to %d bytes for model %s\n", sizeof(model_t) * (*count), models[(*count)-1].name);
-#endif
-  for (i = 0; i < 24; i++) {
-    models[(*count)-1].node[i] = rotations[i] * 90.0;
-  }
-  return models;
-}
+int models = (sizeof(model) / sizeof(struct model_s));
 
-/* filename is the name of the file to load
- * models is the pointer to where the models will be kept
- * returns a new pointer to models
- * count is number of models read
- */
-model_t * load_modelfile(char * filename, model_t * models, int * count)
-{
-  int c;
-  FILE * f;
-  char buffy[256];
-  int rotations[24];
-  int name = 1;
-  int rots = 0;
-
-  f = fopen(filename, "r");
-  if (f == NULL) {
-    int error_msg_len = strlen(filename) + 33 + 1;
-    char * error_msg = (char *) malloc(sizeof(char) * error_msg_len);
-    sprintf(error_msg, "Unable to open model data file \"%s\"", filename);
-    perror(error_msg);
-    free(error_msg);
-    return models;
-  }
-    
-  while ((c = getc(f)) != EOF) {
-    switch (c) {
-      /* ignore comments */
-      case '#':
-        while (c != '\n')
-          c = getc(f);
-        break;
-      case ':':
-        buffy[name-1] = '\0';
-        name = 0;
-        break;
-      case '\n':
-        if (rots > 0) {
-#ifdef DEBUG
-          /* print out the model we just read in */
-          int i;
-          printf("%s: ", buffy);
-          for (i = 0; i < rots; i++) {
-            switch (rotations[i]) {
-              case LEFT:
-                printf("L");
-                break;
-              case RIGHT:
-                printf("R");
-                break;
-              case PIN:
-                printf("P");
-                break;
-              case ZERO:
-                printf("Z");
-                break;
-            }
-          }
-          printf("\n");
-#endif
-          models = add_model(models, buffy, rotations, count);
-        }
-        name = 1;
-        rots = 0;
-        break;
-      default:
-        if (name) {
-          buffy[name-1] = c;
-          name++;
-          if (name > 255)
-            fprintf(stderr, "buffy overflow warning\n");
-        } else {
-          switch (c) {
-            case '0':
-            case 'Z':
-              rotations[rots] = ZERO;
-              rots++;
-              break;
-            case '1':
-            case 'L':
-              rotations[rots] = LEFT;
-              rots++;
-              break;
-            case '2':
-            case 'P':
-              rotations[rots] = PIN;
-              rots++;
-              break;
-            case '3':
-            case 'R':
-              rotations[rots] = RIGHT;
-              rots++;
-              break;
-            default:
-              break;
-          }
-        }
-        break;
-    }
-  }
-  return models;
-}
+#define VOFFSET 0.045
 
-model_t * load_models(char * dirpath, model_t * models, int * count)
-{
-  char name[1024];
-  struct dirent * dp;
-  DIR * dfd;
-
-  if ((dfd = opendir(dirpath)) == NULL) {
-    if (strstr(dirpath, "data") == NULL)
-/*      fprintf(stderr, "load_models: can't read %s/\n", dirpath); */
-    return models;
-  }
-  while ((dp = readdir(dfd)) != NULL) {
-    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
-      continue;
-    if (strlen(dirpath) + strlen(dp->d_name) + 2 > sizeof(name))
-      fprintf(stderr, "load_models: name %s/%s too long\n", dirpath, dp->d_name);
-    else {
-      sprintf(name, "%s/%s", dirpath, dp->d_name);
-      if (strcmp(&name[(int) strlen(name) - 7], "glsnake") == 0) {
-#ifdef DEBUG
-        fprintf(stderr, "load_models: opening %s\n", name); 
-#endif
-        models = load_modelfile(name, models, count);
-      }
-    }
-  }
-  closedir(dfd);
-  return models;
-}
+#define X_MASK 1
+#define Y_MASK 2
+#define Z_MASK 4
 
-/* snake metrics */
-#define X_MASK 1
-#define Y_MASK 2
-#define Z_MASK 4
-#define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 ))
+/* the connecting string that holds the snake together */
+#define MAGICAL_RED_STRING 0
 
-int cross_product(int src_dir, int dest_dir)
-{
-  return X_MASK * (GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, Z_MASK) -
-      GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, Y_MASK)) +
-    Y_MASK * (GETSCALAR(src_dir, Z_MASK) * GETSCALAR(dest_dir, X_MASK) -
-      GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Z_MASK)) +
-    Z_MASK * (GETSCALAR(src_dir, X_MASK) * GETSCALAR(dest_dir, Y_MASK) -
-      GETSCALAR(src_dir, Y_MASK) * GETSCALAR(dest_dir, X_MASK));
-}
+#define GETSCALAR(vec,mask) ((vec)==(mask) ? 1 : ((vec)==-(mask) ? -1 : 0 ))
 
-void calc_snake_metrics(glsnake_configuration * bp)
-{
-  int src_dir, dest_dir;
-  int i, x, y, z;
-  int prev_src_dir = -Y_MASK;
-  int prev_dest_dir = Z_MASK;
-  int grid[25][25][25];
-
-  /* zero the grid */
-  memset(&grid, 0, sizeof(int) * 25*25*25);
-
-  bp->is_legal = 1;
-  x = y = z = 12;
-
-  /* trace path of snake and keep record for is_legal */
-  for (i = 0; i < 23; i++) {
-    /* establish new state variables */
-    src_dir = -prev_dest_dir;
-    x += GETSCALAR(prev_dest_dir, X_MASK);
-    y += GETSCALAR(prev_dest_dir, Y_MASK);
-    z += GETSCALAR(prev_dest_dir, Z_MASK);
-
-    switch ((int) (bp->node[i].dest_angle / 90.0)) {
-      case ZERO:
-        dest_dir = -prev_src_dir;
-        break;
-      case PIN:
-        dest_dir = prev_src_dir;
-        break;
-      case RIGHT:
-      case LEFT:
-        dest_dir = cross_product(prev_src_dir, prev_dest_dir);
-        if (bp->node[i].dest_angle == (int) (RIGHT * 90.0))
-          dest_dir = -dest_dir;
-        break;
-      default:
-        /* prevent spurious "might be used uninitialised" warnings */
-        dest_dir = 0;
-        break;
-    }
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
 
-    if (grid[x][y][z] == 0)
-      grid[x][y][z] = src_dir + dest_dir;
-    else if (grid[x][y][z] + src_dir + dest_dir == 0)
-      grid[x][y][z] = 8;
-    else
-      bp->is_legal = 0;
-
-    prev_src_dir = src_dir;
-    prev_dest_dir = dest_dir;
-  }
-
-  /* determine if the snake is cyclic */
-  bp->is_cyclic = (dest_dir == Y_MASK && x == 12 && y == 11 && x == 12);
-
-  /* determine last turn */
-  bp->last_turn = -1;
-  if (bp->is_cyclic) {
-    switch (src_dir) {
-      case -Z_MASK:
-        bp->last_turn = ZERO * 90.0;
-        break;
-      case Z_MASK:
-        bp->last_turn = PIN * 90.0;
-        break;
-      case X_MASK:
-        bp->last_turn = LEFT * 90.0;
-        break;
-      case -X_MASK:
-        bp->last_turn = RIGHT * 90.0;
-        break;
-    }
-  }
-}
+#define RAND(n) ((random() & 0x7fffffff) % ((long) (n)))
+#define RANDSIGN() ((random() & 1) ? 1 : -1)
 
-void set_colours(glsnake_configuration * bp, int immediate)
-{
-  /* set target colour */
-  if (!bp->is_legal) {
-    bp->colour_t[0] = 0.5;
-    bp->colour_t[1] = 0.5;
-    bp->colour_t[2] = 0.5;
-  } else if (bp->is_cyclic) {
-    bp->colour_t[0] = 0.4;
-    bp->colour_t[1] = 0.8;
-    bp->colour_t[2] = 0.2;
-  } else {
-    bp->colour_t[0] = 0.3;
-    bp->colour_t[1] = 0.1;
-    bp->colour_t[2] = 0.9;
-  }
-  if (immediate) {
-    bp->colour_i[0] = bp->colour_t[0] - bp->colour[0];
-    bp->colour_i[1] = bp->colour_t[1] - bp->colour[1];
-    bp->colour_i[2] = bp->colour_t[2] - bp->colour[2];
-  } else {
-    /* instead of 50.0, I should actually work out how many times this gets
-     * called during a morph */
-    bp->colour_i[0] = (bp->colour_t[0] - bp->colour[0]) / 50.0;
-    bp->colour_i[1] = (bp->colour_t[1] - bp->colour[1]) / 50.0;
-    bp->colour_i[2] = (bp->colour_t[2] - bp->colour[2]) / 50.0;
-  }
-}
+/* the triangular prism what makes up the basic unit */
+float solid_prism_v[][3] = {
+    /* first corner, bottom left front */
+    { VOFFSET, VOFFSET, 1.0 },
+    { VOFFSET, 0.00, 1.0 - VOFFSET },
+    { 0.00, VOFFSET, 1.0 - VOFFSET },
+    /* second corner, rear */
+    { VOFFSET, VOFFSET, 0.00 },
+    { VOFFSET, 0.00, VOFFSET },
+    { 0.00, VOFFSET, VOFFSET },
+    /* third, right front */
+    { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 1.0 },
+    { 1.0 - VOFFSET / M_SQRT1_2, 0.0, 1.0 - VOFFSET },
+    { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, 1.0 - VOFFSET },
+    /* fourth, right rear */
+    { 1.0 - VOFFSET / M_SQRT1_2, VOFFSET, 0.0 },
+    { 1.0 - VOFFSET / M_SQRT1_2, 0.0, VOFFSET },
+    { 1.0 - VOFFSET * M_SQRT1_2, VOFFSET, VOFFSET },
+    /* fifth, upper front */
+    { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 1.0 },
+    { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, 1.0 - VOFFSET },
+    { 0.0, 1.0 - VOFFSET / M_SQRT1_2, 1.0 - VOFFSET},
+    /* sixth, upper rear */
+    { VOFFSET, 1.0 - VOFFSET / M_SQRT1_2, 0.0 },
+    { VOFFSET / M_SQRT1_2, 1.0 - VOFFSET * M_SQRT1_2, VOFFSET },
+    { 0.0, 1.0 - VOFFSET / M_SQRT1_2, VOFFSET }};
+
+float solid_prism_n[][3] = {/* corners */
+    { -VOFFSET, -VOFFSET, VOFFSET },
+    { VOFFSET, -VOFFSET, VOFFSET },
+    { -VOFFSET, VOFFSET, VOFFSET },
+    { -VOFFSET, -VOFFSET, -VOFFSET },
+    { VOFFSET, -VOFFSET, -VOFFSET },
+    { -VOFFSET, VOFFSET, -VOFFSET },
+    /* edges */
+    { -VOFFSET, 0.0, VOFFSET },
+    { 0.0, -VOFFSET, VOFFSET },
+    { VOFFSET, VOFFSET, VOFFSET },
+    { -VOFFSET, 0.0, -VOFFSET },
+    { 0.0, -VOFFSET, -VOFFSET },
+    { VOFFSET, VOFFSET, -VOFFSET },
+    { -VOFFSET, -VOFFSET, 0.0 },
+    { VOFFSET, -VOFFSET, 0.0 },
+    { -VOFFSET, VOFFSET, 0.0 },
+    /* faces */
+    { 0.0, 0.0, 1.0 },
+    { 0.0, -1.0, 0.0 },
+    { M_SQRT1_2, M_SQRT1_2, 0.0 },
+    { -1.0, 0.0, 0.0 },
+    { 0.0, 0.0, -1.0 }};
+
+float wire_prism_v[][3] = {{ 0.0, 0.0, 1.0 },
+                          { 1.0, 0.0, 1.0 },
+                          { 0.0, 1.0, 1.0 },
+                          { 0.0, 0.0, 0.0 },
+                          { 1.0, 0.0, 0.0 },
+                          { 0.0, 1.0, 0.0 }};
+
+float wire_prism_n[][3] = {{ 0.0, 0.0, 1.0},
+                          { 0.0,-1.0, 0.0},
+                          { M_SQRT1_2, M_SQRT1_2, 0.0},
+                          {-1.0, 0.0, 0.0},
+                          { 0.0, 0.0,-1.0}};
+
+static struct glsnake_cfg * glc = NULL;
+#ifdef HAVE_GLUT
+# define bp glc
+#endif
 
-void start_morph(int model_index, int immediate, glsnake_configuration * bp)
-{
-  int i;
-
-  for (i = 0; i < 23; i++) {
-    bp->node[i].dest_angle = bp->models[model_index].node[i];
-    if (immediate)
-      bp->node[i].cur_angle = bp->models[model_index].node[i];
-  }
-  
-  calc_snake_metrics(bp);
-  set_colours(bp, 0);
-  bp->cur_model = model_index;
-  bp->morphing = 1;
-}
+typedef float (*morphFunc)(long);
 
-/* convex hull */
+#ifdef HAVE_GLUT
+/* forward definitions for GLUT functions */
+void calc_rotation();
+inline void ui_mousedrag();
+#endif
 
-/* model labels */
-void draw_label(ModeInfo * mi)
-{
-  glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
-  
-  glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT);
-  glDisable(GL_LIGHTING);
-  glDisable(GL_DEPTH_TEST);
-  glMatrixMode(GL_PROJECTION);
-  glPushMatrix();
-  glLoadIdentity();
-  glMatrixMode(GL_MODELVIEW);
-  glPushMatrix();
-  glLoadIdentity();
-  gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
-  glColor3f(1.0, 1.0, 0.0);
-  {
-    char * s;
-    int i, /* w, */ l;
-
-    if (bp->interactive)
-      s = "interactive";
-    else
-      s = bp->models[bp->cur_model].name;
-
-    l = strlen(s);
-    /*
-    w = 0;
-    for (i = 0; i < l; i++) {
-      w += (bp->font->per_char 
-          ? bp->font->per_char[((int)s[i]) - bp->font->min_char_or_byte2].rbearing 
-          : bp->font->min_bounds.rbearing);
+void gl_init(
+#ifndef HAVE_GLUT
+            ModeInfo * mi
+#endif
+            ) {
+    float light_pos[][3] = {{0.0, 0.0, 20.0}, {0.0, 20.0, 0.0}};
+    float light_dir[][3] = {{0.0, 0.0,-20.0}, {0.0,-20.0, 0.0}};
+
+    glClearColor(0.0, 0.0, 0.0, 0.0);
+    glEnable(GL_DEPTH_TEST);
+    glShadeModel(GL_SMOOTH);
+    glCullFace(GL_BACK);
+    glEnable(GL_CULL_FACE);
+    glEnable(GL_NORMALIZE);
+
+    if (!wireframe) {
+       glColor3f(1.0, 1.0, 1.0);
+       glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]);
+       glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]);
+       glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]);
+       glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]);
+       glEnable(GL_LIGHTING);
+       glEnable(GL_LIGHT0);
+       glEnable(GL_LIGHT1);
+       glEnable(GL_COLOR_MATERIAL);
     }
-    */
-    
-    glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent));
-        /* mi->xgwa.width - w, bp->font->descent + bp->font->ascent); */
-
-    /* fprintf(stderr, "afaf.width = %d, w = %d\n", mi->xgwa.width, w); */
-    
-    for (i = 0; i < l; i++)
-      glCallList(bp->font_list + (int)s[i]);
-  }
-  glPopMatrix();
-  glMatrixMode(GL_PROJECTION);
-  glPopMatrix();
-  glPopAttrib();
 }
 
-/* load the fonts -- this function borrowed from molecule.c */
-static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp)
+void gettime(snaketime *t)
 {
-  const char * font = get_string_resource(res, "Font");
-  XFontStruct * f;
-  Font id;
-  int first, last;
-
-  if (!font)
-    font = "-*-helvetica-medium-r-*-*-*-120-*";
-
-  f = XLoadQueryFont(mi->dpy, font);
-  if (!f)
-    f = XLoadQueryFont(mi->dpy, "fixed");
-
-  id = f->fid;
-  first = f->min_char_or_byte2;
-  last = f->max_char_or_byte2;
-  
-  clear_gl_error();
-  *dlistp = glGenLists((GLuint) last + 1);
-  check_gl_error("glGenLists");
-  glXUseXFont(id, first, last - first + 1, *dlistp + first);
-  check_gl_error("glXUseXFont");
-
-  *fontp = f;
+#ifdef HAVE_GETTIMEOFDAY
+#ifdef GETTIMEOFDAY_TWO_ARGS
+       struct timezone tzp;
+       gettimeofday(t, &tzp);
+#else /* !GETTIMEOFDAY_TWO_ARGS */
+       gettimeofday(t);
+#endif /* !GETTIMEOFDAY_TWO_ARGS */
+#else /* !HAVE_GETTIMEOFDAY */
+#ifdef HAVE_FTIME
+       ftime(t);
+#endif /* HAVE_FTIME */
+#endif /* !HAVE_GETTIMEOFDAY */
 }
 
+#ifndef HAVE_GLUT
+static void load_font(ModeInfo * mi, char * res, XFontStruct ** fontp, GLuint * dlistp) {
+    const char * font = get_string_resource(res, "Font");
+    XFontStruct * f;
+    Font id;
+    int first, last;
 
+    if (!font)
+       font = "-*-helvetica-medium-r-*-*-*-120-*";
 
-/* window management */
-void glsnake_reshape(ModeInfo *mi, int w, int h)
-{
-  glViewport (0, 0, (GLint) w, (GLint) h);
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-  gluPerspective(25.0, w/(GLfloat)h, 1.0, 100.0 );
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
-}
+    f = XLoadQueryFont(mi->dpy, font);
+    if (!f)
+       f = XLoadQueryFont(mi->dpy, "fixed");
 
-static void gl_init(ModeInfo *mi)
-{
-  /* glsnake_configuration *bp = &glc[MI_SCREEN(mi)]; */
-  int wire = MI_IS_WIREFRAME(mi);
-  float light_pos[][3] = {{0.0,0.0,20.0},{0.0,20.0,0.0}};
-  float light_dir[][3] = {{0.0,0.0,-20.0},{0.0,-20.0,0.0}};
-
-  glClearColor(0.0, 0.0, 0.0, 0.0);
-  glEnable(GL_DEPTH_TEST);
-  glShadeModel(GL_SMOOTH);
-  glCullFace(GL_BACK);
-  glEnable(GL_CULL_FACE);
-  glEnable(GL_NORMALIZE);
-
-  if (!wire) {
-    glColor3f(1.0, 1.0, 1.0);
-    glLightfv(GL_LIGHT0, GL_POSITION, light_pos[0]);
-    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light_dir[0]);
-    glLightfv(GL_LIGHT1, GL_POSITION, light_pos[1]);
-    glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light_dir[1]);
-    glEnable(GL_LIGHTING);
-    glEnable(GL_LIGHT0);
-    glEnable(GL_LIGHT1);
-    glEnable(GL_COLOR_MATERIAL);
-  }
+    id = f->fid;
+    first = f->min_char_or_byte2;
+    last = f->max_char_or_byte2;
+
+    clear_gl_error();
+    *dlistp = glGenLists((GLuint) last + 1);
+    check_gl_error("glGenLists");
+    glXUseXFont(id, first, last - first + 1, *dlistp + first);
+    check_gl_error("glXUseXFont");
+
+    *fontp = f;
 }
+#endif
 
-/* lifted from lament.c */
-#define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
-#define RANDSIGN() ((random() & 1) ? 1 : -1)
+void start_morph(int model_index, int immediate);
 
-void glsnake_init(ModeInfo *mi)
-{
-  glsnake_configuration * bp;
-  int wire = MI_IS_WIREFRAME(mi);
+/* wot initialises it */
+void glsnake_init(
+#ifndef HAVE_GLUT
+ModeInfo * mi
+#endif
+) {
+#ifndef HAVE_GLUT
+    struct glsnake_cfg * bp;
 
-  if (!glc) {
-    glc = (glsnake_configuration *) calloc(MI_NUM_SCREENS(mi), sizeof(glsnake_configuration));
+    /* set up the conf struct and glx contexts */
     if (!glc) {
-      fprintf(stderr, "%s: out of memory\n", progname);
-      exit(1);
+       glc = (struct glsnake_cfg *) calloc(MI_NUM_SCREENS(mi), sizeof(struct glsnake_cfg));
+       if (!glc) {
+           fprintf(stderr, "%s: out of memory\n", progname);
+           exit(1);
+       }
     }
     bp = &glc[MI_SCREEN(mi)];
-  }
-
-  bp = &glc[MI_SCREEN(mi)];
-
-  if ((bp->glx_context = init_GL(mi)) != NULL) {
-    gl_init(mi);
-    glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
-  }
-
-  /* initialise config variables */
-  memset(&bp->node, 0, sizeof(nodeang_t) * 24);
-  bp->m_count = sizeof(default_models) / sizeof(model_t); /* overwrite this in a bit */
-  bp->selected = 11;
-  bp->is_cyclic = 0;
-  bp->is_legal = 1;
-  bp->last_turn = -1;
-  bp->roty = 0.0;
-  bp->rotz = 0.0;
-  bp->morphing = 0;
-  bp->paused = 0;
-  bp->dragging = 0;
-  bp->interactive = 0;
-
-        {
-# ifdef GETTIMEOFDAY_TWO_ARGS
-          struct timezone tzp;
-          gettimeofday(&bp->last_iteration, &tzp);
-# else
-          gettimeofday(&bp->last_iteration);
-# endif
-        }
-
-  memcpy(&bp->last_morph, &(bp->last_iteration),
-               sizeof(bp->last_morph));
-  /* srand((unsigned int) bp->last_iteration.time); */
-
-  /* load the model files */
-  /* first copy the defaults to bp->m_count */
-  bp->models = (model_t *) malloc(sizeof(model_t) * bp->m_count);
-  memcpy(bp->models, default_models, bp->m_count * sizeof(model_t));
-  /* then add on models from the Debian model file location */
-  bp->models = load_models("/usr/share/glsnake", bp->models, &(bp->m_count));
-
-  bp->m = bp->cur_model = RAND(bp->m_count);
-  start_morph(bp->cur_model, 1, bp);
-
-  calc_snake_metrics(bp);
-  set_colours(bp, 1);
-
-  /* set up a font for the labels */
-  if (labels)
-    load_font(mi, "labelfont", &bp->font, &bp->font_list);
-  
-  bp->node_list = glGenLists(1);
-  glNewList(bp->node_list, GL_COMPILE);
-  if (!wire) {
+
+    if ((bp->glx_context = init_GL(mi)) != NULL) {
+       gl_init(mi);
+       glsnake_reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+    }
+#else
+    gl_init();
+#endif
+
+    /* initialise conf struct */
+    memset(&bp->node, 0, sizeof(float) * NODE_COUNT);
+
+    bp->selected = 11;
+    bp->is_cyclic = 0;
+    bp->is_legal = 1;
+    bp->last_turn = -1;
+    bp->morphing = 0;
+    bp->paused = 0;
+    bp->new_morph = 0;
+
+    gettime(&bp->last_iteration);
+    memcpy(&bp->last_morph, &bp->last_iteration, sizeof(bp->last_morph));
+
+    bp->prev_colour = bp->next_colour = COLOUR_ACYCLIC;
+    bp->next_model = RAND(models);
+    bp->prev_model = START_MODEL;
+    start_morph(bp->prev_model, 1);
+
+    /* set up a font for the labels */
+#ifndef HAVE_GLUT
+    if (titles)
+       load_font(mi, "labelfont", &bp->font, &bp->font_list);
+#endif
+    
+    /* build a solid display list */
+    glc->node_solid = glGenLists(1);
+    glNewList(glc->node_solid, GL_COMPILE);
     /* corners */
     glBegin(GL_TRIANGLES);
     glNormal3fv(solid_prism_n[0]);
@@ -757,7 +1497,7 @@ void glsnake_init(ModeInfo *mi)
     glVertex3fv(solid_prism_v[6]);
     glVertex3fv(solid_prism_v[7]);
     glVertex3fv(solid_prism_v[8]);
-
+    
     glNormal3fv(solid_prism_n[2]);
     glVertex3fv(solid_prism_v[12]);
     glVertex3fv(solid_prism_v[13]);
@@ -767,18 +1507,17 @@ void glsnake_init(ModeInfo *mi)
     glVertex3fv(solid_prism_v[3]);
     glVertex3fv(solid_prism_v[4]);
     glVertex3fv(solid_prism_v[5]);
-  
+    
     glNormal3fv(solid_prism_n[4]);
     glVertex3fv(solid_prism_v[9]);
     glVertex3fv(solid_prism_v[11]);
     glVertex3fv(solid_prism_v[10]);
-
+    
     glNormal3fv(solid_prism_n[5]);
     glVertex3fv(solid_prism_v[16]);
     glVertex3fv(solid_prism_v[15]);
     glVertex3fv(solid_prism_v[17]);
     glEnd();
-
     /* edges */
     glBegin(GL_QUADS);
     glNormal3fv(solid_prism_n[6]);
@@ -786,90 +1525,93 @@ void glsnake_init(ModeInfo *mi)
     glVertex3fv(solid_prism_v[12]);
     glVertex3fv(solid_prism_v[14]);
     glVertex3fv(solid_prism_v[2]);
-  
+    
     glNormal3fv(solid_prism_n[7]);
     glVertex3fv(solid_prism_v[0]);
     glVertex3fv(solid_prism_v[1]);
     glVertex3fv(solid_prism_v[7]);
     glVertex3fv(solid_prism_v[6]);
-  
+    
     glNormal3fv(solid_prism_n[8]);
     glVertex3fv(solid_prism_v[6]);
     glVertex3fv(solid_prism_v[8]);
     glVertex3fv(solid_prism_v[13]);
     glVertex3fv(solid_prism_v[12]);
-  
+    
     glNormal3fv(solid_prism_n[9]);
     glVertex3fv(solid_prism_v[3]);
     glVertex3fv(solid_prism_v[5]);
     glVertex3fv(solid_prism_v[17]);
     glVertex3fv(solid_prism_v[15]);
-  
+    
     glNormal3fv(solid_prism_n[10]);
     glVertex3fv(solid_prism_v[3]);
     glVertex3fv(solid_prism_v[9]);
     glVertex3fv(solid_prism_v[10]);
     glVertex3fv(solid_prism_v[4]);
-  
+    
     glNormal3fv(solid_prism_n[11]);
     glVertex3fv(solid_prism_v[15]);
     glVertex3fv(solid_prism_v[16]);
     glVertex3fv(solid_prism_v[11]);
     glVertex3fv(solid_prism_v[9]);
-  
+    
     glNormal3fv(solid_prism_n[12]);
     glVertex3fv(solid_prism_v[1]);
     glVertex3fv(solid_prism_v[2]);
     glVertex3fv(solid_prism_v[5]);
     glVertex3fv(solid_prism_v[4]);
-  
+    
     glNormal3fv(solid_prism_n[13]);
     glVertex3fv(solid_prism_v[8]);
     glVertex3fv(solid_prism_v[7]);
     glVertex3fv(solid_prism_v[10]);
     glVertex3fv(solid_prism_v[11]);
-  
+    
     glNormal3fv(solid_prism_n[14]);
     glVertex3fv(solid_prism_v[13]);
     glVertex3fv(solid_prism_v[16]);
     glVertex3fv(solid_prism_v[17]);
     glVertex3fv(solid_prism_v[14]);
     glEnd();
-  
+    
     /* faces */
     glBegin(GL_TRIANGLES);
     glNormal3fv(solid_prism_n[15]);
     glVertex3fv(solid_prism_v[0]);
     glVertex3fv(solid_prism_v[6]);
     glVertex3fv(solid_prism_v[12]);
-  
+    
     glNormal3fv(solid_prism_n[19]);
     glVertex3fv(solid_prism_v[3]);
     glVertex3fv(solid_prism_v[15]);
     glVertex3fv(solid_prism_v[9]);
     glEnd();
-  
+    
     glBegin(GL_QUADS);
     glNormal3fv(solid_prism_n[16]);
     glVertex3fv(solid_prism_v[1]);
     glVertex3fv(solid_prism_v[4]);
     glVertex3fv(solid_prism_v[10]);
     glVertex3fv(solid_prism_v[7]);
-  
+    
     glNormal3fv(solid_prism_n[17]);
     glVertex3fv(solid_prism_v[8]);
     glVertex3fv(solid_prism_v[11]);
     glVertex3fv(solid_prism_v[16]);
     glVertex3fv(solid_prism_v[13]);
-  
+    
     glNormal3fv(solid_prism_n[18]);
     glVertex3fv(solid_prism_v[2]);
     glVertex3fv(solid_prism_v[14]);
     glVertex3fv(solid_prism_v[17]);
     glVertex3fv(solid_prism_v[5]);
     glEnd();
-  } else {
+    glEndList();
+    
     /* build wire display list */
+    glc->node_wire = glGenLists(1);
+    glNewList(glc->node_wire, GL_COMPILE);
     glBegin(GL_LINE_STRIP);
     glVertex3fv(wire_prism_v[0]);
     glVertex3fv(wire_prism_v[1]);
@@ -880,333 +1622,944 @@ void glsnake_init(ModeInfo *mi)
     glVertex3fv(wire_prism_v[5]);
     glVertex3fv(wire_prism_v[3]);
     glEnd();
-  
     glBegin(GL_LINES);
     glVertex3fv(wire_prism_v[1]);
     glVertex3fv(wire_prism_v[4]);
     glVertex3fv(wire_prism_v[2]);
     glVertex3fv(wire_prism_v[5]);
     glEnd();
-  }
-  glEndList();
+    glEndList();
+    
+#ifdef HAVE_GLUT
+    /* initialise the rotation */
+    calc_rotation();
+#endif
 }
 
-/* "jwz?  no way man, he's my idle" -- Jaq, 2001.
- * I forget the context :( */
-void glsnake_idol(glsnake_configuration * bp)
-{
-  /* time since last iteration */
-  long iter_msec;
-  /* time since the beginning of last morph */
-  long morf_msec;
-  float iter_angle_max;
-  int i;
-  struct timeval current_time;
-  int still_morphing;
-
-  /* Do nothing to the model if we are paused */
-  if (bp->paused) {
-    /* Avoid busy waiting when nothing is changing */
-    usleep(1);
-    return;
-  }
-
-        {
-# ifdef GETTIMEOFDAY_TWO_ARGS
-          struct timezone tzp;
-          gettimeofday(&current_time, &tzp);
-# else
-          gettimeofday(&current_time);
-# endif
-        }
-
-  /* <spiv> Well, ftime gives time with millisecond resolution.
-   * <Jaq> if current time is exactly equal to last iteration, 
-   *       then don't do this block
-   * <spiv> (or worse, perhaps... who knows what the OS will do)
-   * <spiv> So if no discernable amount of time has passed:
-   * <spiv>   a) There's no point updating the screen, because
-   *             it would be the same
-   * <spiv>   b) The code will divide by zero
-   */
-  iter_msec = ((long) current_time.tv_usec - bp->last_iteration.tv_usec)/1000L + 
-        ((long) current_time.tv_sec - bp->last_iteration.tv_sec) * 1000L;
-  if (iter_msec) {
-    /* save the current time */
-    memcpy(&bp->last_iteration, &current_time,
-                       sizeof(bp->last_iteration));
+void draw_title(
+#ifndef HAVE_GLUT
+               ModeInfo * mi
+#endif
+               ) {
+#ifndef HAVE_GLUT
+    struct glsnake_cfg * bp = &glc[MI_SCREEN(mi)];
+#endif
+
+    /* draw some text */
+    glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT);
+    glDisable(GL_LIGHTING);
+    glDisable(GL_DEPTH_TEST);
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+#ifdef HAVE_GLUT
+    gluOrtho2D(0, glc->width, 0, glc->height);
+#else
+    gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
+#endif
+    glColor3f(1.0, 1.0, 1.0);
+    {
+       char interactstr[] = "interactive";
+       char * s;
+       int i = 0;
+#ifdef HAVE_GLUT
+       int w;
+#endif
+       
+       if (interactive)
+           s = interactstr;
+       else
+           s = model[glc->next_model].name;
+#ifdef HAVE_GLUT
+       w = glutBitmapLength(GLUT_BITMAP_HELVETICA_12, (unsigned char *) s);
+       glRasterPos2f(glc->width - w - 3, 4);
+       while (s[i] != '\0')
+           glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, s[i++]);
+#else
+       glRasterPos2f(10, mi->xgwa.height - 10 - (bp->font->ascent + bp->font->descent));
+       while (s[i] != '\0')
+           glCallList(bp->font_list + (int)s[i++]);
+#endif
+    }
+    glPopMatrix();
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glPopAttrib();
+}
+
+/* apply the matrix to the origin and stick it in vec */
+void matmult_origin(float rotmat[16], float vec[4]) {
+#if 1
+    vec[0] = 0.5 * rotmat[0] + 0.5 * rotmat[4] + 0.5 * rotmat [8] + 1 * rotmat[12];
+    vec[1] = 0.5 * rotmat[1] + 0.5 * rotmat[5] + 0.5 * rotmat [9] + 1 * rotmat[13];
+    vec[2] = 0.5 * rotmat[2] + 0.5 * rotmat[6] + 0.5 * rotmat[10] + 1 * rotmat[14];
+    vec[3] = 0.5 * rotmat[3] + 0.5 * rotmat[7] + 0.5 * rotmat[11] + 1 * rotmat[15];
+#else
+    vec[0] = 0 * rotmat [0] + 0 * rotmat [1] + 0 * rotmat [2] + 1 * rotmat [3];
+    vec[1] = 0 * rotmat [4] + 0 * rotmat [5] + 0 * rotmat [6] + 1 * rotmat [7];
+    vec[2] = 0 * rotmat [8] + 0 * rotmat [9] + 0 * rotmat[10] + 1 * rotmat[11];
+    vec[3] = 0 * rotmat[12] + 0 * rotmat[13] + 0 * rotmat[14] + 1 * rotmat[15];
+#endif
+    vec[0] /= vec[3];
+    vec[1] /= vec[3];
+    vec[2] /= vec[3];
+    vec[3] = 1.0;
+}
+
+/* wot gets called when the winder is resized */
+void glsnake_reshape(
+#ifndef HAVE_GLUT
+                    ModeInfo * mi,
+#endif
+                    int w, int h) {
+    glViewport(0, 0, (GLint) w, (GLint) h);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    gluPerspective(zoom, w/(GLfloat)h, 0.05, 100.0);
+    gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
+    glMatrixMode(GL_MODELVIEW);
+    /*gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);*/
+    glLoadIdentity();
+#ifdef HAVE_GLUT
+    glc->width = w;
+    glc->height = h;
+#endif
+}
+
+/* Returns the new dst_dir for the given src_dir and dst_dir */
+int cross_product(int src_dir, int dst_dir) {
+    return X_MASK*(GETSCALAR(src_dir,Y_MASK) * GETSCALAR(dst_dir,Z_MASK) -
+                  GETSCALAR(src_dir,Z_MASK) * GETSCALAR(dst_dir,Y_MASK))+ 
+       Y_MASK*(GETSCALAR(src_dir,Z_MASK) * GETSCALAR(dst_dir,X_MASK) -
+               GETSCALAR(src_dir,X_MASK) * GETSCALAR(dst_dir,Z_MASK))+ 
+       Z_MASK*(GETSCALAR(src_dir,X_MASK) * GETSCALAR(dst_dir,Y_MASK) -
+               GETSCALAR(src_dir,Y_MASK) * GETSCALAR(dst_dir,X_MASK));
+}
+
+/* calculate orthogonal snake metrics
+ *  is_legal  = true if model does not pass through itself
+ *  is_cyclic = true if last node connects back to first node
+ *  last_turn = for cyclic snakes, specifes what the 24th turn would be
+ */
+void calc_snake_metrics(void) {
+    int srcDir, dstDir;
+    int i, x, y, z;
+    int prevSrcDir = -Y_MASK;
+    int prevDstDir = Z_MASK;
+    int grid[25][25][25];
     
-    /* work out if we have to switch models */
-    morf_msec = (bp->last_iteration.tv_usec - bp->last_morph.tv_usec)/1000L +
-      ((long) (bp->last_iteration.tv_sec - bp->last_morph.tv_sec) * 1000L);
-
-    if ((morf_msec > statictime) && !bp->interactive) {
-      memcpy(&bp->last_morph, &(bp->last_iteration),
-                               sizeof(bp->last_morph));
-      start_morph(RAND(bp->m_count), 0, bp);
+    /* zero the grid */
+    memset(&grid, 0, sizeof(int) * 25*25*25);
+    
+    glc->is_legal = 1;
+    x = y = z = 12;
+    
+    /* trace path of snake - and keep record for is_legal */
+    for (i = 0; i < NODE_COUNT - 1; i++) {
+       /*int ang_card;*/ /* cardinal direction of node angle */
+       /* establish new state vars */
+       srcDir = -prevDstDir;
+       x += GETSCALAR(prevDstDir, X_MASK);
+       y += GETSCALAR(prevDstDir, Y_MASK);
+       z += GETSCALAR(prevDstDir, Z_MASK);
+
+       switch ((int) model[glc->next_model].node[i]) {
+         case (int) (ZERO):
+           dstDir = -prevSrcDir;
+           break;
+         case (int) (PIN):
+           dstDir = prevSrcDir;
+           break;
+         case (int) (RIGHT):
+         case (int) (LEFT):
+           dstDir = cross_product(prevSrcDir, prevDstDir);
+           if (model[glc->next_model].node[i] == (int) (RIGHT))
+               dstDir = -dstDir;
+           break;
+         default:
+           /* Prevent spurious "might be used 
+            * uninitialised" warnings when compiling
+            * with -O2 */
+           dstDir = 0;
+           break;
+       }
+       
+       if (grid[x][y][z] == 0)
+           grid[x][y][z] = srcDir + dstDir;
+       else if (grid[x][y][z] + srcDir + dstDir == 0)
+           grid[x][y][z] = 8;
+       else
+           glc->is_legal = 0;
+       
+       prevSrcDir = srcDir;
+       prevDstDir = dstDir;
+    }  
+    
+    /* determine if the snake is cyclic */
+    glc->is_cyclic = (dstDir == Y_MASK && x == 12 && y == 11 && z == 12);
+    
+    /* determine last_turn */
+    glc->last_turn = -1;
+    if (glc->is_cyclic)
+       switch (srcDir) {
+         case -Z_MASK: glc->last_turn = ZERO; break;
+         case Z_MASK:  glc->last_turn = PIN; break;
+         case X_MASK:  glc->last_turn = LEFT; break;
+         case -X_MASK: glc->last_turn = RIGHT; break;
+       }
+}
+
+/* work out how far through the current morph we are */
+float morph_percent(void) {
+    float retval;
+    int i;
+
+    /* extend this function later with a case statement for each of the
+     * morph schemes */
+
+    /* when morphing all nodes at once, the longest morph will be the node
+     * that needs to rotate 180 degrees.  For each node, work out how far it
+     * has to go, and store the maximum rotation and current largest angular
+     * difference, returning the angular difference over the maximum. */
+    {
+       float rot_max = 0.0, ang_diff_max = 0.0;
+
+       for (i = 0; i < NODE_COUNT - 1; i++) {
+           float rot, ang_diff;
+
+           /* work out the maximum rotation this node has to go through
+            * from the previous to the next model, taking into account that
+            * the snake always morphs through the smaller angle */
+           rot = fabs(model[glc->prev_model].node[i] -
+                      model[glc->next_model].node[i]);
+           if (rot > 180.0) rot = 180.0 - rot;
+           /* work out the difference between the current position and the
+            * target */
+           ang_diff = fabs(glc->node[i] -
+                           model[glc->next_model].node[i]);
+           if (ang_diff > 180.0) ang_diff = 180 - ang_diff;
+           /* if it's the biggest so far, record it */
+           if (rot > rot_max) rot_max = rot;
+           if (ang_diff > ang_diff_max) ang_diff_max = ang_diff;
+       }
+       
+       /* ang_diff / rot approaches 0, we want the complement */
+       retval = 1.0 - (ang_diff_max / rot_max);
+       /* protect against naan */
+       if (isnan(retval) || isinf(retval)) retval = 1.0;
     }
+    /*printf("morph_pct = %f\n", retval);*/
+    return retval;
+}
+
+void morph_colour(void) {
+    float percent, compct; /* complement of percentage */
+
+    percent = morph_percent();
+    compct = 1.0 - percent;
+
+    glc->colour[0][0] = colour[glc->prev_colour][0][0] * compct + colour[glc->next_colour][0][0] * percent;
+    glc->colour[0][1] = colour[glc->prev_colour][0][1] * compct + colour[glc->next_colour][0][1] * percent;
+    glc->colour[0][2] = colour[glc->prev_colour][0][2] * compct + colour[glc->next_colour][0][2] * percent;
+
+    glc->colour[1][0] = colour[glc->prev_colour][1][0] * compct + colour[glc->next_colour][1][0] * percent;
+    glc->colour[1][1] = colour[glc->prev_colour][1][1] * compct + colour[glc->next_colour][1][1] * percent;
+    glc->colour[1][2] = colour[glc->prev_colour][1][2] * compct + colour[glc->next_colour][1][2] * percent;
+}
+
+/* Start morph process to this model */
+void start_morph(int model_index, int immediate) {
+    /* if immediate, don't bother morphing, go straight to the next model */
+    if (immediate) {
+       int i;
 
-    if (bp->interactive && !bp->morphing) {
-      usleep(1);
-      return;
+       for (i = 0; i < NODE_COUNT; i++)
+           glc->node[i] = model[model_index].node[i];
     }
 
-    if (!bp->dragging && !bp->interactive) {
-      bp->roty += 360/((1000/yspin)/iter_msec);
-      bp->rotz += 360/((1000/zspin)/iter_msec);
+    glc->prev_model = glc->next_model;
+    glc->next_model = model_index;
+    glc->prev_colour = glc->next_colour;
+
+    calc_snake_metrics();
+    if (!glc->is_legal)
+       glc->next_colour = COLOUR_INVALID;
+    else if (altcolour)
+       glc->next_colour = COLOUR_AUTHENTIC;
+    else if (glc->is_cyclic)
+       glc->next_colour = COLOUR_CYCLIC;
+    else
+       glc->next_colour = COLOUR_ACYCLIC;
+
+    if (immediate) {
+       glc->colour[0][0] = colour[glc->next_colour][0][0];
+       glc->colour[0][1] = colour[glc->next_colour][0][1];
+       glc->colour[0][2] = colour[glc->next_colour][0][2];
+       glc->colour[1][0] = colour[glc->next_colour][1][0];
+       glc->colour[1][1] = colour[glc->next_colour][1][1];
+       glc->colour[1][2] = colour[glc->next_colour][1][2];
     }
+    glc->morphing = 1;
 
-    /* work out the maximum angle for this iteration */
-    iter_angle_max = 90.0 * (velocity/1000.0) * iter_msec;
+    morph_colour();
+}
 
+/* Returns morph progress */
+float morph(long iter_msec) {
+    /* work out the maximum angle for this iteration */
+    int still_morphing;
+    float iter_angle_max, largest_diff, largest_progress;
+    int i;
+
+    if (glc->new_morph)
+       glc->new_morph = 0;
+       
+    iter_angle_max = 90.0 * (angvel/1000.0) * iter_msec;
+       
     still_morphing = 0;
-    for (i = 0; i < 24; i++) {
-      float cur_angle = bp->node[i].cur_angle;
-      float dest_angle = bp->node[i].dest_angle;
-      if (cur_angle != dest_angle) {
-        still_morphing = 1;
-        if (fabs(cur_angle - dest_angle) <= iter_angle_max)
-          bp->node[i].cur_angle = dest_angle;
-        else if (fmod(cur_angle - dest_angle + 360, 360) > 180)
-          bp->node[i].cur_angle = fmod(cur_angle + iter_angle_max, 360);
-        else
-          bp->node[i].cur_angle = fmod(cur_angle + 360 - iter_angle_max, 360);
-      }
+    largest_diff = largest_progress = 0.0;
+    for (i = 0; i < NODE_COUNT; i++) {
+       float curAngle = glc->node[i];
+       float destAngle = model[glc->next_model].node[i];
+       if (curAngle != destAngle) {
+           still_morphing = 1;
+           if (fabs(curAngle-destAngle) <= iter_angle_max)
+               glc->node[i] = destAngle;
+           else if (fmod(curAngle-destAngle+360,360) > 180)
+               glc->node[i] = fmod(curAngle + iter_angle_max, 360);
+           else
+               glc->node[i] = fmod(curAngle+360 - iter_angle_max, 360);
+           largest_diff = MAX(largest_diff, fabs(destAngle-glc->node[i]));
+           largest_progress = MAX(largest_diff, fabs(glc->node[i] - model[glc->prev_model].node[i]));
+       }
     }
+       
+    return MIN(largest_diff / largest_progress, 1.0);
+}
 
-    if (!still_morphing)
-      bp->morphing = 0;
+#ifdef HAVE_GLUT
+void glsnake_idle();
 
-    /* colour cycling */
-    if (fabs(bp->colour[0] - bp->colour_t[0]) <= fabs(bp->colour_i[0]))
-      bp->colour[0] = bp->colour_t[0];
-    else
-      bp->colour[0] += bp->colour_i[0];
-    if (fabs(bp->colour[1] - bp->colour_t[1]) <= fabs(bp->colour_i[1]))
-      bp->colour[1] = bp->colour_t[1];
-    else
-      bp->colour[1] += bp->colour_i[1];
-    if (fabs(bp->colour[2] - bp->colour_t[2]) <= fabs(bp->colour_i[2]))
-      bp->colour[2] = bp->colour_t[2];
-    else
-      bp->colour[2] += bp->colour_i[2];
-  } else {
-    /* We are going too fast, so we may as well let the 
-     * cpu relax a little by sleeping for a millisecond. */
-    usleep(1);
-  }
+void restore_idle(int value)
+{
+    glutIdleFunc(glsnake_idle);
 }
+#endif
 
-static void
-snake_bounding_box (ModeInfo *mi,
-                    GLfloat *x1, GLfloat *y1, GLfloat *z1,
-                    GLfloat *x2, GLfloat *y2, GLfloat *z2)
+void quick_sleep(void)
 {
-  glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
-  int i;
-  GLdouble identity[16] = { 1, 0, 0, 0,
-                            0, 1, 0, 0,
-                            0, 0, 1, 0,
-                            0, 0, 0, 1 };
-  GLint vp[4] = { 0, 0, 1, 1 };
-  *x1 = *x2 = 0;
-  *y1 = *y2 = 0;
-  *z1 = *z2 = 0;
-
-  glPushMatrix();
-  glLoadIdentity();
-  for (i = 0; i < 24; i++)
-    {
-      GLdouble model[16];
-      GLdouble x, y, z;
-      GLfloat ang = bp->node[i].cur_angle;
-
-      glGetDoublev (GL_MODELVIEW_MATRIX, model);
-      gluProject (0, 0, 0, model, identity, vp, &x, &y, &z);
-fprintf (stderr, "%2d: %5.2f %5.2f %5.2f\n", i, (float)x, (float)y, (float)z);
-
-      if      (x < *x1) *x1 = x;
-      else if (x > *x2) *x2 = x;
-      if      (y < *y1) *y1 = y;
-      else if (y > *y2) *y2 = y;
-      if      (z < *z1) *z1 = z;
-      else if (z > *z2) *z2 = z;
-
-      glTranslatef(0.5, 0.5, 0.5);
-      glRotatef(90, 0.0, 0.0, -1.0);
-      glTranslatef(1.0 + explode, 0.0, 0.0);
-      glRotatef(180 + ang, 1.0, 0.0, 0.0);
-      glTranslatef(-0.5, -0.5, -0.5);
-    }
-fprintf(stderr, "\n");
-  glPopMatrix();
-
-#if 0
-  *x1 -= 1;
-  *y1 -= 1;
-  *z1 -= 1;
-  *x2 += 1;
-  *y2 += 1;
-  *z2 += 1;
+#ifdef HAVE_GLUT
+    /* By using glutTimerFunc we can keep responding to 
+     * mouse and keyboard events, unlike using something like
+     * usleep. */
+    glutIdleFunc(NULL);
+    glutTimerFunc(1, restore_idle, 0);
+#else
+    usleep(1);
 #endif
 }
 
+void glsnake_idle(
+#ifndef HAVE_GLUT
+                 struct glsnake_cfg * bp
+#endif
+                 ) {
+    /* time since last iteration */
+    long iter_msec;
+    /* time since the beginning of last morph */
+    long morf_msec;
+    float iter_angle_max;
+    snaketime current_time;
+    /*    morphFunc transition; */
+    int still_morphing;
+    int i;
+    
+    /* Do nothing to the model if we are paused */
+    if (glc->paused) {
+       /* Avoid busy waiting when nothing is changing */
+       quick_sleep();
+#ifdef HAVE_GLUT
+       glutSwapBuffers();
+       glutPostRedisplay();
+#endif
+       return;
+    }
 
-static void
-draw_bounding_box (ModeInfo *mi)
-{
-  static GLfloat c1[4] = { 0.4, 0.4, 0.4, 1.0 };
-  static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
-  int wire = MI_IS_WIREFRAME(mi);
-  GLfloat x1, y1, z1, x2, y2, z2;
-  snake_bounding_box (mi, &x1, &y1, &z1, &x2, &y2, &z2);
-
-  glColor3f (c1[0], c1[1], c1[2]);
-/*  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);*/
-  glFrontFace(GL_CCW);
-
-  glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
-  glNormal3f(0, 1, 0);
-  glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2);
-  glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1);
-  glEnd();
-  glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
-  glNormal3f(0, -1, 0);
-  glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2);
-  glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1);
-  glEnd();
-  glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
-  glNormal3f(0, 0, 1);
-  glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1);
-  glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1);
-  glEnd();
-  glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
-  glNormal3f(0, 0, -1);
-  glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2);
-  glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2);
-  glEnd();
-  glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
-  glNormal3f(1, 0, 0);
-  glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2);
-  glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1);
-  glEnd();
-  glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
-  glNormal3f(-1, 0, 0);
-  glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2);
-  glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
-  glEnd();
-
-  glPushAttrib (GL_LIGHTING);
-  glDisable (GL_LIGHTING);
-
-  glColor3f (c2[0], c2[1], c2[2]);
-  glBegin(GL_LINES);
-  if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0;
-  if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0;
-  if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0;
-  glVertex3f(x1, 0,  0);  glVertex3f(x2, 0,  0); 
-  glVertex3f(0 , y1, 0);  glVertex3f(0,  y2, 0); 
-  glVertex3f(0,  0,  z1); glVertex3f(0,  0,  z2); 
-  glEnd();
-
-  glPopAttrib();
+    /* <spiv> Well, ftime gives time with millisecond resolution.
+     * <spiv> (or worse, perhaps... who knows what the OS will do)
+     * <spiv> So if no discernable amount of time has passed:
+     * <spiv>   a) There's no point updating the screen, because
+     *             it would be the same
+     * <spiv>   b) The code will divide by zero
+     */
+    gettime(&current_time);
+    
+    iter_msec = (long) GETMSECS(current_time) - GETMSECS(glc->last_iteration) + 
+       ((long) GETSECS(current_time) - GETSECS(glc->last_iteration)) * 1000L;
+
+    if (iter_msec) {
+       /* save the current time */
+       memcpy(&glc->last_iteration, &current_time, sizeof(snaketime));
+       
+       /* work out if we have to switch models */
+       morf_msec = GETMSECS(glc->last_iteration) - GETMSECS(glc->last_morph) +
+           ((long) (GETSECS(glc->last_iteration)-GETSECS(glc->last_morph)) * 1000L);
+
+       if ((morf_msec > statictime) && !interactive && !glc->morphing) {
+           /*printf("starting morph\n");*/
+           memcpy(&glc->last_morph, &(glc->last_iteration), sizeof(glc->last_morph));
+           start_morph(RAND(models), 0);
+       }
+       
+       if (interactive && !glc->morphing) {
+           quick_sleep();
+           return;
+       }
+       
+       /*      if (!glc->dragging && !glc->interactive) { */
+       if (!interactive) {
+           
+           yspin += 360/((1000/yangvel)/iter_msec);
+           zspin += 360/((1000/zangvel)/iter_msec);
+           /*
+           yspin += 360 * (yangvel/1000.0) * iter_msec;
+           zspin += 360 * (zangvel/1000.0) * iter_msec;
+           */
+           
+           /*printf("yspin: %f, zspin: %f\n", yspin, zspin);*/
+
+       }
+
+       /* work out the maximum angle we could turn this node in this
+        * timeslice, iter_msec milliseconds long */
+       iter_angle_max = 90.0 * (angvel/1000.0) * iter_msec;
+
+       still_morphing = 0;
+       for (i = 0; i < NODE_COUNT; i++) {
+           float cur_angle = glc->node[i];
+           float dest_angle = model[glc->next_model].node[i];
+           if (cur_angle != dest_angle) {
+               still_morphing = 1;
+               if (fabs(cur_angle - dest_angle) <= iter_angle_max)
+                   glc->node[i] = dest_angle;
+               else if (fmod(cur_angle - dest_angle + 360, 360) > 180)
+                   glc->node[i] = fmod(cur_angle + iter_angle_max, 360);
+               else
+                   glc->node[i] = fmod(cur_angle + 360 - iter_angle_max, 360);
+           }
+       }
+
+       if (!still_morphing)
+           glc->morphing = 0;
+
+       /* colour cycling */
+       morph_colour();
+       
+#ifdef HAVE_GLUT
+       glutSwapBuffers();
+       glutPostRedisplay();
+#endif
+    } else {
+       /* We are going too fast, so we may as well let the 
+        * cpu relax a little by sleeping for a millisecond. */
+       quick_sleep();
+    }
 }
 
+/* wot draws it */
+void glsnake_display(
+#ifndef HAVE_GLUT
+                    ModeInfo * mi
+#endif
+                    ) {
+#ifndef HAVE_GLUT
+    struct glsnake_cfg * bp = &glc[MI_SCREEN(mi)];
+    Display * dpy = MI_DISPLAY(mi);
+    Window window = MI_WINDOW(mi);
+#endif
 
-void glsnake_draw(ModeInfo *mi)
-{
-  glsnake_configuration *bp = &glc[MI_SCREEN(mi)];
-  Display *dpy = MI_DISPLAY(mi);
-  Window window = MI_WINDOW(mi);
-
-  int i;
-  float ang;
-
-  if (!bp->glx_context)
-      return;
-
-  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
-  gluLookAt(0.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
-
-  /* rotate and translate into snake space */
-  glRotatef(45.0, -5.0, 0.0, 1.0);
-  glTranslatef(-0.5, 0.0, 0.5);
-
-  /* rotate the 0th junction */
-  glTranslatef(0.5, 0.0, 0.5);
-  /* glMultMatrix(rotation); -- quaternion rotation */
-  glRotatef(bp->roty, 0.0, 1.0, 0.0);
-  glRotatef(bp->rotz, 0.0, 0.0, 1.0);
-  glTranslated(-0.5, 0.0, -0.5);
-
-  /* translate middle node to centre */
-  for (i = 11; i >= 0; i--) {
-    ang = bp->node[i].cur_angle;
-    glTranslatef(0.5, 0.5, 0.5);
-    glRotatef(180+ang, -1.0, 0.0, 0.0);
-    glTranslatef(-1.0 - explode, 0.0, 0.0);
-    glRotatef(90, 0.0, 0.0, 1.0);
-    glTranslatef(-0.5, -0.5, -0.5);
-  }
-
-  /* now draw each node along the snake */
-  glPushMatrix();
-  for (i = 0; i < 24; i++) {
-
-    /* choose a colour for this node */
-    if (bp->interactive && (i == bp->selected || i == bp->selected+1))
-      glColor3f(1.0, 1.0, 0.0);
-    else {
-      if (i % 2) {
-        if (scarycolour)
-          glColor3f(0.6, 0.0, 0.9);
-        else
-          glColor3fv(bp->colour);
-      } else {
-        if (scarycolour)
-          glColor3f(0.2, 0.9, 1.0);
-        else
-          glColor3f(1.0, 1.0, 1.0);
-      }
+    int i;
+    float ang;
+    float positions[NODE_COUNT][4]; /* origin points for each node */
+    float com[4]; /* it's the CENTRE of MASS */
+
+#ifndef HAVE_GLUT
+    if (!bp->glx_context)
+       return;
+#endif
+    
+    /* clear the buffer */
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    
+    /* go into the modelview stack */
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    
+    /* get the centre of each node, by moving through the snake and
+     * performing the rotations, then grabbing the matrix at each point
+     * and applying it to the origin */
+    glPushMatrix();
+
+#ifdef HAVE_GLUT
+    /* apply the mouse drag rotation */
+    ui_mousedrag();
+#endif
+    
+    /* apply the continuous rotation */
+    glRotatef(yspin, 0.0, 1.0, 0.0); 
+    glRotatef(zspin, 0.0, 0.0, 1.0); 
+    
+    com[0] = 0.0;
+    com[1] = 0.0;
+    com[2] = 0.0;
+    com[3] = 0.0;
+    for (i = 0; i < NODE_COUNT; i++) {
+       float rotmat[16];
+
+       ang = glc->node[i];
+
+       /*printf("ang = %f\n", ang);*/
+       
+       glTranslatef(0.5, 0.5, 0.5);            /* move to center */
+       glRotatef(90, 0.0, 0.0, -1.0);          /* reorient  */
+       glTranslatef(1.0 + explode, 0.0, 0.0);  /* move to new pos. */
+       glRotatef(180 + ang, 1.0, 0.0, 0.0);    /* pivot to new angle */
+       glTranslatef(-0.5, -0.5, -0.5);         /* return from center */
+
+       glGetFloatv(GL_MODELVIEW_MATRIX, rotmat);
+
+       matmult_origin(rotmat, positions[i]);
+
+       /*printf("positions %f %f %f %f\n", positions[i][0], positions[i][1], positions[i][2], positions[i][3]);*/
+
+       com[0] += positions[i][0];
+       com[1] += positions[i][1];
+       com[2] += positions[i][2];
+       com[3] += positions[i][3];
     }
+    glPopMatrix();
+    com[0] /= NODE_COUNT;
+    com[1] /= NODE_COUNT;
+    com[2] /= NODE_COUNT;
+    com[3] /= NODE_COUNT;
+
+    com[0] /= com[3];
+    com[1] /= com[3];
+    com[2] /= com[3];
 
-#if 0 /* #### */ 
-    if (i == 0) glColor3f(0.0, 1.0, 1.0);
+    /*printf("com: %f, %f, %f, %f\n", com[0], com[1], com[2], com[3]);*/
+
+#if MAGICAL_RED_STRING
+    glPushMatrix();
+    glTranslatef(-com[0], -com[1], -com[2]);
+
+    glDisable(GL_LIGHTING);
+    glColor3f(1.0, 0.0, 0.0);
+    glBegin(GL_LINE_STRIP);
+    for (i = 0; i < NODE_COUNT - 1; i++) {
+       glVertex3fv(positions[i]);
+    }
+    glEnd();
+    glEnable(GL_LIGHTING);
+    /*glTranslatef(com[0], com[1], com[2]);*/
+    glPopMatrix();
 #endif
 
-    /* draw the node */
-    glCallList(bp->node_list);
+    glPushMatrix();
+    glTranslatef(-com[0], -com[1], -com[2]);
 
-    /* now work out where to draw the next one */
+#ifdef HAVE_GLUT
+    /* apply the mouse drag rotation */
+    ui_mousedrag();
+#endif
+    
+    /* apply the continuous rotation */
+    glRotatef(yspin, 0.0, 1.0, 0.0); 
+    glRotatef(zspin, 0.0, 0.0, 1.0); 
+
+    /* now draw each node along the snake -- this is quite ugly :p */
+    for (i = 0; i < NODE_COUNT; i++) {
+       /* choose a colour for this node */
+       if ((i == glc->selected || i == glc->selected+1) && interactive)
+           /* yellow */
+           glColor3f(1.0, 1.0, 0.0);
+       else
+           glColor3fv(glc->colour[(i+1)%2]);
+
+       /* draw the node */
+       if (wireframe)
+           glCallList(glc->node_wire);
+       else
+           glCallList(glc->node_solid);
+
+       /* now work out where to draw the next one */
+       
+       /* Interpolate between models */
+       ang = glc->node[i];
+       
+       glTranslatef(0.5, 0.5, 0.5);            /* move to center */
+       glRotatef(90, 0.0, 0.0, -1.0);          /* reorient  */
+       glTranslatef(1.0 + explode, 0.0, 0.0);  /* move to new pos. */
+       glRotatef(180 + ang, 1.0, 0.0, 0.0);    /* pivot to new angle */
+       glTranslatef(-0.5, -0.5, -0.5);         /* return from center */
+    }
 
-    /* interpolate between models */
-    ang = bp->node[i].cur_angle;
+    glPopMatrix();
+    
+    if (titles)
+#ifdef HAVE_GLUT
+       draw_title();
+#else
+       draw_title(mi);
+#endif
 
-    glTranslatef(0.5, 0.5, 0.5);
-    glRotatef(90, 0.0, 0.0, -1.0);
-    glTranslatef(1.0 + explode, 0.0, 0.0);
-    glRotatef(180 + ang, 1.0, 0.0, 0.0);
-    glTranslatef(-0.5, -0.5, -0.5);
-  }
+#ifndef HAVE_GLUT
+       glsnake_idle(bp);
+#endif
+    
+    glFlush();
+#ifdef HAVE_GLUT
+    glutSwapBuffers();
+#else
+    glXSwapBuffers(dpy, window);
+#endif
+}
 
-  glPopMatrix();
+#ifdef HAVE_GLUT
+/* anything that needs to be cleaned up goes here */
+void unmain() {
+    glutDestroyWindow(glc->window);
+    free(glc);
+}
 
-  if (do_bbox)
-    draw_bounding_box (mi);
+void ui_init(int *, char **);
 
-  if (labels)
-    draw_label(mi);
-  
-  if (mi->fps_p)
-    do_fps (mi);
+int main(int argc, char ** argv) {
+    glc = malloc(sizeof(struct glsnake_cfg));
+    memset(glc, 0, sizeof(struct glsnake_cfg));
 
-  glsnake_idol(bp);
-  
-  glFlush();
-  glXSwapBuffers(dpy, window);
+    glc->width = 320;
+    glc->height = 240;
+    
+    ui_init(&argc, argv);
+
+    gettime(&glc->last_iteration);
+    memcpy(&glc->last_morph, &glc->last_iteration, sizeof(snaketime));
+    srand((unsigned int)GETSECS(glc->last_iteration));
+
+    glc->prev_colour = glc->next_colour = COLOUR_ACYCLIC;
+    glc->next_model = RAND(models);
+    glc->prev_model = 0;
+    start_morph(glc->prev_model, 1);   
+
+    glsnake_init();
+    
+    atexit(unmain);
+    glutSwapBuffers();
+    glutMainLoop();
+    
+    return 0;
+}
+#endif
+
+/*
+ * GLUT FUNCTIONS
+ */
+
+#ifdef HAVE_GLUT
+
+/* trackball quaternions */
+float cumquat[4] = {0.0,0.0,0.0,0.0}, oldquat[4] = {0.0,0.0,0.0,0.1};
+
+/* rotation matrix */
+float rotation[16];
+
+/* mouse drag vectors: start and end */
+float m_s[3], m_e[3];
+
+/* dragging boolean */
+int dragging = 0;
+
+/* this function calculates the rotation matrix based on the quaternions
+ * generated from the mouse drag vectors */
+void calc_rotation() {
+    double Nq, s;
+    double xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz;
+
+    /* this bit ripped from Shoemake's quaternion notes from SIGGRAPH */
+    Nq = cumquat[0] * cumquat[0] + cumquat[1] * cumquat[1] +
+       cumquat[2] * cumquat[2] + cumquat[3] * cumquat[3];
+    s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
+    xs = cumquat[0] *  s; ys = cumquat[1] *  s; zs = cumquat[2] * s;
+    wx = cumquat[3] * xs; wy = cumquat[3] * ys; wz = cumquat[3] * zs;
+    xx = cumquat[0] * xs; xy = cumquat[0] * ys; xz = cumquat[0] * zs;
+    yy = cumquat[1] * ys; yz = cumquat[1] * zs; zz = cumquat[2] * zs;
+
+    rotation[0] = 1.0 - (yy + zz);
+    rotation[1] = xy + wz;
+    rotation[2] = xz - wy;
+    rotation[4] = xy - wz;
+    rotation[5] = 1.0 - (xx + zz);
+    rotation[6] = yz + wx;
+    rotation[8] = xz + wy;
+    rotation[9] = yz - wx;
+    rotation[10] = 1.0 - (xx + yy);
+    rotation[3] = rotation[7] = rotation[11] = 0.0;
+    rotation[12] = rotation[13] = rotation[14] = 0.0;
+    rotation[15] = 1.0;
+}
+
+inline void ui_mousedrag() {
+    glMultMatrixf(rotation);
 }
 
-#endif /* USE_GL */
+void ui_keyboard(unsigned char c, int x, int y) {
+    int i;
+    
+    switch (c) {
+      case 27:  /* ESC */
+      case 'q':
+       exit(0);
+       break;
+      case 'e':
+       explode += DEF_EXPLODE;
+       glutPostRedisplay();
+       break;
+      case 'E':
+       explode -= DEF_EXPLODE;
+       if (explode < 0.0) explode = 0.0;
+       glutPostRedisplay();
+       break;
+      case '.':
+       /* next model */
+       glc->next_model++;
+       glc->next_model %= models;
+       start_morph(glc->next_model, 0);
+       
+       /* Reset last_morph time */
+       gettime(&glc->last_morph);                      
+       break;
+      case ',':
+       /* previous model */
+       glc->next_model = (glc->next_model + models - 1) % models;
+       start_morph(glc->next_model, 0);
+       
+       /* Reset glc->last_morph time */
+       gettime(&glc->last_morph);                      
+       break;
+      case '+':
+       angvel += DEF_ACCEL;
+       break;
+      case '-':
+       if (angvel > DEF_ACCEL)
+           angvel -= DEF_ACCEL;
+       break;
+      case 'i':
+       if (interactive) {
+           /* Reset last_iteration and last_morph time */
+           gettime(&glc->last_iteration);
+           gettime(&glc->last_morph);
+       }
+       interactive = 1 - interactive;
+       glutPostRedisplay();
+       break;
+      case 'w':
+       wireframe = 1 - wireframe;
+       if (wireframe)
+           glDisable(GL_LIGHTING);
+       else
+           glEnable(GL_LIGHTING);
+       glutPostRedisplay();
+       break;
+      case 'p':
+       if (glc->paused) {
+           /* unpausing, reset last_iteration and last_morph time */
+           gettime(&glc->last_iteration);
+           gettime(&glc->last_morph);
+       }
+       glc->paused = 1 - glc->paused;
+       break;
+      case 'd':
+       /* dump the current model so we can add it! */
+       printf("# %s\nnoname:\t", model[glc->next_model].name);
+       for (i = 0; i < NODE_COUNT; i++) {
+           if (glc->node[i] == ZERO)
+               printf("Z");
+           else if (glc->node[i] == LEFT)
+               printf("L");
+           else if (glc->node[i] == PIN)
+               printf("P");
+           else if (glc->node[i] == RIGHT)
+               printf("R");
+           /*
+             else
+             printf("%f", node[i].curAngle);
+           */
+           if (i < NODE_COUNT - 1)
+               printf(" ");
+       }
+       printf("\n");
+       break;
+      case 'f':
+       glc->fullscreen = 1 - glc->fullscreen;
+       if (glc->fullscreen) {
+           glc->old_width = glc->width;
+           glc->old_height = glc->height;
+           glutFullScreen();
+       } else {
+           glutReshapeWindow(glc->old_width, glc->old_height);
+           glutPositionWindow(50,50);
+       }
+       break;
+      case 't':
+       titles = 1 - titles;
+       if (interactive || glc->paused)
+           glutPostRedisplay();
+       break;
+      case 'a':
+       altcolour = 1 - altcolour;
+       break;
+      case 'z':
+       zoom += 1.0;
+       glsnake_reshape(glc->width, glc->height);
+       break;
+      case 'Z':
+       zoom -= 1.0;
+       glsnake_reshape(glc->width, glc->height);
+       break;
+      default:
+       break;
+    }
+}
+
+void ui_special(int key, int x, int y) {
+    int i;
+    float *destAngle = &(model[glc->next_model].node[glc->selected]);
+    int unknown_key = 0;
+
+    if (interactive) {
+       switch (key) {
+         case GLUT_KEY_UP:
+           glc->selected = (glc->selected + (NODE_COUNT - 2)) % (NODE_COUNT - 1);
+           break;
+         case GLUT_KEY_DOWN:
+           glc->selected = (glc->selected + 1) % (NODE_COUNT - 1);
+           break;
+         case GLUT_KEY_LEFT:
+           *destAngle = fmod(*destAngle+(LEFT), 360);
+           glc->morphing = glc->new_morph = 1;
+           break;
+         case GLUT_KEY_RIGHT:
+           *destAngle = fmod(*destAngle+(RIGHT), 360);
+           glc->morphing = glc->new_morph = 1;
+           break;
+         case GLUT_KEY_HOME:
+           start_morph(STRAIGHT_MODEL, 0);
+           break;
+         default:
+           unknown_key = 1;
+           break;
+       }
+    }
+    calc_snake_metrics();
+
+    if (!unknown_key)
+       glutPostRedisplay();
+}
+
+void ui_mouse(int button, int state, int x, int y) {
+    if (button==0) {
+       switch (state) {
+         case GLUT_DOWN:
+           dragging = 1;
+           m_s[0] = M_SQRT1_2 * 
+               (x - (glc->width / 2.0)) / (glc->width / 2.0);
+           m_s[1] = M_SQRT1_2 * 
+               ((glc->height / 2.0) - y) / (glc->height / 2.0);
+           m_s[2] = sqrt(1-(m_s[0]*m_s[0]+m_s[1]*m_s[1]));
+           break;
+         case GLUT_UP:
+           dragging = 0;
+           oldquat[0] = cumquat[0];
+           oldquat[1] = cumquat[1];
+           oldquat[2] = cumquat[2];
+           oldquat[3] = cumquat[3];
+           break;
+         default:
+           break;
+       }
+    }
+    glutPostRedisplay();
+}
+
+void ui_motion(int x, int y) {
+    double norm;
+    float q[4];
+    
+    if (dragging) {
+       /* construct the motion end vector from the x,y position on the
+        * window */
+       m_e[0] = (x - (glc->width/ 2.0)) / (glc->width / 2.0);
+       m_e[1] = ((glc->height / 2.0) - y) / (glc->height / 2.0);
+       /* calculate the normal of the vector... */
+       norm = m_e[0] * m_e[0] + m_e[1] * m_e[1];
+       /* check if norm is outside the sphere and wraparound if necessary */
+       if (norm > 1.0) {
+           m_e[0] = -m_e[0];
+           m_e[1] = -m_e[1];
+           m_e[2] = sqrt(norm - 1);
+       } else {
+           /* the z value comes from projecting onto an elliptical spheroid */
+           m_e[2] = sqrt(1 - norm);
+       }
+
+       /* now here, build a quaternion from m_s and m_e */
+       q[0] = m_s[1] * m_e[2] - m_s[2] * m_e[1];
+       q[1] = m_s[2] * m_e[0] - m_s[0] * m_e[2];
+       q[2] = m_s[0] * m_e[1] - m_s[1] * m_e[0];
+       q[3] = m_s[0] * m_e[0] + m_s[1] * m_e[1] + m_s[2] * m_e[2];
+
+       /* new rotation is the product of the new one and the old one */
+       cumquat[0] = q[3] * oldquat[0] + q[0] * oldquat[3] + 
+           q[1] * oldquat[2] - q[2] * oldquat[1];
+       cumquat[1] = q[3] * oldquat[1] + q[1] * oldquat[3] + 
+           q[2] * oldquat[0] - q[0] * oldquat[2];
+       cumquat[2] = q[3] * oldquat[2] + q[2] * oldquat[3] + 
+           q[0] * oldquat[1] - q[1] * oldquat[0];
+       cumquat[3] = q[3] * oldquat[3] - q[0] * oldquat[0] - 
+           q[1] * oldquat[1] - q[2] * oldquat[2];
+       
+       calc_rotation();
+    }
+    glutPostRedisplay();
+}
+
+void ui_init(int * argc, char ** argv) {
+    glutInit(argc, argv);
+    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
+    glutInitWindowSize(glc->width, glc->height);
+    glc->window = glutCreateWindow("glsnake");
+
+    glutDisplayFunc(glsnake_display);
+    glutReshapeFunc(glsnake_reshape);
+    glutIdleFunc(glsnake_idle);
+    glutKeyboardFunc(ui_keyboard);
+    glutSpecialFunc(ui_special);
+    glutMouseFunc(ui_mouse);
+    glutMotionFunc(ui_motion);
+
+    yangvel = DEF_YANGVEL;
+    zangvel = DEF_ZANGVEL;
+    explode = DEF_EXPLODE;
+    angvel = DEF_ANGVEL;
+    statictime = DEF_STATICTIME;
+    altcolour = DEF_ALTCOLOUR;
+    titles = DEF_TITLES;
+    interactive = DEF_INTERACTIVE;
+    zoom = DEF_ZOOM;
+    wireframe = DEF_WIREFRAME;
+}
+#endif /* HAVE_GLUT */
index 75073fb31a05ec30e73a29b984770e64a3d0a4f3..aa3e876e4e333d075b1e30220298f2b1cf977b83 100644 (file)
@@ -389,9 +389,9 @@ text_extents (const char *string, int *wP, int *hP)
 
         if (w > *wP) *wP = w;
         *hP += line_height;
-        s++;
         lines++;
         if (*s == 0) break;
+        s++;
       }
     else
       s++;
index c0b5a2f01082a4c90f6655e6a5fbcb363bede846..99e92317487e25373e5a456a86d94eb356e74a89 100644 (file)
@@ -100,7 +100,7 @@ bigendian (void)
    extra byte set to 0xFF.
  */
 XImage *
-screen_to_ximage (Screen *screen, Window window)
+screen_to_ximage (Screen *screen, Window window, char **filename_return)
 {
   Display *dpy = DisplayOfScreen (screen);
   Pixmap pixmap = 0;
@@ -113,7 +113,7 @@ screen_to_ximage (Screen *screen, Window window)
   win_height = xgwa.height;
 
   pixmap = XCreatePixmap(dpy, window, xgwa.width, xgwa.height, xgwa.depth);
-  load_random_image (screen, window, pixmap);
+  load_random_image (screen, window, pixmap, filename_return);
 
   /* GL texture sizes must be powers of two. */
   tex_width  = to_pow2(win_width);
index ba0e69949579d21ff813c3d1cf3027c923b96a41..215e950456479387e6a196d20dbfa60b660a28cb 100644 (file)
@@ -18,6 +18,7 @@
    This XImage will be 32 bits per pixel, 8 each per R, G, and B, with the
    extra byte set to 0xFF.
  */
-XImage * screen_to_ximage (Screen *screen, Window window);
+XImage * screen_to_ximage (Screen *screen, Window window,
+                           char **filename_return);
 
 #endif /* __GRAB_XIMAGE_H__ */
index bdcec9ec3e2986d27fb67e7f97f48978b8645cbf..9449c3184212815d7e0b6050bacd3d747fe65890 100644 (file)
@@ -1,8 +1,7 @@
 /* hypertorus --- Shows a hypertorus that rotates in 4d */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)hypertorus.c  1.1 03/05/18 xlockmore";
-
 #endif
 
 /* Copyright (c) 2003 Carsten Steger <carsten@mirsanmir.org>. */
index 8c2d800c2f4b353daeee0d39ab4bfa430dc39943..743d6575542da3501392d1f21730cc6a00026f99 100644 (file)
@@ -96,6 +96,7 @@ extern XtAppContext app;
 #define DEFAULTS       "*delay:        10000       \n" \
                        "*showFPS:      False       \n" \
                        "*wireframe:    False       \n" \
+                       "*geometry:     640x640     \n" \
                        "*count:      " DEF_COUNT " \n" \
                        "*style:      " DEF_STYLE " \n" \
                        "*speed:      " DEF_SPEED " \n" \
@@ -557,7 +558,7 @@ draw_wing (GLfloat w, GLfloat h, GLfloat d, Bool wire)
   int polys = 0;
   int maxx = coords[0][countof(coords[0])-1][0];
   int maxy = coords[0][countof(coords[0])-1][1];
-  int x;
+  unsigned int x;
 
   for (x = 1; x < countof(coords[0]); x++)
     {
diff --git a/hacks/glx/mirrorblob.c b/hacks/glx/mirrorblob.c
new file mode 100644 (file)
index 0000000..679b726
--- /dev/null
@@ -0,0 +1,1250 @@
+/* -*- Mode: C; tab-width: 4 -*- */
+
+/* mirrorblob  Copyright (c) 2003 Jon Dowdall <jon.dowdall@bigpond.com> */
+/*
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind.  The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof.  In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+
+ * Revision History:
+ * 23-Sep-2003:  jon.dowdall@bigpond.com  Created module "mirrorblob"
+ * 19-Oct-2003:  jon.dowdall@bigpond.com  Added texturing
+ *
+ *
+ * The mirrorblob screensaver draws a pulsing blob on the screen.
+ * Options include adding a background (via screen_to_ximage),
+ * texturing the blob, making the blob semi-transparent and varying the
+ * resolution of the blob tessellation.
+ *
+ * The mirrorblob was inspired by a lavalamp is in no way a simulation.
+ * The code is just an attempt to generate some eye-candy.
+ *
+ * Much of xscreensaver code framework is taken from the pulsar module by David
+ * Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski.
+ *
+ */
+
+#include <math.h> 
+#include <stdio.h>
+#include <stdlib.h>
+
+/*-
+ * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
+ * otherwise caddr_t is not defined correctly
+ */
+
+#include <X11/Intrinsic.h>
+
+#ifdef STANDALONE
+# define PROGCLASS                                             "MirrorBlob"
+# define HACK_INIT                                             init_mirrorblob
+# define HACK_DRAW                                             draw_mirrorblob
+# define HACK_RESHAPE                                  reshape_mirrorblob
+# define screensaver_opts                              xlockmore_opts
+
+# define DEF_DELAY                      "10000"
+# define DEF_FPS                    "False"
+# define DEF_WIRE           "False"
+# define DEF_BLEND          "False"
+# define DEF_FOG                    "False"
+# define DEF_ANTIALIAS        "False"
+# define DEF_WALLS            "True"
+# define DEF_TEXTURE          "True"
+# define DEF_COLOUR           "False"
+# define DEF_OFFSET_TEXTURE   "False"
+# define DEF_PAINT_BACKGROUND "True"
+# define DEF_X_RES            "60"
+# define DEF_Y_RES            "32"
+# define DEF_FIELD_POINTS     "5"
+# define DEF_INCREMENTAL      "0"
+# define DEF_FADE_SPEED       "1"
+# define DEF_HOLD_FRAMES      "10240"
+
+#define        DEFAULTS                "*delay:                        " DEF_DELAY                             "\n" \
+                                               "*showFPS:                      " DEF_FPS                               "\n" \
+                                               "*wire:                         " DEF_WIRE                              "\n" \
+                                               "*blend:                        " DEF_BLEND                             "\n" \
+                                               "*fog:                          " DEF_FOG                               "\n" \
+                                               "*antialias:            " DEF_ANTIALIAS                 "\n" \
+                                               "*walls:                        " DEF_WALLS                             "\n" \
+                                               "*texture:                      " DEF_TEXTURE                   "\n" \
+                                               "*colour:                       " DEF_COLOUR                    "\n" \
+                                               "*offset_texture:       " DEF_OFFSET_TEXTURE    "\n" \
+                                               "*bgimage:                      " DEF_PAINT_BACKGROUND  "\n" \
+                                               "*x_resolution:         " DEF_X_RES                             "\n" \
+                                               "*y_resolution:         " DEF_Y_RES                             "\n" \
+                                               "*field_points:         " DEF_FIELD_POINTS              "\n" \
+                                               "*incremental:          " DEF_INCREMENTAL               "\n" \
+                                               "*fade_speed:           " DEF_FADE_SPEED                "\n" \
+                                               "*hold_frames:          " DEF_HOLD_FRAMES               "\n" \
+
+# include "xlockmore.h"                                /* from the xscreensaver distribution */
+#else /* !STANDALONE */
+# include "xlock.h"                                    /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL /* whole file */
+
+#ifdef HAVE_XMU
+# ifndef VMS
+#  include <X11/Xmu/Drawing.h>
+#else  /* VMS */
+#  include <Xmu/Drawing.h>
+# endif /* VMS */
+#endif
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include "GL/glx.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+/*#include <string.h>*/
+#include "grab-ximage.h"
+
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#define WIDTH 1024
+#define HEIGHT 768
+
+#define PI  3.1415926535897
+
+/* */
+static int do_wire;
+static int do_blend;
+static int do_fog;
+static int do_antialias;
+static int do_walls;
+static int do_texture;
+static int do_paint_background;
+static int do_colour;
+static int offset_texture;
+static int x_resolution;
+static int y_resolution;
+static int field_points;
+static int incremental;
+static int fade_speed;
+static int hold_frames;
+
+static XrmOptionDescRec opts[] = {
+  {"-wire",   ".mirrorblob.wire",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+wire",   ".mirrorblob.wire",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-blend",   ".mirrorblob.blend",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+blend",   ".mirrorblob.blend",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-fog",   ".mirrorblob.fog",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+fog",   ".mirrorblob.fog",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-antialias",   ".mirrorblob.antialias",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+antialias",   ".mirrorblob.antialias",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-walls",   ".mirrorblob.walls",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+walls",   ".mirrorblob.walls",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-texture",   ".mirrorblob.texture",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+texture",   ".mirrorblob.texture",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-colour",   ".mirrorblob.colour",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+colour",   ".mirrorblob.colour",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-offset_texture", ".mirrorblob.offset_texture",   XrmoptionNoArg, (caddr_t) "true" },
+  {"+offset_texture", ".mirrorblob.offset_texture",   XrmoptionNoArg, (caddr_t) "false" },
+  {"-bgimage", ".mirrorblob.bgimage", XrmoptionNoArg, (caddr_t) "true" },
+  {"+bgimage", ".mirrorblob.bgimage", XrmoptionNoArg, (caddr_t) "false" },
+  {"-x_res",   ".mirrorblob.x_res",   XrmoptionSepArg, (caddr_t) NULL },
+  {"-y_res",   ".mirrorblob.y_res",   XrmoptionSepArg, (caddr_t) NULL },
+  {"-field_points", ".mirrorblob.field_points",   XrmoptionSepArg, (caddr_t) NULL },
+  {"-incremental", ".mirrorblob.incremental",   XrmoptionSepArg, (caddr_t) NULL },
+  {"-fade_speed", ".mirrorblob.fade_speed",   XrmoptionSepArg, (caddr_t) NULL },
+  {"-hold_frames", ".mirrorblob.hold_frames",   XrmoptionSepArg, (caddr_t) NULL },
+};
+
+
+static argtype vars[] = {
+  {(caddr_t *) &do_wire,      "wire",      "Wire",      DEF_WIRE,      t_Bool},
+  {(caddr_t *) &do_blend,     "blend",     "Blend",     DEF_BLEND,     t_Bool},
+  {(caddr_t *) &do_fog,       "fog",       "Fog",       DEF_FOG,       t_Bool},
+  {(caddr_t *) &do_antialias, "antialias", "Antialias", DEF_ANTIALIAS, t_Bool},
+  {(caddr_t *) &do_walls,     "walls",     "Walls",     DEF_WALLS,     t_Bool},
+  {(caddr_t *) &do_texture,   "texture",   "texture",   DEF_TEXTURE,   t_Bool},
+  {(caddr_t *) &do_colour,    "colour",    "colour",    DEF_COLOUR,    t_Bool},
+  {(caddr_t *) &offset_texture, "offset_texture","offset_texture", DEF_OFFSET_TEXTURE, t_Bool},
+  {(caddr_t *) &do_paint_background,"bgimage","bgimage", DEF_PAINT_BACKGROUND, t_Bool},
+  {(caddr_t *) &x_resolution, "x_res", "X_res", DEF_X_RES, t_Int},
+  {(caddr_t *) &y_resolution, "y_res", "Y_res", DEF_Y_RES, t_Int},
+  {(caddr_t *) &field_points, "field_points", "Field_points", DEF_FIELD_POINTS, t_Int},
+  {(caddr_t *) &incremental, "incremental", "Incremental", DEF_INCREMENTAL, t_Int},
+  {(caddr_t *) &fade_speed, "fade_speed", "fade_speed", DEF_FADE_SPEED, t_Int},
+  {(caddr_t *) &hold_frames, "hold_frames", "hold_frames", DEF_HOLD_FRAMES, t_Int},
+};
+
+
+static OptionStruct desc[] =
+{
+       {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
+       {"-/+ blend", "whether to do enable blending (slower)"},
+       {"-/+ fog", "whether to do enable fog (slower)"},
+       {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
+       {"-/+ walls", "whether to add walls to the blob space (slower)"},
+       {"-/+ texture", "whether to add a texture to the blob (slower)"},
+       {"-/+ colour", "whether to colour the blob"},
+       {"-/+ offset_texture", "whether to offset texture co-ordinates"},
+       {"-/+ bgimage", "whether to display a background texture (slower)"},
+       {"-x_res", "Blob resolution in x direction"},
+       {"-y_res", "Blob resolution in y direction"},
+       {"-field_points", "Number of field points used to disturb blob"},
+       {"-incremental", "Field summation method"},
+       {"-fade_speed", "speed of transistion"},
+       {"-hold_frames", "Number of frames before next image"},
+};
+
+ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc};
+
+#ifdef USE_MODULES
+ModStruct   screensaver_description =
+{"screensaver", "init_mirrorblob", "draw_mirrorblob", "release_mirrorblob",
+ "draw_mirrorblob", "init_mirrorblob", NULL, &screensaver_opts,
+ 1000, 1, 2, 1, 4, 1.0, "",
+ "OpenGL screensaver", 0, NULL};
+#endif
+
+
+/* structure for holding the screensaver data */
+typedef struct {
+  int screen_width, screen_height;
+  GLXContext *glx_context;
+  Window window;
+  XColor fg, bg;
+} screensaverstruct;
+
+static screensaverstruct *Screensaver = NULL;
+
+/*****************************************************************************
+ * Types used in blob code
+ *****************************************************************************/
+
+typedef struct
+{
+  GLdouble x, y;
+} Vector2D;
+
+typedef struct
+{
+  GLdouble x, y, z;
+} Vector3D;
+
+typedef struct
+{
+  GLubyte red, green, blue, alpha;
+} Colour;
+
+/* Data used for sphere tessellation */
+typedef struct
+{
+  double cosyd, sinyd;
+
+  /* Number of x points at each row of the blob */
+  int num_x_points;  
+} Row_Data;
+
+/* Structure to hold sphere distortion data */
+typedef struct
+{
+  double cx, cy, cpower;
+  double mx, my, mpower;
+  double ax, ay, apower;
+  double vx, vy, vpower;
+  Vector3D pos;
+} Field_Data;
+
+/*****************************************************************************
+ * Static blob data
+ *****************************************************************************/
+
+static Row_Data *row_data;
+
+/* Parameters controlling the position of the blob as a whole */
+static Vector3D blob_center = {0.0, 0.0, 0.0};
+static Vector3D blob_anchor = {0.0, 0.0, 0.0};
+static Vector3D blob_velocity = {0.0, 0.0, 0.0};
+static Vector3D blob_force = {0.0, 0.0, 0.0};
+
+/* Count of the total number of points */
+static int num_points;
+
+static Vector3D *dots = NULL;
+static Vector3D *normals = NULL;
+static Colour   *colours = NULL;
+static Vector2D *tex_coords = NULL;
+
+/* Pointer to the field function results */
+static double *field = 0, *wall_field = 0;
+
+Field_Data *field_data;
+
+/* Use 2 textures to allow a gradualr fade between images */
+#define NUM_TEXTURES 2
+static int current_texture;
+
+/* Ratio of used texture size to total texture size */
+GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
+GLuint textures[NUM_TEXTURES];
+
+static int holding = 1;
+static int transitioning = 0;
+static int colour_cycle = 0;
+
+/******************************************************************************
+ *
+ * Change to the projection matrix and set our viewing volume.
+ * 
+ */
+static void
+resetProjection(int width, int height)
+{
+  glMatrixMode( GL_PROJECTION );
+  glLoadIdentity( );
+  gluPerspective( 60.0, 1.0, 1.0, 1024.0 );
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+}
+
+/****************************************************************************
+ *
+ * Load a texture using the screen_to_ximage function.
+ */
+void
+grab_texture(ModeInfo *mi, int texture_index)
+{
+  XImage *ximage;
+
+  ximage = screen_to_ximage (mi->xgwa.screen, mi->window, 0);
+
+  glBindTexture (GL_TEXTURE_2D, textures[texture_index]);
+  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+
+  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ximage->width, ximage->height,
+               0, GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
+
+  tex_width[texture_index] = mi->xgwa.width  / (GLfloat)ximage->width;
+  tex_height[texture_index] = mi->xgwa.height  / (GLfloat)ximage->height;
+
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+  free(ximage->data);
+  ximage->data = 0;
+  XDestroyImage (ximage);
+}
+
+/******************************************************************************
+ *
+ * Initialise the data used to calculate the blob shape.
+ */
+static void
+initializeGL(ModeInfo *mi, GLsizei width, GLsizei height) 
+{
+  GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
+  /* Lighting values */
+  GLfloat lightPos0[] = {50.0f, 10.0f, 20.0f, 1.0f };
+  GLfloat whiteLight0[] = { 0.1f, 0.1f, 0.1f, 1.0f };
+  GLfloat sourceLight0[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+  GLfloat specularLight0[] = { 0.7f, 0.6f, 0.3f, 1.0f };
+
+  GLfloat lightPos1[] = {0.0f, -50.0f, 50.0f, 1.0f };
+  GLfloat whiteLight1[] = { 0.1f, 0.1f, 0.1f, 1.0f };
+  GLfloat sourceLight1[] = { 1.0f, 0.3f, 0.3f, 1.0f };
+  GLfloat specularLight1[] = { 0.7f, 0.6f, 0.3f, 1.0f };
+
+  GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+  /* Setup our viewport. */
+  glViewport( 0, 0, width, height ); 
+
+  glEnable(GL_DEPTH_TEST);
+
+  if (do_antialias)
+  {
+       do_blend = 1;
+       glEnable(GL_LINE_SMOOTH);
+  }
+
+  /* The blend function is used for trasitioning between two images even when
+   * blend is not selected.
+   */
+  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  if (do_wire)
+  {
+       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+  }
+  else
+  {
+       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+  }
+
+  if (do_fog)
+  {
+       glEnable(GL_FOG);
+       glFogi(GL_FOG_MODE, GL_LINEAR);
+       glFogfv(GL_FOG_COLOR, fogColor);
+       glFogf(GL_FOG_DENSITY, 0.35);
+       glFogf(GL_FOG_START, 2.0);
+       glFogf(GL_FOG_END, 10.0);
+  }
+
+  /* Our shading model--Gouraud (smooth). */
+  glShadeModel (GL_SMOOTH);
+
+  /* Culling. */
+  glCullFace (GL_BACK);
+  glEnable (GL_CULL_FACE);
+  glEnable (GL_DEPTH_TEST);
+  glFrontFace (GL_CCW);
+
+
+  /* Set the clear color. */
+  glClearColor( 0, 0, 0, 0 );
+
+  glViewport( 0, 0, width, height );
+
+  glLightfv (GL_LIGHT0, GL_AMBIENT, whiteLight0);
+  glLightfv (GL_LIGHT0, GL_DIFFUSE, sourceLight0);
+  glLightfv (GL_LIGHT0, GL_SPECULAR, specularLight0);
+  glLightfv (GL_LIGHT0, GL_POSITION, lightPos0);
+  glEnable (GL_LIGHT0);
+  glLightfv (GL_LIGHT1, GL_AMBIENT, whiteLight1);
+  glLightfv (GL_LIGHT1, GL_DIFFUSE, sourceLight1);
+  glLightfv (GL_LIGHT1, GL_SPECULAR, specularLight1);
+  glLightfv (GL_LIGHT1, GL_POSITION, lightPos1);
+  glEnable (GL_LIGHT1);
+  glEnable (GL_LIGHTING);
+
+  /* Enable color tracking */
+  glEnable (GL_COLOR_MATERIAL);
+
+  /* Set Material properties to follow glColor values */
+  glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
+
+  /* Set all materials to have specular reflectivity */
+  glMaterialfv (GL_FRONT, GL_SPECULAR, specref);
+  glMateriali (GL_FRONT, GL_SHININESS, 64);
+
+  glEnable (GL_NORMALIZE);
+
+  /* Enable Arrays */
+  if (do_texture)
+  {
+    glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
+
+    glEnable (GL_TEXTURE_2D);
+    current_texture = 0;
+    glGenTextures (NUM_TEXTURES, textures);
+    grab_texture (mi, current_texture);
+    grab_texture (mi, 1 - current_texture);
+
+    glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+  }
+
+  if (do_colour)
+  {
+    glEnableClientState (GL_COLOR_ARRAY);
+  }
+  glEnableClientState (GL_NORMAL_ARRAY);
+  glEnableClientState (GL_VERTEX_ARRAY);
+}
+
+/******************************************************************************
+ *
+ * Calculate the normal vector for a plane given three points in the plane.
+ */
+static void
+calculate_normal (Vector3D point1,
+                  Vector3D point2,
+                  Vector3D point3,
+                  Vector3D *normal)
+{
+  Vector3D vector1, vector2;
+  double magnitude;
+
+  vector1.x = point2.x - point1.x;
+  vector1.y = point2.y - point1.y;
+  vector1.z = point2.z - point1.z;
+
+  vector2.x = point3.x - point2.x;
+  vector2.y = point3.y - point2.y;
+  vector2.z = point3.z - point2.z;
+
+  (*normal).x = vector1.y * vector2.z - vector1.z * vector2.y;
+  (*normal).y = vector1.z * vector2.x - vector1.x * vector2.z;
+  (*normal).z = vector1.x * vector2.y - vector1.y * vector2.x;
+
+  /* Adjust the normal to unit magnitude */
+  magnitude = sqrt ((*normal).x * (*normal).x
+    + (*normal).y * (*normal).y
+    + (*normal).z * (*normal).z);
+
+  /* Watch out for divide by zero/underflow */
+  if (magnitude > 1e-300)
+  {
+    (*normal).x /= magnitude;
+    (*normal).y /= magnitude;
+    (*normal).z /= magnitude;
+  }
+}
+
+/******************************************************************************
+ *
+ * Initialise the data required to draw the blob allocating the memory as
+ * necessary.
+ *
+ * Return 0 on success.
+ */
+static int
+initialise_blob(int width,
+                int height,
+                int field_array_size)
+{
+  /* Loop variables */
+  int x, y, i;
+  double xd;
+
+  colour_cycle = 0;
+
+  row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
+  if (!row_data)
+  {
+    fprintf(stderr, "Couldn't allocate row data buffer\n");
+    return -1;
+  }
+
+  field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
+  if (!field_data)
+  {
+    fprintf(stderr, "Couldn't allocate row data buffer\n");
+    return -1;
+  }
+
+  field = (double *)malloc(field_array_size * sizeof(double));
+  if (!field)
+  {
+    fprintf(stderr, "Couldn't allocate field buffer\n");
+    return -1;
+  }
+
+  wall_field = (double *)malloc(field_array_size * sizeof(double));
+  if (!wall_field)
+  {
+    fprintf(stderr, "Couldn't allocate wall_field buffer\n");
+    return -1;
+  }
+
+  dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
+  if (!dots)
+  {
+    fprintf(stderr, "Couldn't allocate points buffer\n");
+    return -1;
+  }
+  glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) dots);
+
+  normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
+  if (!normals)
+  {
+    fprintf(stderr, "Couldn't allocate normals buffer\n");
+    return -1;
+  }
+  glNormalPointer (GL_DOUBLE, 0, (GLvoid *) normals);
+
+  if (do_colour)
+  {
+    colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
+    if (!colours)
+    {
+      fprintf(stderr, "Couldn't allocate colours buffer\n");
+      return -1;
+    }
+    glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colours);
+  }
+  
+  if (do_texture)
+  {
+    tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
+                                    * sizeof(Vector2D));
+    if (!tex_coords)
+    {
+      fprintf(stderr, "Couldn't allocate tex_coords buffer\n");
+      return -1;
+    }
+    glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) tex_coords);
+  }
+
+  if (! do_texture)
+    do_paint_background = False;
+
+
+  num_points = 0;
+  /* Generate constant row data and count of total number of points */
+  for (y = 0; y < y_resolution; y++)
+  {
+    row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1))
+                   / (double)(y_resolution * y_resolution));
+    row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1))
+                   / (double)(y_resolution * y_resolution));
+    row_data[y].num_x_points = (int)(x_resolution * row_data[y].sinyd + 1.0);
+    num_points += row_data[y].num_x_points;
+  }
+
+  /* Initialise field data */
+  for (i = 0; i < field_points; i++)
+  {
+    field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+    field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+    field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5);
+
+    field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay)
+      * cos(PI *  field_data[i].ax);
+    field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay);
+    field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay)
+      * sin(PI *  field_data[i].ax);
+
+    field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+    field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+    field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5);
+
+    field_data[i].vx = 0.0;
+    field_data[i].vy = 0.0;
+    field_data[i].vpower = 0.0;
+
+    field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
+    field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
+    field_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
+  }
+
+  /* Initialise lookup table of field strength */
+  for (i = 0; i < field_array_size; i++)
+  {
+    xd = 2.0 * (((double)i / (double)field_array_size));
+
+    xd = 3.0 * xd * xd * xd * xd - 0.25 * xd * xd; 
+    field[i] = 0.4 / (field_points * (xd + 0.1));
+
+    xd = 10.0 * (((double)i / (double)field_array_size));
+    wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0);
+  }
+
+  for (y = 0; y < y_resolution; y++)
+  {
+    for (x = 0; x < row_data[y].num_x_points; x++)
+    {
+      i = x + y * x_resolution;
+      xd = 2.0 * (((double)x / (double)row_data[y].num_x_points) - 0.5);
+
+      dots[i].x = row_data[y].sinyd * cos(PI * xd);
+      dots[i].y = row_data[y].cosyd;
+      dots[i].z = row_data[y].sinyd * sin(PI * xd);
+      normals[i].x = row_data[y].sinyd * cos(PI * xd);
+      normals[i].y = row_data[y].cosyd;
+      normals[i].z = row_data[y].sinyd * sin(PI * xd);
+      if (do_texture)
+      {
+        tex_coords[i].x = 2.0 - 2.0 * x / (float) row_data[y].num_x_points;
+        tex_coords[i].y = 1.0 - y / (float) y_resolution;
+      }
+    }
+  }
+  return 0;
+}
+
+
+/******************************************************************************
+ *
+ * Calculate the blob shape.
+ */
+static void
+calc_blob(int width,
+          int height,
+          int field_array_size,
+          float limit)
+{
+  static double freak = 0.0;
+
+  static double v_freak = 0.0007;
+
+  /* Loop variables */
+  int x, y, i, index;
+  /* position of a point */
+  double xd, yd, zd, offset_x, offset_y, offset_z;
+  double strength, radius;
+  double xdist, ydist, zdist;
+  int dist;
+
+  /* Color components */
+
+  colour_cycle++;
+
+  blob_force.x = 0.0;
+  blob_force.y = 0.0;
+  blob_force.z = 0.0;
+  for (y = 0; y < y_resolution; y++)
+  {
+    for (x = 0; x < row_data[y].num_x_points; x++)
+    {
+      index = x + y * x_resolution;
+      xd = 2.0 * PI * (((double)x / (double)row_data[y].num_x_points) - 0.5);
+
+      radius = 1.0 + 0.0 * sin (xd * 10);
+
+      zd = radius * row_data[y].sinyd * sin(xd);
+      xd = radius * row_data[y].sinyd * cos(xd);
+      yd = radius * row_data[y].cosyd;
+
+      offset_x = 0.0;
+      offset_y = 0.0;
+      offset_z = 0.0;
+      strength = 0.0;
+      for ( i = 0; i < field_points; i++)
+      {
+        xdist = field_data[i].pos.x - xd;
+        ydist = field_data[i].pos.y - yd;
+        zdist = field_data[i].pos.z - zd;
+        dist = field_array_size * (xdist * xdist + ydist * ydist
+                                   + zdist * zdist) * 0.1;
+
+        strength += PI * field_data[i].apower;
+
+        if (dist < field_array_size)
+        {
+          offset_x += xd * field_data[i].apower * field[dist];
+          offset_y += yd * field_data[i].apower * field[dist];
+          offset_z += zd * field_data[i].apower * field[dist];
+
+          blob_force.x += 2.0 * xd * field_data[i].apower * field[dist];
+          blob_force.y += 2.0 * yd * field_data[i].apower * field[dist];
+          blob_force.z += 2.0 * zd * field_data[i].apower * field[dist];
+
+          strength *= 2.0 * field[dist];
+        }
+
+        if (incremental)
+        {
+          xd += offset_x * freak * freak;
+          yd += offset_y * freak * freak;
+          zd += offset_z * freak * freak;
+        }
+        if (incremental == 1)
+        {
+          offset_x = 0.0;
+          offset_y = 0.0;
+          offset_z = 0.0;
+        }
+      }
+
+      if (incremental < 3)
+      {
+        xd += offset_x;
+        yd += offset_y;
+        zd += offset_z;
+      }
+      xd += blob_center.x;
+      yd += blob_center.y;
+      zd += blob_center.z;
+
+      if (do_colour)
+      {
+        colours[index].red = 128 + (int)(sin(strength + colour_cycle * 0.01 + 2.0 * PI * x / row_data[y].num_x_points) * 127.0);
+        colours[index].green = 128 + (int)(cos(strength + colour_cycle * 0.025) * 127.0);
+        colours[index].blue = 128 + (int)(sin(strength + colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0);
+        colours[index].alpha = 128 + (int)(cos(strength + colour_cycle * 0.015) * 63.0);
+      }
+
+      /* Add walls */
+      if (do_walls)
+      {
+        if (zd < -limit) zd = -limit;
+        if (zd > limit) zd = limit;
+
+        dist = field_array_size * (zd + limit) * (zd + limit) * 0.5;
+        if (dist < field_array_size)
+        {
+          xd += (xd - blob_center.x) * wall_field[dist];
+          yd += (yd - blob_center.y) * wall_field[dist];
+          blob_force.z += (zd + limit);
+        }
+        else
+        {
+          dist = field_array_size * (zd - limit) * (zd - limit) * 0.5;
+          if (dist < field_array_size)
+          {
+            xd += (xd - blob_center.x) * wall_field[dist];
+            yd += (yd - blob_center.y) * wall_field[dist];
+            blob_force.z -= (zd - limit);
+          }
+
+          if (yd < -limit) yd = -limit;
+          if (yd > limit) yd = limit;
+          
+          dist = field_array_size * (yd + limit) * (yd + limit) * 0.5;
+          if (dist < field_array_size)
+          {
+            xd += (xd - blob_center.x) * wall_field[dist];
+            zd += (zd - blob_center.z) * wall_field[dist];
+            blob_force.y += (yd + limit);
+          }
+          else
+          {
+            dist = field_array_size * (yd - limit) * (yd - limit) * 0.5;
+            if (dist < field_array_size)
+            {
+              xd += (xd - blob_center.x) * wall_field[dist];
+              zd += (zd - blob_center.z) * wall_field[dist];
+              blob_force.y -= (yd - limit);
+            }
+          }
+          
+          if (xd < -limit) xd = -limit;
+          if (xd > limit) xd = limit;
+
+          dist = field_array_size * (xd + limit) * (xd + limit) * 0.5;
+          if (dist < field_array_size)
+          {
+            yd += (yd - blob_center.y) * wall_field[dist];
+            zd += (zd - blob_center.z) * wall_field[dist];
+            blob_force.x += (xd + limit);
+          }
+          else
+          {
+            dist = field_array_size * (xd - limit) * (xd - limit) * 0.5;
+            if (dist < field_array_size)
+            {
+              yd += (yd - blob_center.y) * wall_field[dist];
+              zd += (zd - blob_center.z) * wall_field[dist];
+              blob_force.x -= (xd - limit);
+            }
+          }
+          
+          if (yd < -limit) yd = -limit;
+          if (yd > limit) yd = limit;
+        }
+      }
+        
+      dots[index].x = xd;
+      dots[index].y = yd;
+      dots[index].z = zd;
+    }
+  }
+
+  /* Calculate the normals for each vertex and the texture mapping if required.
+   * Although the code actually calculates the normal for one of the triangles
+   * attached to a vertex rather than the vertex itself the results are not too
+   * bad for with a reasonable number of verticies.
+   */
+  for (y = 1; y < y_resolution - 1; y++)
+  {
+    int index1, index2, index3;
+    if (row_data[y - 1].num_x_points)
+    {
+
+      for (x = 0; x < row_data[y].num_x_points; x++)
+      {
+        if (x == row_data[y].num_x_points - 1)
+        {
+          index1 = y * x_resolution;
+        }
+        else
+        {
+          index1 = x + 1 + y * x_resolution;
+        }
+        index2 = x + y * x_resolution;
+        index3 = ((x + 0.5) * row_data[y - 1].num_x_points
+                  / row_data[y].num_x_points) + (y - 1) * x_resolution;
+        calculate_normal (dots[index1], dots[index2], dots[index3],
+                          &normals[index1]);
+        if (do_texture)
+        {
+          if (offset_texture)
+          {
+            tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
+              * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
+            tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
+              * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
+          }
+          else
+          {
+            tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
+                                                 / (0.5 * PI)));
+            tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
+                                                 / (0.5 * PI)));
+          }
+          tex_coords[index1].x *= tex_width[current_texture];
+          tex_coords[index1].y *= tex_height[current_texture];
+        }
+      }
+    }
+  }
+
+  freak += v_freak;
+  v_freak += -freak / 2000000.0;
+
+  /* Update position and strength of points used to distort the blob */
+  for (i = 0; i < field_points; i++)
+  {
+    field_data[i].vx += field_data[i].mx*(field_data[i].cx - field_data[i].ax);
+    field_data[i].vy += field_data[i].my*(field_data[i].cy - field_data[i].ay);
+    field_data[i].vpower += field_data[i].mpower
+      * (field_data[i].cpower - field_data[i].apower);
+
+    field_data[i].ax += 0.1 * field_data[i].vx;
+    field_data[i].ay += 0.1 * field_data[i].vy;
+    field_data[i].apower += 0.1 * field_data[i].vpower;
+
+    field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay)
+      * cos(PI * field_data[i].ax);
+    field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay);
+    field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay)
+      * sin(PI * field_data[i].ax);
+  }
+        
+  /* Update the center of the whole blob */
+  blob_velocity.x += (blob_anchor.x - blob_center.x) / 80.0
+    + 0.01 * blob_force.x / num_points;
+  blob_velocity.y += (blob_anchor.y - blob_center.y) / 80.0
+    + 0.01 * blob_force.y / num_points;
+  blob_velocity.z += (blob_anchor.z - blob_center.z) / 80.0
+    + 0.01 * blob_force.z / num_points;
+
+  blob_center.x += blob_velocity.x * 0.5;
+  blob_center.y += blob_velocity.y * 0.5;
+  blob_center.z += blob_velocity.z * 0.5;
+
+  blob_velocity.x *= 0.99;
+  blob_velocity.y *= 0.99;
+  blob_velocity.z *= 0.99;
+}
+
+/******************************************************************************
+ *
+ * Draw the blob shape.
+ *
+ * The horrendous indexing to calculate the verticies that form a particular
+ * traiangle is the result of the conversion of my first non-openGL version of
+ * blob to this openGL version.  This may be tidied up when I finally playing
+ * with the more interesting bits of the code.
+ */
+static void
+draw_blob (void) 
+{
+  int x, y, x2, x3;
+  int index1, index2, index3;
+  int lower, upper;
+
+  glMatrixMode (GL_MODELVIEW);
+  glLoadIdentity ();
+
+  /* Move down the z-axis. */
+  glTranslatef( 0.0, 0.0, -5.0 );
+
+  for (y = 1; y < y_resolution; y++)
+  {
+    if (row_data[y - 1].num_x_points)
+    {
+      for (x = 0; x < row_data[y].num_x_points; x++)
+      {
+        glBegin (GL_TRIANGLES);
+        if (x == row_data[y].num_x_points - 1)
+        {
+          index1 = y * x_resolution;
+        }
+        else
+        {
+          index1 = x + 1 + y * x_resolution;
+        }
+        index2 = x + y * x_resolution;
+        index3 = ((x + 0.5) * row_data[y - 1].num_x_points
+                  / row_data[y].num_x_points) + (y - 1) * x_resolution;
+        glArrayElement(index1);
+        glArrayElement(index2);
+        glArrayElement(index3);
+        glEnd();
+
+        lower = floorf((x - 0.5) * row_data[y - 1].num_x_points
+                       / (float)row_data[y].num_x_points);
+        upper = floorf((x + 0.5) * row_data[y - 1].num_x_points
+                       / (float)row_data[y].num_x_points);
+
+        if (upper > lower)
+        {
+          glBegin (GL_TRIANGLE_FAN);
+          index1 = x + y * x_resolution;
+          
+          for (x2 = lower; x2 <= upper; x2++)
+          {
+            x3 = x2;
+            while (x3 < 0) x3 += row_data[y - 1].num_x_points;
+            while (x3 >= row_data[y - 1].num_x_points)
+              x3 -= row_data[y - 1].num_x_points;
+            index2 = x3 + (y - 1) * x_resolution;
+
+            if (x2 < upper)
+            {
+              x3 = x2 + 1;
+              while (x3 < 0) x3 += row_data[y - 1].num_x_points;
+              while (x3 >= row_data[y - 1].num_x_points)
+                x3 -= row_data[y - 1].num_x_points;
+              index3 = x3 + (y - 1) * x_resolution;
+              if (x2 == lower)
+              {
+                glArrayElement(index1);
+              }
+            }
+            glArrayElement(index2);
+          }
+          glEnd ();
+        }
+      }
+    }
+  }
+}
+
+/******************************************************************************
+ *
+ * Draw the background image simply map a texture onto a full screen quad.
+ */
+static void
+draw_background (ModeInfo *mi)
+{
+  glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+  glEnable (GL_TEXTURE_2D);    
+  glDisable(GL_LIGHTING);
+
+  /* Reset the projection matrix to make it easier to get the size of the quad
+   * correct
+   */
+  glMatrixMode(GL_PROJECTION);
+  glPushMatrix();
+  glLoadIdentity();
+
+  glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0);
+
+  glBegin (GL_QUADS);
+    
+  glTexCoord2f (0.0, tex_height[current_texture]);
+  glVertex2i (0, 0);
+    
+  glTexCoord2f (0.0, 0.0);
+  glVertex2i (0, MI_HEIGHT(mi));
+    
+  glTexCoord2f (tex_width[current_texture], 0.0);
+  glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
+    
+  glTexCoord2f (tex_width[current_texture], tex_height[current_texture]);
+  glVertex2i (MI_WIDTH(mi), 0);
+  glEnd();
+
+  glPopMatrix ();
+  glMatrixMode (GL_MODELVIEW);
+  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+/******************************************************************************
+ *
+ * Update the scene.
+ */
+static GLvoid
+drawScene(ModeInfo * mi) 
+{
+  check_gl_error ("drawScene");
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+  glDisable (GL_BLEND);
+
+  /* Ensure the background is drawn with the correct texture */
+  if (do_texture)
+  {
+    glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
+  }
+
+  glColor4ub(255, 255, 255, 255);
+  if (do_paint_background && !do_wire)
+  {
+    draw_background (mi);
+
+    /* When transitioning between two images paint the new image over the old
+     * image with a varying alpha value to get a smooth fade.
+     */
+    if (transitioning)
+    {
+      glDisable (GL_DEPTH_TEST);
+      glEnable (GL_BLEND);
+      glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
+      glColor4ub (255, 255, 255, transitioning);
+
+      draw_background (mi);
+
+      glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
+      glEnable (GL_DEPTH_TEST);
+    }
+    /* Clear the depth buffer bit so the backgound is behind the blob */
+    glClear(GL_DEPTH_BUFFER_BIT);
+  }
+  else
+  {
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  }
+
+  calc_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5);
+
+  glEnable(GL_LIGHTING);
+  glEnable(GL_LIGHT0);
+  glEnable(GL_LIGHT1);
+
+  if (do_blend)
+  {
+    glEnable (GL_BLEND);
+    if (transitioning)
+    {
+      glColor4ub (255, 255, 255, 128 - transitioning / 2);
+    }
+    else
+    {
+      glColor4ub (255, 255, 255, 128);
+    }
+  }
+  else
+  {
+    glDisable (GL_BLEND);
+    glColor4ub (255, 255, 255, 255);
+  }
+  draw_blob();
+
+  /* While transitioning draw the blob twice with a modified alpha channel.
+   * The trasitioning state machine is very crude, it simply counts frames
+   * rather than elapsed time but it works.
+   */
+  if (do_texture)
+  {
+    if (transitioning)
+    {
+      glClear(GL_DEPTH_BUFFER_BIT);
+      glEnable (GL_BLEND);
+      glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
+      if (do_blend)
+      {
+        glColor4ub (255, 255, 255, transitioning / 2);
+      }
+      else
+      {
+        glColor4ub (255, 255, 255, transitioning);
+      }
+      draw_blob ();
+      transitioning += fade_speed;
+      if (transitioning > 255)
+      {
+        transitioning = 0;
+        current_texture = 1 - current_texture;
+        holding = 1;
+      }
+    }
+    if (holding)
+    {
+      holding++;
+      if (holding > hold_frames)
+      {
+        grab_texture (mi, 1 - current_texture);
+        transitioning = 1;
+        holding = 0;
+      }
+    }
+  }
+  
+  /* increment frame-counter */
+/*  frame++; */
+
+/*  check_gl_error ("drawScene"); */
+}
+
+
+void
+draw_mirrorblob(ModeInfo * mi)
+{
+  screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
+  Display    *display = MI_DISPLAY(mi);
+  Window      window = MI_WINDOW(mi);
+
+  if (!gp->glx_context)
+       return;
+
+  glXMakeCurrent(display, window, *(gp->glx_context));
+  drawScene(mi);
+  if (mi->fps_p) do_fps (mi);
+  glXSwapBuffers(display, window);
+}
+
+/* Standard reshape function */
+void
+reshape_mirrorblob(ModeInfo *mi, int width, int height)
+{
+  glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
+  resetProjection(width, height);
+}
+
+void
+init_mirrorblob(ModeInfo * mi)
+{
+  int screen = MI_SCREEN(mi);
+
+  screensaverstruct *gp;
+
+  if (Screensaver == NULL) {
+       if ((Screensaver = (screensaverstruct *)
+         calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
+         return;
+  }
+  gp = &Screensaver[screen];
+
+  gp->window = MI_WINDOW(mi);
+  if ((gp->glx_context = init_GL(mi)) != NULL) {
+       reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+       initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+  } else {
+       MI_CLEARWINDOW(mi);
+  }
+  /* Set the environment variable used by NVIDIA cards to control updating
+   * during the blankning interval */ 
+  setenv ("__GL_SYNC_TO_VBLANK", "1", 1);
+  setenv ("__GL_FSAA_MODE", "1", 1);
+
+  initialise_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
+}
+
+
+/* all sorts of nice cleanup code should go here! */
+void release_mirrorblob(ModeInfo * mi)
+{
+  int screen;
+
+  if (row_data) free(row_data);
+  if (field_data) free(field_data);
+  if (colours) free(colours);
+  if (tex_coords) free(tex_coords);
+  if (dots) free(dots);
+  if (wall_field) free(wall_field);
+  if (field) free(field);
+
+  if (Screensaver != NULL) {
+       for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
+/*       screensaverstruct *gp = &Screensaver[screen];*/
+       }
+       (void) free((void *) Screensaver);
+       Screensaver = NULL;
+  }
+  FreeAllGL(mi);
+}
+#endif
diff --git a/hacks/glx/mirrorblob.man b/hacks/glx/mirrorblob.man
new file mode 100644 (file)
index 0000000..af5965d
--- /dev/null
@@ -0,0 +1,111 @@
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+mirrorblob - Draws a wobbly blob that distorts the image behind it.
+.SH SYNOPSIS
+.B mirrorblob
+[\-display \fIhost:display.screen\fP]
+[\-visual \fIvisual\fP]
+[\-window]
+[\-root]
+[\-wire]
+[\-delay \fInumber\fP]
+[\-fog]
+[\-walls]
+[\-colour]
+[\-texture]
+[\-bgimage]
+[\-offset_texture]
+[\-blend]
+[\-antialias]
+[\-x_res \fInumber\fP]
+[\-y_res \fInumber\fP]
+[\-field_points \fInumber\fP]
+[\-hold_frames \fInumber\fP]
+[\-fps]
+.SH DESCRIPTION
+Draws a wobbling blob, making use of alpha blending, fog,
+textures, and lighting, plus a ``frames per second'' meter so that you can
+tell how fast your graphics card is... Requires OpenGL.
+.SH OPTIONS
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.B \-wire
+Render in wireframe instead of solid.
+.TP 8
+.B \-delay \fInumber\fP
+Per-frame delay, in microseconds.  Default: 10000 (0.01 seconds.).
+.TP 8
+.B \-fog | \-no-fog
+Whether to enable fog.
+.TP 8
+.B \-walls | \-no-walls
+Add walls for blob to hit.
+.TP 8
+.B \-colour | \-no-colour
+Draw coloured blob.  If also textured, the texture will have color mixed in.
+.TP 8
+.B \-texture | \-no-texture
+Whether to wrap a texture image on the blob.
+.TP 8
+.B \-bgimage | \-no-bgimage
+Whether to also draw the texture on the background.
+.TP 8
+.B \-offset_texture | \-no-offset_texture
+Whether to ofset the texture calculations to only use a region of the image
+ under the blob.  This works well when blend is enabled.
+.TP 8
+.B \-blend | \-no-blend
+Whether to enable blending. (see also offset_texture above)
+.TP 8
+.B \-antialias | \-no-antialias
+Whether to anti-alias lines.
+.TP 8
+.B \-x_res \fInumber\fP
+Number of verticies around the middle row of the blob (see also y_res).
+.TP 8
+.B \-y_res \fInumber\fP
+Number of rows of verticies used to calculatethe blob, combined with x_res to
+ indirectly determine the number of triangles used to draw blob.  Larger
+ numbers give a smoother blob but increase calculation times exponentially.
+.TP 8
+.B \-field_points \fInumber\fP
+Number of points used to distort the blob.
+.TP 8
+.B \-hold_frames \fInumber\fP
+Number of frames between changing images.
+.TP 8
+.B \-fade_speed \fInumber\fP
+Speed at which fading occurs when changing images.
+.TP 8
+.B \-fps | \-no-fps
+Whether to show a frames-per-second display at the bottom of the screen.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1)
+.SH COPYRIGHT
+Copyright \(co 2003 by Jon Dowdall.  Permission to use, copy, modify, 
+distribute, and sell this software and its documentation for any purpose is 
+hereby granted without fee, provided that the above copyright notice appear 
+in all copies and that both that copyright notice and this permission notice
+appear in supporting documentation.  No representations are made about the 
+suitability of this software for any purpose.  It is provided "as is" without
+express or implied warranty.
+.SH AUTHOR
+Jon Dowdall.
index eebef51d0a665339be2bf9edd2946970fe57e9e7..f9693ae16f348ed4cc4f5c4224c0475c942fee48 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* moebius --- Moebius Strip II, an Escher-like GL scene with ants. */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)moebius.c    5.01 2001/03/01 xlockmore";
-
 #endif
 
 /*-
index ab3613c5e97c506c176c70ad87dfcef33b0d805c..f2691fa8306c8115db183988d6ace909fb8de366 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* morph3d --- Shows 3D morphing objects */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)morph3d.c    5.01 2001/03/01 xlockmore";
-
 #endif
 
 #undef DEBUG_CULL_FACE
index e89f700944e47cf0d79e6b301f328528f9d2a726..7aa01a5b212a15730eeef8868f431b567d8b68ad 100644 (file)
@@ -1,6 +1,5 @@
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)pipeobjs.c   4.04 97/07/28 xlockmore";
-
 #endif
 
 /*-
index db1e792daddef208426799d0d8eaa0f3028170a4..e20266b573f182a69ef7f2c6770e5a3b5b75b0d0 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* pipes --- 3D selfbuiding pipe system */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)pipes.c      4.07 97/11/24 xlockmore";
-
 #endif
 
 /*-
index d4a9eaa53669d14ced50e3e78ebd8b397ad529cf..911efeb951b05bbed6bc59e6ea659bbec9c8f3d1 100644 (file)
@@ -1,8 +1,7 @@
 /* polytopes --- Shows one of the six regular polytopes rotating in 4d */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)polytopes.c  1.1 03/05/18 xlockmore";
-
 #endif
 
 /* Copyright (c) 2003 Carsten Steger <carsten@mirsanmir.org>. */
index f57b5328387a0dc1a501df0dd487d6c3798cb300..034122c5ae9de26bd54ed666e7265f86b829a631 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* rubik --- Shows an auto-solving Rubik's cube */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)rubik.c      5.01 2001/03/01 xlockmore";
-
 #endif
 
 /*-
index 442bcb05d2ddbb74a727eba55c382e1efa586429..25300483e1585eacab093e4c9fe87d0d0d2ff55f 100644 (file)
@@ -1,6 +1,6 @@
 /* sballs --- balls spinning like crazy in GL */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)sballs.c     5.02 2001/03/10 xlockmore";
 #endif
 
index a3de1b9e2dfd9a7a5404598c57cd31b29d41ad55..3658db6a8ca258e5847af96e05ab5773f52a6a7c 100644 (file)
@@ -1,8 +1,7 @@
 /* atlantis --- Shows moving 3D sea animals */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)shark.c      1.2 98/06/16 xlockmore";
-
 #endif
 
 /* Copyright (c) E. Lassauge, 1998. */
index 3a0a158b0989af4e4d58d8247cd2c31361cb6462..d3df8cdf1bf583b077072cbc05ecfc1564a76b68 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* Sierpinski3D --- 3D sierpinski gasket */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)sierpinski3D.c       00.01 99/11/04 xlockmore";
-
 #endif
 
 /*-
index a4c06902f2b9286f95d8ca319d0d9bf0876cdcce..8e45a4a4266cf3645f9900594e42724a0af2325f 100644 (file)
@@ -768,7 +768,7 @@ static void
 draw_label (ModeInfo *mi, const char *s)
 {
   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
-  int i;
+  unsigned int i;
   
   glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT);
   glDisable(GL_LIGHTING);
@@ -871,7 +871,7 @@ init_spheremonics (ModeInfo *mi)
 
   cc->m_max = 4; /* 9? */
   {
-    int i;
+    unsigned int i;
     for (i = 0; i < countof(cc->dm); i++)
       cc->dm[i] = 1;  /* going up! */
 
index 13974a79d08a71f1d00643f8f4444753b7793413..6248ec954d4d948d5cbef8d528893c086d0a868b 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* sproingies.c - 3D sproingies */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)sproingies.c 4.04 97/07/28 xlockmore";
-
 #endif
 
 /*-
index d00197d77c4663d01bb3b658e10ffb7a327ef2ce..34db7b5634dca19b19403adf050f1fd00785a777 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* sproingiewrap.c - sproingies wrapper */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)sproingiewrap.c      4.07 97/11/24 xlockmore";
-
 #endif
 
 /*-
index 33e73ab03f9318af5d9bb557ce9b6ca1c8948e88..53a30faf73d85493a90a9223caf09258b7e93ebe 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* stairs --- Infinite Stairs, and Escher-like scene. */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)stairs.c     4.07 97/11/24 xlockmore";
-
 #endif
 
 #undef DEBUG_LISTS
index 197f323f476171d29db72c7c78e5348045224fd0..5e41af2a7c8ab386ad8dfba8919fcf026b7258c0 100644 (file)
@@ -443,7 +443,7 @@ get_more_lines (sws_configuration *sc)
           if (sc->buf_tail > (s - sc->buf))
             {
               int i = sc->buf_tail - (s - sc->buf);
-              memcpy (sc->buf, s, i);
+              memmove (sc->buf, s, i);
               sc->buf_tail = i;
               sc->buf[sc->buf_tail] = 0;
             }
index f2a83b6e66c0b9ca3e30c2bc95d8e037ddbef80b..050d40a355b5b9dde9e8b007c37cd02ae895fab4 100644 (file)
@@ -71,12 +71,11 @@ won't.
 
 Some examples:
 .EX
-starwars -columns 30 -program \\
-  'wget -qO- http://webcrawler.com/cgi-bin/SearchTicker'
-starwars -columns 76 -program 'cat /usr/src/linux/README'
+starwars -columns 76 -program 'cat /usr/src/linux*/README'
 starwars -program 'ping www.starwars.com'
 starwars -no-wrap -left -program 'finger @gnu.org'
 starwars -no-wrap -left -program 'ps -ef'
+starwars -no-wrap -left -program 'od -txC /dev/random'
 .EE
 .TP 8
 .B \-size \fIinteger\fP
@@ -138,8 +137,17 @@ to get the default host and display number.
 to get the name of a resource file that overrides the global resources
 stored in the RESOURCE_MANAGER property.
 .SH SEE ALSO
-.BR X (1),
-.BR xscreensaver (1)
+.BR xscreensaver (1),
+.BR fortune (1),
+.BR phosphor (1),
+.BR apple2 (1),
+.BR fontglide (1),
+.BR ljlatest (1),
+.BR dadadodo (1),
+.BR webcollage (1),
+.BR driftnet (1)
+.BR EtherPEG ,
+.BR EtherPeek
 .SH COPYRIGHT
 Copyright \(co 1998-2001 by Jamie Zawinski and Claudio Matsuoka.
 Permission to use, copy, modify, distribute, and sell this software and
index b926f62b2ed7e81580e350a6fc3ae035fd447c4f..1ac784cb400a822f12963dd2348472f2a0d6be48 100644 (file)
 #include <GL/gl.h>
 
 #include "yarandom.h"
+#include "usleep.h"
 #include "stonerview-move.h"
 
 extern int init_view(int *argc, char *argv[]);
 extern void win_draw(void);
 
 
-static void 
-stonerview_usleep (unsigned long usecs)
-{
-  struct timeval tv;
-  tv.tv_sec  = usecs / 1000000L;
-  tv.tv_usec = usecs % 1000000L;
-  (void) select (0, 0, 0, 0, &tv);
-}
-
 int main(int argc, char *argv[])
 {
 
@@ -58,7 +50,7 @@ int main(int argc, char *argv[])
     {
       win_draw();
       move_increment();
-      stonerview_usleep (20000);
+      usleep (20000);
     }
 
   return 0;
index b711debd7a3812ddccabe8c43edebf26074bc5c3..f274741b5557ef854a0ef1f3ebd6eca5b5006667 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* superquadrics --- 3D mathematical shapes */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)superquadrics.c      4.07 97/11/24 xlockmore";
-
 #endif
 
 /*-
index 0848d46db259f49af1bbc477f034bee971106c63..a7fccfeb41fb7b548b1f20eabaa56e982d723941 100644 (file)
@@ -1,8 +1,7 @@
 /* atlantis --- Shows moving 3D sea animals */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)swim.c       1.3 98/06/18 xlockmore";
-
 #endif
 
 /* Copyright (c) E. Lassauge, 1998. */
index 719541003a7e51ea0ffca632cf43475f77cbac77..94e7bc0e661837d2f3499406ad97238d45b6f04a 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* atunnels --- OpenGL Advanced Tunnel Demo */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)tunnel_draw.c        5.02 2002/03/16 xlockmore";
 #endif
 
index afff1d381310290dd227010fb313975558d646cb..15826e4f8894f55bf7f3e45acf69ba0ce15cea30 100644 (file)
@@ -1,8 +1,7 @@
 /* atlantis --- Shows moving 3D sea animals */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)whale.c      1.3 98/06/18 xlockmore";
-
 #endif
 
 /* Copyright (c) E. Lassauge, 1998. */
index ccadc2a6a25569d675b9a23d4c593192719d3210..aec897d559de8a0e7fb4d6c3a05f2001fd41830e 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* grav --- planets spinning around a pulsar */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)grav.c       5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 2c002b54b994a4523a51efdc486aa5df9a2949bd..fc69ea730af60d27e8f3709fba42ce335a0ababe 100644 (file)
@@ -257,7 +257,7 @@ run_circles (Display *dpy, Window window)
   if (cycle_p && cmode != seuss_mode)
     {
       struct timeval now;
-      static struct timeval then = { 0, };
+      static struct timeval then = { 0, };
       unsigned long diff;
 #ifdef GETTIMEOFDAY_TWO_ARGS
       struct timezone tzp;
index 3e05531b45e37bbeebd87d921885550454d70e63..7ca03da7760377aed93d87006bac39bb71ae977d 100644 (file)
@@ -307,7 +307,7 @@ char *defaults [] = {
 XrmOptionDescRec options [] = {   
   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
   { "-subdelay",        ".subdelay",            XrmoptionSepArg, 0 },
-  { 0 },
+  { 0,                 0,                      0,               0 },
 };
 int options_size = (sizeof (options) / sizeof (options[0]));
 
index 949c5b9f1086336c4b9551022ba1ddebfeea3f6a..71bbdc8e911f91a859f866fe3bda5ea96209a289 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* hop --- real plane fractals */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)hop.c        5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index e3e0dc62a658a45d707d1f7d108edd31dbeb5756..29a72c23144d59f3d0b93e42ee91ba284fc765dd 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* ifs --- modified iterated functions system */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)ifs.c        5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 5db775d227af1a29138ba502c95b68b1e75c993d..fad6a8ca4dedf993df6257e681277f1ef84c60c4 100644 (file)
@@ -419,7 +419,7 @@ draw_map (Display *dpy, Window window)
              if (cycle_p)
                {
                  struct timeval now;
-                 static struct timeval then = { 0, };
+                 static struct timeval then = { 0, };
                  unsigned long diff;
 #ifdef GETTIMEOFDAY_TWO_ARGS
                  struct timezone tzp;
index ca52f59d8870f0e77181e835274dbcfa1263cb62..6c28205f508f841229a2b300704179e4a42b1adc 100644 (file)
@@ -150,7 +150,7 @@ read_screen (Display *dpy, Window window, int *widthP, int *heightP)
 
   p = XCreatePixmap(dpy, window, *widthP, *heightP, xgwa.depth);
   XClearWindow(dpy, window);
-  load_random_image (xgwa.screen, window, p);
+  load_random_image (xgwa.screen, window, p, NULL);
   XClearWindow(dpy, window);
 
   return p;
index 2650ad27cf483ac2dafe6d76f2c1a93c354332b8..8080ccb334b35ab485748b2950cef06aa37af662 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* juggle */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)juggle.c     5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
@@ -1474,7 +1473,13 @@ draw_juggle(ModeInfo * mi)
 
        {
          struct timeval tv;
-         (void)gettimeofday(&tv, NULL);
+# ifdef GETTIMEOFDAY_TWO_ARGS
+      struct timezone tzp;
+         gettimeofday(&tv, &tzp);
+# else
+         gettimeofday(&tv);
+# endif
+
          sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
        }
        for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
index 29cbb558c8c5a1306a7d7ebce7c2f3b653f6e863..bff47db3127f8157ef7de3ceb5e6f9e02ce4b1d6 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*-
  * julia --- continuously varying Julia set.
  */
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)julia.c      4.03 97/04/10 xlockmore";
 #endif
 
index 2b0622dd2813bc8c7fd791b7ebe07df7a3784d86..adce89313a8858d38fa7094f6214c6dc96c11833 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* laser --- spinning lasers */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)laser.c      5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index deb37411de1e621b4a9560098fc5be1c33493ea5..8da6bb58d718bfea49accfea251e6435220d0dbd 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* lightning --- fractal lightning bolds */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)lightning.c  5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 63d7d8371077331a85466ff9bc132eb4c0625c18..235ceab9b187840ddd40a666fe1db765feb4c984 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* lisa --- animated full-loop lisajous figures */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)lisa.c       5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 1f2e7c150b32cfeecf906f7fea101c19206f0e06..f0ce93d7c163fe1ee2e6dbf0505036bf9ec99beb 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* lissie --- the Lissajous worm */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)lissie.c     5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 2af69a2f2921f0bca8fe8947b762aa9c0e5b88a6..c0856fda12bb98a93a2cab1b02820b95510dc183 100755 (executable)
@@ -27,7 +27,7 @@ use Text::Wrap qw(wrap);
 use bytes;  # Larry can take Unicode and shove it up his ass sideways.
 
 my $progname = $0; $progname =~ s@.*/@@g;
-my $version = q{ $Revision: 1.8 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
+my $version = q{ $Revision: 1.9 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
 
 my $verbose = 0;
 
@@ -275,14 +275,19 @@ sub lj_latest {
 
   $|=1;  # unbuffer stdout
 
-  if ($verbose) {
-    $_ = $url;
-    s@^[a-z]+:/+([^/?\#]+).*$@$1@;
-    my $host = $_;
-    print STDOUT "Contacting $host...";
-  }
+  $_ = $url;
+  s@^[a-z]+:/+([^/?\#]+).*$@$1@;
+  my $host = $_;
+
+  print STDOUT "Contacting $host..." if ($verbose);
 
   my ($http, $head, $body) = get_document ($url);
+
+  if (!$body) {
+    print STDOUT "$progname: no response from $host\n";
+    return;
+  }
+
   print STDOUT "\n\n" if ($verbose);
 
 #  $body = `cat /tmp/last`;
index 1bbc7d1124923d4df8219b29511b293cf2023dcc..00184aed17dcfc47e0007f983c7d707069ae7a63 100644 (file)
@@ -32,8 +32,9 @@ for use with those
 .BR xscreensaver (1)
 programs that run an external program to generate text.  For example:
 .EX
-phosphor -scale 2 -program 'ljlatest --cols 40'
+fontglide -program ljlatest
 starwars -program ljlatest
+phosphor -scale 2 -program 'ljlatest --cols 40'
 .EE
 .SH OPTIONS
 .I ljlatest
@@ -72,7 +73,9 @@ The output is always ISO-8859-1, regardless of locale.
 .BR xscreensaver (1),
 .BR fortune (1),
 .BR phosphor (1),
+.BR apple2 (1),
 .BR starwars (1),
+.BR fontglide (1),
 .BR dadadodo (1),
 .BR webcollage (1),
 .BR driftnet (1)
index 04419801919d5681feb0379b49a574964022e3aa..f492ee44c4d92cf4224666e5d9591fd4b8125896 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* loop --- Chris Langton's self-producing loops */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)loop.c       5.01 2000/03/15 xlockmore";
-
 #endif
 
 /*-
index cc8047936b79cfc6224c0978ca6d73c0d6c0e136..d814a80c29cd4ddabd5e44d2e90cea825ef740d3 100644 (file)
@@ -56,7 +56,7 @@ typedef struct
   short xpos,ypos;
 } BLOB;
 
-static unsigned char nBlobCount;
+static unsigned int nBlobCount;
 static unsigned char radius;
 static unsigned char delta;
 static unsigned char dradius;
index 6b2378891cac7b2951f60623bba66019b45c86f3..037101aaed5f2ab62295ed81896876ca6197e865 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* mountain -- square grid mountains */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)mountain.c   5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index aa62ab03634250dda84b08e3712a6e2cb0956ea8..03e93ac9c51233bb8b52b10cfa65f5c58ef53b31 100644 (file)
@@ -18,9 +18,8 @@
                                 http://www.nine.org/notw/notw.html
  */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)penrose.c    5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 8298cf2551ee67f265563eee839493923b346fbc..8ba63dd28390b4f467474981837dcf5d06c8f5f7 100644 (file)
@@ -450,7 +450,6 @@ static void
 set_cursor (p_state *state, Bool on)
 {
   if (set_cursor_1 (state, on))
-;
     {
       if (state->cursor_timer)
         XtRemoveTimeOut (state->cursor_timer);
index 3d1fbe12d0e9ef0b087229700c4a234720148cd5..989150ec46367b1a6d0fc3e1da49f16f11c7335b 100644 (file)
@@ -75,10 +75,12 @@ will work, but programs like
 .BR top (1)
 won't.
 
-Here's a good trick, to get phosphor to display recent web search terms:
+For example:
 .EX
-phosphor -program \\
-  'wget -qO- http://webcrawler.com/cgi-bin/SearchTicker'
+phosphor -program 'cat /usr/src/linux*/README'
+phosphor -program 'ping localhost'
+phosphor -program 'ps -e'
+phosphor -program 'od -txC -w6 /dev/random'
 .EE
 .SH ENVIRONMENT
 .PP
@@ -90,9 +92,17 @@ to get the default host and display number.
 to get the name of a resource file that overrides the global resources
 stored in the RESOURCE_MANAGER property.
 .SH SEE ALSO
-.BR wget (1),
-.BR X (1),
-.BR xscreensaver (1)
+.BR xscreensaver (1),
+.BR fortune (1),
+.BR apple2 (1),
+.BR starwars (1),
+.BR fontglide (1),
+.BR ljlatest (1),
+.BR dadadodo (1),
+.BR webcollage (1),
+.BR driftnet (1)
+.BR EtherPEG ,
+.BR EtherPeek
 .SH COPYRIGHT
 Copyright \(co 1999 by Jamie Zawinski.  Permission to use, copy, modify, 
 distribute, and sell this software and its documentation for any purpose is 
index 87b5c744a722d4040dbdb6e002225960156f5f72..3ed5227ba54bf1b41459f9789fde89b389e2f8ab 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* polyominoes --- Shows attempts to place polyominoes into a rectangle */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)polyominoes.c 5.01 2000/12/18 xlockmore";
-
 #endif
 
 /*
diff --git a/hacks/pong.c b/hacks/pong.c
new file mode 100644 (file)
index 0000000..bcfc3fc
--- /dev/null
@@ -0,0 +1,505 @@
+/* pong, Copyright (c) 2003 Jeremy English <jenglish@myself.com>
+ * A pong screen saver
+ *
+ * Modified by Trevor Blackwell <tlb@tlb.org> to use analogtv.[ch] display.
+ * Also added gradual acceleration of the ball, shrinking of paddles, and
+ * scorekeeping.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/*
+ * TLB sez: I haven't actually seen a pong game since I was about 9. Can
+ * someone who has one make this look more realistic? Issues:
+ *
+ *  - the font for scores is wrong. For example '0' was square.
+ *  - was there some kind of screen display when someone won?
+ *  - did the ball move smoothly, or was the X or Y position quantized?
+ *
+ * It could also use better player logic: moving the paddle even when the ball
+ * is going away, and making mistakes instead of just not keeping up with the
+ * speeding ball.
+ *
+ * There is some info at http://www.mameworld.net/discrete/Atari/Atari.htm#Pong
+ *
+ * It says that the original Pong game did not have a microprocessor, or even a
+ * custom integrated circuit. It was all discrete logic.
+ *
+ */
+
+#include "screenhack.h"
+#include "analogtv.h"
+/* #define OUTPUT_POS */
+
+typedef struct _paddle {
+  int x;
+  int y;
+  int w;
+  int h;
+  int wait;
+  int lock;
+  int score;
+} Paddle;
+
+typedef struct _ball {
+  int x;
+  int y;
+  int w;
+  int h;
+} Ball;
+
+static int delay;
+Paddle l_paddle;
+Paddle r_paddle;
+Ball ball;
+static int bx,by;
+static int m_unit;
+static int paddle_rate;
+
+static analogtv *tv;
+static analogtv_input *inp;
+static analogtv_reception reception;
+
+static int paddle_ntsc[4];
+static int field_ntsc[4];
+static int ball_ntsc[4];
+static int score_ntsc[4];
+static int net_ntsc[4];
+
+analogtv_font score_font;
+
+enum {
+  PONG_W = ANALOGTV_VIS_LEN,
+  PONG_H = ANALOGTV_VISLINES,
+  PONG_TMARG = 10
+};
+
+static void
+hit_top_bottom(void)
+{
+  if ( (ball.y <= PONG_TMARG) ||
+       (ball.y+ball.h >= PONG_H) )
+    by=-by;
+}
+
+void
+start_game(void)
+{
+  /*Init the ball*/
+  ball.x = PONG_W/2;
+  ball.y = PONG_H/2;
+  bx = m_unit;
+  by = m_unit;
+
+  l_paddle.wait = 1;
+  l_paddle.lock = 0;
+  r_paddle.wait = 0;
+  r_paddle.lock = 0;
+  paddle_rate = m_unit-1;
+
+  if (l_paddle.h > 10) l_paddle.h= l_paddle.h*19/20;
+  if (r_paddle.h > 10) r_paddle.h= r_paddle.h*19/20;
+}
+
+static void
+hit_paddle(void)
+{
+  if ( ball.x + ball.w >= r_paddle.x &&
+       bx > 0 ) /*we are traveling to the right*/
+    {
+      if ((ball.y + ball.h > r_paddle.y) &&
+          (ball.y < r_paddle.y + r_paddle.h))
+        {
+          bx=-bx;
+          l_paddle.wait = 0;
+          r_paddle.wait = 1;
+          r_paddle.lock = 0;
+          l_paddle.lock = 0;
+        }
+      else
+        {
+          r_paddle.score++;
+          start_game();
+        }
+    }
+
+  if (ball.x <= l_paddle.x + l_paddle.w &&
+      bx < 0 ) /*we are traveling to the left*/
+    {
+      if ( ball.y + ball.h > l_paddle.y &&
+           ball.y < l_paddle.y + l_paddle.h)
+        {
+          bx=-bx;
+          l_paddle.wait = 1;
+          r_paddle.wait = 0;
+          r_paddle.lock = 0;
+          l_paddle.lock = 0;
+        }
+      else
+        {
+          l_paddle.score++;
+          start_game();
+        }
+    }
+}
+
+static void
+init_pong (Display *dpy, Window window)
+{
+  tv=analogtv_allocate(dpy, window);
+  analogtv_set_defaults(tv, "");
+  tv->event_handler = screenhack_handle_event;
+
+  analogtv_make_font(dpy, window, &score_font,
+                     4, 6, NULL );
+
+  /* If you think we haven't learned anything since the early 70s,
+     look at this font for a while */
+  analogtv_font_set_char(&score_font, '0',
+                        "****"
+                        "*  *"
+                        "*  *"
+                        "*  *"
+                        "*  *"
+                        "****");
+  analogtv_font_set_char(&score_font, '1',
+                        "   *"
+                        "   *"
+                        "   *"
+                        "   *"
+                        "   *"
+                        "   *");
+  analogtv_font_set_char(&score_font, '2',
+                        "****"
+                        "   *"
+                        "****"
+                        "*   "
+                        "*   "
+                        "****");
+  analogtv_font_set_char(&score_font, '3',
+                        "****"
+                        "   *"
+                        "****"
+                        "   *"
+                        "   *"
+                        "****");
+  analogtv_font_set_char(&score_font, '4',
+                        "*  *"
+                        "*  *"
+                        "****"
+                        "   *"
+                        "   *"
+                        "   *");
+  analogtv_font_set_char(&score_font, '5',
+                        "****"
+                        "*   "
+                        "****"
+                        "   *"
+                        "   *"
+                        "****");
+  analogtv_font_set_char(&score_font, '6',
+                        "****"
+                        "*   "
+                        "****"
+                        "*  *"
+                        "*  *"
+                        "****");
+  analogtv_font_set_char(&score_font, '7',
+                        "****"
+                        "   *"
+                        "   *"
+                        "   *"
+                        "   *"
+                        "   *");
+  analogtv_font_set_char(&score_font, '8',
+                        "****"
+                        "*  *"
+                        "****"
+                        "*  *"
+                        "*  *"
+                        "****");
+  analogtv_font_set_char(&score_font, '9',
+                        "****"
+                        "*  *"
+                        "****"
+                        "   *"
+                        "   *"
+                        "   *");
+
+  score_font.y_mult *= 2;
+  score_font.x_mult *= 2;
+
+#ifdef OUTPUT_POS
+  printf("screen(%d,%d,%d,%d)\n",0,0,PONG_W,PONG_H);
+#endif
+
+  inp=analogtv_input_allocate();
+  analogtv_setup_sync(inp, 0, 0);
+
+  reception.input = inp;
+  reception.level = 2.0;
+  reception.ofs=0;
+#if 0
+  if (random()) {
+    reception.multipath = frand(1.0);
+  } else {
+#endif
+    reception.multipath=0.0;
+#if 0
+  }
+#endif
+
+  delay = get_integer_resource ("delay", "Integer");
+  if (delay < 0) delay = 0;
+
+  /*Init the paddles*/
+  l_paddle.x = 8;
+  l_paddle.y = 100;
+  l_paddle.w = 16;
+  l_paddle.h = PONG_H/4;
+  l_paddle.wait = 1;
+  l_paddle.lock = 0;
+  r_paddle = l_paddle;
+  r_paddle.x = PONG_W - 8 - r_paddle.w;
+  r_paddle.wait = 0;
+  /*Init the ball*/
+  ball.x = PONG_W/2;
+  ball.y = PONG_H/2;
+  ball.w = 16;
+  ball.h = 8;
+
+  m_unit = get_integer_resource ("speed", "Integer");
+
+  start_game();
+
+  analogtv_lcp_to_ntsc(ANALOGTV_BLACK_LEVEL, 0.0, 0.0, field_ntsc);
+  analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, ball_ntsc);
+  analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, paddle_ntsc);
+  analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, score_ntsc);
+  analogtv_lcp_to_ntsc(100.0, 0.0, 0.0, net_ntsc);
+
+  analogtv_draw_solid(inp,
+                      ANALOGTV_VIS_START, ANALOGTV_VIS_END,
+                      ANALOGTV_TOP, ANALOGTV_BOT,
+                      field_ntsc);
+}
+
+static void
+p_logic(Paddle *p)
+{
+  int targ;
+  if (bx > 0) {
+    targ = ball.y + by * (r_paddle.x-ball.x) / bx;
+  }
+  else if (bx < 0) {
+    targ = ball.y - by * (ball.x - l_paddle.x - l_paddle.w) / bx;
+  }
+  else {
+    targ = ball.y;
+  }
+  if (targ > PONG_H) targ=PONG_H;
+  if (targ < 0) targ=0;
+
+  if (targ < p->y && !p->lock)
+  {
+    p->y -= paddle_rate;
+  }
+  else if (targ > (p->y + p->h) && !p->lock)
+  {
+    p->y += paddle_rate;
+  }
+  else
+  {
+    int move=targ - (p->y + p->h/2);
+    if (move>paddle_rate) move=paddle_rate;
+    if (move<-paddle_rate) move=-paddle_rate;
+    p->y += move;
+    p->lock = 1;
+  }
+}
+
+static void
+p_hit_top_bottom(Paddle *p)
+{
+  if(p->y <= PONG_TMARG)
+  {
+    p->y = PONG_TMARG;
+  }
+  if((p->y + p->h) >= PONG_H)
+  {
+    p->y = PONG_H - p->h;
+  }
+}
+
+/*
+  XFillRectangle (dpy, window, gc, p->x, p->y, p->w, p->h);
+  if (old_v > p->y)
+  {
+    XClearArea(dpy,window, p->x, p->y + p->h,
+      p->w, (old_v + p->h) - (p->y + p->h), 0);
+  }
+  else if (old_v < p->y)
+  {
+    XClearArea(dpy,window, p->x, old_v, p->w, p->y - old_v, 0);
+  }
+*/
+static void
+paint_paddle(analogtv_input *inp, Paddle *p)
+{
+  analogtv_draw_solid(inp,
+                      ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
+                      ANALOGTV_TOP, ANALOGTV_BOT,
+                      field_ntsc);
+
+  analogtv_draw_solid(inp,
+                      ANALOGTV_VIS_START + p->x, ANALOGTV_VIS_START + p->x + p->w,
+                      ANALOGTV_TOP + p->y, ANALOGTV_TOP + p->y + p->h,
+                      paddle_ntsc);
+}
+
+/*
+  XClearArea(dpy,window, old_ballx, old_bally, ball.d, ball.d, 0);
+  XFillRectangle (dpy, window, gc, ball.x, ball.y, ball.d, ball.d);
+  XFillRectangle (dpy, window, gc, xgwa.width / 2, 0, ball.d, xgwa.height);
+*/
+
+static void
+erase_ball(analogtv_input *inp)
+{
+  analogtv_draw_solid(inp,
+                      ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w,
+                      ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h,
+                      field_ntsc);
+}
+
+static void
+paint_ball(analogtv_input *inp)
+{
+  analogtv_draw_solid(inp,
+                      ANALOGTV_VIS_START + ball.x, ANALOGTV_VIS_START + ball.x + ball.w,
+                      ANALOGTV_TOP + ball.y, ANALOGTV_TOP + ball.y + ball.h,
+                      ball_ntsc);
+}
+
+static void
+paint_score(analogtv_input *inp)
+{
+  char buf[256];
+
+  analogtv_draw_solid(inp,
+                      ANALOGTV_VIS_START, ANALOGTV_VIS_END,
+                      ANALOGTV_TOP, ANALOGTV_TOP + 10+ score_font.char_h * score_font.y_mult,
+                      field_ntsc);
+
+  sprintf(buf, "%d",r_paddle.score%256);
+  analogtv_draw_string(inp, &score_font, buf,
+                       ANALOGTV_VIS_START + 130, ANALOGTV_TOP + 8,
+                       score_ntsc);
+
+  sprintf(buf, "%d",l_paddle.score%256);
+  analogtv_draw_string(inp, &score_font, buf,
+                       ANALOGTV_VIS_END - 200, ANALOGTV_TOP + 8,
+                       score_ntsc);
+
+}
+
+static void
+paint_net(analogtv_input *inp)
+{
+  int x,y;
+
+  x=(ANALOGTV_VIS_START + ANALOGTV_VIS_END)/2;
+
+  for (y=ANALOGTV_TOP; y<ANALOGTV_BOT; y+=6) {
+    analogtv_draw_solid(inp, x-2, x+2, y, y+3,
+                        net_ntsc);
+    analogtv_draw_solid(inp, x-2, x+2, y+3, y+6,
+                        field_ntsc);
+
+  }
+}
+
+static void
+play_pong (void)
+{
+  erase_ball(inp);
+
+  ball.x += bx;
+  ball.y += by;
+
+  if ((random()%40)==0) {
+    if (bx>0) bx++; else bx--;
+  }
+
+  if (!r_paddle.wait)
+  {
+    p_logic(&r_paddle);
+  }
+  if (!l_paddle.wait)
+  {
+    p_logic(&l_paddle);
+  }
+
+  p_hit_top_bottom(&r_paddle);
+  p_hit_top_bottom(&l_paddle);
+
+  hit_top_bottom();
+  hit_paddle();
+
+  #ifdef OUTPUT_POS
+  printf("(%d,%d,%d,%d)\n",ball.x,ball.y,ball.w,ball.h);
+  #endif
+
+  paint_score(inp);
+
+  paint_net(inp);
+
+  if (1) {
+    paint_paddle(inp, &r_paddle);
+    paint_paddle(inp, &l_paddle);
+  }
+  if (1) paint_ball(inp);
+
+  analogtv_handle_events(tv);
+
+  analogtv_init_signal(tv, 0.04);
+  analogtv_reception_update(&reception);
+  analogtv_add_signal(tv, &reception);
+  analogtv_draw(tv);
+}
+
+\f
+char *progclass = "pong";
+
+char *defaults [] = {
+  "*delay:     10000",
+  "*speed:      6",
+  ANALOGTV_DEFAULTS
+  "*TVContrast:      150",
+  0
+};
+
+XrmOptionDescRec options [] = {
+  { "-delay",          ".delay",       XrmoptionSepArg, 0 },
+  { "-percent",         ".percent",     XrmoptionSepArg, 0 },
+  { "-speed",           ".speed",     XrmoptionSepArg, 0 },
+  ANALOGTV_OPTIONS
+  { 0, 0, 0, 0 }
+};
+
+void
+screenhack (Display *dpy, Window window)
+{
+  init_pong (dpy, window);
+  while (1)
+    {
+      play_pong ();
+    }
+}
+
diff --git a/hacks/pong.man b/hacks/pong.man
new file mode 100644 (file)
index 0000000..833486e
--- /dev/null
@@ -0,0 +1,74 @@
+.TH XScreenSaver 1 "" "X Version 11"
+.SH NAME
+pong - Pong Home Video Game Emulator
+.SH SYNOPSIS
+.B pong
+[\-display \fIhost:display.screen\fP] [\-foreground \fIcolor\fP]
+[\-background \fIcolor\fP] [\-window] [\-root] [\-mono] [\-install]
+[\-visual \fIvisual\fP] [\-delay \fIseconds\fP]
+.SH DESCRIPTION
+The
+.I pong 
+The pong program simulates an ancient Pong home video game, as well as
+various artifacts from displaying it on a color TV set.
+.SH OPTIONS
+.I pong
+accepts the following options:
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-mono 
+If on a color display, pretend we're on a monochrome display.
+.TP 8
+.B \-install
+Install a private colormap for the window.
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-delay \fIdelay\fP
+The delay between displaying one crash and another.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH X RESOURCES
+Notable X resources supported include the following which correspond
+to standard TV controls:
+.BR analogTVTint ,
+.BR analogTVColor ,
+.BR analogTVBrightness ,
+and
+.BR analogTVContrast
+
+which correspond to standard TV controls. They range from 0 to
+100,except for tint which is an angle between -180 and +180.
+.SH TRADEMARKS
+Pong may be a trademark.
+
+.SH SEE ALSO
+.BR X (1),
+.BR xscreensaver (1),
+.BR xanalogtv (1),
+.BR apple2 (1)
+.SH COPYRIGHT
+2003 by Jeremy English.  Permission to use, copy, modify, 
+distribute, and sell this software and its documentation for any purpose is 
+hereby granted without fee, provided that the above copyright notice appear 
+in all copies and that both that copyright notice and this permission notice
+appear in supporting documentation.  No representations are made about the 
+suitability of this software for any purpose.  It is provided "as is" without
+express or implied warranty.
+.SH AUTHOR
+Original Pong program by Jeremy English <jenglish@myself.com>. Scoring
+and television emulation by Trevor Blackwell <tlb@tlb.org>.
index 323f1c1c34a2b51dd3996941f7ce7421c2d01bd8..d90cea5cbfd6728e808493d203c011087f335860 100644 (file)
@@ -466,7 +466,7 @@ setup_X(Display * disp, Window win)
 
     gc = XCreateGC(display, window, gcflags, &gcv);
 
-    load_random_image (xwa.screen, window, window);
+    load_random_image (xwa.screen, window, window, NULL);
 
     orig_map = XGetImage(display, window, 0, 0, xwa.width, xwa.height,
                         ~0L, ZPixmap);
index 65a1fdd738880c6eea00f0aa9577c8d4956d0117..dde56239ee4083bc088205c1722038820dc123f2 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* rotor --- a swirly rotor */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)rotor.c      5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 76b3b3fb0c0adc03c319051eedb0ad30465ab970..9425dc5410551de849026ad4ea6b4071ce0c6a6c 100644 (file)
@@ -321,7 +321,7 @@ static void setup_X (Display * disp, Window win)
        if (use_subwindow_mode_p (xwa.screen, window))  /* see grabscreen.c */
                gcflags |= GCSubwindowMode;
        gc = XCreateGC (display, window, gcflags, &gcv);
-        load_random_image (xwa.screen, window, window);
+        load_random_image (xwa.screen, window, window, NULL);
 
        orig_map = XGetImage (display, window, 0, 0, width, height, ~0L, ZPixmap);
 
index e0a5ac212bb74c5d36bb76e25015bf4d20814b0d..8d5215630417dd4aa6f262e4e9f292b5c7099763 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* sierpinski --- Sierpinski's triangle fractal */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)sierpinski.c 5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index ae08b2ca8a6e3f9d6a79f690230f0d1e774d90cb..78a30843fbb3485525402a6435811a02bbd0d427 100644 (file)
@@ -36,7 +36,7 @@ init_slide (Display *dpy, Window window)
   Visual *visual;
 
   XGetWindowAttributes (dpy, window, &xgwa);
-  load_random_image (xgwa.screen, window, window);
+  load_random_image (xgwa.screen, window, window, NULL);
   cmap = xgwa.colormap;
   visual = xgwa.visual;
   max_width = xgwa.width;
index 1a3ac1581ec49b359bd64a421517a5b7387bf4b9..93ace4fc0cc593364fe0bfcbd88281462bb24a74 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* slip --- lots of slipping blits */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)slip.c       5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
@@ -113,7 +112,7 @@ prepare_screen(ModeInfo * mi, slipstruct * sp)
 #ifdef STANDALONE                        /* jwz -- sometimes hack the desktop image! */
        if (halfrandom(sp, 2) == 0) {
       load_random_image (DefaultScreenOfDisplay(display),
-                                 MI_WINDOW(mi), MI_WINDOW(mi));
+                                 MI_WINDOW(mi), MI_WINDOW(mi), NULL);
        }
 #endif
 
index add3d6f1442bfe06997a9986221b123940449903..00f7b3f6fc5e7d1a4da7ca874fe4b6b65212b83a 100644 (file)
@@ -38,7 +38,7 @@
  * software for any purpose.  It is provided "as is" without express or 
  * implied warranty.
  *
- * $Revision: 1.25 $
+ * $Revision: 1.29 $
  *
  * Version 1.0 April 27, 1998.
  * - Initial version
@@ -482,7 +482,7 @@ lookupHost(ping_target *target)
                                 (ip[2] << 16) |
                                 (ip[1] <<  8) |
                                 (ip[0]));
-      hent = gethostbyaddr (ip, 4, AF_INET);
+      hent = gethostbyaddr ((const char *) ip, 4, AF_INET);
 
       if (debug_p > 1)
         fprintf (stderr, "%s:   %s => %s\n",
@@ -1012,8 +1012,13 @@ sendping(ping_info *pi, ping_target *pt)
     ICMP_CHECKSUM(icmph) = 0;
     ICMP_ID(icmph) = pi->pid;
     ICMP_SEQ(icmph) = pi->seq++;
+# ifdef GETTIMEOFDAY_TWO_ARGS
     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
                 (struct timezone *) 0);
+# else
+    gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
+# endif
+
     strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
           pt->name);
     ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
@@ -1117,7 +1122,7 @@ getping(sonar_info *si, ping_info *pi)
     /* Local Variables */
 
     struct sockaddr from;
-    unsigned int fromlen;
+    unsigned int fromlen;  /* Posix says socklen_t, but that's not portable */
     int result;
     u_char packet[1024];
     struct timeval now;
@@ -1175,7 +1180,11 @@ getping(sonar_info *si, ping_info *pi)
 
        /* Check the packet */
 
+# ifdef GETTIMEOFDAY_TWO_ARGS
        gettimeofday(&now, (struct timezone *) 0);
+# else
+       gettimeofday(&now);
+# endif
        ip = (struct ip *) packet;
         iphdrlen = IP_HDRLEN(ip) << 2;
        icmph = (struct ICMP *) &packet[iphdrlen];
@@ -1904,6 +1913,7 @@ parse_mode (Bool ping_works_p)
 # endif /* HAVE_PING */
 
       for (next = token;
+           *next &&
            *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
            next++)
         ;
@@ -2032,7 +2042,11 @@ screenhack(Display *dpy, Window win)
 
        /* Call the sensor and display the results */
 
+# ifdef GETTIMEOFDAY_TWO_ARGS
        gettimeofday(&start, (struct timezone *) 0);
+# else
+       gettimeofday(&start);
+# endif
        bl = sensor(si, sensor_info);
        Sonar(si, bl);
 
@@ -2042,7 +2056,11 @@ screenhack(Display *dpy, Window win)
        if (si->current == 0)
          si->sweepnum++;
        XSync (dpy, False);
+# ifdef GETTIMEOFDAY_TWO_ARGS
        gettimeofday(&finish, (struct timezone *) 0);
+# else
+       gettimeofday(&finish);
+# endif
        sleeptime = si->delay - delta(&start, &finish);
         screenhack_handle_events (dpy);
        if (sleeptime > 0L)
index c8ca46caa7ee1092cc4ab9c633c52a3d7f3a3535..2323eb6eaf630725e47b2a4c43e2667247ea34b1 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* sphere --- a bunch of shaded spheres */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)sphere.c     5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 214cbfbbaa524d1b80ff6c5c786b70b9cbb128e9..a471acc189b05610e636e08d9f727880a8954c95 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* spiral --- spiraling dots */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)spiral.c     5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 4f76dd28dcffb0f205cc8e04483f0c3c66371e15..eebe5f3775ec749b89c534ab8947726a200195f6 100644 (file)
@@ -111,7 +111,7 @@ init_hack (Display *dpy, Window window)
 
   /* grab screen to pixmap */
   pm = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth);
-  load_random_image (xgwa.screen, window, pm);
+  load_random_image (xgwa.screen, window, pm, NULL);
   XClearWindow(dpy, window);
   XFlush (dpy);
 
index f8f88a663c4ab5fe95e6509fd42422a15d37d1f8..c730259c41b1d482b339b94cac992a496d64700a 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* strange --- strange attractors */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)strange.c    5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 9f1801b3533ff9cb0873c3a06025256451a1e8c1..93f0c0a63da6721ffdeb3d0e8b4064680de2b171 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 4 -*-
  * swirl --- swirly color-cycling patterns.
  */
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)swirl.c      4.00 97/01/01 xlockmore";
 #endif
 
index eb35ae2b82e5d36a13116c346d39d82e010b8d67..44a2a59f294e7cb96eacac744f37ccfc07989c79 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* thornbird --- continuously varying Thornbird set */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)thornbird.c  5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 83bdc067f364f5461d764a02552fdb840f7dd8e5..1fa8c62a478ffb5f6ca8279549daaa9dd98c460b 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* triangle --- create a triangle-mountain */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)triangle.c   4.04 97/07/28 xlockmore";
-
 #endif
 
 /*-
index e51621f46a6627ac0327f8cd3a66936d530ea729..e118851c3b840558ac1ca3da98e8884fa707c2a1 100644 (file)
@@ -143,7 +143,7 @@ static void grabImage (XWindowAttributes *xwa)
        XGetImage (display, window, 0, 0, windowWidth, windowHeight,
                   ~0L, ZPixmap);
 
-    load_random_image (screen, window, window);
+    load_random_image (screen, window, window, NULL);
     sourceImage = XGetImage (display, window, 0, 0, windowWidth, windowHeight,
                             ~0L, ZPixmap);
 
index f4151cd4c254604b70125f3d02cebde5ab581fd8..a816d736a405f86bdd6391754aa8736518289f13 100644 (file)
@@ -1,9 +1,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* vines --- vine fractals */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)vines.c      5.00 2000/11/01 xlockmore";
-
 #endif
 
 /*-
index 99fef88b679fa2280ed9c91979301e34b4ce3010..a661b3a41f278b0d78a84aa8ea2297a134ee6d39 100644 (file)
@@ -279,7 +279,7 @@ write_pixbuf (GdkPixbuf *pb, const char *file)
           perror (buf);
           exit (1);
         }
-      fprintf (stderr, " %ldK\n", (st.st_size + 1023) / 1024);
+      fprintf (stderr, " %luK\n", (st.st_size + 1023) / 1024);
     }
 
   fclose (out);
index 523fcc436af2b20c28bda14a474239071ee575bd..118d7dd961c1a49a10513dd1d93f00c4959c9307 100644 (file)
@@ -603,9 +603,9 @@ screenhack (Display *display, Window window)
                 int size;
                 size = (int)(15.0 + 5.0 * sin((double)internal_time / 180.0));
                /* First delete the old circle... */
-                if (!info->trail && ( !dbeclear_p ||
+                if (!info->trail && ( !dbeclear_p
 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
-                   !backb
+                   || !backb
 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
                    )) {
                     XSetForeground(display, bgc, BlackPixel(display, screen));
index 7eb5fdf226d7a51ebe931539f376f6f84f29d4f3..e57367d5045faff0754dc161ac4fcbdf10554eb1 100644 (file)
@@ -2,9 +2,8 @@
 /* -*- Mode: C; tab-width: 4 -*- */
 /* worm --- draw wiggly worms */
 
-#if !defined( lint ) && !defined( SABER )
+#if 0
 static const char sccsid[] = "@(#)worm.c       4.04 97/07/28 xlockmore";
-
 #endif
 
 /*-
diff --git a/hacks/xanalogtv.c b/hacks/xanalogtv.c
new file mode 100644 (file)
index 0000000..605acac
--- /dev/null
@@ -0,0 +1,448 @@
+/* xanalogtv, Copyright (c) 2003 Trevor Blackwell <tlb@tlb.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation.  No representations are made about the suitability of this
+ * software for any purpose.  It is provided "as is" without express or 
+ * implied warranty.
+ *
+ *
+ * Simulate test patterns on an analog TV. Concept similar to xteevee
+ * in this distribution, but a totally different implementation based
+ * on the simulation of an analog TV set in utils/analogtv.c. Much
+ * more realistic, but needs more video card bandwidth.
+ *
+ * It flips around through simulated channels 2 through 13. Some show
+ * pictures from your images directory, some show color bars, and some
+ * just have static. Some channels receive two stations simultaneously
+ * so you see a ghostly, misaligned image.
+ *
+ * It's easy to add some test patterns by compiling in an XPM, but I
+ * can't find any that are clearly freely redistributable.
+ *
+ */
+
+#include <math.h>
+#include "screenhack.h"
+#include "xpm-pixmap.h"
+#include "analogtv.h"
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <X11/Xutil.h>
+#include <X11/Intrinsic.h>
+
+#include "images/logo-50.xpm"
+
+/* #define DEBUG 1 */
+/* #define USE_TEST_PATTERNS */
+
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+static analogtv *tv=NULL;
+
+analogtv_font ugly_font;
+
+static void
+update_smpte_colorbars(analogtv_input *input)
+{
+  int col;
+  int xpos, ypos;
+  int black_ntsc[4];
+
+  /* 
+     SMPTE is the society of motion picture and television engineers, and
+     these are the standard color bars in the US. Following the partial spec
+     at http://broadcastengineering.com/ar/broadcasting_inside_color_bars/
+     These are luma, chroma, and phase numbers for each of the 7 bars.
+  */
+  double top_cb_table[7][3]={
+    {75, 0, 0.0},    /* gray */
+    {69, 31, 167.0}, /* yellow */
+    {56, 44, 283.5}, /* cyan */
+    {48, 41, 240.5}, /* green */
+    {36, 41, 60.5},  /* magenta */
+    {28, 44, 103.5}, /* red */
+    {15, 31, 347.0}  /* blue */
+  };
+  double mid_cb_table[7][3]={
+    {15, 31, 347.0}, /* blue */
+    {7, 0, 0},       /* black */
+    {36, 41, 60.5},  /* magenta */
+    {7, 0, 0},       /* black */
+    {56, 44, 283.5}, /* cyan */
+    {7, 0, 0},       /* black */
+    {75, 0, 0.0}     /* gray */
+  };
+
+  analogtv_lcp_to_ntsc(0.0, 0.0, 0.0, black_ntsc);
+
+  analogtv_setup_sync(input, 1, 0);
+  analogtv_setup_teletext(input);
+
+  for (col=0; col<7; col++) {
+    analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.00, 0.68, 
+                                top_cb_table[col][0], 
+                                top_cb_table[col][1], top_cb_table[col][2]);
+    
+    analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.68, 0.75, 
+                                mid_cb_table[col][0], 
+                                mid_cb_table[col][1], mid_cb_table[col][2]);
+  }
+
+  analogtv_draw_solid_rel_lcp(input, 0.0, 1.0/6.0,
+                              0.75, 1.00, 7, 40, 303);   /* -I */
+  analogtv_draw_solid_rel_lcp(input, 1.0/6.0, 2.0/6.0,
+                              0.75, 1.00, 100, 0, 0);    /* white */
+  analogtv_draw_solid_rel_lcp(input, 2.0/6.0, 3.0/6.0,
+                              0.75, 1.00, 7, 40, 33);    /* +Q */
+  analogtv_draw_solid_rel_lcp(input, 3.0/6.0, 4.0/6.0,
+                              0.75, 1.00, 7, 0, 0);      /* black */
+  analogtv_draw_solid_rel_lcp(input, 12.0/18.0, 13.0/18.0,
+                              0.75, 1.00, 3, 0, 0);      /* black -4 */
+  analogtv_draw_solid_rel_lcp(input, 13.0/18.0, 14.0/18.0,
+                              0.75, 1.00, 7, 0, 0);      /* black */
+  analogtv_draw_solid_rel_lcp(input, 14.0/18.0, 15.0/18.0,
+                              0.75, 1.00, 11, 0, 0);     /* black +4 */
+  analogtv_draw_solid_rel_lcp(input, 5.0/6.0, 6.0/6.0,
+                              0.75, 1.00, 7, 0, 0);      /* black */
+
+
+  ypos=ANALOGTV_V/5;
+  xpos=ANALOGTV_VIS_START + ANALOGTV_VIS_LEN/2;
+
+  {
+    char localname[256];
+    if (gethostname (localname, sizeof (localname))==0) {
+      localname[sizeof(localname)-1]=0; /* "The returned name is null-
+                                           terminated unless insufficient 
+                                           space is provided" */
+      localname[24]=0; /* limit length */
+
+      analogtv_draw_string_centered(input, &ugly_font, localname,
+                                    xpos, ypos, black_ntsc);
+    }
+  }
+  ypos += ugly_font.char_h*5/2;
+
+  analogtv_draw_xpm(tv, input,
+                    logo_50_xpm, xpos - 100, ypos);
+
+  ypos += 58;
+
+#if 0
+  analogtv_draw_string_centered(input, &ugly_font, "Please Stand By", xpos, ypos);
+  ypos += ugly_font.char_h*4;
+#endif
+
+  {
+    char timestamp[256];
+    time_t t = time ((time_t *) 0);
+    struct tm *tm = localtime (&t);
+
+    /* Y2K: It is OK for this to use a 2-digit year because it's simulating a
+       TV display and is purely decorative. */
+    strftime(timestamp, sizeof(timestamp)-1, "%y.%m.%d %H:%M:%S ", tm);
+    analogtv_draw_string_centered(input, &ugly_font, timestamp,
+                                  xpos, ypos, black_ntsc);
+  }
+
+  
+  input->next_update_time += 1.0;
+}
+
+#if 0
+static void
+draw_color_square(analogtv_input *input)
+{
+  double xs,ys;
+
+  analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, 0.0, 1.0,
+                              30.0, 0.0, 0.0);
+  
+  for (xs=0.0; xs<0.9999; xs+=1.0/15.0) {
+    analogtv_draw_solid_rel_lcp(input, xs, xs, 0.0, 1.0,
+                                100.0, 0.0, 0.0);
+  }
+
+  for (ys=0.0; ys<0.9999; ys+=1.0/11.0) {
+    analogtv_draw_solid_rel_lcp(input, 0.0, 1.0, ys, ys,
+                                100.0, 0.0, 0.0);
+  }
+
+  for (ys=0.0; ys<0.9999; ys+=0.01) {
+    
+    analogtv_draw_solid_rel_lcp(input, 0.0/15, 1.0/15, ys, ys+0.01,
+                                40.0, 45.0, 103.5*(1.0-ys) + 347.0*ys);
+
+    analogtv_draw_solid_rel_lcp(input, 14.0/15, 15.0/15, ys, ys+0.01,
+                                40.0, 45.0, 103.5*(ys) + 347.0*(1.0-ys));
+  }
+
+  for (ys=0.0; ys<0.9999; ys+=0.02) {
+    analogtv_draw_solid_rel_lcp(input, 1.0/15, 2.0/15, ys*2.0/11.0+1.0/11.0, 
+                                (ys+0.01)*2.0/11.0+1.0/11.0,
+                                100.0*(1.0-ys), 0.0, 0.0);
+  }
+
+
+}
+#endif
+
+char *progclass = "XAnalogTV";
+
+char *defaults [] = {
+  "*delay:          5",
+  ANALOGTV_DEFAULTS
+  0,
+};
+
+XrmOptionDescRec options [] = {
+  { "-delay",          ".delay",               XrmoptionSepArg, 0 },
+  ANALOGTV_OPTIONS
+  { 0, 0, 0, 0 }
+};
+
+
+#ifdef USE_TEST_PATTERNS
+
+#include "images/earth.xpm"
+
+char **test_patterns[] = {
+  earth_xpm,
+};
+
+#endif
+
+
+enum {
+  N_CHANNELS=12, /* Channels 2 through 13 on VHF */
+  MAX_MULTICHAN=2
+}; 
+
+typedef struct chansetting_s {
+
+  analogtv_reception recs[MAX_MULTICHAN];
+  double noise_level;
+
+  int dur;
+} chansetting;
+
+static struct timeval basetime;
+
+static int
+getticks(void)
+{
+  struct timeval tv;
+  gettimeofday(&tv,NULL);
+  return ((tv.tv_sec - basetime.tv_sec)*1000 +
+          (tv.tv_usec - basetime.tv_usec)/1000);
+}
+
+int
+analogtv_load_random_image(analogtv *it, analogtv_input *input)
+{
+  Pixmap pixmap;
+  XImage *image=NULL;
+  int width=ANALOGTV_PIC_LEN;
+  int height=width*3/4;
+  int rc;
+
+  pixmap=XCreatePixmap(it->dpy, it->window, width, height, it->visdepth);
+  XSync(it->dpy, False);
+  load_random_image(it->screen, it->window, pixmap, NULL);
+  image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
+  XFreePixmap(it->dpy, pixmap);
+
+  /* Make sure the window's background is not set to None, and get the
+     grabbed bits (if any) off it as soon as possible. */
+  XSetWindowBackground (it->dpy, it->window,
+                        get_pixel_resource ("background", "Background",
+                                            it->dpy, it->xgwa.colormap));
+  XClearWindow (it->dpy, it->window);
+
+  analogtv_setup_sync(input, 1, (random()%20)==0);
+  rc=analogtv_load_ximage(it, input, image);
+  if (image) XDestroyImage(image);
+  XSync(it->dpy, False);
+  return rc;
+}
+
+int
+analogtv_load_xpm(analogtv *it, analogtv_input *input, char **xpm)
+{
+  Pixmap pixmap;
+  XImage *image;
+  int width,height;
+  int rc;
+
+  pixmap=xpm_data_to_pixmap (it->dpy, it->window, xpm,
+                             &width, &height, NULL);
+  image = XGetImage(it->dpy, pixmap, 0, 0, width, height, ~0L, ZPixmap);
+  XFreePixmap(it->dpy, pixmap);
+  rc=analogtv_load_ximage(it, input, image);
+  if (image) XDestroyImage(image);
+  XSync(it->dpy, False);
+  return rc;
+}
+
+enum { MAX_STATIONS = 6 };
+static int n_stations;
+static analogtv_input *stations[MAX_STATIONS];
+
+
+void add_stations(void)
+{
+  while (n_stations < MAX_STATIONS) {
+    analogtv_input *input=analogtv_input_allocate();
+    stations[n_stations++]=input;
+
+    if (n_stations==1) {
+      input->updater = update_smpte_colorbars;
+      input->do_teletext=1;
+    }
+#ifdef USE_TEST_PATTERNS
+    else if (random()%5==0) {
+      j=random()%countof(test_patterns);
+      analogtv_setup_sync(input);
+      analogtv_load_xpm(tv, input, test_patterns[j]);
+      analogtv_setup_teletext(input);
+    }
+#endif
+    else {
+      analogtv_load_random_image(tv, input);
+      input->do_teletext=1;
+    }
+  }
+}
+
+void
+screenhack (Display *dpy, Window window)
+{
+  int i;
+  int curinputi;
+  int change_ticks;
+  int using_mouse=0;
+  int change_now;
+  chansetting chansettings[N_CHANNELS];
+  chansetting *cs;
+  int last_station=42;
+  int delay = get_integer_resource("delay", "Integer");
+  if (delay < 1) delay = 1;
+
+  analogtv_make_font(dpy, window, &ugly_font, 7, 10, "6x10");
+  
+  tv=analogtv_allocate(dpy, window);
+  tv->event_handler = screenhack_handle_event;
+
+  add_stations();
+
+  analogtv_set_defaults(tv, "");
+  tv->need_clear=1;
+
+  if (random()%4==0) {
+    tv->tint_control += pow(frand(2.0)-1.0, 7) * 180.0;
+  }
+  if (1) {
+    tv->color_control += frand(0.3);
+  }
+
+  for (i=0; i<N_CHANNELS; i++) {
+    memset(&chansettings[i], 0, sizeof(chansetting));
+
+    chansettings[i].noise_level = 0.06;
+    chansettings[i].dur = 1000*delay;
+
+    if (random()%6==0) {
+      chansettings[i].dur=600;
+    }
+    else {
+      int stati;
+      for (stati=0; stati<MAX_MULTICHAN; stati++) {
+        analogtv_reception *rec=&chansettings[i].recs[stati];
+        int station;
+        while (1) {
+          station=random()%n_stations;
+          if (station!=last_station) break;
+          if ((random()%10)==0) break;
+        }
+        last_station=station;
+        rec->input = stations[station];
+        rec->level = pow(frand(1.0), 3.0) * 2.0 + 0.05;
+        rec->ofs=random()%ANALOGTV_SIGNAL_LEN;
+        if (random()%3) {
+          rec->multipath = frand(1.0);
+        } else {
+          rec->multipath=0.0;
+        }
+        if (stati) {
+          /* We only set a frequency error for ghosting stations,
+             because it doesn't matter otherwise */
+          rec->freqerr = (frand(2.0)-1.0) * 3.0;
+        }
+
+        if (rec->level > 0.3) break;
+        if (random()%4) break;
+      }
+    }
+  }
+
+  gettimeofday(&basetime,NULL);
+
+  curinputi=0;
+  cs=&chansettings[curinputi];
+  change_ticks = cs->dur + 1500;
+
+  tv->powerup=0.0;
+  while (1) {
+    int curticks=getticks();
+    double curtime=curticks*0.001;
+
+    change_now=0;
+    if (analogtv_handle_events(tv)) {
+      using_mouse=1;
+      change_now=1;
+    }
+    if (change_now || (!using_mouse && curticks>=change_ticks 
+                       && tv->powerup > 10.0)) {
+      curinputi=(curinputi+1)%N_CHANNELS;
+      cs=&chansettings[curinputi];
+      change_ticks = curticks + cs->dur;
+      /* Set channel change noise flag */
+      tv->channel_change_cycles=200000;
+    }
+
+    for (i=0; i<MAX_MULTICHAN; i++) {
+      analogtv_reception *rec=&cs->recs[i];
+      analogtv_input *inp=rec->input;
+      if (!inp) continue;
+
+      if (inp->updater) {
+        inp->next_update_time = curtime;
+        (inp->updater)(inp);
+      }
+      rec->ofs += rec->freqerr;
+    }
+
+    tv->powerup=curtime;
+
+    analogtv_init_signal(tv, cs->noise_level);
+    for (i=0; i<MAX_MULTICHAN; i++) {
+      analogtv_reception *rec=&cs->recs[i];
+      analogtv_input *inp=rec->input;
+      if (!inp) continue;
+
+      analogtv_reception_update(rec);
+      analogtv_add_signal(tv, rec);
+    }
+    analogtv_draw(tv);
+  }
+
+  XSync(dpy, False);
+  XClearWindow(dpy, window);
+  
+  if (tv) analogtv_release(tv);
+}
+
diff --git a/hacks/xanalogtv.man b/hacks/xanalogtv.man
new file mode 100644 (file)
index 0000000..15b4c99
--- /dev/null
@@ -0,0 +1,86 @@
+.TH XScreenSaver 1 "10-Oct-03" "X Version 11"
+.SH NAME
+xanalogtv - Simulate reception on an old analog TV set
+.SH SYNOPSIS
+.B xanalogtv
+[\-display \fIhost:display.screen\fP] [\-window] [\-root] [\-install]
+[\-visual \fIvisual\fP] 
+[\-cycle] [\-no-cycle]
+.SH DESCRIPTION
+.I xanalogtv
+shows a simulation of an old TV set showing test patterns and any
+other images you have provided. It reproduces a wide range of TV
+reception bummage: snow, bloom, ghosting, and loss of vertical and
+horizontal sync. It also simulates the TV warming up. It will cycle
+through 12 channels, some with images you give it, and some with color
+bars or nothing but static.
+.PP
+The images that it uses will be grabbed from the portion of the screen
+underlying the window, or from the system's video input, or from a
+random file on disk, as indicated by the \fIgrabDesktopImages\fP,
+\fIgrabVideoFrames\fP, and \fIchooseRandomImages\fP options in the
+\fI~/.xscreensaver\fP file; see
+.BR xscreensaver-demo (1)
+for more details. It looks best with a video input or
+your digital photo collection.
+.PP
+.SH OPTIONS
+.I xanalogtv
+accepts the following options:
+.TP 8
+.B \-window
+Draw on a newly-created window.  This is the default.
+.TP 8
+.B \-root
+Draw on the root window.
+.TP 8
+.B \-install
+Install a private colormap for the window.
+.TP 8
+.B \-visual \fIvisual\fP
+Specify which visual to use.  Legal values are the name of a visual class,
+or the id number (decimal or hex) of a specific visual.
+.TP 8
+.B \-cycle
+Cycle through all the available modes.  This is the default.
+.TP 8
+.B \-no-cycle
+Don't cycle modes.
+.SH ENVIRONMENT
+.PP
+.TP 8
+.B DISPLAY
+to get the default host and display number.
+.TP 8
+.B XENVIRONMENT
+to get the name of a resource file that overrides the global resources
+stored in the RESOURCE_MANAGER property.
+.SH X RESOURCES
+Notable X resources supported include the following which correspond
+to standard TV controls:
+.BR analogTVTint ,
+.BR analogTVColor ,
+.BR analogTVBrightness ,
+and
+.BR analogTVContrast .
+They range from 0 to 100, except for tint which is an angle 
+between -180 and +180.
+
+.SH SEE ALSO
+.BR X (1),
+.BR xteevee (1),
+.BR apple2 (1),
+.BR bsod (1),
+.BR xscreensaver (1),
+.BR xscreensaver\-demo (1),
+.BR xscreensaver\-getimage (1)
+.SH COPYRIGHT
+Copyright \(co 2003 by Trevor Blackwell.  Permission to use, copy, modify, 
+distribute, and sell this software and its documentation for any purpose is 
+hereby granted without fee, provided that the above copyright notice appear 
+in all copies and that both that copyright notice and this permission notice
+appear in supporting documentation.  No representations are made about the 
+suitability of this software for any purpose.  It is provided "as is" without
+express or implied warranty. 
+.SH AUTHOR
+Trevor Blackwell <tlb@tlb.org>
index 622a59de97e74659d5a05f7a6118cc09d757aac2..b492561a7e55afb7d0c1404c657ec92394cf4016 100755 (executable)
@@ -718,6 +718,8 @@ char *defaults [] = {
 };
 
 XrmOptionDescRec options [] = {
+  { "-foreground",".foreground",     XrmoptionSepArg, 0 },
+  { "-fg",        ".foreground",     XrmoptionSepArg, 0 },
   { "-delay",     ".delay",          XrmoptionSepArg, 0 },
   { "-bitmap",    ".bitmap",         XrmoptionSepArg, 0 },
   { "-baseline",  ".bitmapBaseline", XrmoptionSepArg, 0 },
index 3fc5c2c1d09ec0805b5679da93acd5a594f79257..ecfa04b5769e29d656d714079d1fdbef035e1702 100644 (file)
@@ -4,7 +4,9 @@ xflame - draws animated flames
 .SH SYNOPSIS
 .B xflame
 [\-display \fIhost:display.screen\fP] [\-window] [\-root] [\-install]
-[\-visual \fIvisual\fP] [\-hspread \fIint\fP] [\-vspread \fIint\fP]
+[\-visual \fIvisual\fP]
+[\-foreground \fIcolor\fP]
+[\-hspread \fIint\fP] [\-vspread \fIint\fP]
 [\-residual \fIint\fP] [\-variance \fIint\fP] [\-vartrend \fIint\fP] 
 [\-bloom \| \-no\-bloom] 
 [\-bitmap \fIxbm\-file\fP] [\-baseline \fIint\fP]
@@ -29,6 +31,9 @@ Install a private colormap for the window.
 Specify which visual to use.  Legal values are the name of a visual class,
 or the id number (decimal or hex) of a specific visual.
 .TP 8
+.B \-foreground \fIcolor\fP\fP or \fB\-fg\fP \fIcolor\fP\fP
+The color of the flames; default red.  (The background color is always black.)
+.TP 8
 .B \-bitmap \fIfilename\fP\fP
 Specifies the bitmap file to use (a monochrome XBM file.)
 The name "none" means not to use a bitmap at all.
index f2036f10146ca4642c574fbbc3dad25dc12180cf..0bb91cb469a71efccd5ef6f2702ad09ea9e779cf 100644 (file)
@@ -231,26 +231,7 @@ void xsublim_Sig_Catch(int sig_Number)
        Xsublim_Sig_Last = sig_Number;
 }
 
-/* Get the screensaver's window ============================================ */
-static XErrorHandler Xsublim_Ss_Handler = NULL;
-static int           Xsublim_Ss_Status;
-
 /* This was all basically swiped from driver/remote.c and util/vroot.h */
-static int xsublim_Ss_Handler(Display* handle_Display,
-            XErrorEvent* handle_Error)
-{
-       if (handle_Error->error_code == BadWindow)
-       {
-               Xsublim_Ss_Status = BadWindow;
-               return 0;
-       }
-       if (Xsublim_Ss_Handler == NULL)
-       {
-               fprintf(stderr,"%s: ",progname);
-               abort();
-       }
-       return (*Xsublim_Ss_Handler)(handle_Display,handle_Error);
-}
 static Window xsublim_Ss_GetWindow(Display* ss_Display)
 {
   Screen *s = DefaultScreenOfDisplay (ss_Display);
index a1d6932f43f7ada3fa3aebd4d4d4d3bf302e9ed7..11d1c03649ae2e1fef7c0a3d6475bbf765abd357 100644 (file)
@@ -364,7 +364,7 @@ void screenhack(Display* x_Disp,Window x_Win)
 
        /* Grab the screen to give us time to do whatever we want */
        XGetWindowAttributes(x_Disp,x_Win,&x_WinAttr);
-        load_random_image (x_WinAttr.screen, x_Win, x_Win);
+        load_random_image (x_WinAttr.screen, x_Win, x_Win, NULL);
 
        x_GcVal.subwindow_mode = IncludeInferiors;
        x_Gc = XCreateGC(x_Disp,x_Win,GCSubwindowMode,&x_GcVal);
index 39b502486f28261a964817f6d004e61460c699a6..d80fa39fe308019e455bb6ac121f576c46913481 100644 (file)
@@ -29,6 +29,10 @@ file; see
 .BR xscreensaver-demo (1)
 for more details.
 .PP
+See also
+.BR xanalogtv (1)
+for a more sophisticated (but more graphics-intensive) implementation
+of this concept.
 .SH OPTIONS
 .I xteevee
 accepts the following options:
@@ -107,6 +111,9 @@ mode, as a percentage.
 should simulate more TV problems.
 .SH SEE ALSO
 .BR X (1),
+.BR xanalogtv (1),
+.BR apple2 (1),
+.BR bsod (1),
 .BR xscreensaver (1),
 .BR xscreensaver\-demo (1),
 .BR xscreensaver\-getimage (1)
index 8911c7ba4cb39c1f1911d6c1b2aa991efd9442fb..eb34ee37972d10b28fdebd6556922021ecf6d6ee 100644 (file)
@@ -102,7 +102,7 @@ static void init_hack(Display *dpy, Window window)
 
   orig_map = NULL;
   pm = XCreatePixmap(dpy, window, sizex, sizey, xgwa.depth);
-  load_random_image (xgwa.screen, window, pm);
+  load_random_image (xgwa.screen, window, pm, NULL);
 
   if (!lenses) {
     orig_map = XGetImage(dpy, pm, 0, 0, sizex, sizey, ~0L, ZPixmap);
index 25faa4b21ebd696b44863ee4ce4cb5bd56cb8edb..c82f00661f60f3065b0741a91ecd11991d4d926e 100644 (file)
@@ -279,7 +279,10 @@ maintainer-clean: distclean
        rm -f $(GMOFILES)
 
 depend:
-distdepend: generate_potfiles_in update-po $(DISTFILES)
+# fuck off.  love, jwz.
+#distdepend: generate_potfiles_in update-po $(DISTFILES)
+distdepend::
+
 
 # jwz: Generates po/POTFILES.in by examining the source tree:
 # that way we don't have to keep this list up to date as files are added.
index 3f12a9f8b8d0d66a6aafce21b3f160d17a0c119f..0ef28022981b78c5527282a031461adfe6ad445e 100644 (file)
-# Auto-generated: Sun Sep  7 17:00:03 PDT 2003
+# Auto-generated: Mon Oct 13 17:43:29 PDT 2003
 driver/demo-Gtk.c
 driver/demo-Gtk-conf.c
 driver/demo-Gtk-support.c
 driver/demo-Gtk-widgets.c
-driver/screensaver-properties.desktop.in.h
-driver/screensaver-properties.desktop.in.h
-driver/xscreensaver-demo.glade.h
-driver/xscreensaver-demo.glade2.h
-driver/xscreensaver-demo.glade2.h
-driver/xscreensaver-demo.glade.h
-hacks/config/anemone.xml.h
-hacks/config/antspotlight.xml.h
-hacks/config/ant.xml.h
-hacks/config/apollonian.xml.h
-hacks/config/atlantis.xml.h
-hacks/config/attraction.xml.h
-hacks/config/atunnel.xml.h
-hacks/config/barcode.xml.h
-hacks/config/blaster.xml.h
-hacks/config/blitspin.xml.h
-hacks/config/blocktube.xml.h
-hacks/config/bouboule.xml.h
-hacks/config/bouncingcow.xml.h
-hacks/config/boxed.xml.h
-hacks/config/braid.xml.h
-hacks/config/bsod.xml.h
-hacks/config/bubble3d.xml.h
-hacks/config/bubbles.xml.h
-hacks/config/bumps.xml.h
-hacks/config/cage.xml.h
-hacks/config/ccurve.xml.h
-hacks/config/circuit.xml.h
-hacks/config/cloudlife.xml.h
-hacks/config/compass.xml.h
-hacks/config/coral.xml.h
-hacks/config/cosmos.xml.h
-hacks/config/critical.xml.h
-hacks/config/crystal.xml.h
-hacks/config/cubenetic.xml.h
-hacks/config/cubestorm.xml.h
-hacks/config/cynosure.xml.h
-hacks/config/dangerball.xml.h
-hacks/config/decayscreen.xml.h
-hacks/config/deco.xml.h
-hacks/config/deluxe.xml.h
-hacks/config/demon.xml.h
-hacks/config/discrete.xml.h
-hacks/config/distort.xml.h
-hacks/config/drift.xml.h
-hacks/config/electricsheep.xml.h
-hacks/config/endgame.xml.h
-hacks/config/engine.xml.h
-hacks/config/epicycle.xml.h
-hacks/config/eruption.xml.h
-hacks/config/euler2d.xml.h
-hacks/config/extrusion.xml.h
-hacks/config/fadeplot.xml.h
-hacks/config/fireflies.xml.h
-hacks/config/flag.xml.h
-hacks/config/flame.xml.h
-hacks/config/flipflop.xml.h
-hacks/config/flipscreen3d.xml.h
-hacks/config/flow.xml.h
-hacks/config/fluidballs.xml.h
-hacks/config/flurry.xml.h
-hacks/config/flyingtoasters.xml.h
-hacks/config/forest.xml.h
-hacks/config/galaxy.xml.h
-hacks/config/gears.xml.h
-hacks/config/gflux.xml.h
-hacks/config/glblur.xml.h
-hacks/config/glforestfire.xml.h
-hacks/config/glknots.xml.h
-hacks/config/glmatrix.xml.h
-hacks/config/glplanet.xml.h
-hacks/config/glslideshow.xml.h
-hacks/config/glsnake.xml.h
-hacks/config/gltext.xml.h
-hacks/config/goban.xml.h
-hacks/config/goop.xml.h
-hacks/config/grav.xml.h
-hacks/config/greynetic.xml.h
-hacks/config/halftone.xml.h
-hacks/config/halo.xml.h
-hacks/config/helix.xml.h
-hacks/config/hopalong.xml.h
-hacks/config/hyperball.xml.h
-hacks/config/hypercube.xml.h
-hacks/config/hypertorus.xml.h
-hacks/config/ifs.xml.h
-hacks/config/imsmap.xml.h
-hacks/config/interference.xml.h
-hacks/config/jigglypuff.xml.h
-hacks/config/jigsaw.xml.h
-hacks/config/juggle.xml.h
-hacks/config/julia.xml.h
-hacks/config/kaleidescope.xml.h
-hacks/config/klein.xml.h
-hacks/config/kumppa.xml.h
-hacks/config/lament.xml.h
-hacks/config/laser.xml.h
-hacks/config/lavalite.xml.h
-hacks/config/lightning.xml.h
-hacks/config/lisa.xml.h
-hacks/config/lissie.xml.h
-hacks/config/lmorph.xml.h
-hacks/config/loop.xml.h
-hacks/config/maze.xml.h
-hacks/config/menger.xml.h
-hacks/config/metaballs.xml.h
-hacks/config/moebius.xml.h
-hacks/config/moire2.xml.h
-hacks/config/moire.xml.h
-hacks/config/molecule.xml.h
-hacks/config/morph3d.xml.h
-hacks/config/mountain.xml.h
-hacks/config/munch.xml.h
-hacks/config/nerverot.xml.h
-hacks/config/noseguy.xml.h
-hacks/config/pedal.xml.h
-hacks/config/penetrate.xml.h
-hacks/config/penrose.xml.h
-hacks/config/petri.xml.h
-hacks/config/phosphor.xml.h
-hacks/config/piecewise.xml.h
-hacks/config/pipes.xml.h
-hacks/config/polyominoes.xml.h
-hacks/config/polytopes.xml.h
-hacks/config/popsquares.xml.h
-hacks/config/pulsar.xml.h
-hacks/config/pyro.xml.h
-hacks/config/qix.xml.h
-hacks/config/queens.xml.h
-hacks/config/rd-bomb.xml.h
-hacks/config/ripples.xml.h
-hacks/config/rocks.xml.h
-hacks/config/rorschach.xml.h
-hacks/config/rotor.xml.h
-hacks/config/rotzoomer.xml.h
-hacks/config/rubik.xml.h
-hacks/config/sballs.xml.h
-hacks/config/shadebobs.xml.h
-hacks/config/sierpinski3d.xml.h
-hacks/config/sierpinski.xml.h
-hacks/config/slidescreen.xml.h
-hacks/config/slip.xml.h
-hacks/config/sonar.xml.h
-hacks/config/speedmine.xml.h
-hacks/config/sphereEversion.xml.h
-hacks/config/spheremonics.xml.h
-hacks/config/sphere.xml.h
-hacks/config/spiral.xml.h
-hacks/config/spotlight.xml.h
-hacks/config/sproingies.xml.h
-hacks/config/squiral.xml.h
-hacks/config/ssystem.xml.h
-hacks/config/stairs.xml.h
-hacks/config/starfish.xml.h
-hacks/config/starwars.xml.h
-hacks/config/stonerview.xml.h
-hacks/config/strange.xml.h
-hacks/config/superquadrics.xml.h
-hacks/config/swirl.xml.h
-hacks/config/t3d.xml.h
-hacks/config/thornbird.xml.h
-hacks/config/triangle.xml.h
-hacks/config/truchet.xml.h
-hacks/config/twang.xml.h
-hacks/config/vermiculate.xml.h
-hacks/config/vidwhacker.xml.h
-hacks/config/vines.xml.h
-hacks/config/wander.xml.h
-hacks/config/webcollage.xml.h
-hacks/config/whirlwindwarp.xml.h
-hacks/config/whirlygig.xml.h
-hacks/config/worm.xml.h
-hacks/config/xaos.xml.h
-hacks/config/xdaliclock.xml.h
-hacks/config/xearth.xml.h
-hacks/config/xfishtank.xml.h
-hacks/config/xflame.xml.h
-hacks/config/xjack.xml.h
-hacks/config/xlyap.xml.h
-hacks/config/xmatrix.xml.h
-hacks/config/xmountains.xml.h
-hacks/config/xrayswarm.xml.h
-hacks/config/xsnow.xml.h
-hacks/config/xspirograph.xml.h
-hacks/config/xteevee.xml.h
-hacks/config/zoom.xml.h
+driver/screensaver-properties.desktop.in
+driver/xscreensaver-demo.glade
+driver/xscreensaver-demo.glade2
+hacks/config/anemone.xml
+hacks/config/antspotlight.xml
+hacks/config/ant.xml
+hacks/config/apollonian.xml
+hacks/config/apple2.xml
+hacks/config/atlantis.xml
+hacks/config/attraction.xml
+hacks/config/atunnel.xml
+hacks/config/barcode.xml
+hacks/config/blaster.xml
+hacks/config/blitspin.xml
+hacks/config/blocktube.xml
+hacks/config/bouboule.xml
+hacks/config/bouncingcow.xml
+hacks/config/boxed.xml
+hacks/config/braid.xml
+hacks/config/bsod.xml
+hacks/config/bubble3d.xml
+hacks/config/bubbles.xml
+hacks/config/bumps.xml
+hacks/config/cage.xml
+hacks/config/ccurve.xml
+hacks/config/circuit.xml
+hacks/config/cloudlife.xml
+hacks/config/compass.xml
+hacks/config/coral.xml
+hacks/config/cosmos.xml
+hacks/config/critical.xml
+hacks/config/crystal.xml
+hacks/config/cubenetic.xml
+hacks/config/cubestorm.xml
+hacks/config/cynosure.xml
+hacks/config/dangerball.xml
+hacks/config/decayscreen.xml
+hacks/config/deco.xml
+hacks/config/deluxe.xml
+hacks/config/demon.xml
+hacks/config/discrete.xml
+hacks/config/distort.xml
+hacks/config/drift.xml
+hacks/config/electricsheep.xml
+hacks/config/endgame.xml
+hacks/config/engine.xml
+hacks/config/epicycle.xml
+hacks/config/eruption.xml
+hacks/config/euler2d.xml
+hacks/config/extrusion.xml
+hacks/config/fadeplot.xml
+hacks/config/fireflies.xml
+hacks/config/flag.xml
+hacks/config/flame.xml
+hacks/config/flipflop.xml
+hacks/config/flipscreen3d.xml
+hacks/config/flow.xml
+hacks/config/fluidballs.xml
+hacks/config/flurry.xml
+hacks/config/flyingtoasters.xml
+hacks/config/fontglide.xml
+hacks/config/forest.xml
+hacks/config/galaxy.xml
+hacks/config/gears.xml
+hacks/config/gflux.xml
+hacks/config/glblur.xml
+hacks/config/glforestfire.xml
+hacks/config/glknots.xml
+hacks/config/glmatrix.xml
+hacks/config/glplanet.xml
+hacks/config/glslideshow.xml
+hacks/config/glsnake.xml
+hacks/config/gltext.xml
+hacks/config/goban.xml
+hacks/config/goop.xml
+hacks/config/grav.xml
+hacks/config/greynetic.xml
+hacks/config/halftone.xml
+hacks/config/halo.xml
+hacks/config/helix.xml
+hacks/config/hopalong.xml
+hacks/config/hyperball.xml
+hacks/config/hypercube.xml
+hacks/config/hypertorus.xml
+hacks/config/ifs.xml
+hacks/config/imsmap.xml
+hacks/config/interference.xml
+hacks/config/jigglypuff.xml
+hacks/config/jigsaw.xml
+hacks/config/juggle.xml
+hacks/config/julia.xml
+hacks/config/kaleidescope.xml
+hacks/config/klein.xml
+hacks/config/kumppa.xml
+hacks/config/lament.xml
+hacks/config/laser.xml
+hacks/config/lavalite.xml
+hacks/config/lightning.xml
+hacks/config/lisa.xml
+hacks/config/lissie.xml
+hacks/config/lmorph.xml
+hacks/config/loop.xml
+hacks/config/maze.xml
+hacks/config/menger.xml
+hacks/config/metaballs.xml
+hacks/config/moebius.xml
+hacks/config/moire2.xml
+hacks/config/moire.xml
+hacks/config/molecule.xml
+hacks/config/morph3d.xml
+hacks/config/mountain.xml
+hacks/config/munch.xml
+hacks/config/nerverot.xml
+hacks/config/noseguy.xml
+hacks/config/pedal.xml
+hacks/config/penetrate.xml
+hacks/config/penrose.xml
+hacks/config/petri.xml
+hacks/config/phosphor.xml
+hacks/config/piecewise.xml
+hacks/config/pipes.xml
+hacks/config/polyominoes.xml
+hacks/config/polytopes.xml
+hacks/config/popsquares.xml
+hacks/config/pulsar.xml
+hacks/config/pyro.xml
+hacks/config/qix.xml
+hacks/config/queens.xml
+hacks/config/rd-bomb.xml
+hacks/config/ripples.xml
+hacks/config/rocks.xml
+hacks/config/rorschach.xml
+hacks/config/rotor.xml
+hacks/config/rotzoomer.xml
+hacks/config/rubik.xml
+hacks/config/sballs.xml
+hacks/config/shadebobs.xml
+hacks/config/sierpinski3d.xml
+hacks/config/sierpinski.xml
+hacks/config/slidescreen.xml
+hacks/config/slip.xml
+hacks/config/sonar.xml
+hacks/config/speedmine.xml
+hacks/config/sphereEversion.xml
+hacks/config/spheremonics.xml
+hacks/config/sphere.xml
+hacks/config/spiral.xml
+hacks/config/spotlight.xml
+hacks/config/sproingies.xml
+hacks/config/squiral.xml
+hacks/config/ssystem.xml
+hacks/config/stairs.xml
+hacks/config/starfish.xml
+hacks/config/starwars.xml
+hacks/config/stonerview.xml
+hacks/config/strange.xml
+hacks/config/superquadrics.xml
+hacks/config/swirl.xml
+hacks/config/t3d.xml
+hacks/config/thornbird.xml
+hacks/config/triangle.xml
+hacks/config/truchet.xml
+hacks/config/twang.xml
+hacks/config/vermiculate.xml
+hacks/config/vidwhacker.xml
+hacks/config/vines.xml
+hacks/config/wander.xml
+hacks/config/webcollage.xml
+hacks/config/whirlwindwarp.xml
+hacks/config/whirlygig.xml
+hacks/config/worm.xml
+hacks/config/xanalogtv.xml
+hacks/config/xaos.xml
+hacks/config/xdaliclock.xml
+hacks/config/xearth.xml
+hacks/config/xfishtank.xml
+hacks/config/xflame.xml
+hacks/config/xjack.xml
+hacks/config/xlyap.xml
+hacks/config/xmatrix.xml
+hacks/config/xmountains.xml
+hacks/config/xrayswarm.xml
+hacks/config/xsnow.xml
+hacks/config/xspirograph.xml
+hacks/config/xteevee.xml
+hacks/config/zoom.xml
index 1263c13424c9798af1a8b760c09b9475adadb25b..5396e455767d95e95976f298c15406c34338aaf6 100644 (file)
--- a/po/ca.po
+++ b/po/ca.po
@@ -11,7 +11,7 @@ msgstr ""
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 
 #: driver/demo-Gtk-conf.c:731
index b1f5f457d8a6db94d18159a5e180e4d5ef52e760..23ec69a9b20c88a8d84b48403c3ff051e34f56c7 100644 (file)
--- a/po/fi.po
+++ b/po/fi.po
@@ -11,7 +11,7 @@ msgstr ""
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 
 #: driver/demo-Gtk-conf.c:731
index 7978e2f73c286cb2961c6326ac6a9919f691a342..fe67763929e1f26718b2290d8fbee336fd74de64 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -11,7 +11,7 @@ msgstr ""
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 
 #: driver/demo-Gtk-conf.c:731
index 1454e1ac898396c90f801fdf72e8437348b459c6..deb65d2dcf2d2428cc92e710b682bc133a58d596 100644 (file)
@@ -11,7 +11,7 @@ msgstr ""
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
 "Content-Transfer-Encoding: 8bit\n"
 
 #: driver/demo-Gtk-conf.c:731
index 81a68fc0a70a687b75896a598fbfc2b0cdbe986a..b6eb530a7e33cebe0a1bde064ce42a0936f31ce9 100644 (file)
--- a/setup.com
+++ b/setup.com
@@ -6,6 +6,7 @@ $ mydir  = mydisk+f$directory()
 $ anemone      :== $'mydir'anemone
 $ ant          :== $'mydir'ant
 $ apollonian   :== $'mydir'apollonian
+$ apple2       :== $'mydir'apple2
 $ attraction   :== $'mydir'attraction
 $ barcode      :== $'mydir'barcode
 $ blaster      :== $'mydir'blaster
@@ -37,6 +38,7 @@ $ flag                :== $'mydir'flag
 $ flame                :== $'mydir'flame
 $ flow         :== $'mydir'flow
 $ fluidballs   :== $'mydir'fluidballs
+$ fontglide    :== $'mydir'fontglide
 $ forest       :== $'mydir'forest
 $ galaxy       :== $'mydir'galaxy
 $ goop         :== $'mydir'goop
@@ -77,6 +79,7 @@ $ petri               :== $'mydir'petri
 $ phosphor     :== $'mydir'phosphor
 $ piecewise    :== $'mydir'piecewise
 $ polyominoes  :== $'mydir'polyominoes
+$ pong         :== $'mydir'pong
 $ popsquares   :== $'mydir'popsquares
 $ pyro         :== $'mydir'pyro
 $ qix          :== $'mydir'qix
@@ -111,6 +114,7 @@ $ webcollage-helper :== $'mydir'webcollage-helper
 $ whirlwindwarp        :== $'mydir'whirlwindwarp
 $ whirlygig    :== $'mydir'whirlygig
 $ worm         :== $'mydir'worm
+$ xanalogtv    :== $'mydir'xanalogtv
 $ xflame       :== $'mydir'xflame
 $ xjack                :== $'mydir'xjack
 $ xlyap                :== $'mydir'xlyap
index 2c426be83e797329566c7304e2a819b68274c53b..0568d9b911557479c89c795d5fe4399e42efc1d0 100644 (file)
@@ -104,7 +104,7 @@ static void
 checkerboard (Screen *screen, Drawable drawable)
 {
   Display *dpy = DisplayOfScreen (screen);
-  int x, y;
+  unsigned int x, y;
   int size = 24;
   XColor fg, bg;
   XGCValues gcv;
@@ -180,7 +180,8 @@ hack_subproc_environment (Display *dpy)
    When grabbing desktop images, the Window will be unmapped first.
  */
 void
-load_random_image (Screen *screen, Window window, Drawable drawable)
+load_random_image (Screen *screen, Window window, Drawable drawable,
+                   char **name_ret)
 {
   Display *dpy = DisplayOfScreen (screen);
   char *grabber = get_string_resource ("desktopGrabber", "DesktopGrabber");
@@ -218,4 +219,24 @@ load_random_image (Screen *screen, Window window, Drawable drawable)
   system (cmd);
   free (cmd);
   XSync (dpy, True);
+
+  if (name_ret) 
+    {
+      Atom type;
+      int format;
+      unsigned long nitems, bytesafter;
+      char *name=NULL;
+
+      *name_ret = NULL;
+
+      if (XGetWindowProperty (dpy, window,
+                              XInternAtom (dpy, XA_XSCREENSAVER_IMAGE_FILENAME,
+                                           False),
+                              0, 1024, False, XA_STRING,
+                              &type, &format, &nitems, &bytesafter,
+                              (unsigned char **) &name)
+          == Success
+          && type != None)
+        *name_ret = strdup(name);
+    }
 }
index d9a520976c00b52234c01485427e77cab27bc5e1..f9ce753c7a2337746e64de145aca8b81033d8718 100644 (file)
@@ -223,7 +223,7 @@ use_subwindow_mode_p(Screen *screen, Window window)
 static void
 install_screen_colormaps (Screen *screen)
 {
-  int i;
+  unsigned int i;
   Display *dpy = DisplayOfScreen (screen);
   Window real_root;
   Window parent, *kids = 0;
@@ -260,7 +260,7 @@ install_screen_colormaps (Screen *screen)
 
 
 void
-grab_screen_image (Screen *screen, Window window)
+grab_screen_image_internal (Screen *screen, Window window)
 {
   Display *dpy = DisplayOfScreen (screen);
   XWindowAttributes xgwa;
index cf8a96ef00a316e10e84c0707716be2933cbf52f..3d9b16eb9e2cf92ed86e5c6ff3dddc2ae39a5b6e 100644 (file)
    desktop, or from the system's video input, depending on user
    preferences.
 
+   If it is from a file, then it will be returned in `filename_return'.
+   filename_return may be NULL; also, NULL may be returned (e.g., if
+   it's a screenshot or video capture.)
+
    Many colors may be allocated from the window's colormap.
  */
 extern void load_random_image (Screen *screen,
                                Window top_level_window,
-                               Drawable target_window_or_pixmap);
-
-
-/* Uh, don't call this. */
-extern void grab_screen_image (Screen *, Window);
-
+                               Drawable target_window_or_pixmap, 
+                               char **filename_return);
 
 /* Whether one should use GCSubwindowMode when drawing on this window
    (assuming a screen image has been grabbed onto it.)  Yes, this is a
@@ -49,4 +49,12 @@ extern Bool use_subwindow_mode_p(Screen *screen, Window window);
  */
 extern Bool top_level_window_p(Screen *screen, Window window);
 
+
+/* Don't call this: this is for the "xscreensaver-getimage" program only. */
+extern void grab_screen_image_internal (Screen *, Window);
+
+/* Don't use this: this is how "xscreensaver-getimage" and "grabclient.c"
+   pass the file name around. */
+#define XA_XSCREENSAVER_IMAGE_FILENAME "_SCREENSAVER_IMAGE_FILENAME"
+
 #endif /* __GRABSCREEN_H__ */
index 706675910e1818736d0b61218427fb98719dc0d4..6eec9a5f46b83339ea2f3b23809099991d8ecfad 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1992, 1996, 1997
+/* xscreensaver, Copyright (c) 1992, 1996, 1997, 2003
  *  Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
 # include <stdlib.h>
 #endif
 
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
 #if defined(VMS)
 # include <descrip.h>
 # include <stdio.h>
index ca230993859386685930c87d4c7469d64869ebf5..9efafdeffc4c6de97fd5762bb9da26af39f19605 100644 (file)
@@ -1,2 +1,2 @@
 static const char screensaver_id[] =
-       "@(#)xscreensaver 4.13 (07-Sep-2003), by Jamie Zawinski (jwz@jwz.org)";
+       "@(#)xscreensaver 4.14 (25-Oct-2003), by Jamie Zawinski (jwz@jwz.org)";
index 1bb616dcd327872832ecfce6654025d6f0d1840c..3b58b8aea49ac40fdfa28837e1dd13ea8ea85401 100644 (file)
@@ -1,25 +1,25 @@
 Begin3
 Title:          xscreensaver
-Version:        4.13
-Entered-date:   07SEP03
+Version:        4.14
+Entered-date:   25OCT03
 Description:    A modular screen saver and locker for the X Window System.
                 Highly customizable: allows the use of any program that
                 can draw on the root window as a display mode.
-                More than 160 display modes are included in this package.
+                More than 175 display modes are included in this package.
 Keywords:       screen saver, screen lock, lock, xlock, X11
 Author:         jwz@jwz.org (Jamie Zawinski)
 Maintained-by:  jwz@jwz.org (Jamie Zawinski)
 Primary-site:   http://www.jwz.org/xscreensaver/
-                 xscreensaver-4.13.tar.gz
-                65K   xscreensaver.README
+                3988K xscreensaver-4.14.tar.gz
+                66K   xscreensaver.README
                 1K    xscreensaver.lsm
 Alternate-site: sunsite.unc.edu /pub/Linux/X11/screensavers/
-                 xscreensaver-4.13.tar.gz
-                65K   xscreensaver.README
+                3988K xscreensaver-4.14.tar.gz
+                66K   xscreensaver.README
                 1K    xscreensaver.lsm
 Alternate-site: ftp.x.org /contrib/applications/
-                 xscreensaver-4.13.tar.gz
-                65K   xscreensaver.README
+                3988K xscreensaver-4.14.tar.gz
+                66K   xscreensaver.README
                 1K    xscreensaver.lsm
 Platforms:      Linux, Irix, SunOS, Solaris, HPUX, AIX, FreeBSD, NetBSD,
                 BSDI, SCO, OSF1, Ultrix, VMS.
index 552817dcfdb3f5bd956b8551f1a643ee87f8d563..a0b59c910e8d7c7182bd726eab47f99e73621736 100755 (executable)
@@ -27,7 +27,7 @@ Entered-date:   $DATE
 Description:    A modular screen saver and locker for the X Window System.
                 Highly customizable: allows the use of any program that
                 can draw on the root window as a display mode.
-                More than 160 display modes are included in this package.
+                More than 175 display modes are included in this package.
 Keywords:       screen saver, screen lock, lock, xlock, X11
 Author:         jwz@jwz.org (Jamie Zawinski)
 Maintained-by:  jwz@jwz.org (Jamie Zawinski)
index 5c53dd0f881a312d33b53aafc67c76c993c4aff9..d51bffad1e031cde878b2ba94c5a0caa980f539a 100644 (file)
@@ -1,5 +1,5 @@
 %define        name            xscreensaver
-%define        version         4.13
+%define        version         4.14
 %define        release         1
 %define        serial          1
 %define        x11_prefix      /usr/X11R6
@@ -34,7 +34,7 @@ Buildroot:    %{_tmppath}/%{name}-%{version}-root
 A modular screen saver and locker for the X Window System.
 Highly customizable: allows the use of any program that
 can draw on the root window as a display mode.
-More than 160 display modes are included in this package.
+More than 175 display modes are included in this package.
 %{?USE_GL:See also the xscreensaver-gl package, which}
 %{?USE_GL:includes optional OpenGL display modes.}
 
@@ -42,7 +42,7 @@ More than 160 display modes are included in this package.
 Un économiseur d'écran et verrouillage modulaire pour X-Window.
 Hautement configurable: permet l'utilisation de n'importe quel programme
 qui peut dessiner dans la fenêtre root.
-Plus de 160 modes d'affichage sont inclus dans ce paquet.
+Plus de 175 modes d'affichage sont inclus dans ce paquet.
 %{?USE_GL:Voir aussi le paquet xscreensaver-gl, qui inclut}
 %{?USE_GL:des modules optionnels OpenGL.}
 
@@ -189,7 +189,3 @@ if [ -d $RPM_BUILD_ROOT-gl ]; then rm -r $RPM_BUILD_ROOT-gl ; fi
 #
 %{?USE_GL:%files -f exes-gl gl}
 %{?USE_GL:%defattr(-,root,root)}
-
-%changelog
-* Wed Mar 05 2003 Eric Lassauge <lassauge@mail.dotcom.fr>
-- Updated for xscreensaver-4.08 with french translations